网络知识 娱乐 相信我,SDRAM真的不难(八)----综合读写模块(FIFO)

相信我,SDRAM真的不难(八)----综合读写模块(FIFO)

写在前面

        本文是SDRAM系列文章的第八篇,对SDRAM的综合读写模块(FIFO)进行了详细介绍、代码编写与仿真。

        其他博文链接:相信我,SDRAM真的不难----汇总篇(电梯直达)​​​​​​​


1、为什么要引入FIFO模块?

        在上一篇博文中,我们简单地实现了SDRAM综合读写模块的读写功能,但同时发现了如下问题:

  • 调用该模块的其他模块必须与此模块时钟同步,否则将引入跨时钟域传输的问题
  • 虽然仲裁模块可以解决读写请求、刷新请求的冲突,但是在存在冲突时,无法将冲突数据缓存会造成数据丢失(例如写操作、刷新操作冲突。则必定会执行刷新操作后再执行写操作,而在等待刷新操作完成的这段时间可能会将原来要写入的数据覆盖)

        这个问题我们可以通过引入FIFO做为缓存从而解决。首先FIFO能解决多bit数据跨时钟域同步的问题;其次,在存在SDRAM操作冲突时可以将数据寄存在FIFO以防止数据被覆盖或丢失;通过引入FIFO使得对SDARM的操作实际上变成了对FIFO的操作,而FIFO的控制时序又相对简单,所以能大大简化操作。

2、FIFO模块设计

        应设计两个FIFO:写FIFO模块、读FIFO模块。

  • 写FIFO模块:用户侧将要写入的数据存入写FIFO,待SDRAM响应了写请求后,从写FIFO中读取数据作为数据源写入SDRAM
  • 读FIFO模块:用户侧发起读请求,待SDRAM响应了读请求后,从SDRAM中读取的数据被写入读FIFO,用户侧从读FIFO读取数据

        结合上文的综合读写模块(无FIFO)SDRAM_ctrl可以设计出FIFO模块的接口,如下图:

 FIFO模块的综合框图如下:

        FIFO模块的Verilog代码设计主要有以下几点:

  • 分别例化2个双端口异步fifo模块,分别作为读模块与写模块
  • 读、写响应信号均为异步信号,需要打拍采取其下降沿
  • 每当写FIFO模块中数据量大于突发长度时,就进行SDRAM写入操作;每当读FIFO模块中数据量小于突发长度时,就进行SDRAM读取操作
  • 写请求优先级高于读请求

        根据以上编写的代码如下(需要自己例化fifo,位宽16,深度1024,这里不赘述):

`timescale  1ns/1ns

module  fifo_ctrl
(
    input   wire            sdram_clk		,   //sdram时钟
    input   wire            sdram_rst_n		,   //sdram复位信号
//写fifo信号
    input   wire            wr_fifo_wr_clk  ,   //写FIFO写时钟
    input   wire            wr_fifo_rst		,   //写复位信号	
    input   wire            wr_fifo_wr_req  ,   //写FIFO写请求
    input   wire    [15:0]  wr_fifo_wr_data ,   //写FIFO写数据
    input   wire    [23:0]  sdram_wr_b_addr ,   //写SDRAM首地址
    input   wire    [23:0]  sdram_wr_e_addr ,   //写SDRAM末地址
    input   wire    [9:0]   wr_burst_len    ,   //写SDRAM数据突发长度
    output  wire    [9:0]   wr_fifo_num     ,   //写fifo中的数据量
//读fifo信号
    input   wire            rd_fifo_rd_clk  ,   //读FIFO读时钟
    input   wire            rd_fifo_rd_req  ,   //读FIFO读请求
    input   wire    [23:0]  sdram_rd_b_addr ,   //读SDRAM首地址
    input   wire    [23:0]  sdram_rd_e_addr ,   //读SDRAM末地址
    input   wire    [9:0]   rd_burst_len    ,   //读SDRAM数据突发长度
    input   wire            rd_fifo_rst		,   //读复位信号
    output  wire    [15:0]  rd_fifo_rd_data ,   //读FIFO读数据
    output  wire    [9:0]   rd_fifo_num     ,   //读fifo中的数据量

    input   wire            read_valid      ,   //SDRAM读使能
    input   wire            init_end        ,   //SDRAM初始化完成标志
//SDRAM写信号
    input   wire            sdram_wr_ack    ,   //SDRAM写响应
    output  reg             sdram_wr_req    ,   //SDRAM写请求
    output  reg     [23:0]  sdram_wr_addr   ,   //SDRAM写地址
    output  wire    [15:0]  sdram_data_in   ,   //写入SDRAM的数据
//SDRAM读信号
    input   wire            sdram_rd_ack    ,   //SDRAM响应
    input   wire    [15:0]  sdram_data_out  ,   //读出SDRAM数据
    output  reg             sdram_rd_req    ,   //SDRAM读请求
    output  reg     [23:0]  sdram_rd_addr       //SDRAM读地址
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//wire define
wire	sdram_wr_ack_fall ;   //写响应信号下降沿
wire	sdram_rd_ack_fall ;   //读响应信号下降沿

//reg define
reg		sdram_wr_ack_d1	;     //写响应打1拍
reg		sdram_wr_ack_d2	;     //写响应打2拍
reg		sdram_rd_ack_d1	;     //读响应打1拍
reg		sdram_rd_ack_d2	;     //读响应打2拍

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//wr_ack_dly:写响应信号打拍,采集下降沿
always@(posedge sdram_clk or negedge sdram_rst_n)begin
    if(!sdram_rst_n)begin
        sdram_wr_ack_d1 <=  1'b0;
        sdram_wr_ack_d2 <=  1'b0;	
	end
    else begin
        sdram_wr_ack_d1 <=  sdram_wr_ack;
		sdram_wr_ack_d2 <=  sdram_wr_ack_d1;	
	end
end

//rd_ack_dly:读响应信号打拍,采集下降沿
always@(posedge sdram_clk or negedge sdram_rst_n)begin
    if(!sdram_rst_n)begin
        sdram_rd_ack_d1 <=  1'b0;
        sdram_rd_ack_d2 <=  1'b0;	
	end
    else begin
        sdram_rd_ack_d1 <=  sdram_rd_ack;
		sdram_rd_ack_d2 <=  sdram_rd_ack_d1;	
	end
end

//sdram_wr_ack_fall,sdram_rd_ack_fall:检测读写响应信号下降沿
assign  sdram_wr_ack_fall = (sdram_wr_ack_d2 & ~sdram_wr_ack_d1);
assign  sdram_rd_ack_fall = (sdram_rd_ack_d2 & ~sdram_rd_ack_d1);

//sdram_wr_addr:sdram写地址
always@(posedge sdram_clk or negedge sdram_rst_n)begin
    if(!sdram_rst_n)
        sdram_wr_addr <= 24'd0;
    else if(wr_fifo_rst)
        sdram_wr_addr <= sdram_wr_b_addr;							//复位fifo则地址为初始地址
    else if(sdram_wr_ack_fall) 										//一次突发写结束,更改写地址
        begin
            if(sdram_wr_addr < (sdram_wr_e_addr - wr_burst_len))                       
                sdram_wr_addr   <=  sdram_wr_addr + wr_burst_len;	//未达到末地址,写地址累加
            else        
                sdram_wr_addr   <=  sdram_wr_b_addr;				//到达末地址,回到写起始地址
        end
end		

//sdram_rd_addr:sdram读地址
always@(posedge sdram_clk or negedge sdram_rst_n)begin
    if(!sdram_rst_n)
        sdram_rd_addr <= 24'd0;
    else if(rd_fifo_rst)
        sdram_rd_addr   <=  sdram_rd_b_addr;
    else if(sdram_rd_ack_fall) 										//一次突发读结束,更改读地址
        begin
            if(sdram_rd_addr < (sdram_rd_e_addr - rd_burst_len))                    
                sdram_rd_addr   <=  sdram_rd_addr + rd_burst_len;	//读地址未达到末地址,读地址累加
            else    
                sdram_rd_addr   <=  sdram_rd_b_addr;				//到达末地址,回到首地址
        end
end

//sdram_wr_req,sdram_rd_req:读写请求信号
always@(posedge sdram_clk or negedge sdram_rst_n)begin
    if(!sdram_rst_n)
        begin
            sdram_wr_req <= 1'b0;
            sdram_rd_req = wr_burst_len)begin   	//写FIFO中的数据量达到写突发长度
                sdram_wr_req <=  1'b1;   			//写请求有效
                sdram_rd_req <=  1'b0;
			end
            else if((rd_fifo_num < rd_burst_len) && (read_valid))begin //读FIFO中的数据量小于读突发长度,且读使能信号有效
                sdram_wr_req <=  1'b0;
				sdram_rd_req <=  1'b1;   			//读请求有效
            end
            else begin
				sdram_wr_req <=  1'b0;
				sdram_rd_req <= 1'b0;
			end
        end
    else begin
		sdram_wr_req <= 1'b0;
		sdram_rd_req <= 1'b0;
	end
end

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//

//------------- wr_fifo_data -------------
fifo_data   wr_fifo_data(
    //用户接口
    .wrclk      (wr_fifo_wr_clk ),  //写时钟
    .wrreq      (wr_fifo_wr_req ),  //写请求
    .data       (wr_fifo_wr_data),  //写数据
    //SDRAM接口
    .rdclk      (sdram_clk		),  //读时钟
    .rdreq      (sdram_wr_ack   ),  //读请求
    .q          (sdram_data_in  ),  //读数据

    .rdusedw    (wr_fifo_num    ),  //FIFO中的数据量
    .wrusedw    (               ),
    .aclr       (~sdram_rst_n || wr_fifo_rst)  //清零信号
);

//------------- rd_fifo_data -------------
fifo_data   rd_fifo_data(
    //sdram接口
    .wrclk      (sdram_clk		),  //写时钟
    .wrreq      (sdram_rd_ack   ),  //写请求
    .data       (sdram_data_out ),  //写数据
    //用户接口
    .rdclk      (rd_fifo_rd_clk ),  //读时钟
    .rdreq      (rd_fifo_rd_req ),  //读请求
    .q          (rd_fifo_rd_data),  //读数据

    .rdusedw    (               ),
    .wrusedw    (rd_fifo_num    ),  //FIFO中的数据量
    .aclr       (~sdram_rst_n || rd_fifo_rst)  //清零信号
);

endmodule

        该模块的仿真验证放在后面一并进行。

3、综合读写模块(FIFO)

        现在可以结合FIFO模块与综合读写模块(无FIFO)来设计综合读写模块(FIFO)。该模块为顶层模块,主要需要例化这两个子模块,示意框图如下:

3.1、Verilog代码

        根据上图的功能框图对功能子模块进行例化即可,如下(仅列出顶层模块,子功能模块请见该系列的其他博文): 

`timescale  1ns/1ns

module  sdram_top
(
    input   wire            sdram_clk		,   //sdram时钟
    input   wire            sdram_rst_n		,   //sdram复位信号	
    input   wire            clk_out         ,   //sdram相位偏移时钟(直接给SDRAM芯片)
//写FIFO信号
    input   wire            wr_fifo_wr_clk  ,   //写FIFO写时钟
    input   wire            wr_fifo_rst		,   //写FIFO复位信号	
    input   wire            wr_fifo_wr_req  ,   //写FIFO写请求
    input   wire    [15:0]  wr_fifo_wr_data ,   //写FIFO写数据
    input   wire    [23:0]  sdram_wr_b_addr ,   //写SDRAM首地址
    input   wire    [23:0]  sdram_wr_e_addr ,   //写SDRAM末地址
    input   wire    [9:0]   wr_burst_len    ,   //写SDRAM数据突发长度
    output  wire    [9:0]   wr_fifo_num     ,   //写fifo中的数据量	
//读FIFO信号
    input   wire            rd_fifo_rd_clk  ,   //读FIFO读时钟
    input   wire            rd_fifo_rst		,   //读复位信号	
    input   wire            rd_fifo_rd_req  ,   //读FIFO读请求
    input   wire    [23:0]  sdram_rd_b_addr ,   //读SDRAM首地址
    input   wire    [23:0]  sdram_rd_e_addr ,   //读SDRAM末地址
    input   wire    [9:0]   rd_burst_len    ,   //读SDRAM数据突发长度
    output  wire    [15:0]  rd_fifo_rd_data ,   //读FIFO读数据
    output  wire    [9:0]   rd_fifo_num     ,   //读fifo中的数据量
//功能信号
    input   wire            read_valid      ,   //SDRAM读使能
    output  wire            init_end        ,   //SDRAM初始化完成标志
//SDRAM接口信号
    output  wire            sdram_clk_out	,   //SDRAM芯片时钟
    output  wire            sdram_cke       ,   //SDRAM时钟有效信号
    output  wire            sdram_cs_n      ,   //SDRAM片选信号
    output  wire            sdram_ras_n     ,   //SDRAM行地址选通脉冲
    output  wire            sdram_cas_n     ,   //SDRAM列地址选通脉冲
    output  wire            sdram_we_n      ,   //SDRAM写允许位
    output  wire    [1:0]   sdram_bank		,   //SDRAM的L-Bank地址线
    output  wire    [12:0]  sdram_addr      ,   //SDRAM地址总线
    output  wire    [1:0]   sdram_dqm       ,   //SDRAM数据掩码
    inout   wire    [15:0]  sdram_dq            //SDRAM数据总线
);

//********************************************************************//
//****************** Parameter and Internal Signal *******************//
//********************************************************************//

//wire  define
wire            sdram_wr_req    ;   //sdram 写请求
wire            sdram_wr_ack    ;   //sdram 写响应
wire    [23:0]  sdram_wr_addr   ;   //sdram 写地址
wire    [15:0]  sdram_data_in   ;   //写入sdram中的数据

wire            sdram_rd_req    ;   //sdram 读请求
wire            sdram_rd_ack    ;   //sdram 读响应
wire    [23:0]  sdram_rd_addr   ;   //sdram 读地址
wire    [15:0]  sdram_data_out  ;   //从sdram中读出的数据

//sdram_clk_out:SDRAM芯片时钟
assign  sdram_clk_out = clk_out;
//sdram_dqm:SDRAM数据掩码
assign  sdram_dqm = 2'b00;

//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//

//------------- fifo_ctrl_inst -------------
fifo_ctrl   fifo_ctrl_inst(

//system    signal
    .sdram_clk		(sdram_clk		),  //SDRAM控制时钟
    .sdram_rst_n	(sdram_rst_n	),  //复位信号
//write fifo signal
    .wr_fifo_wr_clk (wr_fifo_wr_clk ),  //写FIFO写时钟
    .wr_fifo_wr_req (wr_fifo_wr_req ),  //写FIFO写请求
    .wr_fifo_wr_data(wr_fifo_wr_data),  //写FIFO写数据
    .sdram_wr_b_addr(sdram_wr_b_addr),  //写SDRAM首地址
    .sdram_wr_e_addr(sdram_wr_e_addr),  //写SDRAM末地址
    .wr_burst_len   (wr_burst_len   ),  //写SDRAM数据突发长度
    .wr_fifo_rst	(wr_fifo_rst	),  //写清零信号
	.wr_fifo_num	(wr_fifo_num	),	//写fifo中的数据量
//read fifo signal
    .rd_fifo_rd_clk (rd_fifo_rd_clk ),  //读FIFO读时钟
    .rd_fifo_rd_req (rd_fifo_rd_req ),  //读FIFO读请求
    .rd_fifo_rd_data(rd_fifo_rd_data),  //读FIFO读数据
    .rd_fifo_num    (rd_fifo_num    ),  //读FIFO中的数据量
    .sdram_rd_b_addr(sdram_rd_b_addr),  //读SDRAM首地址
    .sdram_rd_e_addr(sdram_rd_e_addr),  //读SDRAM末地址
    .rd_burst_len   (rd_burst_len   ),  //读SDRAM数据突发长度
    .rd_fifo_rst	(rd_fifo_rst	),  //读清零信号
//USER ctrl signal
    .read_valid     (read_valid     ),  //SDRAM读使能
    .init_end       (init_end       ),  //SDRAM初始化完成标志
//SDRAM ctrl of write
    .sdram_wr_ack   (sdram_wr_ack   ),  //SDRAM写响应
    .sdram_wr_req   (sdram_wr_req   ),  //SDRAM写请求
    .sdram_wr_addr  (sdram_wr_addr  ),  //SDRAM写地址
    .sdram_data_in  (sdram_data_in  ),  //写入SDRAM的数据
//SDRAM ctrl of read
    .sdram_rd_ack   (sdram_rd_ack   ),  //SDRAM读请求
    .sdram_data_out (sdram_data_out ),  //SDRAM读响应
    .sdram_rd_req   (sdram_rd_req   ),  //SDRAM读地址
    .sdram_rd_addr  (sdram_rd_addr  )  //读出SDRAM数据

);

//------------- sdram_ctrl_inst -------------
sdram_ctrl  sdram_ctrl_inst(

    .sdram_clk		(sdram_clk        ),   //系统时钟
    .sdram_rst_n	(sdram_rst_n	),   //复位信号,低电平有效
//SDRAM 控制器写端口
    .sdram_wr_req   (sdram_wr_req   ),   //写SDRAM请求信号
    .sdram_wr_addr  (sdram_wr_addr  ),   //SDRAM写操作的地址
    .wr_burst_len   (wr_burst_len   ),   //写sdram时数据突发长度
    .sdram_data_in  (sdram_data_in  ),   //写入SDRAM的数据
    .sdram_wr_ack   (sdram_wr_ack   ),   //写SDRAM响应信号
//SDRAM 控制器读端口
    .sdram_rd_req   (sdram_rd_req   ),  //读SDRAM请求信号
    .sdram_rd_addr  (sdram_rd_addr  ),  //SDRAM写操作的地址
    .rd_burst_len   (rd_burst_len   ),  //读sdram时数据突发长度
    .sdram_data_out (sdram_data_out ),  //从SDRAM读出的数据
    .init_end       (init_end       ),  //SDRAM 初始化完成标志
    .sdram_rd_ack   (sdram_rd_ack   ),  //读SDRAM响应信号
//FPGA与SDRAM硬件接口
    .sdram_cke      (sdram_cke      ),  // SDRAM 时钟有效信号
    .sdram_cs_n     (sdram_cs_n     ),  // SDRAM 片选信号
    .sdram_ras_n    (sdram_ras_n    ),  // SDRAM 行地址选通脉冲
    .sdram_cas_n    (sdram_cas_n    ),  // SDRAM 列地址选通脉冲
    .sdram_we_n     (sdram_we_n     ),  // SDRAM 写允许位
    .sdram_bank		(sdram_bank		),  // SDRAM L-Bank地址线
    .sdram_addr     (sdram_addr     ),  // SDRAM 地址总线
    .sdram_dq       (sdram_dq       )   // SDRAM 数据总线

);

endmodule

3.2、Testbench

        Testbench除了例化了之前编写的子功能模块外,还例化了一个PLL模块,分别输出50M、100M和相位偏移-30°的100M时钟信号(PLL模块就不给出了,都看到SDRAM了,不可能PLL都不会吧?)。

        该Testbench预期实现的功能为:

  • 例化一个综合读写模块(FIFO):突发读写,突发长度10,初始操作地址为24'h000_000
  • 先从初始地址开始往SDRAM中写入30个数据,分别为1~30
  • 然后一直对SDRAM的地址(24'h000_000~24'h000_01e)这30个地址进行数据读取

        该Testbench如下:

`timescale  1ns/1ns

module  tb_sdram_top();

//********************************************************************//
//****************** Internal Signal and Defparam ********************//
//********************************************************************//
//wire define
//clk_gen
wire            clk_50m         ;   //PLL输出50M时钟
wire            clk_100m        ;   //PLL输出100M时钟
wire            clk_100m_shift  ;   //PLL输出100M时钟,相位偏移-30deg
wire            locked          ;   //PLL时钟锁定信号
wire            rst_n           ;   //复位信号,低有效
//sdram
wire            sdram_clk_out	;   //SDRAM时钟
wire            sdram_cke       ;   //SDRAM时钟使能信号
wire            sdram_cs_n      ;   //SDRAM片选信号
wire            sdram_ras_n     ;   //SDRAM行选通信号
wire            sdram_cas_n     ;   //SDRAM列选题信号
wire            sdram_we_n      ;   //SDRAM写使能信号
wire    [1:0]   sdram_bank		;   //SDRAM L-Bank地址
wire    [12:0]  sdram_addr      ;   //SDRAM地址总线
wire    [15:0]  sdram_dq        ;   //SDRAM数据总线
wire            sdram_dqm       ;   //SDRAM数据总线
//sdram_ctrl
// wire            sdram_wr_ack    ;   //数据写阶段写响应
// wire            sdram_rd_ack    ;   //数据读阶段响应

wire    [9:0]   wr_fifo_num     ;   //fifo_ctrl模块中写fifo中的数据量
wire			init_end		;

wire    [9:0]   rd_fifo_num     ;   //fifo_ctrl模块中读fifo中的数据量
wire    [15:0]  rfifo_rd_data   ;   //fifo_ctrl模块中读fifo读数据

//reg define
reg             sys_clk         ;   //系统时钟
reg             sys_rst_n       ;   //复位信号
reg             wr_en           ;   //写使能
reg     [15:0]  wr_data_in      ;   //写数据
reg             rd_en           ;   //读使能
reg     [2:0]   cnt_wr_wait     ;   //数据写入间隔计数
reg     [9:0]   cnt_rd_data     ;   //读出数据计数
reg             wr_data_flag    ;   //fifo_ctrl模块中写fifo写使能
reg             read_valid      ;   //读有效信号

//defparam
//重定义仿真模型中的相关参数
defparam sdram_model_plus_inst.addr_bits = 13;          //地址位宽
defparam sdram_model_plus_inst.data_bits = 16;          //数据位宽
defparam sdram_model_plus_inst.col_bits  = 9;           //列地址位宽
defparam sdram_model_plus_inst.mem_sizes = 2*1024*1024; //L-Bank容量

//********************************************************************//
//***************************** Main Code ****************************//
//********************************************************************//

//时钟、复位信号
initial
  begin
    sys_clk     =   1'b1  ;
    sys_rst_n   <=  1'b0  ;
    #200
    sys_rst_n   <=  1'b1  ;
  end

always  #10 sys_clk = ~sys_clk;

//rst_n:复位信号
assign  rst_n = sys_rst_n & locked;

//wr_en:写数据使能
always@(posedge clk_50m or negedge rst_n)
    if(rst_n == 1'b0)
        wr_en   <=  1'b1;
    else    if(wr_data_in == 10'd30)
        wr_en   <=  1'b0;
    else
        wr_en   <=  wr_en;

//cnt_wr_wait:数据写入间隔计数
always@(posedge clk_50m or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_wr_wait <=  3'd0;
    else    if(wr_en == 1'b1)
        cnt_wr_wait <=  cnt_wr_wait + 1'b1;
    else
        cnt_wr_wait <=  3'd0;

//wr_data_flag:fifo_ctrl模块中写fifo写使能
always@(posedge clk_50m or negedge rst_n)
    if(rst_n == 1'b0)
        wr_data_flag    <=  1'b0;
    else    if(cnt_wr_wait == 3'd7)
        wr_data_flag    <=  1'b1;
    else
        wr_data_flag    <=  1'b0;

//read_valid:数据读使能
always@(posedge clk_50m or negedge rst_n)
    if(rst_n == 1'b0)
        read_valid  <=  1'b1;
    else    if(rd_fifo_num == 10'd30)
        read_valid  <=  1'b0;


//wr_data_in:写数据
always@(posedge clk_50m or negedge rst_n)
    if(rst_n == 1'b0)
        wr_data_in  <=  16'd0;
    else    if(cnt_wr_wait == 3'd7)
        wr_data_in  <=  wr_data_in + 1'b1;
    else
        wr_data_in  <=  wr_data_in;

//rd_en:读数据使能
always@(posedge clk_50m or negedge rst_n)
    if(rst_n == 1'b0)
        rd_en   <=  1'b0;
    else    if(cnt_rd_data == 4'd9)
        rd_en   <=  1'd0;
    else    if((wr_en == 1'b0))
        rd_en   <=  1'b1;
    else
        rd_en   <=  rd_en;

//cnt_rd_data:读出数据计数
always@(posedge clk_50m or negedge rst_n)
    if(rst_n == 1'b0)
        cnt_rd_data <=  0;
    else    if(rd_en == 1'b1)
        cnt_rd_data <=  cnt_rd_data + 1'b1;
    else
        cnt_rd_data <=  0;


//********************************************************************//
//*************************** Instantiation **************************//
//********************************************************************//

//------------- clk_gen_inst -------------
clk_gen clk_gen_inst (
    .inclk0     (sys_clk        ),
    .areset     (~sys_rst_n     ),
    .c0         (clk_50m        ),
    .c1         (clk_100m       ),
    .c2         (clk_100m_shift ),

    .locked     (locked         )
);

//------------- sdram_top_inst -------------
sdram_top   sdram_top_inst(
    .sdram_clk			(clk_100m       ),  //sdram 控制器参考时钟
    .clk_out            (clk_100m_shift ),  //用于输出的相位偏移时钟
    .sdram_rst_n		(rst_n          ),  //系统复位
//用户写端口
    .wr_fifo_wr_clk     (clk_50m        ),  //写端口FIFO: 写时钟
    .wr_fifo_wr_req     (wr_data_flag   ),  //写端口FIFO: 写使能
    .wr_fifo_wr_data    (wr_data_in     ),  //写端口FIFO: 写数据
    .sdram_wr_b_addr    (24'd0          ),  //写SDRAM的首地址
    .sdram_wr_e_addr    (24'd30         ),  //写SDRAM的末地址
    .wr_burst_len       (10'd10         ),  //写SDRAM时的数据突发长度
    .wr_fifo_rst		(~rst_n         ),  //写地址复位信号
	.wr_fifo_num		(wr_fifo_num	),	//写fifo中的数据量
//用户读端口
    .rd_fifo_rd_clk     (clk_50m        ),  //读端口FIFO: 读时钟
    .rd_fifo_rd_req     (rd_en          ),  //读端口FIFO: 读使能
    .rd_fifo_rd_data    (rfifo_rd_data  ),  //读端口FIFO: 读数据
    .sdram_rd_b_addr    (24'd0          ),  //读SDRAM的首地址
    .sdram_rd_e_addr    (24'd30         ),  //读SDRAM的末地址
    .rd_burst_len       (10'd10         ),  //从SDRAM中读数据时的突发长度
    .rd_fifo_rst		(~rst_n         ),  //读地址复位信号
    .rd_fifo_num        (rd_fifo_num    ),  //读fifo中的数据量
//用户控制端口
    .read_valid         (read_valid     ),  //SDRAM 读使能
	.init_end			(init_end		),
//SDRAM 芯片接口
    .sdram_clk_out		(sdram_clk_out	),  //SDRAM 芯片时钟
    .sdram_cke          (sdram_cke      ),  //SDRAM 时钟有效
    .sdram_cs_n         (sdram_cs_n     ),  //SDRAM 片选
    .sdram_ras_n        (sdram_ras_n    ),  //SDRAM 行有效
    .sdram_cas_n        (sdram_cas_n    ),  //SDRAM 列有效
    .sdram_we_n         (sdram_we_n     ),  //SDRAM 写有效
    .sdram_bank			(sdram_bank		),  //SDRAM Bank地址
    .sdram_addr         (sdram_addr     ),  //SDRAM 行/列地址
    .sdram_dq           (sdram_dq       ),  //SDRAM 数据
    .sdram_dqm          (sdram_dqm      )   //SDRAM 数据掩码
);

//-------------sdram_model_plus_inst-------------
sdram_model_plus    sdram_model_plus_inst(
    .Dq     (sdram_dq       ),
    .Addr   (sdram_addr     ),
    .Ba     (sdram_bank		),
    .Clk    (sdram_clk_out	),
    .Cke    (sdram_cke      ),
    .Cs_n   (sdram_cs_n     ),
    .Ras_n  (sdram_ras_n    ),
    .Cas_n  (sdram_cas_n    ),
    .We_n   (sdram_we_n     ),
    .Dqm    (2'b0           ),
    .Debug  (1'b1           )

);

endmodule

3.3、仿真结果

        SDRAM 仿真模型打印信息如下:

  • 首先进行了初始化操作
  • 然后对地址0-29的30个存储单位分别写入数据1-30
  • 随后重复读取上一步写入的30个数据

        仿真结果如下图:

        仿真结果截图的图较长,将其分解讲解: 

        在上图中:初始化尚未完成,但是testbench文件(用户端)中已经是要对SDRAM进行数据写入了,由于写FIFO的存在,所以这批数据被写入了写FIFO缓存,等待SDRAM初始化完成后对写请求进行响应。

        在上图中:初始化已经完成,且写请求拉高,所以开始往SDRAM中写入数据。写FIFO中的数据在减少,表示数据被SDRAM读出(即写入SDRAM),突发长度为10,所以30个数据分3次写入SDRAM。 

         在上图中:读请求拉高,所以开始从SDRAM中读取数据。数据被用户侧读出(即读取SDRAM),突发长度为10,所以30个数据分3次从SDRAM读出。

        在上图中:此后读请求一直拉高,所以一直在重复从SDRAM中读取数据。

4、其他

        至此,整个SDRAM系列终于写完了,本文开头有整个系列的链接。

        想要整个工程的朋友,可以在留言留下邮箱,我第一时间发给你。