FPGA——SPI总线控制flash(3)含代码

前面几篇详细 介绍了SPI的原理,并且实现了对flash芯片的写使能,读状态,擦除,页读,页写

​​​​​​FPGA——SPI总线详解(概念)_居安士的博客-CSDN博客_fpga芯片

FPGA——SPI总线控制flash(1)(含代码)_居安士的博客-CSDN博客

FPGA——SPI总线控制flash(2)(含代码)_居安士的博客-CSDN博客

接下来我们需要将各个模块汇合到一起:

目录

总线应答模块

SPI控制模块

SPI_FLASH总模块 


总线应答模块

之前的总线应答都是写在TB文件里面,现在所以模块写齐了之后,需要将多个模块的总线应答汇总在一个模块里,根据请求总线的信号,回复ack。当接收到总线同意通信的信号后,将spi_cs ,spi_clk ,spi_dout ,spi_din传递给总线,传递结束后,请求总线关闭,回复ack。

总线控制模块需要完成的任务:

  1. 控制5个模块(写使能,读状态,擦除,页写,页读)的总线应答
  2. 根据模块给spi_bus信号(spi_cs ,spi_clk ,spi_dout ,spi_din)
  3. 流程图如下:

 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

猜你喜欢

转载自blog.csdn.net/weixin_46188211/article/details/125947775