(1)FLASH芯片 W25Q16BV
芯片引腳圖:
內(nèi)部結(jié)構(gòu)圖:
存儲(chǔ)區(qū)域總共分成了32塊,每塊64KB。每塊又分成了16個(gè)部分,每個(gè)部分4KB。方便進(jìn)行讀取和局部操作。
電路設(shè)計(jì)
(2)SPI協(xié)議
SPI的四種模式
這里使用這個(gè)模式:
主機(jī)和從機(jī)在時(shí)鐘上升沿放入要輸出的數(shù)據(jù),在時(shí)鐘下降沿讀取要輸入的數(shù)據(jù)。
8個(gè)時(shí)鐘后交換一個(gè)字節(jié)8位數(shù)據(jù)(高位在前)。
(3)芯片部分命令
有個(gè)輸入時(shí)序的要求 開(kāi)始時(shí)CS拉低等待(tSLCH要求最小5ns)再開(kāi)始, 結(jié)束時(shí)CS拉高等待(tSHSL用100ns )再進(jìn)行下一次操作。
這個(gè)寄存器的第一位數(shù)據(jù)可以判斷操作是否完成(BUSY位)
1.Write Enable(06h)
寫(xiě)使能:開(kāi)始時(shí)CS拉低等待(tSLCH要求最小5ns)再開(kāi)始,結(jié)束時(shí)CS拉高等待(tSHSL取100ns )再進(jìn)行下一次操作。
2.Chip Erase (C7h / 60h)
整片擦除,要判斷操作是否完成
3.寫(xiě)指令(02h)
數(shù)據(jù)寫(xiě)多了會(huì)把之前的數(shù)據(jù)覆蓋掉,要判斷操作是否完成。
4.讀指令(03h)
要判斷操作是否完成
(4)代碼
1. FPGA做主機(jī)的SPI協(xié)議
對(duì)信號(hào)進(jìn)行同步和提前準(zhǔn)備:
100m時(shí)鐘和clk_cnt配合進(jìn)行數(shù)據(jù)的讀取和輸出(clk_cnt有等于1和0的時(shí)候)
spi_clk基于100m時(shí)鐘輸出一個(gè)相當(dāng)于clk_cnt的延時(shí)半個(gè)周期的時(shí)鐘,確保輸入輸出數(shù)據(jù)穩(wěn)定。
module spi_drive(
input clk_100m ,
input sys_rst_n ,
//user interface
input spi_start ,//spi開(kāi)啟使能。
input [7:0 ] spi_cmd ,//FLAH操作指令
input [23:0] spi_addr ,//FLASH地址
input [7:0 ] spi_data ,//FLASH寫(xiě)入的數(shù)據(jù)
input [3:0 ] cmd_cnt ,
output idel_flag_r ,//空閑狀態(tài)標(biāo)志的上升沿
output reg w_data_req ,//FLASH寫(xiě)數(shù)據(jù)請(qǐng)求
output reg [7:0] r_data ,//FLASH讀出的數(shù)據(jù)
output reg erro_flag ,//讀出的數(shù)據(jù)錯(cuò)誤標(biāo)志
//spi interface
output reg spi_cs ,//SPI從機(jī)的片選信號(hào),低電平有效。
output reg spi_clk ,//主從機(jī)之間的數(shù)據(jù)同步時(shí)鐘。
output reg spi_mosi ,//數(shù)據(jù)引腳,主機(jī)輸出,從機(jī)輸入。
input spi_miso //數(shù)據(jù)引腳,主機(jī)輸入,從機(jī)輸出。
);
//狀態(tài)機(jī)
parameter IDLE =4'd0;//空閑狀態(tài)
parameter WEL =4'd1;//寫(xiě)使能狀態(tài)
parameter S_ERA =4'd2;//扇區(qū)擦除狀態(tài)
parameter C_ERA =4'd3;//全局擦除
parameter READ =4'd4;//讀狀態(tài)
parameter WRITE =4'd5;//寫(xiě)狀態(tài)
parameter R_STA_REG =4'd6;
//指令集
parameter WEL_CMD =8'h06;
parameter S_ERA_CMD =8'h20;
parameter C_ERA_CMD =8'hc7;
parameter READ_CMD =8'h03;
parameter WRITE_CMD =8'h02;
parameter R_STA_REG_CMD=8'h05;
//wire define
wire idel_flag;
//reg define
reg[3:0] current_state ;
reg[3:0] next_state ;
reg[7:0 ] data_buffer ;
reg[7:0 ] cmd_buffer ;
reg[7:0 ] sta_reg ;
reg[23:0] addr_buffer ;
reg[31:0] bit_cnt ;
reg clk_cnt ;
reg dely_cnt ;
reg[31:0] dely_state_cnt ;
reg[7:0 ] rd_data_buffer ;
reg spi_clk0 ;
reg stdone ;
reg[7:0 ] data_check ;
reg idel_flag0 ;
reg idel_flag1 ;
//*****************************************************
//** main code
//*****************************************************
//*抓取上升沿
assign idel_flag=(current_state==IDLE)?1:0;//空閑狀態(tài)標(biāo)志
assign idel_flag_r=idel_flag0&&(~idel_flag1);//空閑狀態(tài)標(biāo)志的上升沿
//*抓取上升沿要用的
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)begin
idel_flag0<=1'b1;
idel_flag1<=1'b1;
end
else begin
idel_flag0<=idel_flag;
idel_flag1<=idel_flag0;
end
end
//請(qǐng)求數(shù)據(jù) + 把數(shù)據(jù)放入buffer
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
w_data_req<=1'b0;
else if((bit_cnt+2)%8==0&&bit_cnt>=30&&clk_cnt==0&¤t_state==WRITE) //提前2個(gè)時(shí)鐘周期
w_data_req<=1'b1;
else
w_data_req<=1'b0;
end
always @(posedge clk_100m or negedge sys_rst_n )begin//讀出的數(shù)據(jù)移位寄存
if(!sys_rst_n)
rd_data_buffer<=8'd0;
else if(bit_cnt>=32&&bit_cnt<=2080&&clk_cnt==0&¤t_state==READ)
rd_data_buffer<={rd_data_buffer[6:0],spi_miso};
else
rd_data_buffer<=rd_data_buffer;
end
always @(posedge clk_100m or negedge sys_rst_n )begin//檢查讀出的數(shù)據(jù)是否正確
if(!sys_rst_n)
data_check<=8'd0;
else if(bit_cnt%8==0&&bit_cnt>=40&&clk_cnt==1&¤t_state==READ)
data_check<=data_check+1'd1;
else
data_check<=data_check;
end
always @(posedge clk_100m or negedge sys_rst_n )begin//讀出的數(shù)據(jù)
if(!sys_rst_n)
r_data<=8'd0;
else if(bit_cnt%8==0&&bit_cnt>38&&clk_cnt==1&¤t_state==READ)
r_data<=rd_data_buffer;
else
r_data<=r_data;
end
always @(posedge clk_100m or negedge sys_rst_n )begin//讀出的數(shù)據(jù)錯(cuò)誤標(biāo)志
if(!sys_rst_n)
erro_flag<=1'd0;
else if(bit_cnt>32&&bit_cnt<=2080&¤t_state==READ&&cmd_cnt==6)begin
if(data_check!=r_data)
erro_flag<=1'd1;
else
erro_flag<=erro_flag;
end
else
erro_flag<=erro_flag;
end
//*把數(shù)據(jù)放入buffer 提前一個(gè)周期
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
data_buffer<=8'd0;
else if((bit_cnt+1)%8==0&&bit_cnt>30&&clk_cnt==1)//*把數(shù)據(jù)放入buffer 提前一個(gè)周期
data_buffer<=spi_data;
else if(clk_cnt==1&¤t_state==WRITE&&bit_cnt>=32)
data_buffer<={data_buffer[6:0],data_buffer[7]};
else
data_buffer<=data_buffer;
end
//*----位移cmd指令存儲(chǔ)器 開(kāi)始:cs選中且dely未生效,提前了100mhz的周期------------
//使50mhz時(shí)數(shù)據(jù)提前半個(gè)周期獲得
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
cmd_buffer<=8'd0;
else if(spi_cs==0&&dely_cnt==0)
cmd_buffer<=spi_cmd;
else if(clk_cnt==1&&(current_state==WEL||current_state==S_ERA||current_state==C_ERA
||current_state==READ||current_state==WRITE||current_state==R_STA_REG)&&bit_cnt<8)
cmd_buffer<={cmd_buffer[6:0],1'b1};
else
cmd_buffer<=cmd_buffer;
end
//取出地址每一位
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
addr_buffer<=8'd0;
else if(spi_cs==0&&dely_cnt==0)
addr_buffer<=spi_addr;
else if(clk_cnt==1&&(current_state==READ||current_state==WRITE)&&bit_cnt>=8&&bit_cnt<32)
addr_buffer<={addr_buffer[22:0],addr_buffer[23]};
else
addr_buffer<=addr_buffer;
end
//------------使能后clk_cnt輸出50M時(shí)鐘用于操作信號(hào)--------------
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
clk_cnt<=1'd0;
else if(dely_cnt==1)
clk_cnt<=clk_cnt+1'd1;
else
clk_cnt<=1'd0;
end
//*---------cs選中器件后的信號(hào)輸出的 dely_cnt 可以認(rèn)為是使能操作------------
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
dely_cnt<=1'd0;
else if(spi_cs==0)begin
if(dely_cnt<1)
dely_cnt<=dely_cnt+1'd1;
else
dely_cnt<=dely_cnt;
end
else
dely_cnt<=1'd0;
end
//*-----------------結(jié)束的延時(shí)計(jì)時(shí)器------------------------------------
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
dely_state_cnt<=1'd0;
else if(spi_cs)begin
if(dely_state_cnt<400000000)
dely_state_cnt<=dely_state_cnt+1'd1;
else
dely_state_cnt<=dely_state_cnt;
end
else
dely_state_cnt<=1'd0;
end
//*-------------------------bit讀寫(xiě)計(jì)數(shù)---------------------
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
bit_cnt<=11'd0;
else if(dely_cnt==1)begin
if(clk_cnt==1'b1)
bit_cnt<=bit_cnt+1'd1;
else
bit_cnt<=bit_cnt;
end
else
bit_cnt<=11'd0;
end
狀態(tài)機(jī) :每個(gè)狀態(tài)該干什么,怎么轉(zhuǎn)移
修改里面的命令和轉(zhuǎn)態(tài)就可以移植到其他的地方了。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-755619.html
//三段式狀態(tài)機(jī)
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n)
current_state<=IDLE;
else
current_state<=next_state;
end
always @(*)begin
case(current_state)
IDLE: begin
if(spi_start&&spi_cmd==WEL_CMD)
next_state=WEL;
else if(spi_start&&spi_cmd==C_ERA_CMD)
next_state=C_ERA;
else if(spi_start&&spi_cmd==S_ERA_CMD)
next_state=S_ERA;
else if(spi_start&&spi_cmd==READ_CMD)
next_state=READ;
else if(spi_start&&spi_cmd==WRITE_CMD)
next_state=WRITE;
else if(spi_start&&spi_cmd==R_STA_REG_CMD)
next_state=R_STA_REG;
else
next_state=IDLE;
end
WEL: begin
if(stdone&&bit_cnt>=8)
next_state=IDLE;
else
next_state=WEL;
end
S_ERA: begin
if(stdone)
next_state=IDLE;
else
next_state=S_ERA;
end
C_ERA: begin
if(stdone)
next_state=IDLE;
else
next_state=C_ERA;
end
READ: begin
if(stdone&&bit_cnt>=8)
next_state=IDLE;
else
next_state=READ;
end
WRITE: begin
if(stdone&&bit_cnt>=8)
next_state=IDLE;
else
next_state=WRITE;
end
R_STA_REG: begin
if(stdone)
next_state=IDLE;
else
next_state=R_STA_REG;
end
default: next_state=IDLE;
endcase
end
always @(posedge clk_100m or negedge sys_rst_n )begin
if(!sys_rst_n) begin
spi_cs<=1'b1;
spi_clk<=1'b0;
spi_clk0<=1'b0;
spi_mosi<=1'b0;
stdone<=1'b0;
end
else begin
case(current_state)
IDLE: begin
spi_cs<=1'b1;
spi_clk<=1'b0;
spi_mosi<=1'b0;
end
WEL: begin
stdone<=1'b0;
spi_cs<=1'b0;
if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt==8&&clk_cnt==0)begin
stdone<=1'b1;
spi_clk<=1'b0;
spi_mosi<=1'b0;
end
else if(bit_cnt==8&&clk_cnt==1)begin
spi_cs<=1'b1;
end
end
C_ERA: begin
stdone<=1'b0;
if(dely_state_cnt==10)
spi_cs<=1'b0;
else if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt==8&&clk_cnt==0)begin
stdone<=1'b1;
spi_clk<=1'b0;
spi_mosi<=1'b0;
end
else if(bit_cnt==8&&clk_cnt==1)begin
spi_cs<=1'b1;
end
end
S_ERA: begin
stdone<=1'b0;
if(dely_state_cnt==10)
spi_cs<=1'b0;
else if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin
spi_cs<=1'b0;
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=addr_buffer[23];
end
else if(bit_cnt==32&&clk_cnt==0) begin
spi_cs<=1'b1;
spi_clk<=1'b0;
spi_mosi<=1'b0;
stdone<=1'b1;
end
end
READ: begin
stdone<=1'b0;
if(dely_state_cnt==10)
spi_cs<=1'b0;
else if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=addr_buffer[23];
end
else if(bit_cnt>=32&&bit_cnt<2080)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=1'b0;
end
else if(bit_cnt==2080&&clk_cnt==0) begin
spi_clk<=1'b0;
spi_mosi<=1'b0;
stdone<=1'b1;
end
else if(bit_cnt==2080&&clk_cnt==1) begin
spi_cs<=1'b1;
end
end
WRITE: begin
stdone<=1'b0;
if(dely_state_cnt==10)
spi_cs<=1'b0;
else if(dely_cnt==1&&bit_cnt<8) begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt>=8&&bit_cnt<32&&spi_cs==0)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=addr_buffer[23];
end
else if(bit_cnt>=32&&bit_cnt<2080)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=data_buffer[7];
end
else if(bit_cnt==2080&&clk_cnt==0) begin
spi_clk<=1'b0;
spi_mosi<=1'b0;
stdone<=1'b1;
end
else if(bit_cnt==2080&&clk_cnt==1) begin
spi_cs<=1'b1;
end
end
R_STA_REG:begin
stdone<=1'b0;
if(dely_state_cnt==10)
spi_cs<=1'b0;
else if(dely_cnt==1&&bit_cnt<8)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=cmd_buffer[7];
end
else if(bit_cnt==8)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
spi_mosi<=1'b0;
end
else if(~spi_miso&&bit_cnt%8==0)begin
spi_clk<=1'b0;
spi_cs<=1'b1;
stdone<=1'b1;
end
else if(~spi_cs&&dely_cnt==1)begin
spi_clk0<=~spi_clk0;
spi_clk<=spi_clk0;
end
end
default: begin
stdone<=1'b0;
spi_cs<=1'b1;
spi_clk<=1'b0;
spi_clk0<=1'b0;
spi_mosi<=1'b0;
end
endcase
end
end
endmodule
2. SPI協(xié)議的使用
首先系統(tǒng)開(kāi)始運(yùn)行,來(lái)幾個(gè)周期延伸。
spi_start信號(hào)只是一個(gè)周期脈沖。
idel_flag_r是進(jìn)入空閑狀態(tài)的標(biāo)志位也就是意味著上一步操作完成。
cmd計(jì)數(shù)指令不斷加來(lái)切換不同的命令。
spi_cmd 輸出命令文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-755619.html
module flash_rw(
input sys_clk ,
input sys_rst_n ,
input idel_flag_r ,
input w_data_req ,
output reg[3:0 ] cmd_cnt ,
output reg spi_start ,//spi開(kāi)啟使能。
output reg[7:0 ] spi_cmd ,
output reg[7:0 ] spi_data
);
//指令集
parameter WEL_CMD =16'h06;
parameter S_ERA_CMD =16'h20;
parameter C_ERA_CMD =16'hc7;
parameter READ_CMD =16'h03;
parameter WRITE_CMD =16'h02;
parameter R_STA_REG_CMD=8'h05 ;
//reg define
reg[3:0] flash_start;
//SPI 要寫(xiě)入的數(shù)據(jù)
always @(posedge sys_clk or negedge sys_rst_n )begin
if(!sys_rst_n)
flash_start<=0;
else if(flash_start<=5)
flash_start<=flash_start+1;
else
flash_start<=flash_start;
end
always @(posedge sys_clk or negedge sys_rst_n )begin
if(!sys_rst_n)
cmd_cnt<=0;
else if(flash_start==4)
spi_start<=1'b1;
else if(idel_flag_r&&cmd_cnt<10)begin
cmd_cnt<=cmd_cnt+1;
spi_start<=1'b1;
end
else begin
cmd_cnt<=cmd_cnt;
spi_start<=1'b0;
end
end
always @(posedge sys_clk or negedge sys_rst_n )begin
if(!sys_rst_n)
spi_data<=8'd0;
else if(w_data_req)
spi_data<=spi_data+1'b1;
else
spi_data<=spi_data;
end
always @(*)begin
case(cmd_cnt)
0:spi_cmd=WEL_CMD;
1:spi_cmd=C_ERA_CMD;
2:spi_cmd=R_STA_REG_CMD;
3:spi_cmd=WEL_CMD;
4:spi_cmd=WRITE_CMD;
5:spi_cmd=R_STA_REG_CMD;
6:spi_cmd=READ_CMD;
7:spi_cmd=WEL_CMD;
8:spi_cmd=S_ERA_CMD;
9:spi_cmd=R_STA_REG_CMD;
10:spi_cmd=READ_CMD;
default:;
endcase
end
endmodule
到了這里,關(guān)于FPGA模塊——SPI協(xié)議(讀寫(xiě)FLASH)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!