驅(qū)動 SPI Flash(M25P16)讀寫)
Verilog 實現(xiàn) SPI 器件 Flash 驅(qū)動文章目錄Verilog 實現(xiàn) SPI 器件 Flash 驅(qū)動一、器件說明本工程實際使用的核心功能用到指令簡表二、項目設(shè)計三、代碼實現(xiàn)控制寫的模塊 ctrl_w控制讀模塊 ctrl_r四、上板驗證4.1 上板硬件與引腳約束五、總結(jié)與參考資料5.1 關(guān)鍵要點總結(jié)5.2 參考資料摘要本文基于 AWC_C4 DVK 開發(fā)板使用 Verilog 實現(xiàn) FPGA 驅(qū)動 SPI Flash 芯片 M25P16 的讀寫。涵蓋頁編程、扇區(qū)擦除、讀狀態(tài)寄存器等核心指令通過自定義串口幀與同步 FIFO 解決速率匹配并給出 ctrl_w/ctrl_r 模塊代碼與上板驗證。一、器件說明本次工程使用 SPI 串行 Flash 芯片M25P16?VMN6TP該芯片為 AWC_C4 DVK 開發(fā)板板載 AS 主動串行配置 Flash。開發(fā)板硬件電路說明開發(fā)板板載 M25P16?VMN6TP原生用于 FPGA 上電 AS 模式加載配置程序。引腳映射FPGA ? Flash信號名稱M25P16引腳FPGA主控引腳描述I/O標(biāo)準(zhǔn)DATA02H2MOSIFPGA 輸出→Flash 輸入3.3VDCLK6H1SCLKSPI 主機(jī)時鐘輸出3.3VASDO5C1MISOFlash輸出→FPGA輸入3.3VNCSO1D2S_N片選低有效FPGA輸出→Flash輸入3.3V硬件電路細(xì)節(jié)W_N(引腳3)、HOLD_N(引腳7)開發(fā)板硬件直接上拉VCC_3V3寫保護(hù)、HOLD保持功能硬件永久關(guān)閉軟件無需處理VCC接3.3V搭配0.1uF去耦電容VSS接地DATA0、DCLK、ASDO串聯(lián)20Ω電阻做阻抗匹配。存儲架構(gòu)M25P16存儲容量16Mbit2MB32個Sector扇區(qū)每個扇區(qū)64KB每扇區(qū)分256Page頁每頁256字節(jié)地址范圍0x000000 ~ 0x1FFFFF。參數(shù)特性扇區(qū)擦寫壽命10萬次數(shù)據(jù)保存20年僅支持SPIMode0CPOL0 CPHA0、Mode3CPOL1 CPHA1。寫操作前必須擦除。擦除后存儲單元全部為0xFF頁編程只能將bit由1改寫為0不能把0改寫回1。本工程實際使用的核心功能舍棄未使用功能DP 深度掉電、喚醒 RES、寫狀態(tài)寄存器 WRSR、高速讀 FAST_READ、寫禁止 WRDI、整片擦除 BE。禁用整片擦除 BE板載 Flash 存儲 FPGA 配置程序執(zhí)行整片擦除會導(dǎo)致開發(fā)板無法啟動。寫使能 WREN(0x06)頁編程、扇區(qū)擦除前必須發(fā)送 WREN執(zhí)行完寫/擦除硬件自動清除 WEL 寫使能鎖存位。頁編程 PP(0x02)實現(xiàn)多字節(jié)寫入發(fā)送 WREN → PP 指令 24bit 地址 待寫數(shù)據(jù)拉高 S_N 片選后進(jìn)入芯片內(nèi)部tPP編程周期。限制單次最多寫同一頁連續(xù) 256 字節(jié)跨頁會發(fā)生頁回卷覆蓋頁起始數(shù)據(jù)。扇區(qū)擦除 SE(0xD8)擦除指定一整個 64KB 扇區(qū)擦除后扇區(qū)全部字節(jié)變?yōu)?xFF發(fā)送前必須先發(fā) WREN。讀狀態(tài)寄存器 RDSR(0x05)只讀取 WIP 忙位用來輪詢判斷芯片內(nèi)部編程/擦除操作是否完成。WIP1芯片忙內(nèi)部操作執(zhí)行中WIP0操作完成可以下發(fā)下一條指令。讀數(shù)據(jù) READ(0x03)從指定24bit地址開始連續(xù)讀取存儲器數(shù)據(jù)。用到指令簡表指令助記符指令碼地址字節(jié)功能寫使能WREN0x060打開寫使能鎖存讀狀態(tài)寄存器RDSR0x050讀取 WIP 忙狀態(tài)位讀數(shù)據(jù)READ0x033讀取 Flash 存儲數(shù)據(jù)頁編程PP0x023頁寫編程最多 256 字節(jié)扇區(qū)擦除SE0xD83擦除單個 64KB 扇區(qū)二、項目設(shè)計前置文章上一篇實現(xiàn) FPGA 內(nèi)部 SPI 主?從閉環(huán)模擬。M25P16 需要 WREN 寫使能、扇區(qū)擦除、頁寫、狀態(tài)輪詢、讀數(shù)據(jù)。自定義串口下發(fā)幀格式0xFE data_len wr_lenth cmd addr0 addr1 addr2 data1 data2… 0xEE0xFE幀頭0xEE幀尾data_len本次 SPI 交互總字節(jié)數(shù)wr_lenth主機(jī)發(fā)送字節(jié)數(shù)量cmdM25P16 指令碼addr0?addr224 bit 存儲地址data1~dataN頁編程待寫入的數(shù)據(jù)速率匹配矛盾UART 串口下發(fā)字節(jié)慢SPI 收發(fā)速度快引入兩組 8 bit×256 同步 FIFO 做速率緩沖。ctrl_w寫控制模塊解析串口幀指令、地址、待寫數(shù)據(jù)存入寫 FIFOSPI每完成1字節(jié)寫done_w從FIFO讀出下一個字節(jié)給到SPI主機(jī)。ctrl_r讀控制模塊SPI從Flash讀出的數(shù)據(jù)寫入讀 FIFOSPI整組通信結(jié)束后逐字節(jié)送給tx模塊回傳上位機(jī)。依靠done_w / done_r / done_spi / done_tx握手信號完成跨模塊時序?qū)R。工程模塊分層rx模塊復(fù)用UART回環(huán)工程輸出data_rx、done_rxctrl_w寫控制模塊解析串口自定義幀F(xiàn)IFO緩存發(fā)送數(shù)據(jù)輸出SPI主機(jī)控制信號spi_m SPI主機(jī)模塊復(fù)用上篇博文輸出SCLK、CS_N、MOSI接收MISOctrl_r讀控制模塊緩存SPI讀出數(shù)據(jù)FIFO緩沖輸出給txtx模塊復(fù)用UART把讀到的數(shù)據(jù)回傳給PC串口助手top頂層完成全部子模塊、FIFO IP 例化與信號互聯(lián)。硬件提醒本開發(fā)板DCLK/DATA0/ASDO/NCSO為FPGA AS配置復(fù)用引腳Quartus需要正確配置避免AS模式占用IO。整體工程模塊框圖三、代碼實現(xiàn)說明rx、tx、spi_mSPI 主機(jī)模塊復(fù)用上一篇SPI主從模擬通信文章ctrl_w、ctrl_r 為本篇新增控制模塊調(diào)用 Quartus 同步 FIFO IP8 bit?256 深度??刂茖懙哪K ctrl_w//接收rx的并行數(shù)據(jù)單字節(jié)一個一個的來所以在接收的過程中以done_rx信號作為條件 //可以用狀態(tài)機(jī)來劃分各節(jié)點接收信號,該模塊作用就是解析數(shù)據(jù)幀 //rx:fe **data_len wr_lenth cmd** addr0 addr1 addr2 data... ee/傳輸?shù)膸Y(jié)構(gòu) //引入 FIFO 匹配速率進(jìn)行后續(xù)多字節(jié)寫數(shù)據(jù) //用到指令寫使能、扇區(qū)擦除、頁編程、讀狀態(tài)寄存器、讀數(shù)據(jù) module ctrl_w( input wire clk , input wire rst_n , input wire [7:0] data_rx ,//rx發(fā)送過來的單個指令 input wire done_rx ,//數(shù)據(jù)有效信號 input wire done_w , input wire done_spi , output wire start_spi , output wire [7:0] data_len , output wire [7:0] wr_lenth , output wire [7:0] data_i_spi ); //參數(shù) //狀態(tài)定義 parameter IDLE 1 ,//等待包頭 LENTH 2 , W_LEN 3 , DATA 4 ,//包尾跳到STOP STOP 5 ; reg [3:0] state_c,state_n; //狀態(tài)轉(zhuǎn)移 wire IDLE_LENTH , LENTH_W_LEN , W_LEN_DATA , DATA_STOP , STOP_IDLE ; //內(nèi)部信號 reg [7:0] data_len_r ; reg [7:0] wr_lenth_r ; //FIFO8*256-----存地址和數(shù)據(jù)先進(jìn)先出模式 //寫 reg wr_en ;//寫使能 reg [7:0] data_fifo ;//FIFO 的輸入數(shù)據(jù) //讀 wire rd_en ;//讀使能 wire [7:0] q ;//讀出的數(shù)據(jù) //握手信號 wire empty ;//空信號 wire full ;//滿信號 wire [7:0] usedw ;//數(shù)據(jù)量 //狀態(tài)機(jī)第一段 always (posedge clk or negedge rst_n) begin if (rst_n 0) begin state_c IDLE; end else begin state_c state_n; end end //狀態(tài)機(jī)第二段 always (*) begin if (rst_n 0) begin state_n IDLE; end else begin case (state_c) IDLE : state_n (IDLE_LENTH ) ? LENTH : state_c; LENTH : state_n (LENTH_W_LEN) ? W_LEN : state_c; W_LEN : state_n (W_LEN_DATA ) ? DATA : state_c; DATA : state_n (DATA_STOP ) ? STOP : state_c; STOP : state_n (STOP_IDLE ) ? IDLE : state_c; default : state_n IDLE; endcase end end //狀態(tài)轉(zhuǎn)移條件 assign IDLE_LENTH (state_c IDLE ) done_rx data_rx 8hfe; assign LENTH_W_LEN (state_c LENTH) done_rx; assign W_LEN_DATA (state_c W_LEN) done_rx; assign DATA_STOP (state_c DATA ) done_rx data_rx 8hee; assign STOP_IDLE (state_c STOP ) done_spi; //狀態(tài)機(jī)第三段 always (posedge clk or negedge rst_n) begin if (rst_n 0) begin data_len_r 0; wr_lenth_r 0; wr_en 0; end else begin case (state_c) IDLE :; LENTH :begin if (done_rx) begin data_len_r data_rx; end end W_LEN :begin if (done_rx) begin wr_lenth_r data_rx; end end DATA :begin if (done_rx data_rx ! 8hee) begin wr_en 1; data_fifo data_rx; end else begin wr_en 0; data_fifo 0; end end STOP :; default :begin data_len_r 0; wr_lenth_r 0; wr_en 0; end endcase end end assign rd_en done_w; //輸出信號 assign start_spi (DATA_STOP) ; assign data_len data_len_r; assign wr_lenth wr_lenth_r; assign data_i_spi q ; //同步 FIFO 8*256 fifo fifo_inst_w ( .aclr (~rst_n || done_spi),//保險 .clock (clk ), .data (data_fifo),//done_rx .rdreq (rd_en ),//done_w .wrreq (wr_en ),//done_rx .empty (empty ), .full (full ), .q (q ), .usedw (usedw ) ); endmodule控制讀模塊 ctrl_rmodule ctrl_r ( input wire clk , input wire rst_n , input wire [7:0] data_o_spi ,//spi的輸出數(shù)據(jù)也就是讀出的 input wire done_r ,//完成一次讀數(shù)據(jù)的握手信號 input wire done_tx ,//讀數(shù)據(jù)控制讀使能的信號 input wire done_spi ,//數(shù)據(jù)寄存 FIFO 結(jié)束 output wire [7:0] data_out ,//tx的輸入 output wire rd_done //tx的開始 ); //FIFO8*256 //寫 FIFO wire wr_en ;//寫使能 wire [7:0] data_fifo ;//FIFO 的輸入數(shù)據(jù) //讀 FIFO wire rd_en ;//讀使能 reg rd_enr ;//讀使能寄存 wire [7:0] q ;//讀出的數(shù)據(jù) //握手信號 wire empty ;//空信號 wire full ;//滿信號 wire [7:0] usedw ;//數(shù)據(jù)量 //寫 FIFO assign wr_en done_r; assign data_fifo data_o_spi; //讀 FIFO assign rd_en done_tx; assign data_out q; assign rd_done (done_spi || rd_enr) (empty 0); always (posedge clk or negedge rst_n) if(rst_n 0) begin rd_enr 0; end else begin rd_enr rd_en; end //同步 FIFO 8*256 fifo fifo_inst_r ( .aclr (~rst_n ), .clock (clk ), .data (data_fifo),//done_r .rdreq (rd_en ),//done_tx .wrreq (wr_en ),//done_r .empty (empty ), .full (full ), .q (q ), .usedw (usedw ) ); endmoduletop頂層模塊、Testbench仿真模塊省略頂層例化 rx、ctrl_w、spi_m、ctrl_r、tx 模塊例化兩組 FIFO IP 核。四、上板驗證4.1 上板硬件與引腳約束復(fù)用開發(fā)板板載 M25P16引腳映射信號FPGA 引腳說明SPI_SCLKH1DCLKSPI_MOSIH2DATA0SPI_MISOC1ASDOSPI_CS_ND2NCSOS_N 低有效片選DCLK/DATA0/ASDO/NCSO 是 FPGA AS 配置復(fù)用引腳需要在 Quartus 中關(guān)閉 AS 配置占用才能當(dāng)作普通 IO 使用硬件 W_N、HOLD_N 已經(jīng)板載上拉 3.3V。五、總結(jié)與參考資料5.1 關(guān)鍵要點總結(jié)本工程基于 AWC_C4 DVK 開發(fā)板使用 Verilog 完整實現(xiàn)了 SPI 串行 Flash 芯片 M25P16 的讀寫驅(qū)動核心要點如下WREN 寫使能是寫操作的前置條件頁編程、扇區(qū)擦除前必須發(fā)送 WREN(0x06) 打開寫使能鎖存執(zhí)行完寫/擦除后硬件自動清除 WEL 位下一次寫操作前需重新發(fā)送。頁編程存在 256 字節(jié)邊界限制單次 PP(0x02) 最多寫同一頁連續(xù) 256 字節(jié)跨頁會發(fā)生頁回卷覆蓋頁起始數(shù)據(jù)因此跨頁寫入必須分多次下發(fā)。寫操作前必須擦除Flash 存儲單元擦除后為0xFF頁編程只能將 bit 由 1 改寫為 0不能把 0 改寫回 1本工程禁用整片擦除 BE避免破壞板載 FPGA 配置程序。FIFO 解決速率匹配矛盾UART 串口下發(fā)字節(jié)慢、SPI 收發(fā)速度快引入兩組 8 bit×256 同步 FIFO 做速率緩沖配合done_w / done_r / done_spi / done_tx握手信號完成跨模塊時序?qū)R。狀態(tài)機(jī)解析自定義串口幀ctrl_w 模塊通過狀態(tài)機(jī)解析0xFE data_len wr_lenth cmd addr data 0xEE幀結(jié)構(gòu)將指令、地址、待寫數(shù)據(jù)存入寫 FIFO 后逐字節(jié)交給 SPI 主機(jī)。AS 配置復(fù)用引腳需在 Quartus 中配置DCLK/DATA0/ASDO/NCSO 為 FPGA AS 配置復(fù)用引腳需關(guān)閉 AS 配置占用后才能當(dāng)作普通 IO 使用。5.2 參考資料M25P16 數(shù)據(jù)手冊STMicroelectronics M25P16 Serial Flash Memory Datasheet16 MbitSPI 總線接口支持 Mode0/Mode3AWC_C4 DVK 開發(fā)板原理圖板載 M25P16?VMN6TP 的 AS 主動串行配置電路與引腳映射Quartus Prime 用戶手冊FPGA 引腳分配與 AS 配置復(fù)用引腳設(shè)置說明上一篇 SPI 主從模擬通信文章FPGA 內(nèi)部 SPI 主?從閉環(huán)模擬實現(xiàn)作為本工程rx、tx、spi_m模塊的復(fù)用基礎(chǔ)