flash读写学习笔记与spi接口及简单测试验证
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
验证结果如下
转自:https://www.cnblogs.com/uiojhi/p/7604221.html