【DDR3 控制器设计】(6)DDR3 的读写模块添加 FIFO 接口设计

发布于:2022-11-09 ⋅ 阅读:(548) ⋅ 点赞:(0)

写在前面

本系列为 DDR3 控制器设计总结,此系列包含 DDR3 控制器相关设计:认识 MIG、初始化、读写操作、FIFO 接口等。通过此系列的学习可以加深对 DDR3 读写时序的理解以及 FIFO 接口设计等,附上汇总博客直达链接。

【DDR3 控制器设计】系列博客汇总篇(附直达链接)


目录

实验任务

实验环境

实验介绍

FIFO 接口设计

程序设计

写指令 FIFO 模块

写数据 FIFO 模块

读指令 FIFO 模块

读数据 FIFO 模块

顶层模块

testbench 设计

仿真测试

汇总篇


实验任务

在读写模块的基础上添加 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 接口设计等,附上汇总博客直达链接。

【DDR3 控制器设计】系列博客汇总篇(附直达链接)

本文含有隐藏内容,请 开通VIP 后查看