Serial Flash Loader读写Flash存储芯片

时间:2022-10-13 00:14:43

主要通过调用Quartus的ip核Serial Flash Loader对M25P16的擦除,写和读

因为Serial Flash Loader没有仿真库,所以验证数据是否写入和读出正确时,这里采用Quartus 自带的SignalTap II 在线调试验证的

先来看一下Serial Flash Loader的调用
Serial Flash Loader读写Flash存储芯片

代码实现分为SPI和SPI_ctrl两个模块,Flash的擦除,写入,读写都在SPI_ctrl模块中,当然也可以单独分开,擦除,写入,读写各作为一个模块

先来一下RTL视图吧
Serial Flash Loader读写Flash存储芯片

实现以3个按键消抖后分别控制擦除,写入,和读

时序图:
SE(擦除)
Serial Flash Loader读写Flash存储芯片
PP(页写入)
Serial Flash Loader读写Flash存储芯片
RD(读)
Serial Flash Loader读写Flash存储芯片

实现代码:

1.按键模块

//key_press 主要代码
//当按键按下时经过消抖延时后 产生一个按下的脉冲标志key_in_flag
module key_press(
input sclk,
input rst_n,
input key_in,
output reg key_in_flag
);
// .....
always @(posedge sclk or negedge rst_n)
if(rst_n ==1'b0)
state <= IDLE;
else begin
case(state)
IDLE : if(key_neg_flag == 1'b1)
state <=PRE_FLAG;
PRE_FLAG: if(pre_end_10ms == 1'b1 && key_in == 1'b0)
state <=KEY_FLAG;
else if(pre_end_10ms == 1'b1 && key_in == 1'b1)
state <= IDLE;
KEY_FLAG: if(key_pos_flag == 1'b1)
state <= POST_FLAG;
POST_FLAG: if(post_end_10ms == 1'b1)
state <= IDLE;
default : state <=IDLE;
endcase
end

//key flag
always @(posedge sclk or negedge rst_n)
if(rst_n == 1'b0)
key_in_flag <= 1'b0;
else if(state == KEY_FLAG && key_cnt == END_20MS)
key_in_flag <= 1'b1;
else
key_in_flag <= 1'b0;
//.......
endmodule

2.SPI_ctrl模块

module SPI_ctrl(
input clk,
input rst_n,
input key_se,
input key_wr,
input key_rd,

output reg CS_n,
output reg SCK,
output reg DATA_IN
);
//-------------instruction------
parameter
WR_Instru = 8'h06,
SE_Instru = 8'hd8,
PP_Instru = 8'h02,
RD_Instru = 8'h03,
ADDR = 24'h000000,
WR_DATA = 8'h0f;//写一个byte为例
//--------部分主要代码-------------
//---------state ctrl falg ----------------------------------------
wire wr_state_flag,se_state_flag,pp_state_flag,rd_state_flag;
assign wr_state_flag = (state == WREN && cnt_sck == 7'd19)? 1'b1:1'b0;
assign se_state_flag = (state == SE && cnt_sck == 7'd67)? 1'b1:1'b0;
assign pp_state_flag = (state == PP && cnt_sck == 7'd83)? 1'b1:1'b0;
assign rd_state_flag = (state == RD && cnt_sck == 7'd83)? 1'b1:1'b0;

//---------en_sck ctrl flag ----------------------------------------
wire en_sck_wr,en_sck_se,en_sck_pp,en_sck_rd;
assign en_sck_wr = (state == WREN && cnt_sck == 7'd17)? 1'b1:1'b0;
assign en_sck_se = (state == SE && cnt_sck == 7'd65) ? 1'b1:1'b0;
assign en_sck_pp = (state == PP && cnt_sck == 7'd81) ? 1'b1:1'b0;
assign en_sck_rd = (state == RD && cnt_sck == 7'd81) ? 1'b1:1'b0;
//----------chip selection ctrl----------------------------------------
always @(posedge clk or negedge rst_n)
if(!rst_n)
CS_n <= 1'b1;
else if(key_se || key_wr || key_rd || cnt_delay == 4'd15)
CS_n <= 1'b0;
else if(wr_state_flag||se_state_flag||pp_state_flag||rd_state_flag)
CS_n <= 1'b1;
else
CS_n <= CS_n;

//------------cs_n==0 cnt_sck++-----------------------------------------
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_sck <= 7'd0;
else if(CS_n == 1'b0)
cnt_sck <= cnt_sck + 1'b1;
else
cnt_sck <= 7'd0;

//-------------SCK signel en-----------------------------------------
always @(posedge clk or negedge rst_n)
if(!rst_n)
en_sck <= 1'b0;
else if(cnt_sck == 7'd1)
en_sck <= 1'b1;
else if(en_sck_wr ||en_sck_se || en_sck_pp || en_sck_rd)
en_sck <= 1'b0;
else
en_sck <= en_sck;

//-----------SCK = ~SCK-----------------------------------------------
always @(posedge clk or negedge rst_n)
if(!rst_n)
SCK <= 1'b0;
else if((state == WREN||state == SE||state == PP || state == RD) && en_sck)
SCK <= ~SCK;
else
SCK <= 1'b0;

//-------------DATA_IN ctrl--------------------------------------------
always @(posedge clk or negedge rst_n)
if(!rst_n)
data_wr <= WR_Instru;
else if(state == WREN && en_sck == 1'b1 && SCK == 1'b0)
data_wr <= {data_wr[6:0],data_wr[7]};
else
data_wr <= data_wr;

always @(posedge clk or negedge rst_n)
if(!rst_n)
data_se <= {SE_Instru,ADDR};
else if(state == SE && en_sck == 1'b1 && SCK == 1'b0)
data_se <= {data_se[30:0],data_se[31]};
else
data_se <= data_se;

//-------------DATA_IN------------------------------
always @(posedge clk or negedge rst_n)
if(!rst_n)
DATA_IN <= 1'b0;
else begin
case(state)
WREN : DATA_IN <= data_wr[7];
SE : DATA_IN <= data_se[31];
PP : DATA_IN <= data_pp[39];
RD : DATA_IN <= data_rd[31];
default : DATA_IN <= 1'b0;
endcase
end

always @(posedge clk or negedge rst_n)
if(!rst_n)
data_pp <= {PP_Instru,ADDR,WR_DATA};
else if(state == PP && en_sck == 1'b1 && SCK == 1'b0)
data_pp <= {data_pp[38:0],data_pp[39]};
else
data_pp <= data_pp;

always @(posedge clk or negedge rst_n)
if(!rst_n)
shift_flag <= 1'b0;
else if(cnt_sck > 7'd0 && cnt_sck <= 7'd63)
shift_flag <= 1'b1;
else
shift_flag <= 1'b0;

always @(posedge clk or negedge rst_n)
if(!rst_n)
data_rd <= {RD_Instru,ADDR};
else if(state == RD && en_sck == 1'b1 && SCK == 1'b0 && shift_flag == 1'b1)
data_rd <= {data_rd[30:0],data_rd[31]};
else
data_rd <= data_rd;

//------------key state choose flag-----------------------------------
always @(posedge clk or negedge rst_n)
if(!rst_n)
key_se_flag <= 1'b0;
else if(key_se)
key_se_flag <= 1'b1;
else if(se_state_flag)// SE state end
key_se_flag <= 1'b0;
else
key_se_flag <= key_se_flag;

always @(posedge clk or negedge rst_n)
if(!rst_n)
key_wr_flag <= 1'b0;
else if(key_wr)
key_wr_flag <= 1'b1;
else if(pp_state_flag)// PP state end
key_wr_flag <= 1'b0;
else
key_wr_flag <= key_wr_flag;

always @(posedge clk or negedge rst_n)
if(!rst_n)
key_rd_flag <= 1'b0;
else if(key_rd)
key_rd_flag <= 1'b1;
else if(rd_state_flag)// RD state end
key_rd_flag <= 1'b0;
else
key_rd_flag <= key_rd_flag;

//-------------DELAY state keep------------------------------------------
always @(posedge clk or negedge rst_n)
if(!rst_n)
cnt_delay <= 4'd0;
else if(state == DELAY)begin
if(cnt_delay == 4'd15)
cnt_delay <= 4'd0;
else
cnt_delay <= cnt_delay + 1'b1;
end
else
cnt_delay <= 4'd0;

//-------------------state skip-----------------------------------------
always @(posedge clk or negedge rst_n)
if(!rst_n)
state <= IDLE;
else begin
case(state)
IDLE : if(CS_n == 1'b0 && key_rd_flag)
state <= RD;
else if(CS_n == 1'b0)
state <= WREN;
else
state <= IDLE;
WREN : if(CS_n == 1'b1)
state <= DELAY;
else
state <= WREN;
DELAY : if(CS_n == 1'b0 && key_se_flag)
state <= SE;
else if(CS_n == 1'b0 && key_wr_flag)
state <= PP;
else
state <= DELAY;
SE : if(CS_n == 1'b1)
state <= IDLE;
else
state <= SE;
PP : if(CS_n == 1'b1)
state <= IDLE;
else
state <= PP;
RD : if(CS_n == 1'b1)
state <= IDLE;
else
state <= RD;
default : state <= IDLE;
endcase
end

endmodule

3.TOP模块
//因为Serial Flash Loader使用的是专用引脚,所以这里不用设置输出

module SPI(
input clk,
input rst_n,
input key_se,
input key_wr,
input key_rd

);

//M25P16根据手可知最大达到 50 MHz SPI Bus Interface
//而手册中read data byte可知,a maximum frequency fR,读数据最大频率20Mhz
//所以这里SCK信号是12.5MHz
//比较简单的实现方法是:通过PLL对系统时钟进行分频得到25Mhz即可
key_press key_se_inst(
.sclk(clk_25M),
.rst_n(rst_n),
.key_in(key_se),
.key_in_flag(key_se_flag)
);
//....
SPI_ctrl SPI_ctrl_inst(
.clk(clk_25M),
.rst_n(rst_n),
.key_se(key_se_flag),
.key_wr(key_wr_flag),
.key_rd(key_rd_flag),

.CS_n(CS_n),
.SCK(SCK),
.DATA_IN(DATA_IN)
);

spi_flash spi_flash_inst (
.asdo_in ( DATA_IN ),
.asmi_access_granted ( 1'b1 ),
.dclk_in ( SCK ),
.ncso_in ( CS_n ),
.noe_in ( 1'
b0 ),
.asmi_access_request ( ),
.data0_out ( )
);

因为Serial Flash Loader不能仿真验证,这里只对擦除,写入和读的时序进行仿真
Serial Flash Loader读写Flash存储芯片

时序达到要求后,进一步使用Signaltap验证是否准确写入和读出

擦除验证
Serial Flash Loader读写Flash存储芯片
写入验证
Serial Flash Loader读写Flash存储芯片
读出验证
Serial Flash Loader读写Flash存储芯片

这里可以看到 DATA0_OUT的输出是’h0f 与我们写入的数据一致