基于ARM Cortex-M和Eclipse的SWO单总线输出
????????最近在MCU on Eclipse網(wǎng)站上看到Erich Styger所寫(xiě)的一篇有關(guān)通過(guò)SWD的跟蹤接口SWO獲取ARM Cortex-M相關(guān)信息的文章,文章結(jié)構(gòu)明晰,講解透徹,本人深受啟發(fā),特意將其翻譯過(guò)來(lái)供各位同仁參考。當(dāng)然限于個(gè)人水平,有不當(dāng)之處懇請(qǐng)指正。原文網(wǎng)址:https://mcuoneclipse.com/2016/10/17/tutorial-using-single-wire-output-swo-with-arm-cortex-m-and-eclipse/
?
????????作為一個(gè)標(biāo)準(zhǔn)過(guò)程,我將一些控制臺(tái)功能添加到我的嵌入式應(yīng)用程序中。這樣我就有了一個(gè)命令行接口,可以檢查和修改目標(biāo)系統(tǒng)。ARM Cortex-M的一個(gè)有趣的硬件特性是單線輸出(SWO) ,它允許通過(guò)一根線路將數(shù)據(jù)(例如字符串)發(fā)送到32個(gè)不同的激勵(lì)端口。
?
ARM調(diào)試器接頭上的SWO引腳
調(diào)試跟蹤輸出?SWO!
????????我使用普通的UART/SCI作為標(biāo)準(zhǔn)的文本和命令行與目標(biāo)板的接口。然而,在許多板上,UART被應(yīng)用程序使用。
有一個(gè)Semihosting(半托管),但是速度非常慢,而且取決于調(diào)試器和工具鏈/庫(kù),加上需要消耗FLASH和RAM,所以我不建議在任何情況下使用Semihosting。
【注】Semihosting是針對(duì)ARM目標(biāo)機(jī)的一種機(jī)制,它能夠根據(jù)應(yīng)用程序代碼的輸入/輸出請(qǐng)求,與運(yùn)行有調(diào)試功能的主機(jī)通訊。這種技術(shù)允許主機(jī)為通常沒(méi)有輸入和輸出功能的目標(biāo)硬件提供主機(jī)資源。
????????也有一個(gè)USB CDC,但這需要一個(gè)USB接口,一個(gè)USB棧和一個(gè)具有USB功能的微控制器。不適用于所有情況。
????????還有一個(gè)Segger RTT,它很小,速度快,最好的是不需要任何特殊的引腳。但只能工作于Segger的調(diào)試探測(cè)器。
ARM SWO
????????但還有另一件事:ARM SWO跟蹤端口是由ARM為Cortexm-M定義的。從技術(shù)上講,SWO是一個(gè)單一的跟蹤引腳,它被用來(lái)以從CPU核心時(shí)鐘中提取一個(gè)特定的時(shí)鐘頻率發(fā)送數(shù)據(jù)包。您可以將SWO看作是一種使用特殊格式發(fā)送數(shù)據(jù)包的UART TX引腳。可以使用多達(dá)32種包類型(或激勵(lì))。發(fā)送什么樣的數(shù)據(jù)取決于應(yīng)用程序,并且只需要很少的CPU處理或代碼。
????????常見(jiàn)SWO用法是:
- ?按字符串發(fā)送調(diào)試信息
- ?記錄中斷的進(jìn)入與退出
- ?記錄函數(shù)的進(jìn)入與退出
- ?周期性的PC值采樣
- ?事件提示
- ?變量或內(nèi)存單元修改超時(shí)
????????最常見(jiàn)的用法之一就是第一種:使用SWO以UART樣式打印來(lái)自目標(biāo)的調(diào)試消息。并且這也正是我再這篇文章中將要表達(dá)的。還有另一種編碼(曼徹斯特編碼),這里不做介紹。
ARM CoreSight
????????SWO是ARM CoreSight調(diào)試功能塊的一部分,而ARM CoreSight則通常是Cortex-M3,M4,M7的一部分:
?
CoreSight功能塊(來(lái)自:http://www.arm.com/files/pdf/AT_-_Advanced_Debug_of_Cortex-M_Systems.pdf)
如上圖所示,通過(guò)SWO(或SWV)ITM和DWT跟蹤消息可以發(fā)送。對(duì)于指令跟蹤需要4個(gè)額外的跟蹤引腳
SWO引腳
????????使用SWO的前提條件是這個(gè)引腳可以在調(diào)試頭中使用。這是我的TWR-K64F120M板的情況:
?
trace swo引腳(來(lái)自:TWR-K64F120M原理圖)
????????如上圖所示,SWO跟蹤引腳是與JTAG TDO引腳共享的。所以這就意味著SWO不能在JTAG中使用,而只能在SWD中使用。
所以仔細(xì)檢查你板子的原理圖確定他是否支持SWO。例如FRDM-K64F(是TWR-K64F120M上一個(gè)版本),它的SWO是沒(méi)有被引到調(diào)試頭的:
?
FRDM-K64F上沒(méi)有SWO
調(diào)試探針與SWO
????????為了能夠使用SWO,我需要一個(gè)能夠讀取SWO引腳的調(diào)試探測(cè)器。例如,Freescale/NXP OpenSDA在Freedom和Tower模塊板載調(diào)試接口硬件就不支持SWO。
????????然而,外部的Segger J-Link卻支持SWO引腳。下面我有一個(gè)J-Link EDU連接到TWR-K64F120M板的調(diào)試和跟蹤端口:
?
j-link edu連接到跟蹤端口
通過(guò)SWO引腳發(fā)送調(diào)試信息的源碼
????????為了通過(guò)SWO寫(xiě)調(diào)試消息到主機(jī),需要一小段代碼。在Github上有一個(gè)可用的完整代碼的例子項(xiàng)目:
https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/KDS/TWR-K64F120M/TWR-K64F120M_Demo/Sources
????????外部工具(比如Segger RTT查看器)可以在硬件中設(shè)置SWO。我的建議是從應(yīng)用程序初始化它。因?yàn)?/span>SWO跟蹤輸出時(shí)鐘是從CPU時(shí)鐘派生而來(lái)的,所以初始化函數(shù)需要這個(gè)時(shí)鐘加上SWO端口號(hào)來(lái)初始化。下面是我用來(lái)初始化SWO輸出的代碼,默認(rèn)為64k波特率:
/*!
?* \brief Initialize the SWO trace port for debug message printing
?* \param portBits Port bit mask to be configured
?* \param cpuCoreFreqHz CPU core clock frequency in Hz
?*/
void SWO_Init(uint32_t portBits, uint32_t cpuCoreFreqHz) {
??uint32_t SWOSpeed = 64000; /* default 64k baud rate */
??uint32_t SWOPrescaler = (cpuCoreFreqHz / SWOSpeed) - 1; /* SWOSpeed in Hz, note that cpuCoreFreqHz is expected to be match the CPU core clock */
?
??CoreDebug->DEMCR = CoreDebug_DEMCR_TRCENA_Msk; /* enable trace in core debug */
??*((volatile unsigned *)(ITM_BASE + 0x400F0)) = 0x00000002; /* "Selected PIN Protocol Register": Select which protocol to use for trace output (2: SWO NRZ, 1: SWO Manchester encoding) */
??*((volatile unsigned *)(ITM_BASE + 0x40010)) = SWOPrescaler; /* "Async Clock Prescaler Register". Scale the baud rate of the asynchronous output */
??*((volatile unsigned *)(ITM_BASE + 0x00FB0)) = 0xC5ACCE55; /* ITM Lock Access Register, C5ACCE55 enables more write access to Control Register 0xE00 :: 0xFFC */
??ITM->TCR = ITM_TCR_TraceBusID_Msk | ITM_TCR_SWOENA_Msk | ITM_TCR_SYNCENA_Msk | ITM_TCR_ITMENA_Msk; /* ITM Trace Control Register */
??ITM->TPR = ITM_TPR_PRIVMASK_Msk; /* ITM Trace Privilege Register */
??ITM->TER = portBits; /* ITM Trace Enable Register. Enabled tracing on stimulus ports. One bit per stimulus port. */
??*((volatile unsigned *)(ITM_BASE + 0x01000)) = 0x400003FE; /* DWT_CTRL */
??*((volatile unsigned *)(ITM_BASE + 0x40304)) = 0x00000100; /* Formatter and Flush Control Register */
}
????????在我的主應(yīng)用程序中,我像這樣初始化它(對(duì)于一個(gè)有24MHz核心時(shí)鐘的系統(tǒng)):
#define CPU_CORE_FREQUENCY_HZ 120000000 /* CPU core frequency in Hz */
?SWO_Init(0x1, CPU_CORE_FREQUENCY_HZ);
在SWO_PrintChar()函數(shù)中完成打印:
/*!
?* \brief Sends a character over the SWO channel
?* \param c Character to be sent
?* \param portNo SWO channel number, value in the range of 0 to 31
?*/
void SWO_PrintChar(char c, uint8_t portNo) {
??volatile int timeout;
?
??/* Check if Trace Control Register (ITM->TCR at 0xE0000E80) is set */
??if ((ITM->TCR&ITM_TCR_ITMENA_Msk) == 0) { /* check Trace Control Register if ITM trace is enabled*/
????return; /* not enabled? */
??}
??/* Check if the requested channel stimulus port (ITM->TER at 0xE0000E00) is enabled */
??if ((ITM->TER & (1ul<<portNo))==0) { /* check Trace Enable Register if requested port is enabled */
????return; /* requested port not enabled? */
??}
??timeout = 5000; /* arbitrary timeout value */
??while (ITM->PORT[0].u32 == 0) {
????/* Wait until STIMx is ready, then send data */
????timeout--;
????if (timeout==0) {
??????return; /* not able to send */
????}
??}
??ITM->PORT[0].u16 = 0x08 | (c<<8);
}
????????上面的代碼使用了一個(gè)非常簡(jiǎn)單的超時(shí)機(jī)制:重要的一點(diǎn)是,如果SWO沒(méi)有啟用或者SWO端口沒(méi)有準(zhǔn)備好,那么應(yīng)用程序就會(huì)被阻塞。
????????為了更方便地打印字符串,我使用以下函數(shù):
/*!
?* \brief Sends a string over SWO to the host
?* \param s String to send
?* \param portNumber Port number, 0-31, use 0 for normal debug strings
?*/
void SWO_PrintString(const char *s, uint8_t portNumber) {
??while (*s!='\0') {
????SWO_PrintChar(*s++, portNumber);
??}
}
????????要通過(guò)SWO發(fā)送一個(gè)“hello”,它就像這樣簡(jiǎn)單:
SWO_PrintString("hello world with SWO\r\n", 0);
????????第一個(gè)參數(shù)是要發(fā)送的字符串,第二個(gè)是SWO跟蹤通道號(hào)。
GNU ARM Eclipse查看器
????????要接收主機(jī)上的SWO跟蹤輸出,GNU ARM Eclipse插件內(nèi)置了Segger J-Link探測(cè)器的SWO支持。
????????SWO僅支持SWD(單線調(diào)試)模式,不支持JTAG模式。所以確保SWD被選為調(diào)試協(xié)議:
?
SWD調(diào)試
????????在GNU ARM Eclipse調(diào)試配置中,啟用SWO并指定CPU頻率和SWO頻率(請(qǐng)參見(jiàn)http://gnuarmeclipse.github.io/debug/jlink/上的有關(guān)頻率的文檔)。我必須提供CPU頻率(在我的情況下為120 MHz),并且可以將SWO頻率設(shè)置為0,以便J-Link自動(dòng)確定速度)。在端口掩碼中指定使用的端口(作為位掩碼),因此0x1用于使用端口0:
?
SWO的設(shè)置
????????這樣,在目標(biāo)板上運(yùn)行應(yīng)用程序就會(huì)在Eclipse控制臺(tái)視圖中顯示輸出:
?
Eclipse控制臺(tái)視圖
Segger SWO Viewer
????????Segger有一個(gè)特殊的SWO Viewer(命令行和GUI版本)。
????????在GUI版本中,我指定使用的設(shè)備,它可以感測(cè)跟蹤時(shí)鐘:
?
segger gui swo viewer
????????在查看器中,我可以打開(kāi)/關(guān)閉端口,并查看收到的數(shù)據(jù):
?
segger j-link swo viewer
Telnet: Putty
????????但是,沒(méi)有需要查看SWO數(shù)據(jù)的花哨的查看器或Eclipse。Segger默認(rèn)使用端口2332:
?
SEGGER SWO端口
????????我可以配置任何telnet客戶端(例如PuTTY)在端口2332上打開(kāi)會(huì)話:
?
putty telnet會(huì)話設(shè)置
????????我在PuTTY中得到的輸出:
?
SWO在PuTTY中的輸出
綜述
????????ARM SWO跟蹤引腳允許向主機(jī)發(fā)送跟蹤消息。一個(gè)常見(jiàn)的用法是向主機(jī)發(fā)送調(diào)試或其他消息。SWO只需要一個(gè)引腳,僅適用于SWD(而不是JTAG),并且在目標(biāo)上需要很少的代碼和資源。不幸的是,許多電路板沒(méi)有將SWO跟蹤引腳路由到調(diào)試頭,因此如果您正在進(jìn)行自己的設(shè)計(jì),則至少應(yīng)考慮將SWO路由到調(diào)試頭。
????????雖然SWO跟蹤輸出很大,但是限于高端Cortex-M,我沒(méi)有在Cortex-M0(+)中找到它,它只是輸出,需要一個(gè)支持它的調(diào)試探針/接口。至少與Eclipse和GNU ARM Eclipse插件結(jié)合Segger J-Link探針SWO輸出對(duì)我來(lái)說(shuō)非常棒。
????????另一方面,Segger RTT的功能更加多樣化,也非常快。它適用于所有ARM Cortex,最重要的是不需要額外的引腳。然而,它在目標(biāo)系統(tǒng)上需要更多的開(kāi)銷和RAM資源。此外,它允許發(fā)送和接收數(shù)據(jù)。所以對(duì)于串行調(diào)試消息打印,Segger RTT對(duì)我來(lái)說(shuō)聽(tīng)起來(lái)更好的解決方案。
????????接下來(lái)就是快樂(lè)SWO中了!
相關(guān)鏈接
- ?SWO on Kinetis:https://community.nxp.com/thread/318058
- ?SWO與GNU ARM Eclipse插件:http : //gnuarmeclipse.github.io/debug/jlink/
- ?Segger SWO Viewer:https : //www.segger.com/j-link-swo-viewer.html
- ?GNU ARM Eclipse插件:http : //gnuarmeclipse.github.io/debug/
- ?Eclipse RxTx插件,用于解析SWO數(shù)據(jù):http : //forum.segger.com/index.php? page=Thread&threadID=1010
- ?ARM CoreSight概述:http : //www.arm.com/files/pdf/AT_-_Advanced_Debug_of_Cortex-M_Systems.pdf
- ?GitHub上的示例代碼:https://github.com/ErichStyger/mcuoneclipse/tree/master/Examples/KDS/TWR-K64F120M/TWR-K64F120M_Demo/Sources
歡迎關(guān)注:
總結(jié)
以上是生活随笔為你收集整理的基于ARM Cortex-M和Eclipse的SWO单总线输出的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Golang 词法分析器浅析
- 下一篇: Modbus协议栈开发笔记之七:Modb