FPGA学习系列:内存128M的flash芯片设计
扫描二维码
随时随地手机看文章
FLASH闪存 闪存的英文名称是"Flash Memory",一般简称为"Flash",它属于内存器件的一种,是一种不挥发性( Non-Volatile )内存。闪存的物理特性与常见的内存有根本性的差异:目前各类 DDR 、 SDRAM 或者 RDRAM 都属于挥发性内存,只要停止电流供应内存中的数据便无法保持,因此每次电脑开机都需要把数据重新载入内存;闪存在没有电流供应的条件下也能够长久地保持数据,其存储特性相当于硬盘,这项特性正是闪存得以成为各类便携型数字设备的存储介质的基础。
设计原理:
我们的设计用的是W25Q128FV 内存128M的flash芯片,大家可以自行在网上下载器件手册具体看所应用的具体命令和自己项目具体的应用和想发来设计。
这款flash芯片的的存储是一个扇区4KB,一个扇区可以存256个字,一个字是8位,一个块是64KB,一共有256个块组成一个存储flash内存。
我在下面的讲解中,将主要讲实现一下字节的读写,我用的协议是SPI协议,这个芯片支持QSPI,双端口SPI等。flash有三个状态寄存器,每一个状态寄存器的每一位都有各自的功能。大家可以具体的看器件手册,我给大家简单的讲一下第一个状态寄存器。
这个状态寄存器第一位是可读忙和不忙的标志位,大家可以在我们的设计中判断芯片是否忙和不忙来是否进行下一步的操作。第二位是一个写标志的信号,当写使能打开的时候它位1,只有它为1的时候我们才可以进行写,值得一说的不管是页操作,还是擦除等命令后都会使这个标志位变成0。然后前面的命令算的上的是保护命令,具体有使用的逻辑功能。
在flash中我们写数据前先要擦除数据你想擦除的地方,然后进行写,如果没有用过的flash芯片的话那么可以不用擦除。毕竟我们的flash可是掉电不丢失数据的。
我的设计思路是这样的我们先读出我们的器件厂商,和芯片ID,然后记性写命令,写使能打开,页操作写入数据(值得说明的是我们FLASH是新的所以没进行擦除命令,建议擦除---关闭写使能 -- 打开写使能),然后读第一个寄存器判断芯片的第一位是否忙,不忙然后进行读操作之后再数码管上显示出我们写入的数据。
部分操作命令如下
我们的发送格式为在时钟的上升沿写入命令,在时钟的下降沿读出命令,我们用的是标准的SPI协议,端口IO0,和IO1,都是单向的。
写使能时序:
读使能时序:
之后别的时序我们将不展示,大家可以参考器件手册。
设计架构图:
我们的设计是用一个FSM控制器来控制发送什么命令,flash模块判断FSM发送过来的state信号来选择应该执行什么操作,当命令写入或者读出后,会发送一个flag_done命令,这个命令让我们判断上个指令是否完成,如果完成后FAM将发送下一个命令。
设计代码:
设计模块
0 module fsm(clk, rst_n, flag_done, command, addr, state, data);
1
2 input clk, rst_n;
3 input flag_done; //输入标志位
4 output reg [7:0] command; //输出命令
5 output reg [23:0] addr; //输出地址
6 output reg [2:0] state; //输出状态模式
7 output reg [7:0] data; //输出写入数据
8
9 reg [2:0] state_s;
10 reg [20:0] count;
11 always @ (posedge clk)
12 if(!rst_n)
13 begin
14 state_s <= 0;
15 data <= 8'd0;
16 addr <= 24'd0;
17 command <= 8'd0;
18 state <= 0;
19 count <= 0;
20 end
21 else
22 case (state_s)
23 0 : begin
24 if(count < 200) //延迟一段时间
25 count <= count + 1;
26 else
27 begin //发送读厂商ID的命令
28 command <= 8'h90;
29 addr <= 24'd0;
30 state <= 1;
31 count <= 1;
32 end
33 if(flag_done) //检查是否完成
34 state_s <= 1;
35 end
36
37 1 : begin
38 if(count < 200) //延迟一段时间
39 count <= count + 1;
40 else
41 begin //写使能
42 command <= 8'h06;
43 state <= 3;
44 count <= 0;
45 end
46 if(flag_done) //检查是否完成
47 state_s <= 2;
48 end
49
50 2 : begin
51 if(count < 200) //延迟一段时间
52 count <= count + 1;
53 else
54 begin //页操作
55 command <= 8'h02;
56 addr <= 24'd0;
57 state <= 4;
58 data <= 8'haa;
59 count <= 0;
60 end
61 if(flag_done) //检查是否完成
62 state_s <= 3;
63 end
64
65 3 : begin
66 if(count < 200) //延迟一段时间
67 count <= count + 1;
68 else
69 begin //读寄存器
70 command <= 8'h05;
71 count <= 0;
72 state <= 5;
73 end
74 if(flag_done) //检查是否完成
75 state_s <= 4;
76 end
77
78 4 : begin
79 if(count < 200) //延迟一段时间
80 count <= count + 1;
81 else
82 begin //读数据
83 command <= 8'h03;
84 addr <= 24'd0;
85 state <= 2;
86 count <= 0;
87 end
88 end
89
90 default: state_s <= 0;
91 endcase
92
93 endmodule
0 module flash (clk , rst_n, q0, q1, sclk, cs, command, addr, state, data, show_data, flag_done);
1
2 input clk, rst_n;
3 input q0;
4 output reg q1;
5 output reg sclk;
6 output reg cs;
7 input [7:0] command; //输入命令
8 input [23:0] addr; //地址
9 input [2:0] state; //状态
10 input [7:0] data; //数据
11 output reg [23:0] show_data; //显示
12 output reg flag_done; //命令完成标志
13
14 reg [5:0] count;
15 reg [5:0] cnt;
16 reg [31:0] temp;
17 reg [15:0] d;
18 reg [5:0] count_s;
19 reg [7:0] dou;
20 reg [39:0] xie;
21 reg [7:0] r_reg;
22
23 always @ (posedge clk)
24 if(!rst_n)
25 begin
26 sclk <= 1;
27 count_s <= 0;
28 end
29 else if(cs)
30 begin
31 count_s <= 0;
32 sclk <= 1;
33 end
34 else
35 begin
36 if(count_s == 25 - 1) //产生1M的时钟
37 begin
38 count_s <= 0;
39 sclk <= ~sclk;
40 end
41 else
42 count_s <= count_s + 1;
43 end
44
45 reg [1:0] signle_s;
46
47 //边沿检测电路
48 always @ (posedge clk or negedge rst_n)
49 if(!rst_n)
50 begin
51 signle_s <= 2'b11;
52 end
53 else
54 begin
55 signle_s[0] <= sclk;
56 signle_s[1] <= signle_s[0];
57 end
58
59 assign pose_dge = signle_s[0] && ~signle_s[1]; //上升沿脉冲
60 assign nege_dge = ~signle_s[0] && signle_s[1]; //下降沿脉冲
61
62 reg [1:0] s;
63 reg [1:0] s1,s2,s3,s4;
64 always @ (posedge clk or negedge rst_n)
65 if(!rst_n)
66 begin
67 q1 <= 0;
68 count <= 0;
69 cs <= 1;
70 temp <= 0;
71 d <= 0;
72 cnt <= 0;
73 s <= 0;
74 s1 <= 0;
75 s2 <= 0;
76 s3 <= 0;
77 flag_done <= 0;
78 s4 <= 0;
79 end
80 else
81 begin
82 if (state == 1) //state == 1进入读芯片的厂商和ID
83 case (s)
84 0: begin cs <= 0; temp <= {command,addr}; s <= 1; end
85
86 1 : begin
87 if(nege_dge) //下降沿发送数据
88 begin
89 if(count < 32)
90 begin
91 q1 <= temp[31];
92 count <= count + 1;
93 temp <= {temp[30:0],temp[31]};
94 end
95 else
96 begin
97 count <= 0;
98 s <= 2;
99 end
100 end
101 else
102 q1 <= q1;
103 end
104
105 2 : begin
106 if(pose_dge) //上升沿采集数据
107 begin
108 if(count < 16)
109 begin
110 count <= count + 1;
111 d <= {d[14:0],q0};
112 end
113 else
114 begin
115 s <= 3;
116 cs <= 1;
117 count <= 0;
118 flag_done <= 1;
119 show_data <= d;
120 end
121 end
122 else
123 begin
124 s <= 2;
125 end
126 end
127
128 3 : begin
129 flag_done <= 0;
130 end
131
132 endcase
133
134 else if(state == 2) //state == 2进入读模式
135 case (s1)
136 0: begin cs <= 0; temp <= {command,addr}; s1 <= 1; end
137
138 1 :begin
139 if(nege_dge)
140 begin
141 if(count < 32)
142 begin
143 q1 <= temp[31];
144 count <= count + 1;
145 temp <= {temp[30:0],temp[31]};
146 end
147 else
148 begin
149 count <= 0;
150 s1 <= 2;
151 end
152 end
153 else
154 q1 <= q1;
155 end
156
157 2 : begin
158 if(pose_dge)
159 begin
160 if(count < 8)
161 begin
162 count <= count + 1;
163 dou <= {dou[6:0],q0};
164 s1 <= 2;
165 end
166 else
167 begin
168 s1 <= 3;
169 cs <= 1;
170 count <= 0;
171 flag_done <= 1;
172 show_data <= dou;
173 end
174 end
175 else
176 begin
177 s1 <= 2;
178 end
179 end
180
181 3 : begin
182 flag_done <= 0;
183 end
184 endcase
185
186 else if(state == 3) //state == 3 进入写使能模式
187 case (s2)
188 0: begin cs <= 0; temp <= {command,addr}; s2 <= 1; end
189
190 1 :begin
191 if(nege_dge)
192 begin
193 if(count < 8)
194 begin
195 q1 <= temp[31];
196 count <= count + 1;
197 temp <= {temp[30:0],temp[31]};
198 end
199 else
200 begin
201 count <= 0;
202 s2 <= 2;
203 cs <= 1;
204 flag_done <= 1;
205 end
206 end
207 else
208 q1 <= q1;
209 end
210
211 2 : flag_done <= 0;
212 endcase
213
214 else if(state == 4) //state == 4 进入页写操作
215 case (s3)
216 0: begin cs <= 0; xie <= {command,addr,data}; s3 <= 1; end
217
218 1 :begin
219 if(nege_dge)
220 begin
221 if(count < 40)
222 begin
223 q1 <= xie[39];
224 count <= count + 1;
225 xie <= {xie[38:0],xie[39]};
226 end
227 else
228 begin
229 count <= 0;
230 s3 <= 2;
231 cs <= 1;
232 flag_done <= 1;
233 end
234 end
235 else
236 q1 <= q1;
237 end
238
239 2 : flag_done <= 0;
240
241 endcase
242
243 else if(state == 5) //state == 5 进入读第一个状态寄存器 操作
244 case (s4)
245 0: begin cs <= 0; r_reg <= command; s4 <= 1; end
246
247 1 :begin
248 if(nege_dge)
249 begin
250 if(count < 8)
251 begin
252 q1 <= r_reg[7];
253 count <= count + 1;
254 r_reg <= {r_reg[6:0],r_reg[7]};
255 end
256 else
257 begin
258 count <= 0;
259 s4 <= 2;
260 end
261 end
262 else
263 q1 <= q1;
264 end
265
266 2 : begin
267 if(pose_dge)
268 begin
269 if(count < 8)
270 begin
271 count <= count + 1;
272 d <= {d[14:0],q0};
273 end
274 else
275 begin
276 cs <= 1;
277 count <= 0;
278 if(!d[8]) //判断BUSY位忙不忙, 不忙进入下个状态
279 begin
280 flag_done <= 1;
281 s4 <= 3;
282 end
283 else //忙继续读第一个寄存器
284 s4 <= 0;
285 end
286 end
287 else
288 begin
289 s4 <= 2;
290 end
291 end
292
293 3 : flag_done <= 0;
294
295 endcase
296
297 end
298
299 endmodule
图中显示的和我们的设计一样,发送的各个命令也是一样的,我们写入的是AA然后下班接收的也是AA。