本实验学习了通过spi通信协议,驱动flash;完成连续写操作。
连续写:
本质上还是页编程指令,两种连续写的方式:
1,每次只写1byte的数据。
2,每次写满1页数据,计算剩余数据够不够写满1页,并计算地址。
本实验采取方案一。
模块框图:
状态机:
时序图:
代码:
只放spi模块。
module spi (
input wire sys_clk ,
input wire sys_rst_n ,
input wire key_flag ,
input wire miso ,
output reg cs_n ,
output reg sck ,
output reg mosi ,
output reg po_flag ,
output wire [7:0] po_data
);
// localparam define 一般状态机的状态定义用局部参数就可以。
localparam IDLE = 4'b0001 ,
INSTRUCT = 4'b0010 ,
READ = 4'b0100 ,
SEND = 4'b1000 ;
// parameter define 指令,计数器最大值,用全局参数定义。
parameter COMD_REA = 8'h03 , // comd_read
ADDR_SEC = 8'h00 , // address_secter 扇区地址
ADDR_PAG = 8'h00 , // address_page 页地址(行地址)
ADDR_BYT = 8'hc8 , // assress_byte 字节地址
NUM_COMD = 4'd4 ; // 用来记录在指令状态传递指令和地址byte数量
parameter CNT_MAX_BYTE = 11'd260 , // 4 + 要读出的数据。例如: 4 + 256
CNT_MAX_SEND = 20'd53000 ;
// reg signal define
reg [3:0] state_c ;
reg [3:0] state_n ;
reg cnt_20_ns ;
reg [2:0] cnt_bit ;
reg [10:0] cnt_byte ;
reg flag_b ; // flag_byte
reg flagBreg ;
reg flag_R_S ; // flag_byte
reg flag_RSr ;
reg [7:0] datInFifo ; // data_in_fifo
reg flag_data ; // flag_data 采样标志信号
reg flaInFifo ; // flag_in_fifo
reg [19:0] cnt_send ; // uart_tx模块发送1byte数据的等待时间。
reg flaSenEnd ; // 计数器cnt_send计数到CNT_MAX_SEND - 2 拉高一个时钟周期,
reg flag_out_fifo_reg ;
// wire signal define
wire empty ;
wire full ;
wire flaOutFif ; // flag_out_fifo
wire [9:0] usedw ; // fifo中存储的数据量
wire IDLEtoINSTRUCT ;
wire INSTRUCTto_READ ;
wire READtoSEND ;
wire SENDtoIDLE ;
/**********************************************************************/
// // reg signal describe
/*******状态机采用三段式描述*******/
// reg [3:0] state_c ;
// reg [3:0] state_n ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
state_c <= IDLE ;
else
state_c <= state_n ;
end
always @(*) begin
case (state_c)
IDLE : if(IDLEtoINSTRUCT)
state_n <= INSTRUCT ;
else
state_n <= IDLE ;
INSTRUCT : if(INSTRUCTto_READ)
state_n <= READ ;
else
state_n <= INSTRUCT ;
READ : if(READtoSEND)
state_n <= SEND ;
else
state_n <= READ ;
SEND : if(SENDtoIDLE)
state_n <= IDLE ;
else
state_n <= SEND ;
default: state_n <= IDLE ;
endcase
end
assign IDLEtoINSTRUCT = (state_c == IDLE ) && (key_flag) ;
assign INSTRUCTto_READ = (state_c == INSTRUCT) && (flagBreg) ; // 指令的的最后1byte发送完毕
assign READtoSEND = (state_c == READ ) && (flag_RSr) ; // 读完想要的最后1byte
assign SENDtoIDLE = (state_c == SEND ) && (flaSenEnd && empty) ;
// reg cnt_20_ns ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_20_ns <= 1'b0 ;
else if(state_c == INSTRUCT || state_c == READ)
cnt_20_ns <= cnt_20_ns + 1'b1 ;
else if(state_c != INSTRUCT || state_c != READ)
cnt_20_ns <= 1'b0 ;
else
cnt_20_ns <= 1'b0 ;
end
// reg [2:0] cnt_bit ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_bit <=3'd0 ;
else
case (state_c)
IDLE : cnt_bit <=3'd0 ;
INSTRUCT: if(!cnt_20_ns && sck && cnt_bit == 7)
cnt_bit <= 3'd0 ;
else if(!cnt_20_ns && sck)
cnt_bit <= cnt_bit + 1'b1 ;
READ : if(!cnt_20_ns && sck && cnt_bit == 7)
cnt_bit <= 3'd0 ;
else if(!cnt_20_ns && sck)
cnt_bit <= cnt_bit + 1'b1 ;
SEND : cnt_bit <=3'd0 ;
default : cnt_bit <=3'd0 ;
endcase
end
// reg [10:0] cnt_byte ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_byte <= 4'd0 ;
else if(cnt_bit == 7 && cnt_byte == CNT_MAX_BYTE - 1 && !cnt_20_ns && sck)
cnt_byte <= 4'd0 ;
else if(cnt_bit == 7 && !cnt_20_ns && sck)
cnt_byte <= cnt_byte + 1'b1 ;
else
cnt_byte <= cnt_byte ;
end
// reg flag_R_S ;
// reg flag_b ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
flag_b <= 1'b0 ;
flag_R_S <= 1'b0 ;
end
else
case (state_c)
IDLE : begin
flag_b <= 1'b0 ;
flag_R_S <= 1'b0 ;
end
INSTRUCT: begin
if((cnt_byte == NUM_COMD - 1) && (cnt_bit == 7) && !cnt_20_ns && sck)
flag_b <= 1'b1 ;
else
flag_b <= flag_b ;
flag_R_S <= 1'b0 ;
end
READ : begin
if(cnt_byte == NUM_COMD)
flag_R_S <= 1'b0 ;
else if((cnt_byte == CNT_MAX_BYTE - 1) && (cnt_bit == 7) && !cnt_20_ns && sck)
flag_R_S <= 1'b1 ;
flag_b <= 1'b0 ;
end
SEND : begin
flag_b <= 1'b0 ;
flag_R_S <= 1'b0 ;
end
default : begin
flag_b <= 1'b0 ;
flag_R_S <= 1'b0 ;
end
endcase
end
// reg flagBreg ;
// reg flag_RSr ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
flagBreg <= 1'b0 ;
else
flagBreg <= flag_b ;
end
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
flag_RSr <= 1'b0 ;
else
flag_RSr <= flag_R_S ;
end
// reg [7:0] datInFifo ; // data_in_fifo
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
datInFifo <= 1'b0 ;
else if(flag_data)
datInFifo <= {datInFifo[6:0],miso}; // 读flash中数据,先传的低位{miso,datInFifo[7:1]}。
else
datInFifo <= datInFifo ;
end
// reg flag_data ; // flag_data 采样标志信号
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
flag_data <= 1'b0 ;
else if(state_c == READ) begin
if(cnt_20_ns && !sck)
flag_data <= 1'b1 ;
else
flag_data <= 1'b0 ;
end else begin
flag_data <= 1'b0 ;
end
end
// reg flaInFifo ; // flag_in_fifo
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
flaInFifo <= 1'b0 ;
else if(state_c == READ && cnt_bit == 7 && flag_data)
flaInFifo <= 1'b1 ;
else
flaInFifo <= 1'b0 ;
end
// reg [19:0] cnt_send ; // uart_tx模块发送1byte数据的等待时间。
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cnt_send <= 20'd0 ;
else
if(state_c == SEND) begin
if(cnt_send == CNT_MAX_SEND - 1)
cnt_send <= 20'd0 ;
else
cnt_send <= cnt_send + 1'b1 ;
end
else
cnt_send <= 20'd0 ;
end
// reg flag_out_fifo_reg ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
flag_out_fifo_reg <= 1'b0 ;
else if(flaSenEnd && !empty)
flag_out_fifo_reg <= 1'b1 ;
else
flag_out_fifo_reg <= 1'b0 ;
end
// reg flaSenEnd ;
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
flaSenEnd <= 1'b0 ;
else if(cnt_send == CNT_MAX_SEND - 2)
flaSenEnd <= 1'b1 ;
else
flaSenEnd <= 1'b0 ;
end
// output signal describe
// cs_n ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
cs_n <= 1'b1 ;
else
case (state_c)
IDLE : if(key_flag)
cs_n <= 1'b0 ;
else
cs_n <= cs_n ;
INSTRUCT: cs_n <= cs_n ;
READ : cs_n <= cs_n ;
SEND : cs_n <= 1'b1 ;
default : cs_n <= 1'b1 ;
endcase
end
// sck ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
sck <= 1'b0 ;
else
case (state_c)
IDLE : sck <= 1'b0 ;
INSTRUCT: if(cnt_20_ns)
sck <= ~sck ;
else
sck <= sck ;
READ : if(cnt_20_ns)
sck <= ~sck ;
else
sck <= sck ;
SEND : sck <= 1'b0 ;
default : sck <= 1'b0 ;
endcase
end
// mosi ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n) begin
mosi <= 1'b0 ;
end else begin
case (state_c)
IDLE : mosi <= 1'b0 ;
INSTRUCT: case (cnt_byte)
0 : if(cnt_bit == 0)
mosi <= COMD_REA[7] ;
else if(cnt_20_ns && sck)
mosi <= COMD_REA[7 - cnt_bit] ;
else
mosi <= mosi ;
1 : if(cnt_bit == 0)
mosi <= ADDR_SEC[7] ;
else if(cnt_20_ns && sck)
mosi <= ADDR_SEC[7 - cnt_bit] ;
else
mosi <= mosi ;
2 : if(cnt_bit == 0)
mosi <= ADDR_PAG[7] ;
else if(cnt_20_ns && sck)
mosi <= ADDR_PAG[7 - cnt_bit] ;
else
mosi <= mosi ;
3 : if(cnt_bit == 0)
mosi <= ADDR_BYT[7] ;
else if(cnt_20_ns && sck)
mosi <= ADDR_BYT[7 - cnt_bit] ;
else
mosi <= mosi ;
default : mosi <= 1'b0 ;
endcase
READ : mosi <= 1'b0 ;
SEND : mosi <= 1'b0 ;
default : mosi <= 1'b0 ;
endcase
end
end
// po_flag ,
always @(posedge sys_clk or negedge sys_rst_n) begin
if(~sys_rst_n)
po_flag <= 1'b0 ;
else
po_flag <= flag_out_fifo_reg ;
end
// wire [7:0] po_data ;// 直接连接到fifo的输出端口。
// */
/***********************例化FIFO***************************************/
assign flaOutFif = flag_out_fifo_reg ;
fifo_1024x8 fifo_1024x8_inst(
.clock ( sys_clk ) ,
.data ( datInFifo ) ,
.rdreq ( flaOutFif ) ,
.wrreq ( flaInFifo ) ,
.empty ( empty ) ,
.full ( full ) ,
.q ( po_data ) ,
.usedw ( usedw )
);
endmodule
仿真波形:
上版验证成功。