目录
一、完成DDR3芯片的初始化工作。
此次步骤是接着上篇文档进行设计的。(写文章-CSDN创作中心)
在这里我们需要用到的文件有三个:
这三个文件在同一文件夹下,前两个用于进行仿真,因为它们包含了仿真模型。最后一个用于复制例化语句。
此次初始化操作,我们的任务是实现在modelsim上观察到信号init_calib_complete拉高。
并且在此次项目最终,我们需要实现的效果是在hdmi上实现图片显示,这里是利用串口进行传图。因此需要创建两个.v文件,顶层与测试文件。
正如本节标题所示,我们在此仅仅实现初始化,在这里我们的主要任务是模块复用工作。先一个一个来。
1)top_ddr3_hdmi.v
在此模块下需要例化我们生成的ddr3_ctrl ip,这需要从生成的。veo文件进行复制。
比较长,我就截取部分代码,其中需要自己进行归类一下。注意我们还要生成一个pll时钟给ddr3供时。这里自行生成一个pll的ip核放在里面即可。
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/03/03 15:44:43
// Design Name:
// Module Name: top_ddr3_hdmi
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module top_ddr3_hdmi(
//--system---
input sys_clk,
input sys_rst,
//--ddr3---
output [13:0] ddr3_addr ,
output [2:0] ddr3_ba ,
output ddr3_cas_n ,
output [0:0] ddr3_ck_n ,
output [0:0] ddr3_ck_p ,
output [0:0] ddr3_cke ,
output ddr3_ras_n ,
output ddr3_reset_n ,
output ddr3_we_n ,
inout [31:0] ddr3_dq ,
inout [3:0] ddr3_dqs_n ,
inout [3:0] ddr3_dqs_p ,
output init_calib_complete ,
output [0:0] ddr3_cs_n ,
output [3:0] ddr3_dm ,
output [0:0] ddr3_odt
);
//中间端口线路
//pll
wire clk_200M;
//app
wire [27:0] app_addr ;
wire [2:0] app_cmd ;
wire app_en ;
wire [255:0] app_wdf_data ;
wire app_wdf_end ;
wire app_wdf_wren ;
wire [255:0] app_rd_data ;
wire app_rd_data_end ;
wire app_rd_data_valid;
wire app_rdy ;
wire app_wdf_rdy ;
wire app_sr_req ;
wire app_ref_req ;
wire app_zq_req ;
wire app_sr_active ;
wire app_ref_ack ;
wire app_zq_ack ;
wire ui_clk ;
wire ui_clk_sync_rst ;
wire [31:0] app_wdf_mask ;
//-----ddr3 ip
ddr3_ctrl u_ddr3_ctrl (
// Memory interface ports
.ddr3_addr (ddr3_addr ), // output [13:0] ddr3_addr
.ddr3_ba (ddr3_ba ), // output [2:0] ddr3_ba
.ddr3_cas_n (ddr3_cas_n ), // output ddr3_cas_n
.ddr3_ck_n (ddr3_ck_n ), // output [0:0] ddr3_ck_n
.ddr3_ck_p (ddr3_ck_p ), // output [0:0] ddr3_ck_p
.ddr3_cke (ddr3_cke ), // output [0:0] ddr3_cke
.ddr3_ras_n (ddr3_ras_n ), // output ddr3_ras_n
.ddr3_reset_n (ddr3_reset_n ), // output ddr3_reset_n
.ddr3_we_n (ddr3_we_n ), // output ddr3_we_n
.ddr3_dq (ddr3_dq ), // inout [31:0] ddr3_dq
.ddr3_dqs_n (ddr3_dqs_n ), // inout [3:0] ddr3_dqs_n
.ddr3_dqs_p (ddr3_dqs_p ), // inout [3:0] ddr3_dqs_p
.init_calib_complete (init_calib_complete), // output init_calib_complete
.ddr3_cs_n (ddr3_cs_n ), // output [0:0] ddr3_cs_n
.ddr3_dm (ddr3_dm ), // output [3:0] ddr3_dm
.ddr3_odt (ddr3_odt ), // output [0:0] ddr3_odt
// Application interface ports
.app_addr (app_addr ), // input [27:0] app_addr
.app_cmd (app_cmd ), // input [2:0] app_cmd
.app_en (app_en ), // input app_en
.app_wdf_data (app_wdf_data ), // input [255:0] app_wdf_data
.app_wdf_end (app_wdf_end ), // input app_wdf_end
.app_wdf_wren (app_wdf_wren ), // input app_wdf_wren
.app_rd_data (app_rd_data ), // output[255:0] app_rd_data
.app_rd_data_end (app_rd_data_end ), // output app_rd_data_end
.app_rd_data_valid (app_rd_data_valid ), // output app_rd_data_valid
.app_rdy (app_rdy ), // output app_rdy
.app_wdf_rdy (app_wdf_rdy ), // output app_wdf_rdy
.app_sr_req (app_sr_req ), // input app_sr_req
.app_ref_req (app_ref_req ), // input app_ref_req
.app_zq_req (app_zq_req ), // input app_zq_req
.app_sr_active (app_sr_active ), // output app_sr_active
.app_ref_ack (app_ref_ack ), // output app_ref_ack
.app_zq_ack (app_zq_ack ), // output app_zq_ack
.ui_clk (ui_clk ), // output ui_clk
.ui_clk_sync_rst (ui_clk_sync_rst ), // output ui_clk_sync_rst
.app_wdf_mask (app_wdf_mask ), // input [31:0] app_wdf_mask
// System Clock Ports
.sys_clk_i (clk_200M ),
.sys_rst (sys_rst ) // input sys_rst
);
//-----clk ip
ddr3_clk_gen uut_ddr3_clk_gen(
.clk_out1 (clk_200M ), // output clk_out1
.clk_in1 (sys_clk ) // input clk_in1
);
上面就是完整的代码,这个模块引出的端口就是只有ddr引脚接口,内部用户接口没做引出。只是接了连线。至此第一个模块设计完成。
2)top_ddr3_hdmi_tb.v
这个模块也比较简单,就是把引用第一个模块,以及引用ddr3_model.sv这个模块。而ddr3_model的例化语句可以从sim_tb_top.v中找到。具体标在下面。
值得注意的是,因为我们之前设置的32位位宽的双片ddr3,因此需要高两位和低两位分别例化,也就是例化两次(这个非常重要!!!)(ddr3仿真初始化失败,DDR4仿真没有效果_ddr初始化失败-CSDN博客)
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/03/03 16:05:56
// Design Name:
// Module Name: top_ddr3_hdmi_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module top_ddr3_hdmi_tb;
//端口
reg sys_clk;
reg sys_rst;
wire [13:0] ddr3_addr ;
wire [2:0] ddr3_ba ;
wire ddr3_cas_n ;
wire [0:0] ddr3_ck_n ;
wire [0:0] ddr3_ck_p ;
wire [0:0] ddr3_cke ;
wire ddr3_ras_n ;
wire ddr3_reset_n ;
wire ddr3_we_n ;
wire [31:0] ddr3_dq ;
wire [3:0] ddr3_dqs_n ;
wire [3:0] ddr3_dqs_p ;
wire init_calib_complete ;
wire [0:0] ddr3_cs_n ;
wire [3:0] ddr3_dm ;
wire [0:0] ddr3_odt ;
//--top---
top_ddr3_hdmi uut_top_ddr3_hdmi(
//--system---
.sys_clk (sys_clk ),
.sys_rst (sys_rst ),
//--ddr3---
.ddr3_addr (ddr3_addr ),
.ddr3_ba (ddr3_ba ),
.ddr3_cas_n (ddr3_cas_n ),
.ddr3_ck_n (ddr3_ck_n ),
.ddr3_ck_p (ddr3_ck_p ),
.ddr3_cke (ddr3_cke ),
.ddr3_ras_n (ddr3_ras_n ),
.ddr3_reset_n (ddr3_reset_n ),
.ddr3_we_n (ddr3_we_n ),
.ddr3_dq (ddr3_dq ),
.ddr3_dqs_n (ddr3_dqs_n ),
.ddr3_dqs_p (ddr3_dqs_p ),
.init_calib_complete(init_calib_complete),
.ddr3_cs_n (ddr3_cs_n ),
.ddr3_dm (ddr3_dm ),
.ddr3_odt (ddr3_odt )
);
//--ddr3_model
// ddr3_model uut_ddr3_model(
// .rst_n (ddr3_reset_n ),
// .ck (ddr3_ck_p ),
// .ck_n (ddr3_ck_n ),
// .cke (ddr3_cke ),
// .cs_n (ddr3_cs_n ),
// .ras_n (ddr3_ras_n ),
// .cas_n (ddr3_cas_n ),
// .we_n (ddr3_we_n ),
// .dm_tdqs (ddr3_dm ),
// .ba (ddr3_ba ),
// .addr (ddr3_addr ),
// .dq (ddr3_dq ),
// .dqs (ddr3_dqs_p ),
// .dqs_n (ddr3_dqs_n ),
// .tdqs_n ( ),
// .odt (ddr3_odt )
// );
ddr3_model inst_ddr3_model_h (
.rst_n (ddr3_reset_n),
.ck (ddr3_ck_p),
.ck_n (ddr3_ck_n),
.cke (ddr3_cke),
.cs_n (ddr3_cs_n),
.ras_n (ddr3_ras_n),
.cas_n (ddr3_cas_n),
.we_n (ddr3_we_n),
.dm_tdqs (ddr3_dm[3:2]),
.ba (ddr3_ba),
.addr (ddr3_addr),
.dq (ddr3_dq[31:16]),
.dqs (ddr3_dqs_p[3:2]),
.dqs_n (ddr3_dqs_n[3:2]),
.tdqs_n (),
.odt (ddr3_odt)
);
ddr3_model inst_ddr3_model_l (
.rst_n (ddr3_reset_n),
.ck (ddr3_ck_p),
.ck_n (ddr3_ck_n),
.cke (ddr3_cke),
.cs_n (ddr3_cs_n),
.ras_n (ddr3_ras_n),
.cas_n (ddr3_cas_n),
.we_n (ddr3_we_n),
.dm_tdqs (ddr3_dm[1:0]),
.ba (ddr3_ba),
.addr (ddr3_addr),
.dq (ddr3_dq[15:0]),
.dqs (ddr3_dqs_p[1:0]),
.dqs_n (ddr3_dqs_n[1:0]),
.tdqs_n (),
.odt (ddr3_odt)
);
initial begin
sys_clk = 1'b1;
sys_rst <= 1'b0;
#10
sys_rst <= 1'b1;
end
always #10 sys_clk = ~sys_clk;
endmodule
别的就没有了,现在看看仿真效果吧。
在107.560us完成了初始化。
二、讨论分析DDR3芯片native接口的工作时序。
上图是native接口的时序。主要分为命令,读数据和写数据。
总的来说关于DDR的native接口的使用分为三个
1)写命令
2)写数据
3)读数据
关于写命令时序如下:
这里的命令只能为写(000)和读(001),其他都是不被允许的。同时还要注意的是只有当app_en和app_rdy同时为高时,即两者完成握手后,才算是将命令写入成功。
关于写数据时序如下:
实际官方文档是给了三种时序,一种是写命令和写数据同时到达,另外两种是不同时到达,这里并未列出。
这里还有一个信号需要注意app_wdf_end此信号是输入信号,输入到MIG IP核中,此信号与设置的物理层 - 用户端” 的速率比值有关。还有首先明确的是我们的速率比值在上次使用的是4:1。一次的突发长度为8,8*32=256,此时正好一个时钟周期完成8byte的数据输入,因此app_wdf_end与app_wdf_wren是同步的。如果是2:1则不在同步,下面是仿真代码对比(来源于官网)因此在设计的时候需要注意此项。
关于读数据时序如下:
此时,存在两个信号,app_rd_data和app_rd_data_valid。
以上便是native接口的时序,下面在这些时序的基础上,我们需要将MIG IP核的时序进行封装,以便于我们后面方便使用。
三、着手设计DDR3的接口突发时序。
状态机的设计:
状态机的介绍:
1.IDLE状态,此状态作为最开始的状态,在完成所有数据的写入以及最后一个数据读出后都会到达此状态。
2.ABRIT状态,此状态为仲裁状态,用于判断是否进入下一状态跳转。
3.WR状态,此状态为写数据地址状态(这里采用第一种方式),完成数据的写入。
4.RD_ADDR状态,此状态完成读地址的写入,知道此次突发读的最后一个地址完成握手才可以。
5.RD_WAIT状态,读等待状态,读地址和读数据肯定不是同时的,因此需要等待所有的数据读出。
写数据时序:
读数据时序:
注意:在书写状态机时,我们按照野火的教程,将状态跳转与输出变量(burst_wr_done和burst_rd_done)拆开来了。
//**************************************************************************
// *** 名称 : DDR3_burst_wf.v
// *** 作者 : wangfeng
// *** 博客 : 参考 https://www.cnblogs.com/xianyufpga/
// *** 日期 : 2024年3月
// *** 描述 : 完成一次DDR3的突发读写操作
//**************************************************************************
module DDR3_burst_wf
//============================< 参数 >======================================
#(
parameter DDR_DM_W = 4 , //芯片dm位宽
parameter DDR_DQS_W = 4 , //芯片dqs位宽
parameter DDR_BANK_W = 3 , //芯片bank位宽
parameter DDR_ADDR_W = 14 , //芯片地址位宽
parameter DDR_DATA_W = 32 , //芯片数据位宽
//-------------------------------------------------------
parameter APP_DATA_W = 256 , //用户数据位宽
parameter APP_ADDR_W = 28 , //用户地址位宽
//-------------------------------------------------------
parameter BURST_ADDR_W = 25 //外部突发位宽 28-3
)
//============================< 信号 >======================================
(
//时钟和复位 --------------------------------------------
input sys_clk_i , //DDR3 参考时钟
input sys_rst , //FPGA 全局复位
output ui_clk , //DDR3 工作时钟
output DDR3_rst , //DDR3 同步复位
//突发读写接口 ------------------------------------------
input burst_rd_req , //突发读请求
input burst_wr_req , //突发写请求
input [BURST_ADDR_W -4:0] burst_rd_len , //突发读长度
input [BURST_ADDR_W -4:0] burst_wr_len , //突发写长度
input [BURST_ADDR_W -1:0] burst_rd_addr , //突发读地址
input [BURST_ADDR_W -1:0] burst_wr_addr , //突发写地址
output [APP_DATA_W -1:0] burst_rd_data , //突发读数据
input [APP_DATA_W -1:0] burst_wr_data , //突发写数据
output burst_rd_ack , //突发读应答,连接FIFO
output burst_wr_ack , //突发写应答,连接FIFO
output reg burst_rd_done , //突发读完成信号
output reg burst_wr_done , //突发写完成信号
//DDR3芯片接口 ------------------------------------------
output [DDR_ADDR_W -1:0] ddr3_addr ,
output [DDR_BANK_W -1:0] ddr3_ba ,
output ddr3_cas_n ,
output ddr3_ck_n ,
output ddr3_ck_p ,
output ddr3_cke ,
output ddr3_ras_n ,
output ddr3_cs_n ,
output ddr3_reset_n ,
output ddr3_we_n ,
inout [DDR_DATA_W -1:0] ddr3_dq ,
inout [DDR_DQS_W -1:0] ddr3_dqs_n ,
inout [DDR_DQS_W -1:0] ddr3_dqs_p ,
output [DDR_DM_W -1:0] ddr3_dm ,
output ddr3_odt
);
//============================< 信号 >======================================
reg [APP_ADDR_W -1:0] app_addr ;
wire [2:0] app_cmd ;
wire app_en ;
wire [APP_DATA_W -1:0] app_wdf_data ;
wire app_wdf_end ;
wire app_wdf_wren ;
wire [APP_DATA_W -1:0] app_rd_data ;
wire app_rd_data_end ;
wire app_rd_data_valid ;
wire app_rdy ;
wire app_wdf_rdy ;
//-------------------------------------------------------
reg [4:0] state ;
reg [BURST_ADDR_W -4:0] rd_len ;
reg [BURST_ADDR_W -4:0] wr_len ;
reg [BURST_ADDR_W -1:0] rd_addr_cnt ; //读地址计数器
reg [BURST_ADDR_W -1:0] rd_data_cnt ; //读数据计数器
reg [BURST_ADDR_W -1:0] wr_data_cnt ; //一次突发写内的计数器
//============================< 参数 >======================================
localparam DDR3_BL = 8 ;
//-------------------------------------------------------
localparam IDLE = 5'b00001 ; //空闲状态
localparam ARBIT = 5'b00010 ; //仲裁状态
localparam WR = 5'b00100 ; //写准备状态
localparam RD_ADDR = 5'b01000 ; //读状态
localparam RD_WAIT = 5'b10000 ; //读等待状态
//==========================================================================
//== DDR3 IP, input 200Mhz, get 400Mhz, ui 100Mhz
//==========================================================================
ddr3_ctrl uddr3_ctrl
(
.ddr3_addr (ddr3_addr ), //output [13:0]
.ddr3_ba (ddr3_ba ), //output [ 2:0]
.ddr3_cas_n (ddr3_cas_n ), //output
.ddr3_ck_n (ddr3_ck_n ), //output
.ddr3_ck_p (ddr3_ck_p ), //output
.ddr3_cke (ddr3_cke ), //output
.ddr3_ras_n (ddr3_ras_n ), //output
.ddr3_reset_n (ddr3_reset_n ), //output
.ddr3_we_n (ddr3_we_n ), //output
.ddr3_dq (ddr3_dq ), //inout [32:0]
.ddr3_dqs_n (ddr3_dqs_n ), //inout [ 3:0]
.ddr3_dqs_p (ddr3_dqs_p ), //inout [ 3:0]
.init_calib_complete (init_calib_complete ), //output
.ddr3_cs_n (ddr3_cs_n ), //output
.ddr3_dm (ddr3_dm ), //output [ 3:0]
.ddr3_odt (ddr3_odt ), //output
//---------------------------------------------------
.app_addr (app_addr ), //input [27:0]
.app_cmd (app_cmd ), //input [ 2:0]
.app_en (app_en ), //input
.app_wdf_data (app_wdf_data ), //input [256:0]
.app_wdf_end (app_wdf_end ), //input
.app_wdf_wren (app_wdf_wren ), //input
.app_rd_data (app_rd_data ), //output [256:0]
.app_rd_data_end (app_rd_data_end ), //output
.app_rd_data_valid (app_rd_data_valid ), //output
.app_rdy (app_rdy ), //output
.app_wdf_rdy (app_wdf_rdy ), //output
.app_sr_req (1'b0 ), //input
.app_ref_req (1'b0 ), //input
.app_zq_req (1'b0 ), //input
.app_sr_active ( ), //output
.app_ref_ack ( ), //output
.app_zq_ack ( ), //output
.ui_clk (ui_clk ), //output 100Mhz
.ui_clk_sync_rst (ui_clk_sync_rst ), //output
.app_wdf_mask (0 ), //input [31:0]32'b0000_0000_0000_0000
//---------------------------------------------------
.sys_clk_i (sys_clk_i ), //input 200Mhz
.sys_rst (sys_rst ) //input 系统复位
);
//复位信号
assign DDR3_rst = ui_clk_sync_rst | (~init_calib_complete);
//==========================================================================
//== 状态机 状态的跳转 state
//==========================================================================
always@(posedge ui_clk) begin
if(DDR3_rst == 1'b1) begin
state <= IDLE;
end
else begin
case(state)
IDLE: begin
state <= ARBIT;
end
ARBIT: begin
if(burst_wr_req == 1'b1)
state <= WR;
else if(burst_rd_req == 1'b1)
state <= RD_ADDR;
else
state <= state;
end
WR: begin
if(wr_data_cnt == wr_len - 1'b1 && app_wdf_rdy && app_rdy)
state <= IDLE;
else
state <= state;
end
RD_ADDR: begin
if(rd_addr_cnt == rd_len - 1'b1 && app_rdy)
state <= RD_WAIT;
else
state <= state;
end
RD_WAIT: begin
if(rd_data_cnt == rd_len - 1'b1)
state <= IDLE;
else
state <= state;
end
default: state <= IDLE;
endcase
end
end
//==========================================================================
//== 状态机 输出的变量 burst_wr_done burst_rd_done
//==========================================================================
always@(posedge ui_clk) begin
if(DDR3_rst == 1'b1) begin
burst_wr_done <= 1'b0;
burst_rd_done <= 1'b0;
end
else begin
case(state)
IDLE,ARBIT,RD_ADDR: begin
burst_wr_done <= 1'b0;
burst_rd_done <= 1'b0;
end
WR: begin
if(wr_data_cnt == wr_len - 1'b1 && app_wdf_rdy && app_rdy)
burst_wr_done <= 1'b1;
else
burst_wr_done <= 1'b0;
end
RD_WAIT: begin
if(rd_data_cnt == rd_len - 1'b1)
burst_rd_done <= 1'b1;
else
burst_rd_done <= 1'b0;
end
default: begin
burst_wr_done <= 1'b0;
burst_rd_done <= 1'b0;
end
endcase
end
end
//状态机名称,Modelsim测试用
//---------------------------------------------------
reg [55:0] state_name; //1个字符8位宽
always @(*) begin
case(state)
IDLE : state_name = "IDLE";
ARBIT : state_name = "ARBIT";
WR : state_name = "WR";
RD_ADDR : state_name = "RD_ADDR";
RD_WAIT : state_name = "RD_WAIT";
default : state_name = "IDLE";
endcase
end
//==========================================================================
//== 在进入读写状态前锁存读写突发长度
//==========================================================================
always @(posedge ui_clk) begin
if(DDR3_rst == 1'b1)
rd_len <= 'b0;
else if(state == ARBIT && burst_rd_req)
rd_len <= burst_rd_len;
end
always @(posedge ui_clk) begin
if(DDR3_rst == 1'b1)
wr_len <= 'b0;
else if(state == ARBIT && burst_wr_req)
wr_len <= burst_wr_len;
end
//==========================================================================
//== 在一次写突发内,写数据个数计数器不断递增
//==========================================================================
always @(posedge ui_clk) begin
if(DDR3_rst == 1'b1)
wr_data_cnt <= 'b0;
else if(state == WR && app_wdf_rdy && app_rdy) begin
if(wr_data_cnt >= wr_len - 1)
wr_data_cnt <= 'b0;
else
wr_data_cnt <= wr_data_cnt + 'b1;
end
end
//==========================================================================
//== 每次给出读指令时,读地址递增一个突发
//==========================================================================
always @(posedge ui_clk) begin
if(DDR3_rst == 1'b1)
rd_addr_cnt <= 'b0;
else if(state == RD_ADDR && app_rdy) begin
if(rd_addr_cnt >= rd_len - 1)
rd_addr_cnt <= 'b0;
else
rd_addr_cnt <= rd_addr_cnt + 1;
end
end
//==========================================================================
//== 每读出一个数据时,数据个数递增1
//==========================================================================
always @(posedge ui_clk) begin
if(DDR3_rst == 1'b1)
rd_data_cnt <= 'b0;
else if(app_rd_data_valid) begin
if(rd_data_cnt >= rd_len - 1)
rd_data_cnt <= 'b0;
else
rd_data_cnt <= rd_data_cnt + 'b1;
end
end
//==========================================================================
//== 锁存local_address,并且在完成一次突发读写时递增读写地址
//==========================================================================
always @(posedge ui_clk) begin
if(DDR3_rst == 1'b1) begin
app_addr <= 'b0;
end
else if(state == ARBIT && burst_wr_req) begin
app_addr <= burst_wr_addr; //?和外界呈8倍关系
end
else if(state == ARBIT && burst_rd_req) begin
app_addr <= burst_rd_addr; //?和外界呈8倍关系
end
else if(state == WR && (wr_data_cnt < wr_len - 1) && app_wdf_rdy && app_rdy) begin
app_addr <= app_addr + DDR3_BL;
end
else if(state == RD_ADDR && (rd_addr_cnt < rd_len - 1) && app_rdy) begin
app_addr <= app_addr + DDR3_BL;
end
end
//==========================================================================
//== DDR3其他信号
//==========================================================================
//命令
assign app_cmd = (state == RD_ADDR || state == RD_WAIT) ? 3'b001 : 3'b000;
//使能
assign app_en = (state == WR || state == RD_ADDR) ? 1'b1 : 1'b0;
//读数据
assign burst_rd_data = app_rd_data;
//读应答,即读FIFO的写使能
assign burst_rd_ack = app_rd_data_valid;
//写数据
assign app_wdf_data = burst_wr_data;
//写应答,即写FIFO的读使能
assign burst_wr_ack = (state == WR && app_wdf_rdy && app_rdy) ? 1'b1 : 1'b0;
//写使能,指示数据写入
assign app_wdf_wren = burst_wr_ack;
//写结束,4:1模式下二者相等
assign app_wdf_end = app_wdf_wren;
endmodule
四、设计DDR3突发模块的仿真代码。
此次仿真测试代码是这样的,
1.进行第一次数据的写入,这里写入5个数据,0 1 2 3 4(地址: 0 8 16 24 32)
2.进行第一次数据的读出,这里读出6个数据,0 1 2 3 4 x(地址: 0 8 16 24 32 40)
3.写 ,这里写入6个数据,5 6 7 8 9 10(地址: 40 48 56 64 72)
4.读 ,这里读出6个数据,4 5 6 7 8 9 (地址:32 40 48 56 64)
`timescale 1ns / 1ps
//
// Company:
// Engineer:
//
// Create Date: 2024/03/03 16:05:56
// Design Name:
// Module Name: DDR3_burst_wf_tb
// Project Name:
// Target Devices:
// Tool Versions:
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//
module DDR3_burst_wf_tb;
//============================< 参数 >======================================
parameter DDR_DM_W = 4 ; //芯片dm位宽
parameter DDR_DQS_W = 4 ; //芯片dqs位宽
parameter DDR_BANK_W = 3 ; //芯片bank位宽
parameter DDR_ADDR_W = 14 ; //芯片地址位宽
parameter DDR_DATA_W = 32 ; //芯片数据位宽
//-------------------------------------------------------
parameter APP_DATA_W = 256 ; //用户数据位宽
parameter APP_ADDR_W = 27 ; //用户地址位宽
//-------------------------------------------------------
parameter BURST_ADDR_W = 27 ; //外部突发位宽
//============================< 端口 >======================================
reg sys_clk ; //DDR3 参考时钟
reg sys_rst ; //FPGA 全局复位
wire ui_clk ; //DDR3 工作时钟
wire DDR3_rst ; //DDR3 同步复位
//突发读写接口 ------------------------------------------
reg burst_rd_req ; //突发读请求
reg burst_wr_req ; //突发写请求
reg [BURST_ADDR_W -4:0] burst_rd_len ; //突发读长度
reg [BURST_ADDR_W -4:0] burst_wr_len ; //突发写长度
reg [BURST_ADDR_W -1:0] burst_rd_addr ; //突发读地址
reg [BURST_ADDR_W -1:0] burst_wr_addr ; //突发写地址
wire [APP_DATA_W -1:0] burst_rd_data ; //突发读数据
reg [APP_DATA_W -1:0] burst_wr_data ; //突发写数据
wire burst_rd_ack ; //突发读应答,连接FIFO
wire burst_wr_ack ; //突发写应答,连接FIFO
wire burst_rd_done ; //突发读完成信号
wire burst_wr_done ; //突发写完成信号
//DDR3芯片接口 ------------------------------------------
wire [DDR_ADDR_W -1:0] ddr3_addr ;
wire [DDR_BANK_W -1:0] ddr3_ba ;
wire ddr3_cas_n ;
wire ddr3_ck_n ;
wire ddr3_ck_p ;
wire ddr3_cke ;
wire ddr3_ras_n ;
wire ddr3_cs_n ;
wire ddr3_reset_n ;
wire ddr3_we_n ;
wire [DDR_DATA_W -1:0] ddr3_dq ;
wire [DDR_DQS_W -1:0] ddr3_dqs_n ;
wire [DDR_DQS_W -1:0] ddr3_dqs_p ;
wire [DDR_DM_W -1:0] ddr3_dm ;
wire ddr3_odt ;
//==========================================================================
//== 模块例化
//==========================================================================
DDR3_burst_wf
#(
.DDR_DM_W (DDR_DM_W ), //芯片dm位宽
.DDR_DQS_W (DDR_DQS_W ), //芯片dqs位宽
.DDR_BANK_W (DDR_BANK_W ), //芯片bank位宽
.DDR_ADDR_W (DDR_ADDR_W ), //芯片地址位宽
.DDR_DATA_W (DDR_DATA_W ), //芯片数据位宽
//---------------------------------------------------
.APP_DATA_W (APP_DATA_W ), //用户数据位宽
.APP_ADDR_W (APP_ADDR_W ), //用户地址位宽
//---------------------------------------------------
.BURST_ADDR_W (BURST_ADDR_W ) //外部突发位宽
)
u_DDR3_burst_wf
(
.sys_clk_i (sys_clk ), //DDR3 参考时钟
.sys_rst (sys_rst ), //FPGA 全局复位
.ui_clk (ui_clk ), //DDR3 工作时钟
.DDR3_rst (DDR3_rst ), //DDR3 同步复位
//---------------------------------------------------
.burst_rd_req (burst_rd_req ), //突发读请求
.burst_wr_req (burst_wr_req ), //突发写请求
.burst_rd_len (burst_rd_len ), //突发读长度
.burst_wr_len (burst_wr_len ), //突发写长度
.burst_rd_addr (burst_rd_addr ), //突发读地址
.burst_wr_addr (burst_wr_addr ), //突发写地址
.burst_rd_data (burst_rd_data ), //突发读数据
.burst_wr_data (burst_wr_data ), //突发写数据
.burst_rd_ack (burst_rd_ack ), //突发读应答,连接FIFO
.burst_wr_ack (burst_wr_ack ), //突发写应答,连接FIFO
.burst_rd_done (burst_rd_done ), //突发读完成信号
.burst_wr_done (burst_wr_done ), //突发写完成信号
//---------------------------------------------------
.ddr3_addr (ddr3_addr ),
.ddr3_ba (ddr3_ba ),
.ddr3_cas_n (ddr3_cas_n ),
.ddr3_ck_n (ddr3_ck_n ),
.ddr3_ck_p (ddr3_ck_p ),
.ddr3_cke (ddr3_cke ),
.ddr3_ras_n (ddr3_ras_n ),
.ddr3_cs_n (ddr3_cs_n ),
.ddr3_reset_n (ddr3_reset_n ),
.ddr3_we_n (ddr3_we_n ),
.ddr3_dq (ddr3_dq ),
.ddr3_dqs_n (ddr3_dqs_n ),
.ddr3_dqs_p (ddr3_dqs_p ),
.ddr3_dm (ddr3_dm ),
.ddr3_odt (ddr3_odt )
);
ddr3_model inst_ddr3_model_h (
.rst_n (ddr3_reset_n),
.ck (ddr3_ck_p),
.ck_n (ddr3_ck_n),
.cke (ddr3_cke),
.cs_n (ddr3_cs_n),
.ras_n (ddr3_ras_n),
.cas_n (ddr3_cas_n),
.we_n (ddr3_we_n),
.dm_tdqs (ddr3_dm[3:2]),
.ba (ddr3_ba),
.addr (ddr3_addr),
.dq (ddr3_dq[31:16]),
.dqs (ddr3_dqs_p[3:2]),
.dqs_n (ddr3_dqs_n[3:2]),
.tdqs_n (),
.odt (ddr3_odt)
);
ddr3_model inst_ddr3_model_l (
.rst_n (ddr3_reset_n),
.ck (ddr3_ck_p),
.ck_n (ddr3_ck_n),
.cke (ddr3_cke),
.cs_n (ddr3_cs_n),
.ras_n (ddr3_ras_n),
.cas_n (ddr3_cas_n),
.we_n (ddr3_we_n),
.dm_tdqs (ddr3_dm[1:0]),
.ba (ddr3_ba),
.addr (ddr3_addr),
.dq (ddr3_dq[15:0]),
.dqs (ddr3_dqs_p[1:0]),
.dqs_n (ddr3_dqs_n[1:0]),
.tdqs_n (),
.odt (ddr3_odt)
);
//==========================================================================
//== 时钟信号和复位信号
//==========================================================================
initial begin
sys_clk = 1'b1;
sys_rst <= 1'b0;
#10
sys_rst <= 1'b1;
end
always #2.5 sys_clk = ~sys_clk;
//==========================================================================
//== 设计输入信号
//==========================================================================
initial begin
burst_wr_req = 0;
burst_rd_req = 0;
burst_wr_len = 0;
burst_rd_len = 0;
burst_wr_addr = 0;
burst_rd_addr = 0;
burst_wr_data = 0;
@(negedge DDR3_rst); //初始化完成
#10
//----进行第一次数据的写入,这里写入5个数据,0 1 2 3 4
@(posedge ui_clk);
burst_wr_addr = {3'b000,24'd0};
burst_wr_req = 1;
burst_wr_len = 5;
@(posedge burst_wr_ack);
burst_wr_req = 0;
//----进行第一次数据的读出,这里读出6个数据,0 1 2 3 4 x
@(negedge burst_wr_done);
@(posedge ui_clk);
burst_rd_addr = {3'b000,24'd0};
burst_rd_req = 1;
burst_rd_len = 6;
@(posedge ui_clk);
burst_rd_req = 0;
//--------------------------------------------------- 第2次
//写 ,这里写入6个数据,5 6 7 8 9 10
@(negedge burst_rd_done);
@(posedge ui_clk);
burst_wr_addr = {3'b000,24'd40};
burst_wr_req = 1;
burst_wr_len = 6;
@(posedge ui_clk);
burst_wr_req = 0;
//读 ,这里读出6个数据,4 5 6 7 8 9
@(negedge burst_wr_done);
@(posedge ui_clk);
burst_rd_addr = {3'b000,24'd32};
burst_rd_req = 1;
burst_rd_len = 6;
@(posedge ui_clk);
burst_rd_req = 0;
end
always@(posedge ui_clk) begin
if(DDR3_rst == 1'b1)
burst_wr_data <= 'b0;
else if(burst_wr_ack == 1'b1)
burst_wr_data <= burst_wr_data + 1'b1;
end
endmodule
1.进行第一次数据的写入,这里写入5个数据,0 1 2 3 4(地址: 0 8 16 24 32)
2.进行第一次数据的读出,这里读出6个数据,0 1 2 3 4 x(地址: 0 8 16 24 32 40)
3.写 ,这里写入6个数据,5 6 7 8 9 10(地址: 40 48 56 64 72)
4.读 ,这里读出6个数据,4 5 6 7 8 9 (地址:32 40 48 56 64)
至此突发模块设计完毕。