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
验证结果如下