FPGA中的視頻圖像資源,以及想要永久存儲的程序都是要存儲在flash中,flash是FPGA一個不可缺少的部分,flash的種類有很多,根據winbond公司的128Mbit Qual SPI接口的flash,型號為W25Q128BV,作為初學者根據現有的資料去學習,下面的內容主要以這款芯片作參考。前面也提到了三大串行數據傳輸模式UART,I2C,SPI,順道就把SPI的內容也做一下總結,每篇一句話,帶着自己的思考看問題,盡信書不如無書,fighting!!!
一、flash簡單分類
flash分為nor flash和nand flash。nor flash數據線和地址線分開,可以實現ram一樣的隨機尋址功能,可以讀取任何一個字節。但是擦除仍要按塊來擦。nand flash同樣是按塊擦除,但是數據線和地址線復用,不能利用地址線隨機尋址。讀取只能按頁來讀取。NOR Flash的讀取,用戶可以直接運行裝載在NOR FLASH里面的代碼。NAND Flash沒有采取內存RAM的隨機讀取技術,它的讀取是以一次讀取一塊的形式來進行的,通常是一次讀取512個字節,采用這種技術的Flash比較廉價。用戶不能直接運行NAND Flash上的代碼,因此好多使用NAND Flash的開發板除了使用NAND Flah以外,還作上了一塊小的NOR Flash來運行啟動代碼。nandflash引腳上復用,因此讀取速度比nor flash慢一點,但是擦除和寫入速度比nor flash快很多。nand flash內部電路更簡單,因此數據密度大,體積小,成本也低。因此大容量的flash都是nand型的。小容量的2~12M的flash多是nor型的。nor flash可以進行字節尋址,所以程序可以在nor flash中運行。嵌入式系統多用一個小容量的nor flash存儲引導代碼,用一個大容量的nand flash存放文件系統和內核。
二、SPI接口
SPI(serial peripheral Interface)串行外設接口總線系統是一種同步串行外設接口,使MCU與各種外圍設備以串行方式進行通信以交換信息。SPI接口主要應用在EEPROM、FLASH、實時時鍾、AD轉換器,還有數字信號處理器和數字信號解碼器。在CPU和外圍低速器件之間進行同步串行數據傳輸,數據按位傳輸,低位在前,高位在后,全雙工通信。
三、QSPI FLASH硬件介紹
Flash容量由65536個256-byte的page組成,三種擦除方式。一種為Sector(16個page,共4KB),一種為Block擦除(128個page,共32KB),另一種為Chip擦除(整個擦除)。連接的管腳有QSPI_CS, QSPI_CLK, QSPI_MISO0, QSPI_MISO1, QSPI_MISO2, QSPI_MISO3。下面是一些SPI用到的幾個命令。
(1)讀 Manufacturer/Device ID(90h)先發送命令字90,再發送24位的地址(全0),然后接收2個byte的數據(第一個數據是 Manufacturor:FEh,第二個是設備的 Device ID:17h),數據在時鍾的上升沿采樣。
(2) Sector 擦除(20) 先發送命令字 20,再發送 24 位的地址。數據都在時鍾的上升沿采樣。
(3)先發送命令字 05,然后接收 16 位的寄存器數據。數據都在時鍾的上 升沿采樣。
(4)先發送命令字 02,再發送 24 位的地址,然后寫入 256 個編程的數據(數 據的數量可以自己修改, 但不能超過 256 個)。數據都在時鍾的上升沿采樣。
(5)先發送命令字 03,再發送 24 位的地址,然后接收數據。數據在時鍾的上升 沿采樣。
四、程序設計
實現FLASH設備ID的讀取,Sector擦除,Page編程,數據的讀取。由一個頂層設計模塊和一個子模塊組成,同樣通過程序中加入自己的筆記,紅色為所加。SPI 通信程序按照 FLASH 的 SPI 時序把並行的命令,地址或數據轉成串行的數據從 SPI 發 送給 FLASH;在讀的時候接收 SPI 的串行的數據轉化為並行的數據。這里需要注意的是 SPI 發送數據的時候,數據是在時鍾的下降沿改變的。所以讀取數據時,是要在時鍾的上升沿讀取。
1 `timescale 1ns / 1ps 2 ////////////////////////////////////////////////////////////////////////////////// 3 // Module Name: flash_spi 4 ////////////////////////////////////////////////////////////////////////////////// 5 module flash_spi( 6 output flash_clk, 7 output reg flash_cs, //使能信號 8 output reg flash_datain, 9 input flash_dataout, 10 11 input clock25M, 12 input flash_rstn, 13 input [3:0] cmd_type, 14 output reg Done_Sig, 15 input [7:0] flash_cmd, 16 input [23:0] flash_addr, 17 output reg [7:0] mydata_o, 18 output myvalid_o, 19 output reg [2:0] spi_state 20 21 ); 22 23 24 25 assign myvalid_o=myvalid; 26 27 assign flash_clk=spi_clk_en?clock25M:0; 28 29 30 reg myvalid; 31 reg [7:0] mydata; 32 33 reg spi_clk_en=1'b0; 34 reg data_come; 35 36 37 parameter idle=3'b000; 38 parameter cmd_send=3'b001; 39 parameter address_send=3'b010; 40 parameter read_wait=3'b011; 41 parameter write_data=3'b101; 42 parameter finish_done=3'b110; 43 44 45 46 reg [7:0] cmd_reg; //cmd_reg作為flash_cmd輸入命令信號的寄存器,有輸入信號時與flash_cmd一樣都為寄存器變量,但寄存器內部數值可能發生變化。 47 reg [23:0] address_reg; //address_reg同樣作為flash_addr信號的內部寄存器。 48 reg [7:0] cnta; //自己的理解,cnta是一個8位的寄存器變量,代表存儲的是一個數值,這個數值在此時用來表示串轉並或者並轉串的某一位位數數值,同理下面的cntb。 49 reg [8:0] write_cnt; 50 reg [7:0] cntb; 51 reg [8:0] read_cnt; 52 reg [8:0] read_num; 53 54 reg read_finish; 55 56 //發送讀flash命令 57 always @(negedge clock25M) //讀數據在時鍾的下降沿 58 begin 59 if(!flash_rstn) 60 begin 61 flash_cs<=1'b1; 62 spi_state<=idle; 63 cmd_reg<=0; 64 address_reg<=0; 65 spi_clk_en<=1'b0; //SPI clock輸出不使能 66 cnta<=0; 67 write_cnt<=0; 68 read_num<=0; 69 address_reg<=0; 70 Done_Sig<=1'b0; 71 end 72 else 73 begin 74 case(spi_state) 75 idle: begin 76 spi_clk_en<=1'b0; 77 flash_cs<=1'b1; 78 flash_datain<=1'b1; 79 cmd_reg<=flash_cmd; 80 address_reg<=flash_addr; 81 Done_Sig<=1'b0; 82 if(cmd_type[3]==1'b1) begin //如果flash操作命令請求 83 spi_state<=cmd_send; 84 cnta<=7; 85 write_cnt<=0; 86 read_num<=0; 87 end 88 end 89 cmd_send:begin 90 spi_clk_en<=1'b1; //flash的SPI clock輸出 91 flash_cs<=1'b0; //cs拉低 92 if(cnta>0) begin //如果cmd_reg還沒有發送完 93 flash_datain<=cmd_reg[cnta]; //發送bit7~bit1位 94 cnta<=cnta-1'b1; 95 end 96 else begin //發送bit0 97 flash_datain<=cmd_reg[0]; 98 if ((cmd_type[2:0]==3'b001) | (cmd_type[2:0]==3'b100)) begin //如果是Write Enable/disable instruction 99 spi_state<=finish_done; 100 end 101 else if (cmd_type[2:0]==3'b011) begin //如果是read register1 102 spi_state<=read_wait; 103 cnta<=7; 104 read_num<=1; //接收一個數據 105 end 106 else begin //如果是sector erase, page program, read data,read device ID 107 spi_state<=address_send; 108 cnta<=23; 109 end 110 end 111 end 112 address_send:begin 113 if(cnta>0) begin //如果cmd_reg還沒有發送完 114 flash_datain<=address_reg[cnta]; //發送bit23~bit1位 115 cnta<=cnta-1; 116 end 117 else begin //發送bit0 118 flash_datain<=address_reg[0]; 119 if(cmd_type[2:0]==3'b010) begin //如果是 sector erase 120 spi_state<=finish_done; 121 end 122 else if (cmd_type[2:0]==3'b101) begin //如果是page program 123 spi_state<=write_data; 124 cnta<=7; 125 end 126 else if (cmd_type[2:0]==3'b000) begin //如果是讀Device ID 127 spi_state<=read_wait; 128 read_num<=2; //接收2個數據的Device ID 129 end 130 else begin 131 spi_state<=read_wait; 132 read_num<=256; //接收256個數據 133 end 134 end 135 end 136 read_wait: begin 137 if(read_finish) begin 138 spi_state<=finish_done; 139 data_come<=1'b0; 140 end 141 else 142 data_come<=1'b1; 143 end 144 write_data: begin 145 if(write_cnt<256) begin // program 256 byte to flash 146 if(cnta>0) begin //如果data還沒有發送完 147 flash_datain<=write_cnt[cnta]; //發送bit7~bit1位 148 cnta<=cnta-1'b1; 149 end 150 else begin 151 flash_datain<=write_cnt[0]; //發送bit0 152 cnta<=7; 153 write_cnt<=write_cnt+1'b1; 154 end 155 end 156 else begin 157 spi_state<=finish_done; 158 spi_clk_en<=1'b0; 159 end 160 161 end 162 finish_done:begin 163 flash_cs<=1'b1; 164 flash_datain<=1'b1; 165 spi_clk_en<=1'b0; 166 Done_Sig<=1'b1; 167 spi_state<=idle; 168 end 169 default:spi_state<=idle; 170 endcase; 171 end 172 end 173 174 //接收flash數據,把SPI接收的串行數據轉成8位字節 175 always @(posedge clock25M) 176 begin 177 if(!flash_rstn)begin 178 read_cnt<=0; 179 cntb<=0; 180 read_finish<=1'b0; 181 myvalid<=1'b0; 182 mydata<=0; 183 mydata_o<=0; 184 end 185 else 186 if(data_come) begin 187 if(read_cnt<read_num) begin //接收read_num個數據 188 if(cntb<7) begin //接收一個byte的bit0~bit6 189 myvalid<=1'b0; 190 mydata<={mydata[6:0],flash_dataout}; 191 cntb<=cntb+1'b1; 192 end 193 else begin 194 myvalid<=1'b1; //一個byte數據有效 195 mydata_o<={mydata[6:0],flash_dataout}; //接收bit7 196 cntb<=0; 197 read_cnt<=read_cnt+1'b1; 198 end 199 end 200 else begin 201 read_cnt<=0; 202 read_finish<=1'b1; 203 myvalid<=1'b0; 204 end 205 end 206 else begin 207 read_cnt<=0; 208 cntb<=0; 209 read_finish<=1'b0; 210 myvalid<=1'b0; 211 mydata<=0; 212 end 213 end 214 215 endmodule
頂層控制程序
module flash_test ( input CLK, input RSTn, output flash_clk, output flash_cs, output flash_datain, input flash_dataout ); /*******************************/ reg [3:0] i; reg [7:0] flash_cmd; reg [23:0] flash_addr; reg clock25M; reg [3:0] cmd_type; reg [7:0] time_delay; wire Done_Sig; wire [7:0] mydata_o; wire myvalid_o; wire [2:0] spi_state; /*******************************/ //FLASH 擦除,Page Program,讀取程序 /*******************************/ always @ ( posedge clock25M or negedge RSTn ) if( !RSTn ) begin i <= 4'd0; flash_addr <= 24'd0; flash_cmd <= 8'd0; cmd_type <= 4'b0000; time_delay<=0; end else case( i ) 4'd0://讀Device ID if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end else begin flash_cmd <= 8'h90; flash_addr <= 24'd0; cmd_type <= 4'b1000; end 4'd1://寫Write Enable instruction if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end else begin flash_cmd <= 8'h06; cmd_type <= 4'b1001; end 4'd2://Sector擦除 if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type<=4'b0000; end else begin flash_cmd <= 8'h20; flash_addr <= 24'd0; cmd_type <= 4'b1010; end 4'd3://waitting 100 clock if( time_delay<8'd100 ) begin flash_cmd <= 8'h00; time_delay<=time_delay+1'b1; cmd_type <= 4'b0000; end else begin i <= i + 1'b1; time_delay<=0; end 4'd4://讀狀態寄存器1, 等待idle if( Done_Sig ) begin if (mydata_o[0]==1'b0) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end end else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end 4'd5://寫Write disable instruction if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end else begin flash_cmd <= 8'h04; cmd_type <= 4'b1100; end 4'd6://讀狀態寄存器1, 等待idle if( Done_Sig ) begin if (mydata_o[0]==1'b0) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end end else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end 4'd7://寫Write Enable instruction if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end else begin flash_cmd <= 8'h06; cmd_type <= 4'b1001; end 4'd8://waitting 100 clock if( time_delay<8'd100 ) begin flash_cmd <= 8'h00; time_delay<=time_delay+1'b1; cmd_type <= 4'b0000; end else begin i <= i + 1'b1; time_delay<=0; end 4'd9://page program: write 0~255 to flash if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1;cmd_type <= 4'b0000; end else begin flash_cmd <= 8'h02; flash_addr <= 24'd0; cmd_type <= 4'b1101; end 4'd10://waitting if( time_delay<8'd100 ) begin flash_cmd <= 8'h00; time_delay<=time_delay+1'b1; cmd_type <= 4'b0000; end else begin i <= i + 1'b1; time_delay<=0; end 4'd11://讀狀態寄存器1, 等待idle if( Done_Sig ) begin if (mydata_o[0]==1'b0) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end end else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end 4'd12://寫Write disable instruction if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end else begin flash_cmd <= 8'h04; cmd_type <= 4'b1100; end 4'd13://讀狀態寄存器1, 等待idle if( Done_Sig ) begin if (mydata_o[0]==1'b0) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end end else begin flash_cmd <= 8'h05; cmd_type <= 4'b1011; end 4'd14://read 256byte if( Done_Sig ) begin flash_cmd <= 8'h00; i <= i + 1'b1; cmd_type <= 4'b0000; end else begin flash_cmd <= 8'h03; flash_addr <= 24'd0; cmd_type <= 4'b1110; end 4'd15://idle i <= 4'd15; endcase /*****************************/ always @ ( posedge CLK ) if( !RSTn ) clock25M<=1'b0; else clock25M <= ~clock25M; /*****************************/ flash_spi U1 ( .flash_clk(flash_clk ), .flash_cs( flash_cs ), .flash_datain( flash_datain ), .flash_dataout( flash_dataout ), .clock25M( clock25M ), //input clock .flash_rstn( RSTn ), //input reset .cmd_type( cmd_type ), // flash command type .Done_Sig( Done_Sig ), //output done signal .flash_cmd( flash_cmd ), // input flash command .flash_addr( flash_addr ), // input flash address .mydata_o( mydata_o ), // output flash data .myvalid_o( myvalid_o ), // output flash data valid .spi_state(spi_state) ); wire [35:0] CONTROL0; wire [255:0] TRIG0; chipscope_icon icon_debug ( .CONTROL0(CONTROL0) // INOUT BUS [35:0] ); chipscope_ila ila_filter_debug ( .CONTROL(CONTROL0), // INOUT BUS [35:0] // .CLK(dma_clk), // IN .CLK(CLK), // IN .TRIG0(TRIG0) // IN BUS [255:0] //.TRIG_OUT(TRIG_OUT0) ); assign TRIG0[7:0]=mydata_o; assign TRIG0[8]=myvalid_o; assign TRIG0[12:9]=i; assign TRIG0[15:13]=spi_state; assign TRIG0[16]=Done_Sig; assign TRIG0[17]=flash_datain; assign TRIG0[18]=flash_dataout; assign TRIG0[19]=flash_cs; assign TRIG0[20]=flash_clk; endmodule
五、測試驗證
flash驗證如上面讀取器件ID還是需要有實際flash才能較好的獲得驗證結果,下面的驗證程序用來作為驗證數據讀取,人工給予flash_dataout數據.
1 `timescale 1ns/10ps 2 module flash_test(); 3 wire flash_clk; 4 wire flash_cs; 5 wire flash_datain; 6 reg flash_dataout; 7 reg clock25M; 8 reg RSTn; 9 reg [3:0] cmd_type; 10 wire Done_Sig; 11 reg [7:0] flash_cmd; 12 reg [23:0] flash_addr; 13 wire [7:0] mydata_o; 14 wire myvalid_o; 15 wire [2:0] spi_state; 16 17 18 flash_spi U1 19 ( 20 .flash_clk(flash_clk ), 21 .flash_cs( flash_cs ), 22 .flash_datain( flash_datain ), 23 .flash_dataout( flash_dataout ), 24 25 .clock25M( clock25M ), //reg clock 26 .flash_rstn( RSTn ), //reg reset 27 .cmd_type( cmd_type ), // flash command type 28 .Done_Sig( Done_Sig ), //reg done signal 29 .flash_cmd( flash_cmd ), // reg flash command 30 .flash_addr( flash_addr ), // reg flash address 31 .mydata_o( mydata_o ), // reg flash data 32 .myvalid_o( myvalid_o ), // reg flash data valid 33 .spi_state(spi_state) 34 35 ); 36 initial clock25M <= 1'b0; 37 always #20 clock25M <= ~clock25M; 38 39 initial 40 begin 41 RSTn<= 1'b0; 42 flash_addr <= 24'd0; 43 flash_cmd <= 8'd0; 44 cmd_type <= 4'b0000; 45 #100 RSTn <= 1'b1; 46 #100 begin flash_cmd <= 8'h03; flash_addr <= 24'd0; cmd_type <= 4'b1110; end //此處並沒有定義flash_dataout數據,可考慮采用隨機數產生函數來獲取,程序內數據為256位,可適當減小驗證 47 48 #83300 RSTn<= 1'b0; 49 50 #100 $stop; 51 end 52 53 54 endmodule
驗證結果如下