一、概述
RT595 集成了 FlexSPI 電路,用於實現高速串行外部存儲器與主控經片之間的通信,FlexSPI代表Flexible Serial Peripheral Interface,它提供了一種靈活的方式來連接和操作外部閃存設備,例如 Flash、Hyper RAM … 等,主要用於支援各種透過 Standard SPI、Dual SPI、Quad SPI、Octal SPI 進行通訊的設備,RT595 的 FlexSPI 架構如圖 1,藉由 IO_CTL對 IO 引腳進行控制,再對應 SPI Bus port 引腳將其配置到外部 Flash 進行通訊,以 NXP MIMXRT595-EVK 為例,如圖 2 透過對應的 SPI Bus port ( FLEXSPI0 Port ) 與外部 Flash 進行通訊,本文將著重於透過 RT595 的 FlexSPI 模組對外部 Flash 的設定、擦除 / 讀寫進行解析進行解說。
二、 需求物件:
2.1 硬體
2.1.1 NXP RT595 EVK 詳細規格如下列網址所示
https://www.nxp.com/design/development-boards/i-mx-evaluation-and-development-boards/i-mx-rt595-evaluation-kit:MIMXRT595-EVK
2.1.2 Type A to mini B USB Cable : 1 pcs
2.2 軟體
2.2.1 MCUXPRESSO ( IDE ) 軟體開發環境如下列網址所示
https://www.nxp.com/design/software/development-software/mcuxpresso-software-and-tools-/mcuxpresso-integrated-development-environment-ide:MCUXpresso-IDE
2.2.2 NXP RT595 SDK 原廠提供的 RT595 EVK Sample Code 如下列網址所示
https://mcuxpresso.nxp.com/en/welcome
三、 操作方式:
3.1 開啟 evkmimxrt595_flexspi_octal_polling_transfer 的範例,並將範例燒錄到 EVK 上,NXP RT595 EVK 板上配置有燒錄功能的 IC,如圖 3 紅框
3.2 將 USB Cable 連結到模組板 USB Connector,USB Cable 另一端連結到PC 如圖 4
3.3 執行 MCUXPRESSO 並將 NXP 的 SDK 導入到 IDE 中,操作流程如圖 5、6
3.3.1 點擊 import SDK example(s)
3.3.2 彈出 SDK import Wizard 視窗,點選 RT595
3.3.3 點選 Next
3.3.4 點選 sample code
3.3.5 點選 Finish 完成 Project 建立
3.4 Pin define
3.4.1 開啟 SDK 建立好的專案 evkmimxrt595_flexspi_octal_polling_transfer
3.4.2 點選 flexspi_octal_polling_transfer.c ( 如圖 7 )
3.4.3 查看 BOARD_InitPins 中的 Code ( 如圖 7 )
3.4.4 查看 BOARD_InitPins 將在分頁中開啟 pin_mux.c
3.4.5 pin_mux.c 將會看到針對 FlexSPI 使用到的 Pin 設定
3.4.6 宣告一變數 port1_pin18_config 將對 P1_18 ( FLEXSPI0_SCLK ) 的相關參數儲存其中,參數如下 ( 如圖 8 ):
a ) IOPCTL_PIO_FUNC1:選擇 IOPCTL function 為 1 ( FLEXSPI0_SCLK 功能 )
b ) IOPCTL_PIO_PUPD_DI: Pull down 設定
c ) IOPCTL_PIO_PULLDOWN_EN:Pullup / Pulldown 功能 Enable
d ) IOPCTL_PIO_INBUF_EN:對應 Reference Manual 建議,在 SPI 應用下開啟此功能
e ) IOPCTL_PIO_SLEW_RATE_NORMAL:對應 Datasheet建議,在 SPI 應用下使用 Standard mode
f ) IOPCTL_PIO_FULLDRIVE_EN:Full output drive mode
g ) IOPCTL_PIO_ANAMUX_DI:Analog multiplexor disabled
h ) IOPCTL_PIO_PSEDRAIN_DI:Normal push-pull output
i ) IOPCTL_PIO_INV_DI:Input function is not inverted
3.4.7 IOCON_PinMuxSet function 中有 4 個參數需填入如下
a ) IOCON : 對應到 RT595 的 IOPCTL register address ( 0x40004000 )
b ) 1U : 對應到硬體規劃的 GPIO Port 上 ( 這邊宣告為 Port 1 )
c ) 18U : 對應到硬體規劃的 GPIO Port Pin 上 ( 這邊宣告為 Port 1 Pin 18 )
d ) port1_pin18_config : 該參數用來定義 IOPCTL register 的參數,詳細參數說明如 3.4.6 所示
其餘 FLEXSPI0 Pin 的設定方式類似,這邊就不再敘述
3.5 FLEXSPI0 通訊時脈設定 function
3.5.1 透過 BOARD_SetFlexspiClock function 中的參數進行通訊時脈設定,參數定義如下:
a ) FLEXSPI0 : 對應到 RT595 的 FLEXSPI0 register address ( 0x40134000 )
b ) 2 : 將 FLEXSPI0 的 Clock Source 設定為 SYSPLL0 AUX0_PLL_Clock,SYSPLL0 AUX0_PLL_Clock 源自於 SysPLL0 ( 528 MHz ),經過除頻後為 396 MHz
c ) 4 : 將 SYSPLL0 AUX0_PLL_Clock ( 396 MHz ) / 4 等於 99 MHz
3.5.2 查看 flexspi_nor_flash_init 中的 Code ( 如圖 9 )
3.5.3 查看 flexspi_nor_flash_init 將在分頁中開啟 flexspi_octal_flash_ops.c ( 如圖 10 )
3.5.4 customLUT參數:該參數為 RT595 與外部 Flash 通訊時的相關資訊,需對應 Flash 來查看,查詢 customLUT 後 main code 中可以找到對應的 Table,LUT 為 RT595 與 Device 通訊時,RT595 以查表的形式來定義要使用哪種格式與 Device 進行通訊,此處僅是將 customLUT 的參數映射到 localLUT 的結構變數中,當後續使用到該參數在細說該部分
3.5.5 進入 FLEXSPI_GetDefaultConfig function查看 ( 如圖 11 ),function 中所設定的默認值如下:
- config->rxSampleClock = kFLEXSPI_ReadSampleClkLoopbackInternally默認由 MCU FlexSPI 產生 DQS 訊號來作為 Read Sample Clock
- config->enableSckFreeRunning = false 選擇不啟用外部CLK,CLK源自 MCU 本身
- config->enableDoze = true 當系統有打瞌睡模式請求時,AHB 時鐘和串行時鐘將被關閉。
- config->enableHalfSpeedAccess = false 不啟用降速功能 ( 降速除頻值為 2 ),給外部 A_SCLK/B_SCLK
- config->enableSckBDiffOpt = false 不啟用B_SCLK為A_SCLK的差分時鐘,通常做為 CLK_N、CLK_P 使用
- config->enableSameConfigForAll = false 每個 FLEXSPI 都是獨立 Size 計算
- config->seqTimeoutCycle = 0xFFFFU 超時時間參數設定 ( 65535 * 1024 Serial Root Clock cycles ),超時產生中斷並忽略AHB命令
- config->ipGrantTimeoutCycle = 0xFFU IP Command 延遲時間設定 ( 255 * 1024 AHB Clock cycles ),在指定時間後 IP Command 未被下達則觸發中斷
- config->txWatermark = 8 TX Watermark level is 8 * 64 Bits,當 Watermark 的空餘的程度大於或等於該水印值時,可觸發中斷
- config->rxWatermark = 8 RX Watermark level is 8*64 Bits,當 Watermark 的空餘的程度大於或等於該水印值時,可觸發中斷
- config->ahbConfig.ahbGrantTimeoutCycle = 0xFFU AHB 延遲時間設定 ( 255 * 1024 AHB Clock cycles ),超時即產生中斷
- config->ahbConfig.ahbBusTimeoutCycle = 0xFFFFU AHB 在設定時間內未收到或傳輸資料時的 time out 設定 ( 65535 * 1024 ahb clock cycles ),超時即產生中斷
- config->ahbConfig.resumeWaitCycle = 0x20U 配置在暫停命令序列恢復之前的等待空閒狀態周期,等待超過設定值 AHB 個時鐘後超時
- config->ahbConfig.buffer[i].enablePrefetch = true AHB 讀取預取使能
- config->ahbConfig.buffer[i].masterIndex = 0xFU AHB RX Buffer 根據 AHB Master 的 ID (MSTR_ID) 進行分配,內容非0即使用
- config->ahbConfig.buffer[i].bufferSize = 0 AHB RX 緩衝區大小
- config->ahbConfig.buffer[i].enablePrefetch = true AHB 讀取預取使能。
- config->ahbConfig.buffer[i].bufferSize = 256U AHB RX 緩衝區大小
* 上述 i 值為緩衝區規劃
- config->ahbConfig.enableClearAHBBufferOpt = false 當 FlexSPI 返回停止模式 ACK 時,AHB RX/TX 緩衝區不會被自動清除
- config->ahbConfig.enableReadAddressOpt = false 當 flash 以並行模式訪問或 flash 是字尋址時,存在 AHB 讀取突發起始地址對齊限制
- config->ahbConfig.enableAHBPrefetch = false AHB 讀取預取使能
- config->ahbConfig.enableAHBBufferable = false FlexSPI 將在所有數據傳輸到外部設備並且 AHB 命令完成後返回 AHB 總線就緒
- config->ahbConfig.enableAHBCachable = false 當存在 AHB 總線緩存讀取訪問時,FlexSPI 將不會檢查它是否命中 AHB TX 緩衝區
3.5.6 進入 FLEXSPI Config 獨立設定 ( 如圖 12 ) 中所設定的值如下:
- ahbConfig.enableAHBPrefetch = true AHB 讀取預取使能
- rxSampleClock = EXAMPLE_FLEXSPI_RX_SAMPLE_CLOCK Flash Read Sample Clk 來自 DQS Pin,即訊號來自於外部 Flash 的 DQS Pin
- ahbConfig.enableAHBBufferable = true FlexSPI 將在所有數據傳輸到外部設備並且 AHB 命令完成後返回 AHB 總線就緒
- ahbConfig.enableAHBCachable = true 當存在 AHB 總線緩存讀取訪問時,FlexSPI 將不會檢查它是否命中 AHB TX 緩衝區
3.5.7 將 FLEXSPI Config 的參數設定到 FlexSPI register 中
3.5.8 將 deviceconfig 的參數設定到 FlexSPI register 的 Port A1 中 ( 如圖 13 ),deviceconfig 的參數如下:
- .flexspiRootClk = 99000000 FLEXSPI 的時鐘頻率
- .flashSize = FLASH_SIZE Flash 的大小 ( KByte )
- .CSIntervalUnit = kFLEXSPI_CsIntervalUnit1SckCycle CS 的間隔單位可配置為 1 或 256 個週期
- .CSInterval = 2 CS 訊號斷言間隔,通過多個 CS 間隔單位來獲取 CS 訊號斷言間隔週期
- .CSHoldTime = 3 CS 訊號保持時間
- .CSSetupTime = 3 CS 訊號建立時間
- .dataValidTime = 2 對外部設備的數據有效時間
- .columnspace = 0 列空間大小
- .enableWordAddress = 0 是否使能字(4字節)地址
- .AWRSeqIndex = NOR_CMD_LUT_SEQ_IDX_WRITE AHB寫命令的AHB序列ID
- .AWRSeqNumber = 1 AHB 寫命令的序列數目
- .ARDSeqIndex = NOR_CMD_LUT_SEQ_IDX_READ AHB 讀命令序列ID
- .ARDSeqNumber = 1 AHB讀命令的序列數目
- .AHBWriteWaitUnit = kFLEXSPI_AhbWriteWaitUnit2AhbCycle AHB 寫等待單位
- .AHBWriteWaitInterval = 0 AHB 寫等待間隔,通過多個 AHB 寫間隔單位來完成AHB寫等待週期
3.5.8 將 customLUT的參數設定到 FlexSPI register 中 ( 如圖 14 )
3.5.9 設定軟件復位完成後由硬件自動清零
3.5.10 Release CACHE,本範例不會使用到 CACHE 因此這邊不做解說
3.5.11 後續的 flexspi_nor_ 類別的 Function,將會透過輸入 Function 來對應 LUT 的 index 執行出不同的通訊方式,下列範例將透過 get vendor ID ( 如圖 15 ) 參數來解析如何對應到 EVK 上使用的 Flash ( MX25UW51345GXDI00 )
3.5.12 這邊先解析 LUT 的參數
a ) 該 LUT 是透過其他 function 的 CUSTOM_LUT_LENGTH 參數來決定取出 Table 中的哪個參數來使用 ( 如圖 16 )
b ) 以 get vendor ID ( Read ID ) 參數為例 ( 如圖 17 )
b1 . kFLEXSPI_Command_DDR:參數傳輸將以 DDR Mode 傳輸 ( 如圖 16 ),對應到 Transmit Data 後續資料傳輸會以 8 bit 傳輸
b2 . kFLEXSPI_8PAD:參數傳輸將透過 8 Pin Mode 傳輸
b3 . 0x9F:對應 MX25UW51345GXDI00 參數 ( 如圖 18 )
b4 . 0x60:對應 MX25UW51345GXDI00 參數 ( 如圖 18 )
c ) 下一步驟的資料解析如下
c1 . kFLEXSPI_Command_RADDR_DDR:參數傳輸將以 DDR Mode 傳輸 ( 如圖 19 ),對應到 Transmit Data 後續資料傳輸會以 32 bit 傳輸
c2 . kFLEXSPI_8PAD:參數傳輸將透過 8 Pin Mode 傳輸
c3. 0x20:設定傳輸 32 bit 的 0 值 ( 如圖 18 ),若要變更該參數則可修改 flexspi_nor_get_vendor_id Function 中的 flashXfer.deviceAddress 參數
c4. kFLEXSPI_Command_DUMMY_DDR:參數傳輸將以 DDR Mode 傳輸 ( 如圖 20 )
c5. 0x08:對應 MX25UW51345GXDI00 中所需的 Dummy 時間參數 ( 如圖 21 ),需要等到 8 個 DDR mode 下的 SCLK 時間
d ) 下一步驟的資料解析如下
d1 . kFLEXSPI_Command_READ_DDR:參數傳輸將以 DDR Mode 接收 ( 如圖 22 )
d2 . kFLEXSPI_8PAD:參數傳輸將透過 8 Pin Mode 傳輸
d3 . 0x04 : 該參數在這邊不被 function 參考,可以填任意值
d4 . kFLEXSPI_Command_STOP : 該參數是在告知 MCU 傳輸已結束
d5 . kFLEXSPI_1PAD : 參數傳輸將透過 1 Pin Mode 傳輸
d6 . 0x0 : 因 STOP Command 不傳輸實際 Data 因此填 0
圖 22 ( 註 4 )
3.5.13 回到 flexspi_nor_get_vendor_id Function 中查看 ( 如圖 23 ) 先關參數如下:
a ) flashXfer.deviceAddress = 0,MCU 對 Flash 傳輸時的 Address
b ) flashXfer.port = FLASH_PORT,這邊設定為 PortA1,因 NXP EVK 配置使用 PortA1 對 Flash 進行通訊
c ) flashXfer.cmdType = kFLEXSPI_Read,get vendor id 行為為 read,因此這邊設定為 Read
d ) flashXfer.SeqNumber = 1,設定要執行的序列數目,範例中設定 1 即可
e ) flashXfer.seqIndex = NOR_CMD_LUT_SEQ_IDX_READID_OPI,LUT 循址 inedx
f ) flashXfer.data = &temp,Read 後的參數擺設位址
g ) flashXfer.dataSize = 1,讀取的 ID Size 大小
h ) FLEXSPI_TransferBlocking : 透過該 Function 將 flashXfer 的參數設定到 FlexSPI register 中,並啟動傳輸功能
i ) *vendorId = temp : 將從 Flash 中取得的 ID 參數回傳到 vendorId 變數中
3.5.14 後續的其他 Function 的解析方式與上述解析雷同,本範例解析完畢
註 1:作者:NXP Semiconductors;出處:NXP 文件 IMXRT500RM Rev. 0.1 的 Fig.122
註 2:作者:NXP Semiconductors;出處:NXP 文件 spf-45800_d1
註 3 :作者:NXP Semiconductors;出處:NXP 文件 MIMXRT595EVKHUG User's Guide Rev. 0, 的 Fig.1
註 4 :作者:NXP Semiconductors;出處:NXP 文件 MIMXRT595EVKHUG User's Guide Rev. 0, 的 Table 404
註 5 :作者:MACRONIX INTERNATIONAL CO., LTD.;出處: MACRONIX 文件 MX25UM51345G Datasheet 的 Table 6
註 6 :作者:MACRONIX INTERNATIONAL CO., LTD.;出處: MACRONIX 文件 MX25UM51345G Datasheet 的 Figure 13.