1.SPI FLASH的基本特征
本文實現用FPGA來設計SPI FLASH,FLASH型號為W25Q128BV。支持3種通信方式,SPI、Dual SPI和Quad SPI。FLASH的存儲單元無法寫入bit 1,只能寫入bit 0,所以寫入數據之前要將原來的數據擦除(FFh),遇到寫入bit 1的情況不作處理。W25Q128BV的特征為如下圖所示:
2.SPI FLASH的基本結構
W25Q128BV由Block0~Block255共256個Block組成,容量大小為256*64KB=256*64*1024*8bit/1024/1024=128M-bit=16M-Byte。每個Block由Sector0~Sector15共16個Sector組成,每個Sector大小為4KB,由16個Page組成。以第一個Sector為例,第1個Page地址從xx0000h~xx00FF開始,第2個Page地址從xx0100~xx01FF開始,第3個Page地址從xx0200~xx02FF開始,以此類推...,第16個Page地址從xx0F00~xx0FFF開始。每個Page的大小為256個Byte組成,后面會看到Page Programd最大支持256個Byte。
3.SPI FLASH的狀態寄存器
W25Q128BV有兩個狀態寄存器:狀態寄存器1和狀態寄存器2。這些狀態寄存器的標志位在后面指令操作的時候可以用來判斷指令是否完成。
4.SPI FLASH的指令
W25Q128BV的指令可以分為指令碼后面沒有地址和數據、指令碼后面只有地址沒有數據、指令碼后面只有數據沒有地址、指令碼后面既有地址又有數據的情況。
4.1 Read Manufacturer / Device ID (90h)
90h指令用來讀取廠商ID和設備ID,指令先發一個指令碼90h,緊接着是24bit的地址碼000000h,最后讀取出來的第1個字節是廠商ID,第2個字節是設備ID。如果發送的24bit地址碼是000001h,則第1個字節是設備ID,第2個字節才是廠商ID。由此可見,該指令后面既有地址又有讀數據。
4.2 Write Enable (06h)
寫使能指令用來置位狀態寄存器1里面的WEL位。在每次Page Program、Sector Erase、Block Erase、Chip Erase指令前必須發寫使能指令。寫使能指令后面不帶地址和數據。
4.3 Read Status Register-1 (05h) and Read Status Register-2 (35h)
讀狀態寄存器1的指令碼位05h,讀狀態寄存器2的指令碼為35h。讀狀態寄存器可以用來檢測一些指令是否完成,比如上面的寫使能指令發送完成后,可以讀取狀態寄存器1里面的WEL位,看是否為1,以此確定寫使能是否成功。讀狀態寄存器指令碼后面只有數據沒有地址。
4.4 Write Disable (04h)
寫不使能指令用來復位狀態寄存器1的WEL位,指令碼04h發送完成后不發送地址和數據。WEL位在Power-up以及Write Status Register、Erase/Program Security Registers、Page Program、Quad Page Program、Sector Erase、Block Erase、Chip Erase完成后自動復位。
4.5 Sector Erase (20h)
扇區擦除指令用來把一個指定扇區(4K-bytes)置位到擦除狀態(FFh),在發送Sector Erase之前必須先執行Write Enable (06h)指令(置位WEL位)。
4.6 Page Program (02h)
Page Program指令支持1~256個bytes的數據在先前擦除過的位置寫入,在Page Program之前必須執行寫使能指令(置位WEL)。該指令先發送指令碼02h,緊接着是24bit的地址碼A23~A0,然后是至少1個數據字節。如果寫入的是一個完整256字節數據的Page,則地址的最低8位必須是0。如果最后一個地址字節不是0,時鍾數超過了剩余page長度,地址將跳到該page的開始位置。如果一次寫入超過256個字節的數據,地址將跳到page的起始地址,並且覆蓋先前寫入的數據。盡管Page Program指令還沒有執行完,讀狀態寄存器指令仍然能夠檢查BUSY位,如果BUSY位為1,則Page Program指令還在執行,否則Page Program已經執行完畢,設備可以執行其他指令了。在Page Program指令完成后,狀態寄存器的WEL位被復位。
4.7 Read Data (03h)
讀數據指令支持讀取多個數據,先發送指令碼03h,然后是24bit地址碼,指令碼和地址碼在flash端的上升沿鎖存,讀出的數據字節在DO引腳的下降沿移出。當正在進行Erase、Program或者Write時進行讀指令,這時讀指令將被忽略,但對當前正在執行的這幾個操作沒有影響。
5 程序設計
程序里面的狀態機根據指令后面是否帶數據或者地址的情況跳轉,操作流程如下:
1.Read Manufacturer / Device ID (90h),讀取廠商ID和設備ID;
2.Write Enable (06h),置位WEL位;
3.Read Status Register-1 (05h),判斷WEL位是否置位;
4.Sector Erase (20h),擦除第一個Sector(4KB);
5.Read Status Register-1 (05h),判斷Sector Erase是否完成(輪詢BUSY位,直到BUSY位為0,表示完成);
6.Write Disable (04h),復位WEL位;
7.Read Status Register-1 (05h),判斷WEL是否復位;
8.Write Enable (06h),置位WEL位;
9.Read Status Register-1 (05h),判斷WEL位是否置位;
10.Page Program (02h),將1個Page(256個字節)的數據寫入flash;
11.Read Status Register-1 (05h),判斷.Page Program是否完成(輪詢BUSY位,直到BUSY位為0,表示完成);
12.Write Disable (04h),復位WEL位;
13.Read Status Register-1 (05h),判斷WEL是否復位;
14.Read Data (03h),讀出寫入的256個字節。
程序清單如下:
`timescale 1ns / 1ps ////////////////////////////////////////////////////////////////////////////////// // Company: // Engineer: // // Create Date: 2019/04/04 09:42:45 // Design Name: // Module Name: flash_top // Project Name: // Target Devices: // Tool Versions: // Description: // // Dependencies: // // Revision: // Revision 0.01 - File Created // Additional Comments: // ////////////////////////////////////////////////////////////////////////////////// module flash_top ( input i_rst_n , input i_clk , //50MHz output o_flash_cs , output o_flash_clk , output o_flash_din , input i_flash_dout ); reg [ 3:0] r_cmd_type ; reg [ 7:0] r_flash_cmd ; reg [23:0] r_falsh_addr; reg [ 7:0] r_flash_wdata; reg [ 7:0] r_data_num ; wire r_op_done ; wire r_flash_done; wire [ 7:0] r_flash_rdata; reg [ 7:0] r_cnt ; reg r_clk_25MHz ; always @(posedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_clk_25MHz <= 1'b0; else r_clk_25MHz <= !r_clk_25MHz; end always @(posedge r_clk_25MHz or negedge i_rst_n) begin if(!i_rst_n) begin r_cmd_type <= 'd0; r_flash_cmd <= 'd0; r_falsh_addr<= 'd0; r_flash_wdata<= 'd0; r_data_num <= 'd0; r_cnt <= 'd0; end else begin case(r_cnt) 'd0: //Read Manufacturer / Device ID (90h) begin if(r_op_done) begin if(r_flash_rdata == 8'h17) r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1110; //命令類型 r_flash_cmd <= 8'h90; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd1; //數據字節敿 end end 'd1: //Write Enable (06h) begin if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1001; //命令類型 r_flash_cmd <= 8'h06; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd0; //數據字節敿 end end 'd2: //Read Status Register-1 (05h) begin if(r_op_done && r_flash_rdata[1] == 1'b1) //輪詢WEL位,直到WEL置位 //if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1011; //命令類型 r_flash_cmd <= 8'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd0; //數據字節敿 end end 'd3: //Sector Erase (20h) begin if(r_op_done) begin r_cnt <= r_cnt + 1'b1; //r_cnt <= 'd0; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1100; //命令類型 r_flash_cmd <= 8'h20; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd0; //數據字節敿 end end 'd4: //Read Status Register-1 (05h) begin if(r_op_done && r_flash_rdata[0] == 1'b0) //輪詢BUSY位,直到BUSY復位 //if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1011; //命令類型 r_flash_cmd <= 8'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd0; //數據字節敿 end end 'd5: //Write Disable (04h) begin if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1001; //命令類型 r_flash_cmd <= 8'h04; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd0; //數據字節敿 end end 'd6: //Read Status Register-1 (05h) begin if(r_flash_done && r_flash_rdata[1] == 1'b0) //輪詢WEL位,直到WEL復位 //if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1011; //命令類型 r_flash_cmd <= 8'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd0; //數據字節敿 end end 'd7: //Write Enable (06h) begin if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1001; //命令類型 r_flash_cmd <= 8'h06; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd0; //數據字節敿 end end 'd8: //Read Status Register-1 (05h) begin if(r_flash_done && r_flash_rdata[1] == 1'b1) //輪詢WEL位,直到WEL置位 //if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1011; //命令類型 r_flash_cmd <= 8'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd0; //數據字節敿 end end 'd9: //Page Program (02h) begin if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1101; //命令類型 r_flash_cmd <= 8'h02; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h59; //數據 r_data_num <= 8'd255; //數據字節敿 end end 'd10: //Read Status Register-1 (05h) begin if(r_flash_done && r_flash_rdata[0] == 1'b0) //輪詢BUSY位,直到BUSY復位 //if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1011; //命令類型 r_flash_cmd <= 8'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd0; //數據字節敿 end end 'd11: //Write Disable (04h) begin if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1001; //命令類型 r_flash_cmd <= 8'h04; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd0; //數據字節敿 end end 'd12: //Read Status Register-1 (05h) begin if(r_flash_done && r_flash_rdata[1] == 1'b0) //輪詢WEL位,直到WEL復位 //if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1011; //命令類型 r_flash_cmd <= 8'h05; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd0; //數據字節敿 end end 'd13: //Read Data (03h) begin if(r_op_done) begin r_cnt <= r_cnt + 1'b1; r_cmd_type <= 4'b0; r_flash_cmd <= 8'b0; r_falsh_addr<= 24'b0; r_flash_wdata<= 8'b0; r_data_num <= 8'b0; end else begin r_cmd_type <= 4'b1110; //命令類型 r_flash_cmd <= 8'h03; //命令砿 r_falsh_addr<= 24'h000000; //地址 r_falsh_addr<= 24'b0; r_flash_wdata<= 8'h0; //數據 r_data_num <= 8'd255; //數據字節敿 end end 'd14: begin r_cnt <= 'd0; end endcase end end wire [7:0] o_rd_cnt ; wire [2:0] o_flash_cstate; flash_dri flash_dri_inst ( .i_rst_n (i_rst_n ), .i_clk (r_clk_25MHz ), .i_cmd_type (r_cmd_type ), .i_flash_cmd (r_flash_cmd ), .i_falsh_addr (r_falsh_addr ), .i_flash_data (r_flash_wdata ), .i_data_num (r_data_num ), .o_op_done (r_op_done ), .o_flash_done (r_flash_done ), .o_flash_data (r_flash_rdata ), .o_flash_cs (o_flash_cs ), .o_flash_clk (o_flash_clk ), .o_flash_din (o_flash_din ), .i_flash_dout (i_flash_dout ) ); endmodule
module flash_dri ( input i_rst_n , input i_clk , //25MHz input [ 3:0] i_cmd_type , input [ 7:0] i_flash_cmd , input [23:0] i_falsh_addr, input [ 7:0] i_flash_data, input [ 7:0] i_data_num , output o_op_done , output o_flash_done, output [ 7:0] o_flash_data, output o_flash_cs , output o_flash_clk , output o_flash_din , input i_flash_dout ); parameter FLASH_IDLE = 0 ; parameter FLASH_SEND_CMD = 1 ; parameter FLASH_SEND_ADDR = 2 ; parameter FLASH_WR_DATA = 3 ; parameter FLASH_RD_DATA = 4 ; parameter FLASH_END = 5 ; reg [2:0] flash_cstate ; reg [2:0] flash_nstate ; reg [2:0] r_cmd_cnt ; reg [4:0] r_addr_cnt ; reg [2:0] r_data_cnt ; reg r_op_done ; reg r_flash_done; reg [7:0] r_flash_data; reg r_flash_cs ; reg r_flash_din ; reg [7:0] r_wr_num ; reg [7:0] r_rd_num ; reg r_busy ; reg r_rd_valid ; reg [2:0] r_rd_cnt ; reg r_wr_valid ; reg [2:0] r_wr_cnt ; assign o_op_done = r_op_done ; assign o_flash_done= r_flash_done; assign o_flash_data= r_flash_data; assign o_flash_cs = r_flash_cs ; assign o_flash_clk = r_busy? i_clk:1'b0 ; assign o_flash_din = r_flash_din ; always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) flash_cstate <= FLASH_IDLE ; else flash_cstate <= flash_nstate ; end always @(*) begin case(flash_cstate) FLASH_IDLE: begin if(i_cmd_type[3]) flash_nstate <= FLASH_SEND_CMD ; else flash_nstate <= FLASH_IDLE ; end FLASH_SEND_CMD: begin if(r_cmd_cnt == 'd7) begin if(i_cmd_type[2:0] == 3'b001) //命令后不帶地坿Ҍ數據 flash_nstate <= FLASH_END ; else if(i_cmd_type[2:0] == 3'b010) //命令后帶寫數據,無地坿 flash_nstate <= FLASH_WR_DATA ; else if(i_cmd_type[2:0] == 3'b011) //命令后帶讀數據,無地坿 flash_nstate <= FLASH_RD_DATA ; else if(i_cmd_type[2] == 1'b1) //命令后帶地址 flash_nstate <= FLASH_SEND_ADDR ; else flash_nstate <= FLASH_IDLE ; end else begin flash_nstate <= FLASH_SEND_CMD ; end end FLASH_SEND_ADDR: begin if(r_addr_cnt == 'd23) begin if(i_cmd_type[1:0] == 2'b00) //不帶數據 flash_nstate <= FLASH_END ; else if(i_cmd_type[1:0] == 2'b01) //寫數捿 flash_nstate <= FLASH_WR_DATA; else if(i_cmd_type[1:0] == 2'b10) //讀數捿 flash_nstate <= FLASH_RD_DATA; else flash_nstate <= FLASH_IDLE ; end else flash_nstate <= FLASH_SEND_ADDR ; end FLASH_WR_DATA: begin if(r_data_cnt == 'd7 && r_wr_num == 'd0) flash_nstate <= FLASH_END ; else flash_nstate <= FLASH_WR_DATA; end FLASH_RD_DATA: begin if(r_data_cnt == 'd7 && r_rd_num == 'd0) flash_nstate <= FLASH_END ; else flash_nstate <= FLASH_RD_DATA; end FLASH_END: begin flash_nstate <= FLASH_IDLE ; end default: begin flash_nstate <= FLASH_IDLE ; end endcase end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_rd_num <= 'd0; else if(r_rd_cnt == 'd7) begin if(r_rd_num == 'd0) r_rd_num <= 'd0; else r_rd_num <= r_rd_num - 1'b1; end else if(flash_cstate == FLASH_SEND_CMD && i_cmd_type[3:0] == 4'b1110) r_rd_num <= i_data_num ; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_wr_num <= 'd0; else if(r_wr_cnt == 'd7) begin if(r_wr_num == 'd0) r_wr_num <= 'd0; else r_wr_num <= r_wr_num - 1'b1; end else if(flash_cstate == FLASH_SEND_CMD && i_cmd_type[3:0] == 4'b1101) r_wr_num <= i_data_num ; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_op_done<= 1'b0 ; else if(flash_cstate == FLASH_IDLE) r_op_done<= 1'b0 ; else if(flash_cstate == FLASH_END) r_op_done<= 1'b1 ; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_cmd_cnt <= 'd0 ; else if(flash_cstate == FLASH_SEND_CMD) begin if(r_cmd_cnt == 'd7) r_cmd_cnt <= 'd0 ; else r_cmd_cnt <= r_cmd_cnt + 1'b1; end else r_cmd_cnt <= 'd0 ; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_addr_cnt <= 'd0 ; else if(flash_cstate == FLASH_SEND_ADDR) begin if(r_addr_cnt == 'd23) r_addr_cnt <= 'd0 ; else r_addr_cnt <= r_addr_cnt + 1'b1; end else r_addr_cnt <= 'd0 ; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_data_cnt <= 'd0 ; else if(flash_cstate == FLASH_RD_DATA || flash_cstate == FLASH_WR_DATA) begin if(r_data_cnt == 'd7) r_data_cnt <= 'd0 ; else r_data_cnt <= r_data_cnt + 1'b1; end else r_data_cnt <= 'd0 ; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_flash_din <= 1'b1 ; else if(flash_cstate == FLASH_SEND_CMD) r_flash_din <= i_flash_cmd[7 - r_cmd_cnt]; else if(flash_cstate == FLASH_SEND_ADDR) r_flash_din <= i_falsh_addr[23 - r_addr_cnt]; else if(flash_cstate == FLASH_WR_DATA) r_flash_din <= i_flash_data[7 - r_data_cnt]; else r_flash_din <= 1'b1 ; end always @(posedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_flash_data <= 'd255 ; else if(r_rd_valid) r_flash_data[7 - r_rd_cnt] <= i_flash_dout; else if(flash_cstate == FLASH_IDLE) r_flash_data <= 'd255 ; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_wr_valid <= 1'b0 ; else if(flash_cstate == FLASH_WR_DATA) r_wr_valid <= 1'b1; else r_wr_valid <= 1'b0; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_wr_cnt <= 'd0 ; else if(flash_cstate == FLASH_WR_DATA && r_wr_valid) r_wr_cnt <= r_wr_cnt + 1'b1; else r_wr_cnt <= 'd0 ; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_rd_valid <= 1'b0 ; else if(flash_cstate == FLASH_RD_DATA) r_rd_valid <= 1'b1; else r_rd_valid <= 1'b0; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_rd_cnt <= 'd0 ; else if(flash_cstate == FLASH_RD_DATA && r_rd_valid) r_rd_cnt <= r_rd_cnt + 1'b1; else r_rd_cnt <= 'd0 ; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_flash_done <= 1'b0 ; else if(r_rd_cnt == 'd7) r_flash_done <= 1'b1 ; else r_flash_done <= 1'b0 ; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_flash_cs <= 1'b1 ; //else if(flash_cstate == FLASH_IDLE && i_cmd_type[3] == 1'b1) // r_flash_cs <= 1'b0 ; else if(flash_cstate == FLASH_END) r_flash_cs <= 1'b1 ; else if(flash_cstate == FLASH_SEND_CMD) r_flash_cs <= 1'b0 ; end reg [8:0] r_num_cnt ; always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_num_cnt <= 'b0 ; else if(r_flash_done) r_num_cnt <= r_num_cnt + 1'b1 ; else if(flash_cstate == FLASH_IDLE) r_num_cnt <= 'b0 ; end always @(negedge i_clk or negedge i_rst_n) begin if(!i_rst_n) r_busy <= 1'b0 ; else if(flash_cstate == FLASH_END) r_busy <= 1'b0 ; else if(flash_cstate == FLASH_SEND_CMD) r_busy <= 1'b1 ; end endmodule
該程序代碼已在Spartan-6 xc6slx16csg324-2硬件平台上調試通過,可以正常寫入和讀出數據。
5 完結
基於FPGA的SPI FLASH控制器設計已經介紹完畢,如有不妥之處望批評指正,謝謝!