前面几篇详细 介绍了SPI的原理,并且实现了对flash芯片的写使能,读状态,擦除,页读,页写
FPGA——SPI总线详解(概念)_居安士的博客-CSDN博客_fpga芯片
FPGA——SPI总线控制flash(1)(含代码)_居安士的博客-CSDN博客
FPGA——SPI总线控制flash(2)(含代码)_居安士的博客-CSDN博客
接下来我们需要将各个模块汇合到一起:
目录
总线应答模块
之前的总线应答都是写在TB文件里面,现在所以模块写齐了之后,需要将多个模块的总线应答汇总在一个模块里,根据请求总线的信号,回复ack。当接收到总线同意通信的信号后,将spi_cs ,spi_clk ,spi_dout ,spi_din传递给总线,传递结束后,请求总线关闭,回复ack。
总线控制模块需要完成的任务:
- 控制5个模块(写使能,读状态,擦除,页写,页读)的总线应答
- 根据模块给spi_bus信号(spi_cs ,spi_clk ,spi_dout ,spi_din)
- 流程图如下:
0:给5个模块分配标号
1:在请求总线开放信号到来时,根据标号给总线应答开放信号
2:在请求总线关闭信号到来时,采请求总线下降沿,给总线应答关闭信号
总线一次只能通信一个模块,根据分配的标号,将模块输出的spi_cs ,spi_clk ,spi_dout给总线,总线输出的spi_din给模块
module mux_top(
input clk,
input reset,
//写使能
input mux_wren ,
output reg mux_wren_done,
input spi_cs1 ,
input spi_clk1 ,
input spi_dout1 ,
output reg spi_din1 ,
//读状态
input mux_status ,
output reg mux_status_done,
input spi_cs2 ,
input spi_clk2 ,
input spi_dout2 ,
output reg spi_din2 ,
//擦除
input mux_earse ,
output reg mux_earse_done ,
input spi_cs3 ,
input spi_clk3 ,
input spi_dout3 ,
output reg spi_din3 ,
//页写
input mux_write ,
output reg mux_write_done ,
input spi_cs4 ,
input spi_clk4 ,
input spi_dout4 ,
output reg spi_din4 ,
//页读
input mux_read ,
output reg mux_read_done ,
input spi_cs5 ,
input spi_clk5 ,
input spi_dout5 ,
output reg spi_din5 ,
//总线bus
input spi_din ,
output spi_cs ,
output spi_clk ,
output spi_dout
);
//输入打一拍
reg mux_wren_dly ;
reg mux_status_dly;
reg mux_earse_dly ;
reg mux_write_dly ;
reg mux_read_dly ;
always@(posedge clk)begin
if(reset)begin
mux_wren_dly <=1'd0;
mux_status_dly <=1'd0;
mux_earse_dly <=1'd0;
mux_write_dly <=1'd0;
mux_read_dly <=1'd0;
end
else begin
mux_wren_dly <=mux_wren ;
mux_status_dly <=mux_status;
mux_earse_dly <=mux_earse ;
mux_write_dly <=mux_write ;
mux_read_dly <=mux_read ;
end
end
reg [2:0] mux_mode;//选择写、读、擦除等模式
reg [1:0] state;//状态机
always@(posedge clk)begin
if(reset)begin
mux_mode<=1'd0;
state<=2'd0;
end
else begin
mux_wren_done <=1'd0;
mux_status_done<=1'd0;
mux_earse_done <=1'd0;
mux_write_done <=1'd0;
mux_read_done <=1'd0;
case(state)
2'd0:begin //根据请求总线的信号选择当前模式
if(mux_wren_dly)begin
mux_mode<=3'd1;
state<=2'd1;
end
else if(mux_status_dly)begin
mux_mode<=3'd2;
state<=2'd1;
end
else if(mux_earse_dly)begin
mux_mode<=3'd3;
state<=2'd1;
end
else if(mux_write_dly)begin
mux_mode<=3'd4;
state<=2'd1;
end
else if(mux_read_dly)begin
mux_mode<=3'd5;
state<=2'd1;
end
else begin
mux_mode<=3'd0;
state<=2'd0;
end
end
2'd1:begin//回复同意开启总线
case(mux_mode)
3'd1:mux_wren_done<=1'd1;
3'd2:mux_status_done<=1'd1;
3'd3:mux_earse_done<=1'd1;
3'd4:mux_write_done<=1'd1;
3'd5:mux_read_done<=1'd1;
endcase
state<=2'd2;
end
2'd2:begin//回复关闭总线
if(~mux_wren&&mux_wren_dly)begin//在mux_wren下降沿
mux_wren_done<=1'd1;
state<=2'd0;
end
else if(~mux_status&&mux_status_dly)begin
mux_status_done<=1'd1;
state<=2'd0;
end
else if(~mux_earse&&mux_earse_dly)begin
mux_earse_done<=1'd1;
state<=2'd0;
end
else if(~mux_write&&mux_write_dly)begin
mux_write_done<=1'd1;
state<=2'd0;
end
else if(~mux_read&&mux_read_dly)begin
mux_read_done<=1'd1;
state<=2'd0;
end
end
endcase
end
end
//总线经过选择之后,发送cs
assign spi_cs=(mux_mode==3'd1)?spi_cs1
(mux_mode==3'd2)?spi_cs2
(mux_mode==3'd3)?spi_cs3
(mux_mode==3'd4)?spi_cs4
(mux_mode==3'd5)?spi_cs5
1'd1;
//总线经过选择之后,发送clk
assign spi_clk =(mux_mode==3'd1)? spi_clk1 :
(mux_mode==3'd2)? spi_clk2 :
(mux_mode==3'd3)? spi_clk3 :
(mux_mode==3'd4)? spi_clk4 :
(mux_mode==3'd5)? spi_clk5 :
1'd0;
//总线经过选择之后,发送dout
assign spi_dout =(mux_mode==3'd1)? spi_dout1 :
(mux_mode==3'd2)? spi_dout2 :
(mux_mode==3'd3)? spi_dout3 :
(mux_mode==3'd4)? spi_dout4 :
(mux_mode==3'd5)? spi_dout5 :
1'd1;
//把输出的din给模块
always@(*)begin
if(mux_mode==3'd2)begin
spi_din2=spi_din;
end
else if (mux_mode==3'd5)begin
spi_din5=spi_din;
end
else begin
spi_din2=1'd1;
spi_din5=1'd1;
end
end
endmodule
SPI控制模块
spi控制模块需要完成的任务是:
1 定义操作启动信号,利用操作启动信号来控制每种模块的启动信号
定义操作信号(0~9),根据操作指令来控制spi里面5个模块(写使能,读状态,擦除,页写,页读)的每种指令
2 输入想要控制spi里面模块的地址和突发长度
3 输出写类完成信号(写使能,擦除,页写)和读类完成信号(读状态,页读),
以及总线忙信号和总线不忙信号
4 控制写使能模块:输出启动写使能信号,输出写使能指令
5 控制读状态模块:输出启动读状态信号,输出读状态指令,输入读状态数据
6 控制擦除模块:输出启动擦除信号,输出擦除指令,输出擦除的地址
7 控制页写模块:输出页写擦除信号,输出页写指令,输出页写的地址,输出页写的突发长度
8 控制页读模块:输出页读擦除信号,输出页读指令,输出页读的地址,输出页读的突发长度,输入页读的数据
编写代码如下:
module spi_control(
input clk,
input reset,
//操作信号
input op_en ,//启动操作
input [3:0] op_cmd ,//0:写使能04h 1:写使能06h 2:读状态05h 3:读状态35h 4:擦除20h 5:擦除52h 6:擦除D8h 7:擦除整片 8:页写 9:页读
input [23:0]op_addr ,
input [8:0] op_brust_length,
output reg wr_cmd_done , //返回写类done:写使能,页写,擦除
output reg rd_cmd_done ,//返回读类done:读状态寄存器,页读
output reg rd_cmd_fail ,//读状态寄存器为忙
output reg status_ok ,
//写使能
output reg start_wr ,
output reg wren_cmd ,
input wren_done ,
//读状态
output reg start_status ,
output reg status_cmd ,
input[7:0] status_data ,
input status_done ,
//擦除
output reg start_erase ,
output reg [1:0] erase_cmd ,
output reg [23:0]erase_addr ,
input erase_done ,
//页写
output reg start_write ,
output reg [8:0] wr_brust_length ,
output reg [23:0]write_addr ,
input write_done ,
//页读
output reg start_read ,
output reg [8:0] rd_brust_length ,
output reg [23:0]read_addr ,
input read_done
);
//输出start信号和对应的命令
always@(posedge clk)begin
if(reset)begin
wr_cmd_done <=1'd0;
rd_cmd_done <=1'd0;
rd_cmd_fail <=1'd0;
status_ok <=1'd0;
start_wr <=1'd0;
wren_cmd <=1'd0;
start_status <=1'd0;
status_cmd <=1'd0;
start_erase <=1'd0;
erase_cmd <=2'd0;
erase_addr <=24'd0;
start_write <=1'd0;
wr_brust_length<=8'd0;
write_addr <=24'd0;
start_read <=1'd0;
rd_brust_length<=8'd0;
read_addr <=24'd0;
end
else begin
if(op_en)begin
case(op_cmd)
4'd0:begin//写使能04h
start_wr<=1'd1;
wren_cmd<=1'd0;
end
4'd1:begin//写使能06h
start_wr<=1'd1;
wren_cmd<=1'd1;
end
4'd2:begin//读状态05h
start_status<=1'd1;
status_cmd<=1'd0;
end
4'd3:begin//读状态35h
start_status<=1'd1;
status_cmd<=1'd1;
end
4'd4:begin//擦除20h
start_erase<=1'd1;
erase_cmd<=2'd0;
erase_addr <=op_addr;
end
4'd5:begin//擦除52h
start_erase<=1'd1;
erase_cmd<=2'd1;
erase_addr <=op_addr;
end
4'd6:begin//擦除D8h
start_erase<=1'd1;
erase_cmd<=2'd2;
erase_addr <=op_addr;
end
4'd7:begin//擦除整片
start_erase<=1'd1;
erase_cmd<=2'd3;
erase_addr <=op_addr;
end
4'd8:begin//页写
start_write<=1'd1;
wr_brust_length<=op_brust_length;
write_addr <=op_addr;
end
4'd9:begin//页读
start_read<=1'd1;
rd_brust_length<=op_brust_length;
read_addr <=op_addr;
end
endcase
end
else begin
start_wr<=1'd0;
start_status<=1'd0;
start_erase<=1'd0;
start_write<=1'd0;
start_read<=1'd0;
end
end
end
always@(posedge clk)begin//输出写类完成信号
if(wren_done||erase_done||write_done)begin
wr_cmd_done<=1'b1;
end
else begin
wr_cmd_done<=1'b0;
end
end
always@(posedge clk)begin//输出读类完成信号
if(status_done||read_done)begin
rd_cmd_done<=1'b1;
end
else begin
rd_cmd_done<=1'b0;
end
end
always@(posedge clk)begin
if((status_data[0]==1'b0)&&status_done)begin//读状态总线不忙
status_ok<=1'b1;
end
else begin
status_ok<=1'b0;
end
end
always@(posedge clk)begin
if((status_data[0]==1) &&status_done)begin//status_data_dly=1表示状态忙
rd_cmd_fail<=1'b1;
end
else begin
rd_cmd_fail<=1'b0;
end
end
endmodule
SPI_FLASH总模块
接下来把所有的模块例化到一起;
module spi_flash(
input clk,
input reset ,
input clk_wr ,
input clk_rd ,
//控制信号
input op_en ,
input [3:0] op_cmd ,
input [23:0] op_addr ,
input [8:0] op_brust_length,
//done信号
output wr_cmd_done,
output rd_cmd_done,
output rd_cmd_fail,
//bus
output spi_cs ,
output spi_clk ,
output spi_dout,
input spi_din
);
// 写使能
wire start_wr ;
// 读状态
wire start_status ;
//擦
wire start_erase ;
wire erase_cmd_out ;
//写
wire start_write ;
wire din ;
//计时器
wire time_done ;
wire write_en ;
wire erase_en ;
wire erase_cmd ;
//spi控制
wire wren_cmd ;
wire wren_done ;
wire status_cmd ;
wire status_data ;
wire status_done ;
wire erase_addr ;
wire erase_done ;
wire write_addr ;
wire write_done ;
wire read_addr ;
wire read_done ;
//总线模式选择
wire mux_wren ;
wire mux_wren_done ;
wire spi_cs1 ;
wire spi_clk1 ;
wire spi_dout1 ;
wire spi_din1 ;
wire mux_status ;
wire mux_status_done ;
wire spi_cs2 ;
wire spi_clk2 ;
wire spi_dout2 ;
wire spi_din2 ;
wire mux_earse ;
wire mux_earse_done ;
wire spi_cs3 ;
wire spi_clk3 ;
wire spi_dout3 ;
wire spi_din3 ;
wire mux_write ;
wire mux_write_done ;
wire spi_cs4 ;
wire spi_clk4 ;
wire spi_dout4 ;
wire spi_din4 ;
wire mux_read ;
wire mux_read_done ;
wire spi_cs5 ;
wire spi_clk5 ;
wire spi_dout5 ;
wire spi_din5 ;
//-------------------------------------------写使能
wr_enable inst_wr_enable(
.clk (clk),
.reset (reset) ,
.start_wr (start_wr) ,//开始写使能
.wren_cmd (wren_cmd) ,//指令(0/1)
.mux_wren_done (mux_wren_done) ,//总线返回
.wren_done (wren_done) ,//写使能完成
.mux_wren (mux_wren) ,//请求总线
.spi_cs (spi_cs1) ,//总线发送的片选线
.spi_clk (spi_clk1) ,//总线发送的clk线
.spi_dout (spi_dout1) //总线发送的DI线
);
//----------------------------------------读状态
read_status inst_read_status(
.clk (clk),
.reset (reset) ,
.start_status (start_status) ,//开始读状态
.status_cmd (status_cmd) ,//读状态指令
.mux_status_done(mux_status_done) ,//总线返回
.spi_din (spi_din2) ,//由总线输入
.mux_status (mux_status) ,//请求总线
.status_done (status_done) ,//读状态完成
.status_data (status_data) ,//读状态返回8位数据
.spi_cs (spi_cs2) ,//片选信号
.spi_clk (spi_clk2) ,//时钟信号
.spi_dout (spi_dout2) //总线输入
);
//--------------------------------------------------擦除
erase_top erase_top(
.clk (clk) ,
.reset (reset) ,
.start_erase (start_erase) ,
.erase_cmd (erase_cmd) ,//擦除指令
.erase_addr (erase_addr) ,//擦除地址
.mux_erase_done (mux_erase_done) ,
.time_done (time_done) ,//计时完成
.mux_erase (mux_erase) ,
.spi_cs (spi_cs3) ,
.spi_clk (spi_clk3) ,
.spi_dout (spi_dout3) ,
.erase_done (erase_done) ,
.erase_en (erase_en)
);
//-----------------------------------------------------页写
write_top inst_write_top(
.clk (clk),
.reset (reset) ,
.start_write (start_write) ,//启动页写
//fifo部分驱动接口
.brust_length (brust_length) ,//突发长度
.write_addr (write_addr) ,//初始地址
.clk_wr (clk_wr) ,//fifo时钟
.wr_fifo_en (wr_fifo_en) ,//fifo写使能
.din (wr_fifo_data) ,//写数据
.wr_fifo_empty (wr_fifo_empty) ,//fifo空
.wr_fifo_full (wr_fifo_full) ,//fifo满
//总线接口
.spi_cs (spi_cs4) ,//spi片选
.spi_clk (spi_clk4) ,//spi时钟
.spi_dout (spi_dout4) ,//spi输出
.mux_write (mux_write) ,//请求总线
.mux_write_done (mux_write_done) ,//总线应答
.write_done (write_done) ,//页写完成
//计时器
.time_done (time_done) ,//计时完成
.write_en (write_en) //启动计时
);
//-------------------------------------------页读
read_top inst_read_top(
.clk (clk),
.reset (reset) ,
.rd_clk (rd_clk) ,
.start_read (start_read) ,//开始页读
.read_addr (read_addr) , //页读地址
.brust_length (brust_length) ,//突发长度
.rd_fifo_en (rd_fifo_en) ,//fifo读使能
.rd_fifo_data (rd_fifo_data) ,//从fifo读出的数据
.rd_fifo_empty (rd_fifo_empty) ,
.rd_fifo_full (rd_fifo_full) ,
.spi_cs (spi_cs5) ,
.spi_clk (spi_clk5) ,
.spi_dout (spi_dout5) ,
.spi_din (spi_din5) ,
.mux_read (mux_read) ,
.mux_read_done (mux_read_done) ,
.read_done (read_done)
);
//-------------------------------------------time_top模块
erase_time inst_erase_time(
.clk (clk),
.reset (reset),
.write_en (write_en),//页写计时
.erase_en (erase_en),//擦除计时
.erase_cmd (erase_cmd),//擦除模式选择
.time_done (time_done)
);
//------------------------------------------spi_control模块
spi_control inst_spi_control(
.clk (clk),
.reset (reset),
//操作信号
.op_en (op_en),
.op_cmd (op_cmd),//0:写使能04h 1:写使能06h 2:读状态05h 3:读状态35h 4:擦除20h 5:擦除52h 6:擦除D8h 7:擦除整片 8:页写 9:页读
.op_addr (op_addr),
.op_brust_length(op_brust_length),
.wr_cmd_done (wr_cmd_done), //返回写类done:写使能,页写,擦除
.rd_cmd_done (rd_cmd_done),//返回读类done:读状态寄存器,页读
.rd_cmd_fail (rd_cmd_fail),//读状态寄存器为忙
//写使能
.start_wr (start_wr) ,
.wren_cmd (wren_cmd) ,
.wren_done (wren_done) ,
//读状态
.start_status (start_status) ,
.status_cmd (status_cmd) ,
.status_data (status_data) ,
.status_done (status_done) ,
//擦除
.start_erase (start_erase) ,
.erase_cmd (erase_cmd) ,
.erase_addr (erase_addr) ,
.erase_done (erase_done) ,
//页写
.start_write (start_write) ,
.wr_brust_length (wr_brust_length) ,
.write_addr (write_addr) ,
.write_done (write_done) ,
//页读
.start_read (start_read) ,
.rd_brust_length(rd_brust_length) ,
.read_addr (read_addr) ,
.read_done (read_done)
);
//-------------------------------------------总线交互模块
mux_top mux_top(
.clk (clk) ,
.reset (reset) ,
//写使能
.mux_wren (mux_wren) ,
.mux_wren_done (mux_wren_done) ,
.spi_cs1 (spi_cs1) ,
.spi_clk1 (spi_clk1) ,
.spi_dout1 (spi_dout1) ,
.spi_din1 (spi_din1 ) ,
//读状态
.mux_status (mux_status) ,
.mux_status_done (mux_status_done) ,
.spi_cs2 (spi_cs2) ,
.spi_clk2 (spi_clk2) ,
.spi_dout2 (spi_dout2) ,
.spi_din2 (spi_din2) ,
//擦除
.mux_earse (mux_earse) ,
.mux_earse_done (mux_earse_done) ,
.spi_cs3 (spi_cs3) ,
.spi_clk3 (spi_clk3) ,
.spi_dout3 (spi_dout3) ,
.spi_din3 (spi_din3) ,
//页写
.mux_write (mux_write) ,
.mux_write_done (mux_write_done) ,
.spi_cs4 (spi_cs4) ,
.spi_clk4 (spi_clk4) ,
.spi_dout4 (spi_dout4) ,
.spi_din4 (spi_din4) ,
//页读
.mux_read (mux_read) ,
.mux_read_done (mux_read_done) ,
.spi_cs5 (spi_cs5) ,
.spi_clk5 (spi_clk5) ,
.spi_dout5 (spi_dout5) ,
.spi_din5 (spi_din5) ,
//总线bus
.spi_din (spi_din) ,
.spi_cs (spi_cs) ,
.spi_clk (spi_clk) ,
.spi_dout (spi_dout)
);
endmodule