写在前面
本文是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系列终于写完了,本文开头有整个系列的链接。
想要整个工程的朋友,可以在留言留下邮箱,我第一时间发给你。