痞子衡嵌入式:序列NOR Flash的Continuous read模式下軟復位後i.MXRT無法啟動問題解決方案之RESET#

痞子衡發表於2021-05-25

  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是i.MXRT上使能NOR Flash的Continuous read模式在軟復位後無法正常啟動問題的解決經驗

  前一篇文章 《在i.MXRT啟動頭FDCB裡使能序列NOR Flash的Continuous read模式》 裡,痞子衡簡單介紹了Flash的Continuous read模式作用與意義,並且在MIMXRT1170-EVK上嘗試使能了芯成IS25WP128的Continuous read模式做了一次實踐(主要是文中第三節FDCB啟動頭的改動)。

  但其實在i.MXRT上使能Flash的Continuous read模式是有點小陷阱的,如果你在App程式碼裡直接插一句 NVIC_SystemReset() 函式的呼叫,即對主控晶片做一次軟復位,你會發現晶片沒有從Flash正常啟動,這是因為Flash此時仍處於Continuous read模式,這種情況下BootROM有時不能正常配置讀取Flash內容去啟動App。今天痞子衡就來跟大家探討解決這個問題。

  • 本系列會有多篇文章,每篇文章均從一個核心切入點出發,給出一系列具體實現方案。
  • 本系列均以MIMXRT1170-EVK板為示例目標物件,板載Flash型號為芯成IS25WP128(其他i.MXRT晶片和Flash型號下實現流程也差不多,需檢視對應資料手冊)。

一、解決思路

  我們知道無法啟動問題是由於主晶片發生軟復位但Flash仍處於Continuous read模式造成的,要解決這個問題無非如下三個角度,痞子衡會在後面具體實現方案裡按這些角度全部搞一次(如果適用的話)。

  • 一、ROM方面不做任何相關處理,但App在呼叫NVIC_SystemReset()做復位前將Flash先切回到Normal模式;
  • 二、App方面不做任何相關處理,對BootROM相關配置做一些調整,讓BootROM也能正常處理處於Continuous read模式的Flash;
  • 三、ROM和App聯合對Flash模式切換做一些特殊處理。

二、核心切入點(藉助Flash的硬復位引腳功能)

  本文找的核心切入點是利用Flash的硬體復位引腳。Flash的硬體復位引腳有兩種:一種是獨立的,常見於SOIC-16封裝上(這種情況下對板級設計有要求,需要在板級設計時將Flash復位引腳連到主晶片i.MXRT的GPIO上);另一種是複用在IO3上的,常用於SOIC-8封裝上。

  如果是獨立的復位引腳(RESET#),則主晶片GPIO直接做拉低操作即可(注意低電平持續時間的要求,詳見Flash資料手冊);如果是複用的復位引腳(RESET#/IO3),則需要先啟用IO3的復位功能,然後做拉低操作。

  在IS25WP128資料手冊裡可以找到RESET#訊號低電平至少需要持續1us:

三、具體實現

  本章節描述的方法,如果是在App裡(這裡均指XIP App)完成,那麼App裡增加的相關處理程式碼(注意是執行到的全部程式碼)需要是 ramfunc 屬性(即執行在內部RAM裡),這樣操作Flash時可以不受限制。此外程式碼執行前需要把全域性中斷關掉,防止執行過程中有中斷觸發,導致Flash裡的相關IRQHandler函式被執行。

#if (defined(__ICCARM__))
__ramfunc 
#endif
void reset_flash_to_normal(void)
{
    __disable_irq();

    // 處理程式碼,使Flash返回到Normal模式

    NVIC_SystemReset();
}

3.1 僅ROM方面做相關處理

  我們先僅從ROM單方面角度來解決問題,可以先看下痞子衡之前的舊文 《深入i.MXRT1050系列ROM中序列NOR Flash啟動初始化流程》 裡的2.1節。i.MXRT全系列ROM裡關於序列NOR Flash啟動流程大同小異。

  如果要利用ROM裡整合的Flash硬體復位功能,則Flash本身必須包含獨立的硬體RESET#引腳。本系列示例主晶片i.MXRT1170的fusemap表裡關於RESET_PIN的相關定義如下,那麼板級設計時Flash RESET#引腳應該連線到GPIO4[3]或者GPIO2[8](根據fuse 0xC80[5]位而定),並且我們還要將fuse 0xC80[7]位燒寫為1。

3.2 僅App方面做相關處理

  上一小節裡的方法先決條件是Flash要包含獨立RESET#引腳,但實際客戶專案中SOIC-8封裝的Flash選擇更多。所以我們更多應該在複用的RESET#/IO3引腳上做文章,這就要從App方面的角度來解決問題了。

  我們先從IS25WP128資料手冊看看RESET#/IO3引腳詳細功能解釋,主要如下兩點:

1. IO3引腳僅當QE模式不使能(Flash內部Status Register[6] = 0)的時候,其功能才是HOLD#/RESET#
2. IO3引腳複用功能HOLD#/RESET#由Flash內部Read Register[7]位決定,預設值為0,是HOLD#功能

  所以 reset_flash_to_normal() 函式裡我們需要先設Status Register將Flash切到QE不使能的狀態(i.MXRT啟動執行App時,Flash應處於QE使能的狀態),然後再設Read Register將IO3復位功能指定為RESET#,然後拉低IO3對應的GPIO直到滿足復位最小時間要求,最後再將之前改寫的Status Register/Read Register全部恢復。過程中主要涉及如下命令:

  程式碼可以基於 \SDK_2.9.1_MIMXRT1170-EVK\boards\evkmimxrt1170\driver_examples\flexspi\nor\polling_transfer\cm7下面的 flexspi_nor_polling_transfer.c 和 flexspi_nor_flash_ops.c,並新增如下程式碼:

#define NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG 9
#define NOR_CMD_LUT_SEQ_IDX_SETREADPARAM   14

const uint32_t customLUT[CUSTOM_LUT_LENGTH] = {
    // ...

    /* 原來 Write Status Register */
    [4 * NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG] =
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x01, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04),

    // 新增 Set read parameter
    [4 * NOR_CMD_LUT_SEQ_IDX_SETREADPARAM] = 
        FLEXSPI_LUT_SEQ(kFLEXSPI_Command_SDR, kFLEXSPI_1PAD, 0x63, kFLEXSPI_Command_WRITE_SDR, kFLEXSPI_1PAD, 0x04),
};

status_t flexspi_nor_set_flash_register(FLEXSPI_Type *base, uint32_t seqIdx, uint32_t regValue)
{
    flexspi_transfer_t flashXfer;
    status_t status;
    uint32_t writeValue = regValue;

    /* Write enable */
    status = flexspi_nor_write_enable(base, 0);
    if (status != kStatus_Success)
    {
        return status;
    }

    flashXfer.deviceAddress = 0;
    flashXfer.port          = kFLEXSPI_PortA1;
    flashXfer.cmdType       = kFLEXSPI_Write;
    flashXfer.SeqNumber     = 1;
    flashXfer.seqIndex      = seqIdx;
    flashXfer.data          = &writeValue;
    flashXfer.dataSize      = 1;

    status = FLEXSPI_TransferBlocking(base, &flashXfer);
    if (status != kStatus_Success)
    {
        return status;
    }

    status = flexspi_nor_wait_bus_busy(base);

    /* Do software reset. */
    FLEXSPI_SoftwareReset(base);

    return status;
}

// MIMXRT1170-EVK上GPIO10[20]引腳連到了Flash的IO3上
void reset_flash(void)
{
    gpio_pin_config_t gpio_config = {
        .direction = kGPIO_DigitalOutput,
        .outputLogic = 0U,
        .interruptMode = kGPIO_NoIntmode
    };
    GPIO_PinInit(GPIO10, 20U, &gpio_config);
    IOMUXC_SetPinMux(IOMUXC_GPIO_SD_B2_11_GPIO10_IO20, 0U);
  
    // Pin拉高
    GPIO_PinWrite(GPIO10, 20U, 1U);
    SDK_DelayAtLeastUs(10, SystemCoreClock);

    // Pin拉低10us
    GPIO_PinWrite(GPIO10, 20U, 0U);
    SDK_DelayAtLeastUs(10, SystemCoreClock);

    // Pin拉高
    GPIO_PinWrite(GPIO10, 20U, 1U);
}

void reset_flash_to_normal(void)
{
    __disable_irq();

    flexspi_nor_flash_init(EXAMPLE_FLEXSPI);

    // Disable quad mode.
    flexspi_nor_set_flash_register(EXAMPLE_FLEXSPI, NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG, 0x00);
    // Set IO3 pin to Reset func
    flexspi_nor_set_flash_register(EXAMPLE_FLEXSPI, NOR_CMD_LUT_SEQ_IDX_SETREADPARAM, 0x80);
    
    // Drive IO3 to low for at least 1us
    reset_flash();
 
    // Set back IO3 pin func
    flexspi_nor_set_flash_register(EXAMPLE_FLEXSPI, NOR_CMD_LUT_SEQ_IDX_SETREADPARAM, 0x00);
    // Enter quad mode.
    flexspi_nor_set_flash_register(EXAMPLE_FLEXSPI, NOR_CMD_LUT_SEQ_IDX_WRITESTATUSREG, 0x40);
    
    NVIC_SystemReset();
}

  為了保證上述程式碼均執行在RAM裡,工程連結檔案裡(以IAR示例)需做如下改動:

initialize by copy { readwrite, 
                     section .textrw, 
                     object fsl_common.o,
                     object I64DivZer.o,
                     object I64DivMod.o,
                     object fsl_gpio.o,
                     object fsl_flexspi.o,
                     object flexspi_nor_flash_ops.o,
                     object flexspi_nor_polling_transfer.o,
                     section CodeQuickAccess };

3.3 ROM和App聯合處理

  關於ROM和App聯合處理角度,在復位引腳這個切入點上並沒有什麼優勢,此處略去。

  至此,i.MXRT上使能NOR Flash的Continuous read模式在軟復位後無法正常啟動問題的解決經驗痞子衡便介紹完畢了,掌聲在哪裡~~~

歡迎訂閱

文章會同時釋出到我的 部落格園主頁CSDN主頁知乎主頁微信公眾號 平臺上。

微信搜尋"痞子衡嵌入式"或者掃描下面二維碼,就可以在手機上第一時間看了哦。

相關文章