写在前面
本系列为 DDR3 控制器设计总结,此系列包含 DDR3 控制器相关设计:认识 MIG、初始化、读写操作、FIFO 接口等。通过此系列的学习可以加深对 DDR3 读写时序的理解以及 FIFO 接口设计等,附上汇总博客直达链接。
目录
实验任务
在读写模块的基础上添加 FIFO 接口,包括写指令 FIFO、写数据 FIFO、读指令 FIFO、读数据 FIFO,用于缓存指令和数据。
实验环境
开发环境:Vivado 2018.2
FPGA 芯片型号:xc7a100tffg484-2
DDR3 型号:MT41J256M16HA-125
实验介绍
在项目的前几部分对 DDR3 完成写读控制以及仲裁写读控制,成功解决了在指令端口同时下达写指令和读指令是出错的问题,但是读写控制仍然存在一个问题,那就是当进行写/读操作的过程中,再来一条写/读指令,此时控制器是无法执行的,也就是此时的设计只能完成单次读写指令,这对于在实际场景使用是极其不便利的,每次只能完成一次突发读写操作,这样效率太低了!因此设计加上 FIFO 作为缓冲器,满足连续读写操作的需要。
FIFO 接口设计
添加 FIFO 的目的就是便于用户接口端可以随意下发写指令和数据,同时也能随时发出读指令读出数据。首先,依然是写指令的优先级大于读指令,这样写指令端就相当于整个写读操作的启动指令,可以事先将写指令、写数据以及读指令写入相对应的 FIFO 中,利用写指令 FIFO 的空信号作为仲裁模块,当有写指令写入写指令 FIFO 中是过几个时钟周期后,FIFO 空信号 empty 就会拉低,将此信号经反相器接入仲裁模块作为写请求信号,仲裁模块会产生写开始信号给到写控制模块,写模块会到写数据 FIFO 中取出数据给到 DDR3 控制器,这样便完成一次写操作。同理可得在读操作也是一样,大致控制框图如下。
程序设计
首先创建 4 个 FIFO,并对 FIFO 的端口信号进行包装。
这里以写操作为例,首先调用写指令 FIFO
选择 Native 常规接口,并选择由独立时钟的 BRAM 搭建而成的 FIFO,这样可以在读写端口设为不同位宽。
1、选择 First Word Fall Through 模式,这种模式让在读使能拉高时,数据立马就出来,也就是和读使能信号同步,如果选择标准模式的话读出数据相对于读使能信号就会有延时,为了便于时序设计,选择第二种模式。
2、写端口为 40 位,为了方便将写指令(3 位)、写突发长度(8 位)、写地址(29 位)三个信号的位宽由一个端口表示,在内部对指令进行解析即可。
3、勾选相应的信号
其余配置页保持默认即可。
这样就完成写指令 FIFO 配置,对其进行例化调用即可,其余三个 FIFO 也是采用同样的方法进行调用,但是需要注意的是,在数据 FIFO 中需要额外添加一个数据计数端口,这个端口在之后的设计中会用上。
写指令 FIFO 模块
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
// Engineer : Linest-5
// File : wr_cmd_fifo_ctrl.v
// Create : 2022-09-26 15:06:18
// Revise : 2022-09-26 16:24:57
// Module Name : wr_cmd_fifo_ctrl
// Description : 写指令 FIFO 控制模块
// Editor : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
`timescale 1ns/1ps
module wr_cmd_fifo_ctrl(
input wr_cmd_clk,
input ui_clk,
input rst,
input fifo_wr_cmd_en,
input [7:0] fifo_wr_cmd_brust_len,
input [28:0] fifo_wr_cmd_addr,
input [2:0] fifo_wr_cmd_instr,
input fifo_wr_cmd_start,
output fifo_wr_cmd_empty,
output fifo_wr_cmd_full,
output [7:0] wr_brust_len,
output [28:0] wr_addr,
output [2:0] wr_cmd
);
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* 信号定义 */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
wire [39:0] wr_cmd_sum;
wire [39:0] rd_cmd_sum;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Main Code */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
//对突发长度、写地址、写指令组合到一个指令写入FIFO中
assign wr_cmd_sum = (fifo_wr_cmd_en=='d1) ? {fifo_wr_cmd_brust_len,
fifo_wr_cmd_addr,fifo_wr_cmd_instr} : 'd0;
//对输出的总指令分配到各个指令输出
assign wr_brust_len = rd_cmd_sum[39:32];
assign wr_addr = rd_cmd_sum[31:3];
assign wr_cmd = rd_cmd_sum[2:0];
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* 例化写指令 FIFO 模块 */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
wr_cmd_fifo wr_cmd_fifo_inst (
.rst (rst), // input wire rst
.wr_clk (wr_cmd_clk), // input wire wr_clk
.rd_clk (ui_clk), // input wire rd_clk
.din (wr_cmd_sum), // input wire [39 : 0] din
.wr_en (fifo_wr_cmd_en), // input wire wr_en
.rd_en (fifo_wr_cmd_start), // input wire rd_en
.dout (rd_cmd_sum), // output wire [39 : 0] dout
.full (fifo_wr_cmd_full), // output wire full
.empty (fifo_wr_cmd_empty) // output wire empty
);
endmodule
写数据 FIFO 模块
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
// Engineer : Linest-5
// File : wr_data_fifo_ctrl.v
// Create : 2022-09-26 16:26:45
// Revise : 2022-09-26 16:26:45
// Module Name : wr_data_fifo_ctrl
// Description : 写数据 FIFO 控制模块
// Editor : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
`timescale 1ns/1ps
module wr_data_fifo_ctrl(
input wr_data_clk,
input ui_clk,
input rst,
input fifo_wr_data_en,
input [127:0] fifo_wr_data,
input data_req,
output [127:0] wr_data,
output fifo_wr_data_full,
output fifo_wr_data_empty,
output [6:0] fifo_wr_data_count
);
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* 例化写数据 FIFO 模块 */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
wr_data_fifo wr_data_fifo_inst (
.rst (rst), // input wire rst
.wr_clk (wr_data_clk), // input wire wr_clk
.rd_clk (ui_clk), // input wire rd_clk
.din (fifo_wr_data), // input wire [127 : 0] din
.wr_en (fifo_wr_data_en), // input wire wr_en
.rd_en (data_req), // input wire rd_en
.dout (wr_data), // output wire [127 : 0] dout
.full (fifo_wr_data_full), // output wire full
.empty (fifo_wr_data_empty), // output wire empty
.wr_data_count (fifo_wr_data_count) // output wire [6 : 0] wr_data_count
);
endmodule
读指令 FIFO 模块
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
// Engineer : Linest-5
// File : rd_cmd_fifo_ctrl.v
// Create : 2022-09-26 16:39:16
// Revise : 2022-09-26 16:39:16
// Module Name : rd_cmd_fifo_ctrl
// Description : 读指令 FIFO 控制模块
// Editor : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
`timescale 1ns/1ps
module rd_cmd_fifo_ctrl(
input rd_cmd_clk,
input ui_clk,
input rst,
input fifo_rd_cmd_en,
input [7:0] fifo_rd_cmd_brust_len,
input [28:0] fifo_rd_cmd_addr,
input [2:0] fifo_rd_cmd_instr,
input fifo_rd_cmd_start,
output fifo_rd_cmd_empty,
output fifo_rd_cmd_full,
output [7:0] rd_brust_len,
output [28:0] rd_addr,
output [2:0] rd_cmd
);
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* 信号定义 */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
wire [39:0] wr_cmd_sum;
wire [39:0] rd_cmd_sum;
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Main Code */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
//对突发长度、写地址、写指令组合到一个指令写入FIFO中
assign wr_cmd_sum = (fifo_rd_cmd_en=='d1) ? {fifo_rd_cmd_brust_len,
fifo_rd_cmd_addr,fifo_rd_cmd_instr} : 'd0;
//对输出的总指令分配到各个指令输出
assign rd_brust_len = rd_cmd_sum[39:32];
assign rd_addr = rd_cmd_sum[31:3];
assign rd_cmd = rd_cmd_sum[2:0];
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* 例化读指令 FIFO 模块 */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
rd_cmd_fifo rd_cmd_fifo_inst (
.rst (rst), // input wire rst
.wr_clk (rd_cmd_clk), // input wire wr_clk
.rd_clk (ui_clk), // input wire rd_clk
.din (wr_cmd_sum), // input wire [39 : 0] din
.wr_en (fifo_rd_cmd_en), // input wire wr_en
.rd_en (fifo_rd_cmd_start), // input wire rd_en
.dout (rd_cmd_sum), // output wire [39 : 0] dout
.full (fifo_rd_cmd_full), // output wire full
.empty (fifo_rd_cmd_empty) // output wire empty
);
endmodule
读数据 FIFO 模块
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
// Engineer : Linest-5
// File : rd_data_fifo_ctrl.v
// Create : 2022-09-26 17:01:43
// Revise : 2022-09-26 17:01:43
// Module Name : rd_data_fifo_ctrl
// Description : 读数据 FIFO 控制模块
// Editor : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
`timescale 1ns/1ps
module rd_data_fifo_ctrl(
input rd_data_clk,
input ui_clk,
input rst,
input fifo_rd_data_en,
input rd_data_valid,
input [127:0] rd_data,
output [127:0] fifo_rd_data,
output fifo_rd_data_full,
output fifo_rd_data_empty,
output [6:0] fifo_rd_data_count
);
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* 例化读数据 FIFO 模块 */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
rd_data_fifo rd_data_fifo_inst (
.rst (rst), // input wire rst
.wr_clk (ui_clk), // input wire wr_clk
.rd_clk (rd_data_clk), // input wire rd_clk
.din (rd_data), // input wire [127 : 0] din
.wr_en (rd_data_valid), // input wire wr_en
.rd_en (fifo_rd_data_en), // input wire rd_en
.dout (fifo_rd_data), // output wire [127 : 0] dout
.full (fifo_rd_data_full), // output wire full
.empty (fifo_rd_data_empty), // output wire empty
.rd_data_count (fifo_rd_data_count) // output wire [6 : 0] rd_data_count
);
endmodule
顶层模块
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Engineer : Linest-5
/* File : top_ddr3_init.v
/* Create : 2022-09-15 09:58:59
/* Revise : 2022-09-24 21:11:50
/* Module Name :
/* Description :
/* Editor : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
module top_ddr3_init(
// Inouts
inout [15:0] ddr3_dq,
inout [1:0] ddr3_dqs_n,
inout [1:0] ddr3_dqs_p,
// Outputs
output [14:0] ddr3_addr,
output [2:0] ddr3_ba,
output ddr3_ras_n,
output ddr3_cas_n,
output ddr3_we_n,
output ddr3_reset_n,
output [0:0] ddr3_ck_p,
output [0:0] ddr3_ck_n,
output [0:0] ddr3_cke,
output [0:0] ddr3_cs_n,
output [1:0] ddr3_dm,
output [0:0] ddr3_odt,
// Inputs
// Differential system clocks
input sys_clk,
input rst_n
);
wire init_calib_complete;
wire ui_clk;
wire ui_clk_sync_rst;
wire [127:0] wr_data;
wire [7:0] wr_brust_len;
wire wr_start;
wire [28:0] wr_addr;
wire [2:0] wr_cmd;
wire [15:0] wr_mask;
wire data_req;
wire wr_end;
wire app_rdy;
wire app_wdf_rdy;
wire [2:0] app_cmd;
wire app_en;
wire [28:0] app_addr;
wire [127:0] app_wdf_data;
wire app_wdf_wren;
wire [15:0] app_wdf_mask;
wire app_wdf_end;
wire [7:0] rd_brust_len;
wire rd_start;
wire [28:0] rd_addr;
wire [2:0] rd_cmd;
wire [127:0] rd_data;
wire rd_data_valid;
wire rd_end;
wire [127:0] app_rd_data;
wire app_rd_data_end;
wire app_rd_data_valid;
wire [2:0] app_wr_cmd;
wire app_wr_en;
wire [28:0] app_wr_addr;
wire [2:0] app_rd_cmd;
wire app_rd_en;
wire [28:0] app_rd_addr;
/**************FIFO控制部分*******************/
wire user_clk;
//写指令部分
wire fifo_wr_cmd_en;
wire [7:0] fifo_wr_cmd_brust_len;
wire [28:0] fifo_wr_cmd_addr;
wire [2:0] fifo_wr_cmd_instr;
wire fifo_wr_cmd_empty;
wire fifo_wr_cmd_full;
//写数据部分
wire fifo_wr_data_en;
wire [127:0] fifo_wr_data;
wire fifo_wr_data_full ;
wire fifo_wr_data_empty;
wire [6:0] fifo_wr_data_count;
//读指令部分
wire fifo_rd_cmd_en;
wire [7:0] fifo_rd_cmd_brust_len;
wire [28:0] fifo_rd_cmd_addr;
wire [2:0] fifo_rd_cmd_instr;
wire fifo_rd_cmd_empty;
wire fifo_rd_cmd_full;
//读数据部分
wire fifo_rd_data_en;
wire [127:0] fifo_rd_data;
wire fifo_rd_data_full;
wire fifo_rd_data_empty;
wire [6:0] fifo_rd_data_count;
assign app_en = app_wr_en | app_rd_en;
assign app_addr = app_wr_addr | app_rd_addr;
assign app_cmd = (app_wr_en == 'd1) ? 3'b000 : 3'b001;
//DDR工作时钟PLL例化
ddr3_clock ddr3_clock_inst(
.clk_out1(sys_clk_in), // output clk_out1
.clk_in1(sys_clk) // input clk_in1
);
//DDR初始化模块例化
ddr3_init u_ddr3_init (
// Memory interface ports
.ddr3_addr (ddr3_addr), // output [14: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 [15:0] ddr3_dq
.ddr3_dqs_n (ddr3_dqs_n), // inout [1:0] ddr3_dqs_n
.ddr3_dqs_p (ddr3_dqs_p), // inout [1: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 [1:0] ddr3_dm
.ddr3_odt (ddr3_odt), // output [0:0] ddr3_odt
// Application interface ports
.app_addr (app_addr), // input [28: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 [127:0] app_wdf_data
.app_wdf_end (app_wdf_wren), // input app_wdf_end
.app_wdf_wren (app_wdf_wren), // input app_wdf_wren
.app_rd_data (app_rd_data), // output [127: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 (1'b0), // input app_sr_req
.app_ref_req (1'b0), // input app_ref_req
.app_zq_req (1'b0), // 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 [15:0] app_wdf_mask
// System Clock Ports
.sys_clk_i (sys_clk_in), // input sys_clk_i
.sys_rst (rst_n) // input sys_rst
);
//写指令FIFO控制模块例化
wr_cmd_fifo_ctrl inst_wr_cmd_fifo_ctrl (
.wr_cmd_clk (user_clk),//input
.ui_clk (ui_clk),//input
.rst (ui_clk_sync_rst || (~init_calib_complete)),//input
.fifo_wr_cmd_en (fifo_wr_cmd_en),//input
.fifo_wr_cmd_brust_len (fifo_wr_cmd_brust_len),//input
.fifo_wr_cmd_addr (fifo_wr_cmd_addr),//input
.fifo_wr_cmd_instr (fifo_wr_cmd_instr),//input
.fifo_wr_cmd_start (wr_start),//input
.fifo_wr_cmd_empty (fifo_wr_cmd_empty),
.fifo_wr_cmd_full (fifo_wr_cmd_full),
.wr_brust_len (wr_brust_len),
.wr_addr (wr_addr),
.wr_cmd (wr_cmd)
);
//写数据FIFO控制模块例化
wr_data_fifo_ctrl inst_wr_data_fifo_ctrl (
.wr_data_clk (user_clk),//input
.ui_clk (ui_clk),//input
.rst (ui_clk_sync_rst || (~init_calib_complete)),//input
.fifo_wr_data_en (fifo_wr_data_en),//input
.fifo_wr_data (fifo_wr_data),//input
.data_req (data_req),//input
.wr_data (wr_data),
.fifo_wr_data_full (fifo_wr_data_full),
.fifo_wr_data_empty (fifo_wr_data_empty),
.fifo_wr_data_count (fifo_wr_data_count)
);
//读指令FIFO控制模块例化
rd_cmd_fifo_ctrl inst_rd_cmd_fifo_ctrl (
.rd_cmd_clk (user_clk),//input
.ui_clk (ui_clk),//input
.rst (ui_clk_sync_rst || (~init_calib_complete)),//input
.fifo_rd_cmd_en (fifo_rd_cmd_en),//input
.fifo_rd_cmd_brust_len (fifo_rd_cmd_brust_len),//input
.fifo_rd_cmd_addr (fifo_rd_cmd_addr),//input
.fifo_rd_cmd_instr (fifo_rd_cmd_instr),//input
.fifo_rd_cmd_start (rd_start),//input
.fifo_rd_cmd_empty (fifo_rd_cmd_empty),
.fifo_rd_cmd_full (fifo_rd_cmd_full),
.rd_brust_len (rd_brust_len),
.rd_addr (rd_addr),
.rd_cmd (rd_cmd)
);
//读数据FIFO控制模块例化
rd_data_fifo_ctrl inst_rd_data_fifo_ctrl (
.rd_data_clk (user_clk),//input
.ui_clk (ui_clk),//input
.rst (ui_clk_sync_rst || (~init_calib_complete)),//input
.fifo_rd_data_en (fifo_rd_data_en),//input
.rd_data_valid (rd_data_valid),//input
.rd_data (rd_data),//input
.fifo_rd_data (fifo_rd_data),
.fifo_rd_data_full (fifo_rd_data_full),
.fifo_rd_data_empty (fifo_rd_data_empty),
.fifo_rd_data_count (fifo_rd_data_count)
);
//DDR仲裁模块例化
ddr3_arbit inst_ddr3_arbit (
.ui_clk (ui_clk),
.rst (ui_clk_sync_rst || (~init_calib_complete)),
.wr_req (~fifo_wr_cmd_empty),
.rd_req (~fifo_rd_cmd_empty),
.wr_end (wr_end),
.rd_end (rd_end),
.wr_start (wr_start),
.rd_start (rd_start)
);
//DDR写操作模块例化
ddr3_wr_ctrl inst_ddr3_wr_ctrl (
.ui_clk (ui_clk),
.rst (ui_clk_sync_rst || (~init_calib_complete)),
.wr_data (wr_data),
.wr_brust_len (wr_brust_len),
.wr_start (wr_start),
.wr_addr (wr_addr),
.wr_cmd (wr_cmd),
.wr_mask (wr_mask),
.data_req (data_req),
.wr_end (wr_end),
.app_rdy (app_rdy),
.app_wdf_rdy (app_wdf_rdy),
.app_cmd (app_wr_cmd),
.app_en (app_wr_en),
.app_addr (app_wr_addr),
.app_wdf_data (app_wdf_data),
.app_wdf_wren (app_wdf_wren),
.app_wdf_mask (app_wdf_mask),
.app_wdf_end (app_wdf_end)
);
//DDR读操作模块例化
ddr3_rd inst_ddr3_rd (
.ui_clk (ui_clk),
.rst (ui_clk_sync_rst || (~init_calib_complete)),
.init_calib_complete (init_calib_complete),
.rd_brust_len (rd_brust_len),
.rd_start (rd_start),
.rd_addr (rd_addr),
.rd_cmd (rd_cmd),
.rd_data (rd_data),
.rd_data_valid (rd_data_valid),
.rd_end (rd_end),
.app_rdy (app_rdy),
.app_rd_data (app_rd_data),
.app_rd_data_end (app_rd_data_end),
.app_rd_data_valid (app_rd_data_valid),
.app_cmd (app_rd_cmd),
.app_en (app_rd_en),
.app_addr (app_rd_addr)
);
endmodule
编写一下测试代码对设计进行仿真测试。
testbench 设计
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Engineer : Linest-5
/* File : tb_top_ddr3_init.v
/* Create : 2022-09-15 10:10:36
/* Revise : 2022-09-24 19:48:54
/* Module Name :
/* Description :
/* Editor : sublime text3, tab size (4)
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
`timescale 1ns / 1ps
module tb_top_ddr3_init();
reg sys_clk;
reg rst_n;
wire [15:0] ddr3_dq;
wire [1:0] ddr3_dqs_n;
wire [1:0] ddr3_dqs_p;
wire [14:0] ddr3_addr;
wire [2:0] ddr3_ba;
wire ddr3_ras_n;
wire ddr3_cas_n;
wire ddr3_we_n;
wire ddr3_reset_n;
wire [0:0] ddr3_ck_p;
wire [0:0] ddr3_ck_n;
wire [0:0] ddr3_cke;
wire [0:0] ddr3_cs_n;
wire [1:0] ddr3_dm;
wire [0:0] ddr3_odt;
//wr_ddr
reg ui_clk;
reg wr_rst;
reg data_req;
reg [127:0] wr_data;
reg [7:0] wr_brust_len;
reg wr_start;
reg [28:0] wr_addr;
reg [2:0] wr_cmd;
reg wr_end;
//rd_ddr
reg rd_rst;
reg [7:0] rd_brust_len;
reg [127:0] rd_data;
reg rd_start;
reg [28:0] rd_addr;
reg [2:0] rd_cmd;
reg app_rdy;
reg [127:0] app_rd_data;
reg app_rd_data_end;
reg app_rd_data_valid;
reg [2:0] app_cmd;
reg rd_end;
reg wr_req;
reg rd_req;
/**************FIFO控制部分*******************/
reg user_clk;
reg rst;
//写指令部分
reg fifo_wr_cmd_en;
reg [7:0] fifo_wr_cmd_brust_len;
reg [28:0] fifo_wr_cmd_addr;
reg [2:0] fifo_wr_cmd_instr;
reg fifo_wr_cmd_start;
reg fifo_wr_cmd_full;
reg fifo_wr_cmd_full_reg;
wire fifo_wr_cmd_full_flag;
//写数据部分
reg fifo_wr_data_en;
reg [127:0] fifo_wr_data;
reg fifo_wr_data_full ;
reg fifo_wr_data_empty;
reg [6:0] fifo_wr_data_count;
//读指令部分
reg fifo_rd_cmd_en;
reg [7:0] fifo_rd_cmd_brust_len;
reg [28:0] fifo_rd_cmd_addr;
reg [2:0] fifo_rd_cmd_instr;
reg fifo_rd_cmd_start;
reg fifo_rd_cmd_empty;
reg fifo_rd_cmd_full;
//读数据部分
reg fifo_rd_data_en;
reg [127:0] fifo_rd_data;
reg fifo_rd_data_full;
reg fifo_rd_data_empty;
reg [6:0] fifo_rd_data_count;
reg [6:0] rd_fifo_data_cnt;
initial begin
sys_clk = 'd1;
user_clk = 'd1;
rst_n <= 'd0;
#200
rst_n <= 'd1;
end
initial begin
fifo_wr_cmd_en = 'd0;
fifo_wr_cmd_brust_len = 'd64;
fifo_wr_cmd_addr = 'd0;
fifo_wr_cmd_instr = 3'b000;
fifo_wr_data_en = 'd0;
fifo_wr_data = 'd0;
fifo_rd_cmd_en = 'd0;
fifo_rd_cmd_brust_len = 'd64;
fifo_rd_cmd_addr = 'd0;
fifo_rd_cmd_instr = 3'b001;
fifo_rd_data_en = 'd0;
end
initial begin
force inst_top_ddr3_init.user_clk = user_clk; //用户控制时钟
//写指令FIFO控制模块
force inst_top_ddr3_init.fifo_wr_cmd_en = fifo_wr_cmd_en;
force inst_top_ddr3_init.fifo_wr_cmd_brust_len = fifo_wr_cmd_brust_len;
force inst_top_ddr3_init.fifo_wr_cmd_addr = fifo_wr_cmd_addr;
force inst_top_ddr3_init.fifo_wr_cmd_instr = fifo_wr_cmd_instr;
//写数据FIFO控制模块
force inst_top_ddr3_init.fifo_wr_data_en = fifo_wr_data_en;
force inst_top_ddr3_init.fifo_wr_data = fifo_wr_data;
//读指令FIFO控制模块
force inst_top_ddr3_init.fifo_rd_cmd_en = fifo_rd_cmd_en;
force inst_top_ddr3_init.fifo_rd_cmd_brust_len = fifo_rd_cmd_brust_len;
force inst_top_ddr3_init.fifo_rd_cmd_addr = fifo_rd_cmd_addr;
force inst_top_ddr3_init.fifo_rd_cmd_instr = fifo_rd_cmd_instr;
//读数据FIFO控制模块
force inst_top_ddr3_init.fifo_rd_data_en = fifo_rd_data_en;
//数据获取
force rst = inst_top_ddr3_init.inst_ddr3_arbit.rst;
force rd_end = inst_top_ddr3_init.rd_end;
force fifo_rd_data_count = inst_top_ddr3_init.fifo_rd_data_count;
force fifo_wr_cmd_full = inst_top_ddr3_init.fifo_wr_cmd_full;
force wr_end = inst_top_ddr3_init.wr_end;
end
// initial begin
// #100
// wr_cmd_fifo_en();
// end
// initial begin
// #100
// rd_cmd_fifo_en();
// end
//写数据fifo使能信号
always @(posedge user_clk or posedge rst) begin
if (rst) begin
fifo_wr_data_en <= 'd0;
end
else if (fifo_wr_data == 'd63) begin
fifo_wr_data_en <= 'd0;
end
else if (fifo_wr_cmd_en) begin
fifo_wr_data_en <= 'd1;
end
else begin
fifo_wr_data_en <= fifo_wr_data_en;
end
end
//写入fifo数据生成
always @(posedge user_clk or posedge rst) begin
if (rst) begin
fifo_wr_data <= 'd0;
end
else if (fifo_wr_data == 'd63) begin
fifo_wr_data <= 'd0;
end
else if (fifo_wr_data_en) begin
fifo_wr_data <= fifo_wr_data + 'd1;
end
else begin
fifo_wr_data <= fifo_wr_data;
end
end
//读出fifo数据生成
always @(posedge user_clk or posedge rst) begin
if (rst) begin
fifo_rd_data_en <= 'd0;
end
else if (rd_end) begin
fifo_rd_data_en <= 'd1;
end
else if (rd_fifo_data_cnt == 'd63) begin //没有全读出
fifo_rd_data_en <= 'd0;
end
else begin
fifo_rd_data_en <= fifo_rd_data_en;
end
end
always @(posedge user_clk or posedge rst) begin
if (rst) begin
rd_fifo_data_cnt <= 'd0;
end
else if (fifo_rd_data_en && (rd_fifo_data_cnt == 'd63)) begin
rd_fifo_data_cnt <= 'd0;
end
else if (fifo_rd_data_en) begin
rd_fifo_data_cnt <= rd_fifo_data_cnt + 'd1;
end
else begin
rd_fifo_data_cnt <= rd_fifo_data_cnt;
end
end
//生成读指令fifo使能信号
always @(posedge user_clk or posedge rst) begin
if (rst) begin
fifo_rd_cmd_en <= 'd0;
end
else if (wr_end) begin
fifo_rd_cmd_en <= 'd1;
end
else begin
fifo_rd_cmd_en <= 'd0;
end
end
//创建写指令FIFO使能
// task wr_cmd_fifo_en;
// begin
// @ (negedge rst);
// @ (negedge fifo_wr_cmd_full);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// fifo_wr_cmd_en <= 'd1;
// @ (posedge user_clk);
// fifo_wr_cmd_en <= 'd0;
// end
// endtask
always @(posedge user_clk or posedge rst) begin
if (rst) begin
fifo_wr_cmd_en <= 'd0;
end
else if (fifo_wr_cmd_full_flag) begin
fifo_wr_cmd_en <= 'd1;
end
else if (wr_end) begin
fifo_wr_cmd_en <= 'd1;
end
else begin
fifo_wr_cmd_en <= 'd0;
end
end
assign fifo_wr_cmd_full_flag = fifo_wr_cmd_full ^ fifo_wr_cmd_full_reg;
always @(posedge user_clk or posedge rst) begin
if (rst) begin
fifo_wr_cmd_full_reg <= 'd1;
end
else begin
fifo_wr_cmd_full_reg <= fifo_wr_cmd_full;
end
end
// //创建读指令FIFO使能
// task rd_cmd_fifo_en;
// begin
// @ (negedge rst);
// @ (negedge fifo_rd_cmd_full);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// @ (posedge user_clk);
// fifo_rd_cmd_en <= 'd1;
// @ (posedge user_clk);
// fifo_rd_cmd_en <= 'd0;
// end
// endtask
always #10 sys_clk = ~sys_clk;
always #5 user_clk = ~user_clk;
top_ddr3_init inst_top_ddr3_init (
.ddr3_dq (ddr3_dq),
.ddr3_dqs_n (ddr3_dqs_n),
.ddr3_dqs_p (ddr3_dqs_p),
.ddr3_addr (ddr3_addr),
.ddr3_ba (ddr3_ba),
.ddr3_ras_n (ddr3_ras_n),
.ddr3_cas_n (ddr3_cas_n),
.ddr3_we_n (ddr3_we_n),
.ddr3_reset_n (ddr3_reset_n),
.ddr3_ck_p (ddr3_ck_p),
.ddr3_ck_n (ddr3_ck_n),
.ddr3_cke (ddr3_cke),
.ddr3_cs_n (ddr3_cs_n),
.ddr3_dm (ddr3_dm),
.ddr3_odt (ddr3_odt),
.sys_clk (sys_clk),
.rst_n (rst_n)
);
ddr3_model u_comp_ddr3 (
.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],ddr3_dm[0]}),
.ba (ddr3_ba),
.addr (ddr3_addr),
.dq (ddr3_dq[15:0]),
.dqs ({ddr3_dqs_p[1],
ddr3_dqs_p[0]}),
.dqs_n ({ddr3_dqs_n[1],
ddr3_dqs_n[0]}),
.tdqs_n (),
.odt (ddr3_odt)
);
endmodule
仿真测试
下图为进行连续写读指令操作,可以在仿真图看到数据能正常的写入并读出。
每次的突发长度均为 8
初始写地址为 0 并累加
先执行写指令
先将数据写入 FIFO 中,当开始写信号拉高时将数据读出,并写入 DDR3 控制器中。
当写完成信号拉高后,开始将数据读出,读出数据的同步有效信号作为读数据 FIFO 的写使能信号,依次将数据写入 FIFO 中,当读操作完成信号拉高后,用户端开始对读数据 FIFO 进行读操作,经对比写入数据和读出数据一致。
设计验证成功!但是在调试过程中真的痛苦,调试、修改 bug 循环往复,每修改一次都要等待 Vivado 跑好久的仿真,即耗时又耗力,但是在最终调试成功后的成就感可以将之前调试过程中的痛苦一同扫去,我想这就是在学习中成长的必经之路吧!在后面会单独出一篇关于在设计中遇到的问题以及调试解决的过程。
汇总篇
本系列为 DDR3 控制器设计总结,此系列包含 DDR3 控制器相关设计:认识 MIG、初始化、读写操作、FIFO 接口等。通过此系列的学习可以加深对 DDR3 读写时序的理解以及 FIFO 接口设计等,附上汇总博客直达链接。