痞子衡嵌入式:探討i.MXRT下FlexSPI driver實現Flash程式設計時對於中斷支援問題

痞子衡發表於2023-01-02

  大家好,我是痞子衡,是正經搞技術的痞子。今天痞子衡給大家介紹的是i.MXRT下FlexSPI driver實現Flash程式設計時對於中斷支援問題

  前段時間有客戶在官方社群反映 i.MXRT1170 下使用官方 SDK 裡 FlexSPI 驅動去擦寫 Flash 時不能很好地支援全域性中斷。客戶專案裡用了兩塊 NOR Flash,分別掛在不同的 FlexSPI 上,一塊 Flash 用於儲存 XIP 程式碼(FlexSPI1),另一塊 Flash 用於儲存專案資源資料(FlexSPI2),顯然這樣的設計原理上是沒有問題的,那為什麼使能了中斷會出問題呢?今天痞子衡來分析下這個問題:

  • Note: 客戶測試的 SDK 版本為 2.12.1,對應的 FlexSPI driver 版本為 2.3.6

一、為什麼擦寫Flash時經常需要關全域性中斷?

  在具體分析客戶問題之前,我們先來聊聊嵌入式應用裡應對 NOR Flash 的擦寫為何大部分情況下都是要關閉全域性中斷(這裡假設執行程式碼空間與擦寫操作空間在同一個 Flash 上,當然是在不同區域),這其實跟如下兩個特性有關:

1.1 RWW特性(Read-While-Write)

  RWW 特性的意思是在 Flash 執行擦寫命令進入 Busy 狀態期間(Flash 內部狀態暫存器 WIP 位變狀態 1)還能否繼續響應非操作區域的讀訪問。如果 SR[WIP] = 1 時還能夠支援讀訪問,則該 Flash 支援 RWW,反之則不支援 RWW。

  絕大部分 Flash 都是不支援 RWW 特性的,這就是為什麼 Flash 擦寫操作程式碼本身是需要重定向到 RAM 裡去執行(尤其是回讀 SR[WIP] 狀態的程式碼)。對於支援 RWW 特性的 Flash,一般是以 Block 為單位,Flash 擦寫操作程式碼放在 BlockX 裡執行,則可以操作 BlockX 以外的其它 Block 區域,且不需要做程式碼重定向。

  現在你應該知道對於不支援 RWW 的 Flash 為什麼擦寫時需要關閉全域性中斷了,因為無法保證中斷響應相關程式碼全都重定向到 RAM 裡了,所以乾脆在 Flash 擦寫期間不響應任何中斷。

1.2 SCLK Stop特性

  SCLK Stop 特性的意思是在 Flash 執行寫入命令接受主裝置傳輸過來的 Page 資料期間,如果匯流排上 SCLK 停止(一般情況是 FlexSPI 這一端的 TXFIFO 為空或者觸發空條件),則 Flash 能否也暫停接受當前 Page 資料直到 SCLK 繼續輸出從而繼續處理剩下的 Page 資料。

  絕大部分 Flash 是不支援 SCLK Stop 特性的,因此在 MCU 端如果傳輸 Page 資料,需要一次性連續傳輸完成,一旦中途被打斷,則兩次不連續的 Page 資料傳輸可能無法得到想要的 Page 寫入結果。這也是為何 Flash 寫入期間我們需要關閉中斷。

二、FlexSPI外設寫操作設計

  關於 i.MXRT 上的 FlexSPI 外設基本情況,痞子衡有兩篇舊文 《FlexSPI支援在Flash XIP原理》《FlexSPI支援AHB方式寫入Flash》,大家先讀一下有個初步瞭解。這裡痞子衡想重點說一下 FlexSPI 關於 IPG 方式寫操作的設計,下圖為 FlexSPI 外設的模組框圖,痞子衡用綠色線標出了 IPG 方式寫入的通路,這裡大家可以看出其中 IP_TX_FIFO 模組起了重要的資料緩衝作用,驅動裡往 FLEXSPI->TFDRx 暫存器寫入的 Page 資料會先被裝載進 IP_TX_FIFO 裡,然後再傳輸出去。

  不同 i.MXRT 型號上 IP_TX_FIFO 大小不一樣,目前有三種大小: 128/256/1024 Bytes。對於 QuadSPI/OctalSPI NOR Flash 來說,Page 大小一般是 256 Bytes;對於 HyperBus Flash,Page 大小一般是 512 Bytes。所以在 i.MXRT10xx 上 IP_TX_FIFO 是不足以緩衝整個 Page 的,i.MXRT117x 上可以緩衝 QuadSPI/OctalSPI NOR 型別的 Page,i.MXRT118x/5xx/6xx 上則可以緩衝全部 NOR Flash 型別的 Page。對於 Page 資料不能全部緩衝的情況,則需要一邊傳輸一邊緩衝。

型號 FlexSPI外設 IP TX FIFO大小
i.MXRT118x 2 x dual-channel /16-bit 1024 Bytes
i.MXRT117x
i.MXRT116x
1 x dual-channel /8-bit
1 x dual-channel /16-bit
256 Bytes
i.MXRT106x
i.MXRT1042
2 x dual-channel /8-bit 128 Bytes
i.MXRT105x 1 x dual-channel /8-bit 128 Bytes
i.MXRT1024 2 x dual-channel /8-bit 128 Bytes
i.MXRT1021
i.MXRT1015
1 x dual-channel /8-bit 128 Bytes
i.MXRT1011 1 x dual-channel /8-bit 128 Bytes
i.MXRT6xx 1 x dual-channel /8-bit 1024 Bytes
i.MXRT5xx 2 x dual-channel /8-bit 1024 Bytes

  在具體裝載資料進 IP_TX_FIFO 時,主要涉及如下三個 FLEXSPI 暫存器,IP_TX_FIFO 一次只能被填入 watermark level 大小的資料,想要把全部 Page 資料填進 IP_TX_FIFO,需要分多次裝載。只要 FLEXSPI->INTR[IPTXWE] 標誌為 0, 即代表 IP_TX_FIFO 剩餘空間大於等於 watermark level,那麼就可以繼續裝載。

FLEXSPI->IPTXFCR[TXWMRK]  -- 設定一次裝載進 IP_TX_FIFO 的資料長度(即 watermark level),8 Bytes為單位
FLEXSPI->TFDRx            -- 按 watermark level 長度填入 IP_TX_FIFO 裝載資料
FLEXSPI->INTR[IPTXWE]     -- 觸發 IP_TX_FIFO 的一次裝載

三、客戶問題及FlexSPI driver寫操作流程

  前面鋪墊了這麼多,終於來到客戶遇到的 FlexSPI 驅動對於中斷不支援的問題了。因為客戶使用了兩片 Flash,所以不存在 RWW 限制問題,那剩下的原因就跟 SCLK Stop 特性有關,即 IP_TX_FIFO 並沒有緩衝全部的 Page,導致 Page 傳輸過程被中斷打斷了,然後 IP_TX_FIFO 因為緩衝資料全部發完而使 FlexSPI 模組進入了 SCLK Stop 狀態。

  我們直接開啟 fsl_flexspi.c 驅動檔案,找到跟寫操作相關的 FLEXSPI_TransferBlocking() 函式,在函式實現裡可以發現,啟動寫傳輸時序的控制位 FLEXSPI->IPCMD[TRG] 是在 IP_TX_FIFO 填充動作 FLEXSPI_WriteBlocking() 函式之前被開啟的,那這樣的實現確實是不能夠很好地支援中斷的。

四、如何改進FlexSPI driver支援中斷?

  知道了原因所在,改起來也很簡單。如果是 QuadSPI/OctalSPI NOR Flash 型別(Page=256 Bytes),在 i.MXRT117x 上,其 IP_TX_FIFO 大小為 256 Bytes,能夠緩衝全部的 Page 大小,則可以先呼叫 FLEXSPI_WriteBlocking() 裝載全部的 Page 資料,然後再開啟 FLEXSPI->IPCMD[TRG] 去觸發寫傳輸時序,這時候就不怕被中斷打斷了,如下程式碼所示。

  當然下面程式碼只是一個 workaround 式的實現示例,不是一個完整的解決方案,畢竟 FlexSPI 驅動要適配全部 i.MXRT 型號以及全部型別的 NOR Flash,此外還適用 NAND 型 Flash(Page 一般是 2KB),這時候需要根據情況拆分呼叫多次 FLEXSPI_WriteBlocking() 函式(不管怎樣要保證啟動寫傳輸時序前,把 IP_TX_FIFO 先裝滿)。

status_t FLEXSPI_TransferBlocking(FLEXSPI_Type *base, flexspi_transfer_t *xfer)
{
    // 程式碼略去

    /* Start Transfer. */
    if ((xfer->cmdType == kFLEXSPI_Write) || (xfer->cmdType == kFLEXSPI_Config))
    {
        result = FLEXSPI_WriteBlocking(base, xfer->data, xfer->dataSize);
        base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
    }
    else if (xfer->cmdType == kFLEXSPI_Read)
    {
        base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
        result = FLEXSPI_ReadBlocking(base, xfer->data, xfer->dataSize);
    }
    else
    {
        base->IPCMD |= FLEXSPI_IPCMD_TRG_MASK;
    }

    // 程式碼略去
}

  至此,i.MXRT下FlexSPI driver實現Flash程式設計時對於中斷支援問題痞子衡便介紹完畢了,掌聲在哪裡~~~

歡迎訂閱

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

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

相關文章