前面几篇详细 介绍了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。
总线控制模块需要完成的任务:

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控制模块需要完成的任务是:
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

接下来把所有的模块例化到一起;
- 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