痞子衡嵌入式:揭秘i.MXRT600的ISP模式下用J-Link连接后PC总是停在0x1c04a的原因(Debug Mailbox)

事情缘起痞子衡的同事 - 喜欢打破砂锅问到底的Kerry小姐姐,她最近在研究i.MXRT600这款芯片,她发现在芯片ROM串行下载(ISP)模式下,连上芯片USB端口可以在设备管理器中正常看到枚举的HID设备(0x1fc9,0x0020),这个HID设备可配合上位机工具blhost.exe进行应用程序下载。但是当使用JLink正常连上芯片(选择的是MIMXRT685,不是CM33)后,之前的那个HID设备不见了,看起来芯片像是退出了ROM正常运行,这个体验跟i.MXRT1050上不太一样,这是为什么?这其实是Debug Mailbox在捣鬼,且听痞子衡细聊


  大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是i.MXRT600中的Debug Mailbox实现对JLink调试的影响。

  事情缘起痞子衡的同事 - 喜欢打破砂锅问到底的Kerry小姐姐,她最近在研究i.MXRT600这款芯片,她发现在芯片ROM串行下载(ISP)模式下,连上芯片USB端口可以在设备管理器中正常看到枚举的HID设备(0x1fc9,0x0020),这个HID设备可配合上位机工具blhost.exe进行应用程序下载。但是当使用JLink正常连上芯片(选择的是MIMXRT685,不是CM33)后,之前的那个HID设备不见了,看起来芯片像是退出了ROM正常运行,这个体验跟i.MXRT1050上不太一样,这是为什么?这其实是Debug Mailbox在捣鬼,且听痞子衡细聊:

一、引出调试问题

  按照我们之前在i.MXRT1050上的调试经验,将芯片设为串行下载模式后,使用JLink连接上芯片,并halt住内核,此时芯片PC是正常停在ROM区域的(0x200000之后),让我们同样的过程在i.MXRT600上也操作一次:

痞子衡嵌入式:揭秘i.MXRT600的ISP模式下用J-Link连接后PC总是停在0x1c04a的原因(Debug Mailbox)

  我们发现PC指向了0x1c04a,并且不管你如何reset再重新halt,它一直停在这个地方,更奇怪的是这个地方不在ROM区域(0x03000000或0x13000000之后)里,这是怎么回事?

二、什么是Debug Mailbox?

  与i.MXRT1050不同的是,i.MXRT600中引入了Debug Mailbox机制,这个机制由ROM负责实现,因此连接上JLink后的行为是由Debug Mailbox机制决定的。

  翻开i.MXRT600的User Manual,在Debug subsystem这一章节可以找到Debug Mailbox相关信息,Debug Mailbox其实最早是NXP LPC系列新推的一项功能,后来也用在了i.MXRT600上面。

  下图是i.MXRT600的SWD调试系统内部连接图,其中蓝框标出的DM AP便是Debug Mailbox。

痞子衡嵌入式:揭秘i.MXRT600的ISP模式下用J-Link连接后PC总是停在0x1c04a的原因(Debug Mailbox)

  我们知道i.MXRT600是基于Cortex-M33内核的MCU,这款ARM内核主打特点是安全,因此NXP在设计芯片时也加入了很多安全方面的特性,Debug Mailbox便是其一,Debug Mailbox基于NXP debug authentication protocol version 1.0,主要作用是实现外部调试器与芯片内ROM的通信,从而赋予调试器擦写Flash、进入ROM ISP、调试认证等功能。

三、ROM中Debug Mailbox实现

  那么ROM中的Debug Mailbox机制到底是什么?简单理解就是如下一段代码插入了ROM的初始化流程。这个机制其实很简单,就是确保debug特性是正常开启的,然后根据芯片复位类型来初始化debug port并决定要不要进入Mailbox命令处理。

// 确认IFR里debug特性正常开启
if (kStatus_DBG_Success != debug_auth_evaluate_dcfg_socu())
{
    __set_FAULTMASK(1);
    __WFI();
}
volatile uint32_t reset_status = RSTCTRL0->SYSRSTSTAT;
// 根据复位类型设置初始debug port状态
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内核是否发生了软复位(warm reset),正常芯片上电,这个位不会被置1,但是如果有调试器接入给内核发软复位,这个位就会被置位。一旦这个位被置起来,ROM初始化过程中便会进入Mailbox命令处理函数debug_mailbox_GetRequest(),不再往后执行正常的ROM串行下载/启动流程。

痞子衡嵌入式:揭秘i.MXRT600的ISP模式下用J-Link连接后PC总是停在0x1c04a的原因(Debug Mailbox)

  debug_mailbox_GetRequest()函数是Debug Mailbox机制的核心,它借助的是如下三个Mailbox寄存器来实现调试器与ROM的互动。

  • CSW:命令状态寄存器,调试器操作这个寄存器指示ROM进入mailbox命令解析状态
  • REQUEST:请求寄存器,调试器将mailbox命令写入这个寄存器指示ROM去执行
  • RETURN:结果寄存器,调试器通过读这个寄存器获取ROM执行mailbox命令结果

痞子衡嵌入式:揭秘i.MXRT600的ISP模式下用J-Link连接后PC总是停在0x1c04a的原因(Debug Mailbox)

  对于使用者来说,一般借助调试器先向CSW寄存器写入0x21申请re-sync同时reset device ,然后再按需写入如下具体的命令进REQUEST寄存器,便可实现Debug Mailbox相应功能。

#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机制原理,我们再来看JLink连接i.MXRT600时必须要加载执行的如下Script内容(开头痞子衡说了必须选择MIMXRT685,而不是CM33,因为在JLink DLL / JLinkDevices.xml里MIMXRT685才默认指定了配套Script脚本)。关于JLink Script知识,可以先看痞子衡之前文章 《JLink Script文件基础及其在IAR下调用方法》。

  这个脚本内容其实在i.MXRT600的User Manual中已经给出了相应伪代码,通过调用JLINK_CORESIGHT_WriteAP()来写Mailbox寄存器(index 0对应CSW,index 1对应REQUEST),基本是按照前面介绍的Debug Mailbox使用流程来的,最后通过写入START_DEBUG_SESSION命令进REQUEST寄存器开启芯片调试模式。

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);
}

五、芯片调试模式(REQUEST = 0x07)下的状态

  前面讲了JLink Script会使芯片进入调试模式,那调试模式下芯片到底是什么状态?ROM其实是通过如下函数加载执行了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调试的影响

  基于上面分析,最后痞子衡再总结一下Debug Mailbox对JLink调试的影响:

  1. 当芯片在ROM中执行(比如ISP模式,比如Flash中没有应用程序)时,JLink要想正常连接,必须加载使能芯片调试模式的Script才行,否则会连不上芯片。
  2. 通过加载执行JLink Script成功连接上芯片后,PC总是停在0x1c04a,这是Debug Mailbox机制决定的。
  3. 只有当芯片正常启动Flash里的应用程序后(即离开了ROM),用JLink连接芯片(选择CM33,不加载Script),halt住内核,PC指向的才是真实的应用程序位置。

  至此,i.MXRT600中的Debug Mailbox实现对JLink调试的影响痞子衡便介绍完毕了,掌声在哪里~~~


上一篇:VMware tools安装时 客户机操作系统已将 CD-ROM 门锁定,并且可能正在使用CD-ROM


下一篇:痞子衡嵌入式:飞思卡尔Kinetis系列MCU启动那些事(3)- KBOOT配置(FOPT/BOOT Pin/BCA)