大家好,我是痞子衡,是正經(jīng)搞技術的痞子。今天痞子衡給大家介紹的是一種快速定位i.MXRT600板級設計ISP[2-0]啟動模式引腳上電時序問題的方法。
我們知道恩智浦i.MXRT600是主打音頻市場的MCU產(chǎn)品,其也是i.MXRT三位數(shù)平臺的第一款型號。這顆MCU已被眾多知名國際大廠客戶選用,在項目中作為協(xié)處理器負責音頻相關子功能。不少客戶都已經(jīng)到了量產(chǎn)階段,最近痞子衡在支持其中一個量產(chǎn)客戶,客戶遇到了同一批次某幾個板卡無法正常啟動的問題。痞子衡和同事一起排查,最終發(fā)現(xiàn)是ISP[2:0]啟動引腳電平上電時序問題。這其實是個典型問題,痞子衡今天教你一招快速定位此類問題的方法:
一、引出上電時序問題
我們先來看看客戶的問題,下面是客戶板卡簡圖,i.MXRT600是負責音頻功能的協(xié)處理器,它的啟動引腳ISP[2:0]與主應用處理器(AP)連接了起來(客戶項目設計里,AP并不負責控制i.MXRT600的啟動模式),為了防止對i.MXRT600上電ISP引腳采樣有影響,客戶還特地在中間加了一層反向隔離電路。
客戶量產(chǎn)過程中,同一批次幾百塊板卡,有一兩塊板卡上i.MXRT600無法啟動??蛻糇隽松倭緼BA實驗:將無法啟動板卡上的i.MXRT600芯片吹下來,換到能正常啟動板卡上,依然無法啟動。反過來,能正常啟動板卡上的i.MXRT600芯片換到無法正常啟動的板卡上,這塊板卡就能正常啟動了。
從上面ABA實驗來看,似乎不是板級設計問題,像是出問題的板卡上i.MXRT600芯片自身問題。痞子衡拿到一塊出問題的板子,上電后測量了ISP[2:0]引腳電平,其值是3'b011 - FlexSPI Boot from Port A,上電穩(wěn)定后ISP設置是沒問題的,但剛上電時i.MXRT600 BootROM到底采樣到的是什么ISP電平值沒人知道。
下面是客戶板卡上ISP部分反向隔離設計,為了驗證是ISP采樣時機問題,我們特意對電路進行改造將RT600_BOOT0和RT600_BOOT2分別強行拉高和拉低,然后給板卡重新上電,終于板卡能正常啟動了。
所以我們可以得出初步結論,對于i.MXRT600從上電到BootROM進行ISP采樣,這段時間不是一個嚴格固定值,因芯片制造差異,這個時間應該是在一定范圍內(nèi),板級供電設計時上電時間應留有足夠余量??蛻暨@個項目里上電時間余量留得不足,導致無法滿足個別i.MXRT600芯片ISP采樣時間要求。
二、BootROM中對于啟動模式的處理
在介紹快速定位ISP采樣時機問題方法前,痞子衡先帶大家了解下i.MXRT600 BootROM中關于啟動模式的處理流程。
咱們先回顧下痞子衡的舊文 《Boot配置(ISP_Pin/OTP)》,每次i.MXRT600芯片硬復位,OTP中的部分關于系統(tǒng)配置的值會被自動加載到OCOTP模塊相應Shadow Register里(關于OCOTP外設基礎知識可參考 《OTP及其燒寫方法》),BootROM中主要用如下 get_runtime_boot_device_info() 函數(shù)來獲取最終啟動模式,并將其存在全局變量 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系統(tǒng)軟復位去重新執(zhí)行BootROM時,ISP[2:0]引腳狀態(tài)都會被重新采樣,這完全是軟件采樣。ROM中ISP采樣功能函數(shù)如下面 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屬性為數(shù)字輸入模式.
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中關于啟動模式的處理代碼。那么有沒有方法掛上調(diào)試器通過查后門方式直接讀取到 s_bootDeviceInfo 變量值呢?很抱歉,不可以!即使痞子衡也做不到,雖然痞子衡能通過查BootROM map文件得知這個變量放在 0x10012d38 地址處。但是i.MXRT600 BootROM中集成了 《Debug Mailbox機制》,我們無法通過調(diào)試器讀取正常運行后的ROM狀態(tài),這條路行不通。
三、快速定位ISP[2:0]電平采樣問題
第一小節(jié)介紹的客戶項目啟動問題,其實還算比較好定位,因為有ABA實驗在先,基本可以明確問題就出在ISP采樣時機上。但更多時候,在客戶項目研發(fā)階段,沒有ABA實驗的條件,可能僅有一塊板卡,并且Flash配置以及App里啟動頭是否正確都尚待驗證。這種情況下,我們就需要一種快速甄別是否是ISP采樣時機因素導致的啟動問題。
當芯片無法啟動時,我們第一想法肯定是要得知第二小節(jié)介紹里 s_bootDeviceInfo 到底是什么值,這樣我們就能反推ISP引腳到底在ROM里是什么采樣值,但這條路行不通。換個角度想,我們能不能不讓BootROM去采樣ISP引腳,換一種替代方式來決定啟動模式,如果這種情況下能按替代設置去啟動,就也可以反證ISP引腳采樣時機有問題。
這種啟動模式替代設置方法就是痞子衡今天要教大家的方法,利用調(diào)試器臨時改寫OCOTP->OTP_SHADOW[0x60]值(對的,我們可以不燒寫OTP),改寫完成后對芯片做一次軟復位即可。OCOTP->OTP_SHADOW寄存器組僅芯片硬件復位或者執(zhí)行OCOTP更新命令其值才會被重新加載,軟復位不會影響其值。
四、在MIMXRT685-EVK上做一次實驗
讓我們在MIMXRT685-EVK上做一次實驗,這塊板卡上Flash連到了FlexSPI0 Port B上,我們隨意下載一個SDK XIP工程,并將ISP引腳撥碼設為3'b010 - FlexSPI Boot from Port B,復位后芯片能正常啟動,工程運行正常。
現(xiàn)在我們將ISP引腳撥碼設為3'b110 - Serial ISP,軟復位后芯片進入了ISP下載模式,不會從Flash啟動。查芯片頭文件得知 OCOTP->OTP_SHADOW[0x60] 寄存器地址是 0x50130180,我們現(xiàn)在嘗試改寫這個寄存器。
/** 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調(diào)試器,打開J-Link Commander,連接芯片,注意選擇"MIMXRT685_M33",然后使用w4命令在0x50130180地址處寫入0x00000005(在PRIMARY_BOOT_SRC[3:0]定義里,4'b0101是QSPI_B_BOOT)。
繼續(xù)執(zhí)行reset和go命令(或者按一下板載軟復位按鈕RESET BUTTON SW3),這時候你可以看到芯片從Flash正常啟動了,SDK XIP工程又運行起來了,顯然這時候ISP引腳電平設置被忽略了,因此我們找到了有效的啟動模式替代設置方法。
最后有一點要注意,在使用J-Link軟復位時,如果看到如下log,得檢查下配套JLinkScript里的ResetTarget()函數(shù)具體功能,要確保內(nèi)核真的被復位了。
至此,快速定位i.MXRT600板級設計ISP[2-0]啟動模式引腳上電時序問題的方法痞子衡便介紹完畢了,掌聲在哪里~~~