痞子衡嵌入式:快速定位i.MXRT600板级设计ISP[2:0]启动模式引脚上电时序问题的方法
大家好,我是痞子衡,是正经搞技术的痞子。今天痞子衡给大家介绍的是一种快速定位i.MXRT600板级设计ISP[2-0]启动模式引脚上电时序问题的方法。
我们知道恩智浦i.MXRT600是主打音频市场的MCU产品,其也是i.MXRT三位数平台的第一款型号。这颗MCU已被众多知名国际大厂客户选用,在项目中作为协处理器负责音频相关子功能。不少客户都已经到了量产阶段,最近痞子衡在支持其中一个量产客户,客户遇到了同一批次某几个板卡无法正常启动的问题。痞子衡和同事一起排查,最终发现是ISP[2:0]启动引脚电平上电时序问题。这其实是个典型问题,痞子衡今天教你一招快速定位此类问题的方法:
一、引出上电时序问题
我们先来看看客户的问题,下面是客户板卡简图,i.MXRT600是负责音频功能的协处理器,它的启动引脚ISP[2:0]与主应用处理器(AP)连接了起来(客户项目设计里,AP并不负责控制i.MXRT600的启动模式),为了防止对i.MXRT600上电ISP引脚采样有影响,客户还特地在中间加了一层反向隔离电路。
- 注:其实i.MXRT600支持Serial Boot模式,这种模式下i.MXRT600作为协处理器,其应用程序数据可直接由AP通过指定的UART/I2C/SPI/USB下载进i.MXRT600的内部RAM运行,能省去一颗外部Flash。
客户量产过程中,同一批次几百块板卡,有一两块板卡上i.MXRT600无法启动。客户做了少量ABA实验:将无法启动板卡上的i.MXRT600芯片吹下来,换到能正常启动板卡上,依然无法启动。反过来,能正常启动板卡上的i.MXRT600芯片换到无法正常启动的板卡上,这块板卡就能正常启动了。
从上面ABA实验来看,似乎不是板级设计问题,像是出问题的板卡上i.MXRT600芯片自身问题。痞子衡拿到一块出问题的板子,上电后测量了ISP[2:0]引脚电平,其值是3'b011 - FlexSPI Boot from Port A,上电稳定后ISP设置是没问题的,但刚上电时i.MXRT600 BootROM到底采样到的是什么ISP电平值没人知道。
下面是客户板卡上ISP部分反向隔离设计,为了验证是ISP采样时机问题,我们特意对电路进行改造将RT600_BOOT0和RT600_BOOT2分别强行拉高和拉低,然后给板卡重新上电,终于板卡能正常启动了。
所以我们可以得出初步结论,对于i.MXRT600从上电到BootROM进行ISP采样,这段时间不是一个严格固定值,因芯片制造差异,这个时间应该是在一定范围内,板级供电设计时上电时间应留有足够余量。客户这个项目里上电时间余量留得不足,导致无法满足个别i.MXRT600芯片ISP采样时间要求。
二、BootROM中对于启动模式的处理
在介绍快速定位ISP采样时机问题方法前,痞子衡先带大家了解下i.MXRT600 BootROM中关于启动模式的处理流程。
咱们先回顾下痞子衡的旧文 《Boot配置(ISP_Pin/OTP)》,每次i.MXRT600芯片硬复位,OTP中的部分关于系统配置的值会被自动加载到OCOTP模块相应Shadow Register里(关于OCOTP外设基础知识可参考 《OTP及其烧写方法》),BootROM中主要用如下 get_runtime_boot_device_info() 函数来获取最终启动模式,并将其存在全局变量 s_bootDeviceInfo 中。
- 注:代码中 OCOTP->OTP_SHADOW[0x60] 寄存器低四位即芯片参考手册里提及的 PRIMARY_BOOT_SRC[3:0]
static boot_device_info_t s_bootDeviceInfo;
void get_runtime_boot_device_info(void)
{
// 从OTP Shadow Register获取启动模式
uint32_t bootSrc = OCOTP->OTP_SHADOW[0x60] & 0x0f;
boot_device_info_t bootDeviceInfo = { 0 };
if (bootSrc == 0) // Isp Pin
{
// 从ISP[2:0]引脚获取启动模式
bootSrc = get_bootpin_mode();
// 对 bootDeviceInfo 成员进一步赋值
}
else
{
// 对 bootDeviceInfo 成员进一步赋值
}
s_bootDeviceInfo = bootDeviceInfo;
}
每一次i.MXRT600系统软复位去重新执行BootROM时,ISP[2:0]引脚状态都会被重新采样,这完全是软件采样。ROM中ISP采样功能函数如下面 get_bootpin_mode() 所示,代码中做了IO电平去抖处理:
#define BOOT_PIN_DEBOUNCE_READ_COUNT 500
uint32_t get_bootpin_mode(void)
{
uint32_t bootSrc = 0;
// 使能GPIO外设
CLOCK_EnableClock(kCLOCK_HsGpio1);
RESET_PeripheralReset(kHSGPIO1_RST_SHIFT_RSTn);
// 设置ISP引脚为GPIO模式,并使能上拉.
IOPCTL->PIO[1][15] = IOPCTL_PIO_FSEL(0) | IOPCTL_PIO_PUPDENA(1) | IOPCTL_PIO_PUPDSEL(1) | IOPCTL_PIO_IBENA(1);
IOPCTL->PIO[1][16] = IOPCTL_PIO_FSEL(0) | IOPCTL_PIO_PUPDENA(1) | IOPCTL_PIO_PUPDSEL(1) | IOPCTL_PIO_IBENA(1);
IOPCTL->PIO[1][17] = IOPCTL_PIO_FSEL(0) | IOPCTL_PIO_PUPDENA(1) | IOPCTL_PIO_PUPDSEL(1) | IOPCTL_PIO_IBENA(1);
// 配置ISP引脚GPIO属性为数字输入模式.
GPIO->DIR[1] &= ~((1U << 15) | (1U << 16) | (1U << 17));
// Note1: 管脚默认上拉是disable的,ROM需要使能管脚上拉.
// Note2: 从使能管脚上拉到读取管脚值之间应间隔10us以上.
sw_delay_us(10);
// 采样ISP引脚值
for (uint32_t pin = 17; pin >= 15; pin--)
{
uint32_t readCount = 0;
for (uint32_t i = 0; i < BOOT_PIN_DEBOUNCE_READ_COUNT; i++)
{
readCount += GPIO->B[1][pin] & 0x1;
}
if (readCount >= BOOT_PIN_DEBOUNCE_READ_COUNT / 2)
{
bootSrc |= 0x1;
}
if (pin != 15)
{
bootSrc <<= 1;
}
}
return bootSrc;
}
上面就是BootROM中关于启动模式的处理代码。那么有没有方法挂上调试器通过查后门方式直接读取到 s_bootDeviceInfo 变量值呢?很抱歉,不可以!即使痞子衡也做不到,虽然痞子衡能通过查BootROM map文件得知这个变量放在 0x10012d38 地址处。但是i.MXRT600 BootROM中集成了 《Debug Mailbox机制》,我们无法通过调试器读取正常运行后的ROM状态,这条路行不通。
三、快速定位ISP[2:0]电平采样问题
第一小节介绍的客户项目启动问题,其实还算比较好定位,因为有ABA实验在先,基本可以明确问题就出在ISP采样时机上。但更多时候,在客户项目研发阶段,没有ABA实验的条件,可能仅有一块板卡,并且Flash配置以及App里启动头是否正确都尚待验证。这种情况下,我们就需要一种快速甄别是否是ISP采样时机因素导致的启动问题。
当芯片无法启动时,我们第一想法肯定是要得知第二小节介绍里 s_bootDeviceInfo 到底是什么值,这样我们就能反推ISP引脚到底在ROM里是什么采样值,但这条路行不通。换个角度想,我们能不能不让BootROM去采样ISP引脚,换一种替代方式来决定启动模式,如果这种情况下能按替代设置去启动,就也可以反证ISP引脚采样时机有问题。
这种启动模式替代设置方法就是痞子衡今天要教大家的方法,利用调试器临时改写OCOTP->OTP_SHADOW[0x60]值(对的,我们可以不烧写OTP),改写完成后对芯片做一次软复位即可。OCOTP->OTP_SHADOW寄存器组仅芯片硬件复位或者执行OCOTP更新命令其值才会被重新加载,软复位不会影响其值。
四、在MIMXRT685-EVK上做一次实验
让我们在MIMXRT685-EVK上做一次实验,这块板卡上Flash连到了FlexSPI0 Port B上,我们随意下载一个SDK XIP工程,并将ISP引脚拨码设为3'b010 - FlexSPI Boot from Port B,复位后芯片能正常启动,工程运行正常。
现在我们将ISP引脚拨码设为3'b110 - Serial ISP,软复位后芯片进入了ISP下载模式,不会从Flash启动。查芯片头文件得知 OCOTP->OTP_SHADOW[0x60] 寄存器地址是 0x50130180,我们现在尝试改写这个寄存器。
/** OCOTP - Register Layout Typedef */
typedef struct {
__IO uint32_t OTP_SHADOW[496]; /**< OTP shadow register N, array offset: 0x0, array step: 0x4 */
uint8_t RESERVED_0[64];
__IO uint32_t OTP_CTRL; /**< Control/address register, offset: 0x800 */
// 省略...
} OCOTP_Type;
/** Peripheral OCOTP base address */
#define OCOTP_BASE (0x50130000u)
/** Peripheral OCOTP base address */
#define OCOTP_BASE_NS (0x40130000u)
/** Peripheral OCOTP base pointer */
#define OCOTP ((OCOTP_Type *)OCOTP_BASE)
/** Peripheral OCOTP base pointer */
#define OCOTP_NS ((OCOTP_Type *)OCOTP_BASE_NS)
挂上J-Link调试器,打开J-Link Commander,连接芯片,注意选择"MIMXRT685_M33",然后使用w4命令在0x50130180地址处写入0x00000005(在PRIMARY_BOOT_SRC[3:0]定义里,4'b0101是QSPI_B_BOOT)。
继续执行reset和go命令(或者按一下板载软复位按钮RESET BUTTON SW3),这时候你可以看到芯片从Flash正常启动了,SDK XIP工程又运行起来了,显然这时候ISP引脚电平设置被忽略了,因此我们找到了有效的启动模式替代设置方法。
最后有一点要注意,在使用J-Link软复位时,如果看到如下log,得检查下配套JLinkScript里的ResetTarget()函数具体功能,要确保内核真的被复位了。
至此,快速定位i.MXRT600板级设计ISP[2-0]启动模式引脚上电时序问题的方法痞子衡便介绍完毕了,掌声在哪里~~~
欢迎订阅
文章会同时发布到我的 博客园主页、CSDN主页、知乎主页、微信公众号 平台上。
微信搜索"痞子衡嵌入式"或者扫描下面二维码,就可以在手机上第一时间看了哦。
最后欢迎关注痞子衡个人微信公众号【痞子衡嵌入式】,一个专注嵌入式技术的公众号,跟着痞子衡一起玩转嵌入式。
衡杰(痞子衡),目前就职于某全球顶级半导体原厂MCU系统部门,担任高级嵌入式系统应用工程师。
专栏内所有文章的转载请注明出处:http://www.cnblogs.com/henjay724/
与痞子衡进一步交流或咨询业务合作请发邮件至 hengjie1989@foxmail.com
可以关注痞子衡的Github主页 https://github.com/JayHeng,有很多好玩的嵌入式项目。
关于专栏文章有任何疑问请直接在博客下面留言,痞子衡会及时回复免费(划重点)答疑。
痞子衡邮箱已被私信挤爆,技术问题不推荐私信,坚持私信请先扫码付款(5元起步)再发。