大家好,我是痞子衡,是正經(jīng)搞技術(shù)的痞子。今天痞子衡給大家介紹的是 i.MXRT600 中的 Debug Mailbox 實(shí)現(xiàn)對 JLink 調(diào)試的影響。
事情緣起痞子衡的同事 - 喜歡打破砂鍋問到底的 Kerry 小姐姐,她最近在研究 i.MXRT600 這款芯片,她發(fā)現(xiàn)在芯片 ROM 串行下載(ISP)模式下,連上芯片 USB 端口可以在設(shè)備管理器中正??吹矫杜e的 HID 設(shè)備(0x1fc9,0x0020),這個 HID 設(shè)備可配合上位機(jī)工具 blhost.exe 進(jìn)行應(yīng)用程序下載。但是當(dāng)使用 JLink 正常連上芯片(選擇的是 MIMXRT685,不是 CM33)后,之前的那個 HID 設(shè)備不見了,看起來芯片像是退出了 ROM 正常運(yùn)行,這個體驗(yàn)跟 i.MXRT1050 上不太一樣,這是為什么?這其實(shí)是 Debug Mailbox 在搗鬼,且聽痞子衡細(xì)聊:
?
一、引出調(diào)試問題
按照我們之前在 i.MXRT1050 上的調(diào)試經(jīng)驗(yàn),將芯片設(shè)為串行下載模式后,使用 JLink 連接上芯片,并 halt 住內(nèi)核,此時芯片 PC 是正常停在 ROM 區(qū)域的(0x200000 之后),讓我們同樣的過程在 i.MXRT600 上也操作一次:
我們發(fā)現(xiàn) PC 指向了 0x1c04a,并且不管你如何 reset 再重新 halt,它一直停在這個地方,更奇怪的是這個地方不在 ROM 區(qū)域(0x03000000 或 0x13000000 之后)里,這是怎么回事?
?
二、什么是 Debug Mailbox?
與 i.MXRT1050 不同的是,i.MXRT600 中引入了 Debug Mailbox 機(jī)制,這個機(jī)制由 ROM 負(fù)責(zé)實(shí)現(xiàn),因此連接上 JLink 后的行為是由 Debug Mailbox 機(jī)制決定的。
翻開 i.MXRT600 的 User Manual,在 Debug subsystem 這一章節(jié)可以找到 Debug Mailbox 相關(guān)信息,Debug Mailbox 其實(shí)最早是 NXP LPC 系列新推的一項(xiàng)功能,后來也用在了 i.MXRT600 上面。
下圖是 i.MXRT600 的 SWD 調(diào)試系統(tǒng)內(nèi)部連接圖,其中藍(lán)框標(biāo)出的 DM AP 便是 Debug Mailbox。
我們知道 i.MXRT600 是基于 Cortex-M33 內(nèi)核的 MCU,這款 ARM 內(nèi)核主打特點(diǎn)是安全,因此 NXP 在設(shè)計(jì)芯片時也加入了很多安全方面的特性,Debug Mailbox 便是其一,Debug Mailbox 基于 NXP debug authentication protocol version 1.0,主要作用是實(shí)現(xiàn)外部調(diào)試器與芯片內(nèi) ROM 的通信,從而賦予調(diào)試器擦寫 Flash、進(jìn)入 ROM ISP、調(diào)試認(rèn)證等功能。
?
三、ROM 中 Debug Mailbox 實(shí)現(xiàn)
那么 ROM 中的 Debug Mailbox 機(jī)制到底是什么?簡單理解就是如下一段代碼插入了 ROM 的初始化流程。這個機(jī)制其實(shí)很簡單,就是確保 debug 特性是正常開啟的,然后根據(jù)芯片復(fù)位類型來初始化 debug port 并決定要不要進(jìn)入 Mailbox 命令處理。
//?確認(rèn) IFR 里 debug 特性正常開啟
if?(kStatus_DBG_Success?!=?debug_auth_evaluate_dcfg_socu())
{
????__set_FAULTMASK(1);
????__WFI();
}
volatile?uint32_t?reset_status?=?RSTCTRL0->SYSRSTSTAT;
//?根據(jù)復(fù)位類型設(shè)置初始 debug?port 狀態(tài)
if?(kStatus_DBG_Success?!=?debug_auth_hal_set_initial_debug_port_state(reset_status))
{
????__set_FAULTMASK(1);
????__WFI();
}
if?(reset_status?&?0x20)
{
????//?處理 mailbox 收到的來自 debugger 的命令
????debug_mailbox_GetRequest();
}
RSTCTRL0->SYSRSTSTAT[5]位即 ARM_APD_RESET,表明 ARM 內(nèi)核是否發(fā)生了軟復(fù)位(warm reset),正常芯片上電,這個位不會被置 1,但是如果有調(diào)試器接入給內(nèi)核發(fā)軟復(fù)位,這個位就會被置位。一旦這個位被置起來,ROM 初始化過程中便會進(jìn)入 Mailbox 命令處理函數(shù) debug_mailbox_GetRequest(),不再往后執(zhí)行正常的 ROM 串行下載 / 啟動流程。
debug_mailbox_GetRequest()函數(shù)是 Debug Mailbox 機(jī)制的核心,它借助的是如下三個 Mailbox 寄存器來實(shí)現(xiàn)調(diào)試器與 ROM 的互動。
- CSW:命令狀態(tài)寄存器,調(diào)試器操作這個寄存器指示 ROM 進(jìn)入 mailbox 命令解析狀態(tài) REQUEST:請求寄存器,調(diào)試器將 mailbox 命令寫入這個寄存器指示 ROM 去執(zhí)行 RETURN:結(jié)果寄存器,調(diào)試器通過讀這個寄存器獲取 ROM 執(zhí)行 mailbox 命令結(jié)果
對于使用者來說,一般借助調(diào)試器先向 CSW 寄存器寫入 0x21 申請 re-sync 同時 reset device ,然后再按需寫入如下具體的命令進(jìn) REQUEST 寄存器,便可實(shí)現(xiàn) Debug Mailbox 相應(yīng)功能。
#define?ENTER_DEBUGGER_MAILBOX?(0x0001)??//?Start?Mailbox?debug
#define?GET_CRP_LEVEL??????????(0x0002)??//?Deprecated?and?retuen?3
#define?DM_ERASE_FLASH?????????(0x0003)??//?Mass?erase?flash
#define?EXIT_DEBUGGER_MAILBOX??(0x0004)??//?Exit?Mailbox?debug
#define?ENTER_ISP_MODE?????????(0x0005)??//?Enter?specified?ISP?mode
#define?SET_FA_MODE????????????(0x0006)??//?Set?to?"Fault?Analysis"?mode
#define?START_DEBUG_SESSION????(0x0007)??//?Start?Debug?session
#define?DEBUG_AUTH_START???????(0x0010)??//?Start?Debug?Authentication?Protocol
#define?DEBUG_AUTH_RESP????????(0x0011)??//?Debug?Authentication?response
?
四、激活 Debug Mailbox 的 JLink Script
了解了 Debug Mailbox 機(jī)制原理,我們再來看 JLink 連接 i.MXRT600 時必須要加載執(zhí)行的如下 Script 內(nèi)容(開頭痞子衡說了必須選擇 MIMXRT685,而不是 CM33,因?yàn)樵?JLink DLL / JLinkDevices.xml 里 MIMXRT685 才默認(rèn)指定了配套 Script 腳本)。關(guān)于 JLink Script 知識,可以先看痞子衡之前文章 《JLink Script 文件基礎(chǔ)及其在 IAR 下調(diào)用方法》。
這個腳本內(nèi)容其實(shí)在 i.MXRT600 的 User Manual 中已經(jīng)給出了相應(yīng)偽代碼,通過調(diào)用 JLINK_CORESIGHT_WriteAP()來寫 Mailbox 寄存器(index 0 對應(yīng) CSW,index 1 對應(yīng) REQUEST),基本是按照前面介紹的 Debug Mailbox 使用流程來的,最后通過寫入 START_DEBUG_SESSION 命令進(jìn) REQUEST 寄存器開啟芯片調(diào)試模式。
void?InitTarget(void)?{
??int?v;
??JLINK_CORESIGHT_Configure("IRPre=0;DRPre=0;IRPost=0;DRPost=0;IRLenDevice=4");
??//?Pre-select?that?we?have?a?Cortex-M33?connected
??CPU?=?CORTEX_M33;
??//?J-Link?is?allowed?to?use?a?TAP?reset?for?JTAG-chain?auto-detection
??JTAG_AllowTAPReset?=?0;
??JTAG_SetDeviceId(0,?0x6BA02477);
??//?Read?AP?ID?register?to?identify?DM?AP?at?index?2
??JLINK_CORESIGHT_WriteDP(2,?0x020000f0);
??v?=?JLINK_CORESIGHT_ReadAP(3);
??JLINK_SYS_Report1("DAP-IDCODE:",?v);
??//?Select?DM?AP?index?2
??JLINK_CORESIGHT_WriteDP(2,?0x02000000);
??JLINK_CORESIGHT_ReadDP(0);
??//?Active?DebugMailbox
??JLINK_CORESIGHT_WriteAP(0,?0x21);
??JLINK_CORESIGHT_ReadAP(0);
??//?Enter?Debug?Session
??JLINK_CORESIGHT_WriteAP(1,?0x07);
??JLINK_CORESIGHT_ReadAP(0);
}
?
五、芯片調(diào)試模式(REQUEST = 0x07)下的狀態(tài)
前面講了 JLink Script 會使芯片進(jìn)入調(diào)試模式,那調(diào)試模式下芯片到底是什么狀態(tài)?ROM 其實(shí)是通過如下函數(shù)加載執(zhí)行了 0x1c040 - 0x1c04B 處的一小段代碼,并最終停在了 0x1c04a 處的 while(1);,至此真相大白。
void?go_debug_mode(void)
{
#define?VECTOR_DUMMY_ROUTINE?0x0001c000u
#define?APP_ENTRY?(VECTOR_DUMMY_ROUTINE?+?0x40?+?1)
????uint32_t?dummy_loop_routines[]?=?{
????????VECTOR_DUMMY_ROUTINE?+?0x1000,?//?SP
????????APP_ENTRY,?????????????????????//?Reset?Handler
????????APP_ENTRY,?????????????????????//?NMI?Handler
????????APP_ENTRY,?????????????????????//?HardFault_Handler
????????APP_ENTRY,?????????????????????//?MemManage_Handler
????????APP_ENTRY,?????????????????????//?BusFault_Handler
????????APP_ENTRY,?????????????????????//?UsageFault_Handler
????????APP_ENTRY,?????????????????????//?SecureFault_Handler
????????0,?????????????????????????????//?Reserved
????????0,?????????????????????????????//?Reserved
????????0,?????????????????????????????//?Reserved
????????APP_ENTRY,?????????????????????//?SVC_Handler
????????APP_ENTRY,?????????????????????//?DebugMon_Handler
????????0,?????????????????????????????//?Reserved
????????APP_ENTRY,?????????????????????//?PendSV_Handler
????????APP_ENTRY,?????????????????????//?SysTick_Handler
????????//?Below?are?the?binary?codes?for?:?
????????//???register?uint32_t?dummy?=?SCB->CPUID;?
????????//???while(1);
????????0x5000f64eu,
????????0x0000f2ceu,
????????0xe7fe6801u,
????};
????{
????????uint32_t?*dest?=?(uint32_t?*)VECTOR_DUMMY_ROUTINE;
????????uint32_t?*src?=?(uint32_t?*)&dummy_loop_routines[0];
????????for?(uint32_t?i?=?0u;?i?<?ARRAY_SIZE(dummy_loop_routines);?i++)
????????{
????????????*dest++?=?*src++;
????????}
????????jump_to_boot_image(VECTOR_DUMMY_ROUTINE);
????}
}
?
六、Debug Mailbox 對 JLink 調(diào)試的影響
基于上面分析,最后痞子衡再總結(jié)一下 Debug Mailbox 對 JLink 調(diào)試的影響:
- 當(dāng)芯片在 ROM 中執(zhí)行(比如 ISP 模式,比如 Flash 中沒有應(yīng)用程序)時,JLink 要想正常連接,必須加載使能芯片調(diào)試模式的 Script 才行,否則會連不上芯片。通過加載執(zhí)行 JLink Script 成功連接上芯片后,PC 總是停在 0x1c04a,這是 Debug Mailbox 機(jī)制決定的。只有當(dāng)芯片正常啟動 Flash 里的應(yīng)用程序后(即離開了 ROM),用 JLink 連接芯片(選擇 CM33,不加載 Script),halt 住內(nèi)核,PC 指向的才是真實(shí)的應(yīng)用程序位置。
至此,i.MXRT600 中的 Debug Mailbox 實(shí)現(xiàn)對 JLink 調(diào)試的影響痞子衡便介紹完畢了,掌聲在哪里~~~