FPGA學習系列:內(nèi)存128M的flash芯片設(shè)計
FLASH閃存 閃存的英文名稱是"Flash Memory",一般簡稱為"Flash",它屬于內(nèi)存器件的一種,是一種不揮發(fā)性( Non-Volatile )內(nèi)存。閃存的物理特性與常見的內(nèi)存有根本性的差異:目前各類 DDR 、 SDRAM 或者 RDRAM 都屬于揮發(fā)性內(nèi)存,只要停止電流供應內(nèi)存中的數(shù)據(jù)便無法保持,因此每次電腦開機都需要把數(shù)據(jù)重新載入內(nèi)存;閃存在沒有電流供應的條件下也能夠長久地保持數(shù)據(jù),其存儲特性相當于硬盤,這項特性正是閃存得以成為各類便攜型數(shù)字設(shè)備的存儲介質(zhì)的基礎(chǔ)。
設(shè)計原理:
我們的設(shè)計用的是W25Q128FV 內(nèi)存128M的flash芯片,大家可以自行在網(wǎng)上下載器件手冊具體看所應用的具體命令和自己項目具體的應用和想發(fā)來設(shè)計。
這款flash芯片的的存儲是一個扇區(qū)4KB,一個扇區(qū)可以存256個字,一個字是8位,一個塊是64KB,一共有256個塊組成一個存儲flash內(nèi)存。
我在下面的講解中,將主要講實現(xiàn)一下字節(jié)的讀寫,我用的協(xié)議是SPI協(xié)議,這個芯片支持QSPI,雙端口SPI等。flash有三個狀態(tài)寄存器,每一個狀態(tài)寄存器的每一位都有各自的功能。大家可以具體的看器件手冊,我給大家簡單的講一下第一個狀態(tài)寄存器。
這個狀態(tài)寄存器第一位是可讀忙和不忙的標志位,大家可以在我們的設(shè)計中判斷芯片是否忙和不忙來是否進行下一步的操作。第二位是一個寫標志的信號,當寫使能打開的時候它位1,只有它為1的時候我們才可以進行寫,值得一說的不管是頁操作,還是擦除等命令后都會使這個標志位變成0。然后前面的命令算的上的是保護命令,具體有使用的邏輯功能。
在flash中我們寫數(shù)據(jù)前先要擦除數(shù)據(jù)你想擦除的地方,然后進行寫,如果沒有用過的flash芯片的話那么可以不用擦除。畢竟我們的flash可是掉電不丟失數(shù)據(jù)的。
我的設(shè)計思路是這樣的我們先讀出我們的器件廠商,和芯片ID,然后記性寫命令,寫使能打開,頁操作寫入數(shù)據(jù)(值得說明的是我們FLASH是新的所以沒進行擦除命令,建議擦除---關(guān)閉寫使能 -- 打開寫使能),然后讀第一個寄存器判斷芯片的第一位是否忙,不忙然后進行讀操作之后再數(shù)碼管上顯示出我們寫入的數(shù)據(jù)。
部分操作命令如下
我們的發(fā)送格式為在時鐘的上升沿寫入命令,在時鐘的下降沿讀出命令,我們用的是標準的SPI協(xié)議,端口IO0,和IO1,都是單向的。
寫使能時序:
讀使能時序:
之后別的時序我們將不展示,大家可以參考器件手冊。
設(shè)計架構(gòu)圖:
我們的設(shè)計是用一個FSM控制器來控制發(fā)送什么命令,flash模塊判斷FSM發(fā)送過來的state信號來選擇應該執(zhí)行什么操作,當命令寫入或者讀出后,會發(fā)送一個flag_done命令,這個命令讓我們判斷上個指令是否完成,如果完成后FAM將發(fā)送下一個命令。
設(shè)計代碼:
設(shè)計模塊
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; //輸出狀態(tài)模式
7 output reg [7:0] data; //輸出寫入數(shù)據(jù)
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 //發(fā)送讀廠商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 //讀數(shù)據(jù)
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; //狀態(tài)
10 input [7:0] data; //數(shù)據(jù)
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) //產(chǎn)生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) //下降沿發(fā)送數(shù)據(jù)
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) //上升沿采集數(shù)據(jù)
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 進入讀第一個狀態(tài)寄存器 操作
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位忙不忙, 不忙進入下個狀態(tài)
279 begin
280 flag_done <= 1;
281 s4 <= 3;
282 end
283 else //忙繼續(xù)讀第一個寄存器
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
圖中顯示的和我們的設(shè)計一樣,發(fā)送的各個命令也是一樣的,我們寫入的是AA然后下班接收的也是AA。