【DDR3 控制器设计】(3)DDR3 的写操作设计

##~~~~~~~~~  写在前面  ~~~~~~~~~##

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

 

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


目录

 

实验任务

实验环境

实验介绍

接口详解

写时序

实验设计

写控制模块

顶层模块

testbench 设计

仿真波形


 

实验任务

熟悉 DDR3 的 IP 核的写时序,并利用 Verilog 实现对 DDR3 进行写操作,并带有仿真时序图。

实验环境

开发环境:Vivado 2018.2,

FPGA 芯片型号:xc7a100tffg484-2

DDR3 型号:MT41J256M16HA-125

实验介绍

直接对 DDR3 进行读写时序控制是比较困难的,接口复杂且多,但是通过调取 DDR3 控制器 MIG IP 核,间接的对 DDR3 进行控制就会方便很多,控制器给用户端预留了接口,通过查看 MIG IP 核用户手册,对 IP 核进行写控制。

接口详解

以下为 MIG 和 DDR 之间的连接框图,可以看到红框图是用户接口,中间是 MIG 核,右边蓝框图是需要控制的 DDR 接口,用户只需要去配置红框中的接口信号,就可以对 DDR 进行控制读写等操作。

【DDR3 控制器设计】(3)DDR3 的写操作设计_第1张图片

以下 MIG 对用户端的部分接口进行解释

端口名称

端口类型

端口解释

rst

输出

MIG 提供给用户端的复位信号,高有效

clk

输出

MIG 提供给用户端的时钟信号

app_addr

输入

地址总线。29 bit

app_cmd

输入

命令总线,3’b000代表写,3’b001 代表读。3bit

app_en

输入

命令使能信号,该信号有效(高电平),且 app_rdy 也有效时,MIG IP 核才可以接收到用户端发送的app_cmd 和 app_addr。1bit

app_hi_pri

输入

指令的优先级

app_wdf_data

输入

写数据总线,数据位宽为16bit,每次突发长度为8,因此数据总线位宽为128bit。

app_wdf_end

输入

最后一个写数据的标志,该信号有效(高电平)时,代表对应的

app_wdf_data 为当前写的最后一个数据。1bit ,当ui_clk的比例为4:1时,此信号和 app_wdf_wren 一致。

app_wdf_mask

输入

写数据掩码,该信号为写数据的掩码。16bit,每 1 bit对应 1 byte数据的掩码。

app_wdf_wren

输入

写数据有效标志,该信号有效(高电平),且 app_wdf_rdy 也有效时,MIG IP 核才可以接收到用户端发送的 app_wdf_data。1bit

app_rdy

输出

命令空闲信号,该信号有效(高电平),且 app_en 也有效时,MIG IP 核才可以接收到用户端发送的 app_cmd 和 app_addr指令

。1bit

app_rd_data

输出

读数据总线,数据位宽为16bit,每次突发长度为8,因此数据总线位宽为128bit。

app_rd_data_end

输出

最后一个读数据的标志,该信号有效(高电平)时,代表对应的

app_rd_data_end 为当前读的最后一个数据。1bit ,当ui_clk的比例为4:1时,此信号和 app_rd_data_valid 一致。

app_rd_data_valid

输出

读数据有效信号,此信号为高表示读出的数据有效,和读出的数据同步。1bit

app_wdf_rdy

输出

写数据空闲信号,表示 MIG 可以接收数据,该信号有效(高电平),且 app_wdf_wren 也有时, MIG IP 核才可以接收到用户端发送的 app_wdf_data。1bit

app_sr_req

输入

输入保留位,置 0

app_sr_active

输出

输出保留位

app_ref_req

输入

刷新请求信号,此信号为高表示请求向 DRAM 发出刷新命令。

app_ref_ack

输出

刷新请求确认信号,此信号为高表示内存控制器已将请求的刷新命令发送到PHY接口。

app_zq_req

输入

ZQ校准请求信号

app_zq_ack

输出

ZQ校准请求确认信号

写时序

写命令的时序如下,写命令与要写入的初始地址保持同步,并且app_en 和 app_rdy 信号同时拉高时,此时写命令才会被 MIG IP 核所接受。即当图中红框的时刻写指令才会被 MIG IP 核接受。

【DDR3 控制器设计】(3)DDR3 的写操作设计_第2张图片

关于写数据的三个信号 app_wdf_data,app_wdf_wren 和 app_wdf_end,这三个信号之间的关系如下时序图,以突发长度 8 为例。主要有三种情况:

  • 待写入的数据和写命令同步。
  • 待写入的数据比写命令提前若干拍。
  • 待写入的数据比写命令晚,但是最大不晚于写命令两个时钟周期。

本次实验使用第二种对应关系,这样时序比较好控制。

【DDR3 控制器设计】(3)DDR3 的写操作设计_第3张图片

另外说明一下 app_wdf_wren 和 app_wdf_end 两个信号之间的关系,在 A7 DDR3 控制器 IP 核中,突发长度只能为8,而数据为16bit,因此每次突发的数据位宽为16 * 8 = 128bit,同时在 IP 核配置的时候,有个配置选项为配置 DDR3 的物理层与用户端之间的速率关系,如果选择 4:1时,由于DDR为双边采样,因此每次的app_wdf_data为128bit,因此此时 app_wdf_end 信号与 app_wdf_wren 信号同步。当速率比为 2:1 时 app_wdf_data 为 64bit,此时为突发长度数据的前4个,因此 app_wdf_end 为 app_wdf_wren 信号的后半部分,并不是同步关系。如下图所示。

【DDR3 控制器设计】(3)DDR3 的写操作设计_第4张图片

【DDR3 控制器设计】(3)DDR3 的写操作设计_第5张图片

实验设计

实验设计框图如下,通过设计一个写控制模块,对 MIG IP 核进行写控制,在用户端的信号交互只需要提供写使能、数据、地址等信号即可实现对 DDR3 进行写操作。

【DDR3 控制器设计】(3)DDR3 的写操作设计_第6张图片

对上一节的 DDR 初始化实验工程保存名为 ddr3_wr 的工程,基本设计思想为设计写控制模块,并设计顶层模块,顶层模块包含各个模块、IP等,并testbench编写测试文件,最终查看仿真波形验证设计的正确性。

写控制模块

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Engineer    : Linest-5                                                         
/* File        : ddr3_wr_ctrl.v                                                         
/* Create      : 2022-09-19 11:05:07
/* Revise      : 2022-09-19 14:51:14                                                  
/* Module Name : ddr3_wr_ctrl                                                  
/* Description : ddr3写操作模块                                                                         
/* Editor : sublime text3, tab size (4)                                                                                
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

module ddr3_wr_ctrl(
	//时钟和复位
	input                 ui_clk,
	input                 rst,
	//user interface
	input                 init_calib_complete,
	input       [127:0]   wr_data,
	input       [7:0]     wr_brust_len,
	input                 wr_start,
	input       [28:0]    wr_addr,
	input       [2:0]     wr_cmd,
	input       [15:0]    wr_mask,
	output                data_req,
	output reg            wr_end,
	//app interface
	input                 app_rdy,
	input                 app_wdf_rdy,
	output      [2:0]     app_cmd,
	output reg            app_en,
	output reg  [28:0]    app_addr,
	output      [127:0]   app_wdf_data,
	output reg            app_wdf_wren,
	output      [15:0]    app_wdf_mask,
	output                app_wdf_end        
	);

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*      信号申明                                                                    */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
reg  [7:0]      wr_brust_len_reg;
reg  [7:0]      wr_data_cnt;
reg  [7:0]      app_cmd_cnt;

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*      Main Code                                                                  */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
assign app_cmd       =  3'b000;           //3'b000为写操作,3'b001为读操作
assign app_wdf_mask  =  16'd0;            //对所有数据都不使用掩码
assign app_wdf_end   =  app_wdf_wren;     //采用的是4:1,所以end和wren信号一致
assign data_req      =  app_wdf_wren && app_wdf_rdy;
assign app_wdf_data  =  wr_data;

//当开始写信号有效时,对突发长度进行寄存
always @(posedge ui_clk or posedge rst) begin
	if (rst) begin
		wr_brust_len_reg <= 'd0;
	end
	else if (wr_start) begin
		wr_brust_len_reg <= wr_brust_len;
	end
	else begin
		wr_brust_len_reg <= wr_brust_len_reg;
	end
end

//wr_data_cnt写入数据个数计数
always @(posedge ui_clk or posedge rst) begin
	if (rst) begin
		wr_data_cnt <= 'd0;
	end
	else if (data_req && (wr_data_cnt == (wr_brust_len_reg - 'd1))) begin
		wr_data_cnt <= 'd0;
	end
	else if (data_req) begin
		wr_data_cnt <= wr_data_cnt + 'd1;
	end
	else begin
		wr_data_cnt <= wr_data_cnt;
	end
end

//app_wdf_wren写使能信号
always @(posedge ui_clk or posedge rst) begin
	if (rst) begin
		app_wdf_wren <= 'd0;
	end
	else if (app_wdf_wren && app_wdf_rdy && (wr_data_cnt == (wr_brust_len_reg - 'd1))) begin
		app_wdf_wren <= 'd0;
	end
	else if (wr_start) begin
		app_wdf_wren <= 'd1;
	end
	else begin
		app_wdf_wren <= app_wdf_wren;
	end
end

//命令计数信号
always @(posedge ui_clk or posedge rst) begin
	if (rst) begin
		app_cmd_cnt <= 'd0;
	end
	else if (app_en && app_rdy && (app_cmd_cnt == (wr_brust_len_reg - 'd1))) begin
		app_cmd_cnt <= 'd0;
	end
	else if (app_en && app_rdy) begin
		app_cmd_cnt <= app_cmd_cnt + 'd1;
	end
	else begin
		app_cmd_cnt <= app_cmd_cnt;
	end
end

//命令使能信号,当app_en拉高时,命令和地址信号才有效
always @(posedge ui_clk or posedge rst) begin
	if (rst) begin
		app_en <= 'd0;
	end
	else if (app_en && app_rdy && (app_cmd_cnt == (wr_brust_len_reg - 'd1))) begin
		app_en <= 'd0;
	end
	else if (data_req) begin
		app_en <= 'd1;
	end
	else begin
		app_en <= app_en;
	end
end

//写地址app_addr
always @(posedge ui_clk or posedge rst) begin
	if (rst) begin
		app_addr <= 'd0;
	end
	else if (wr_end) begin
		app_addr <= 'd0;
	end
	else if (wr_start) begin
		app_addr <= wr_addr;
	end
	else if (app_en && app_rdy) begin
		app_addr <= app_addr + 'd8;
	end
	else begin
		app_addr <= app_addr;
	end
end

//写完成标志信号
always @(posedge ui_clk or posedge rst) begin
	if (rst) begin
		wr_end <= 'd0;
	end
	else if (app_en && app_rdy && (app_cmd_cnt == (wr_brust_len_reg - 'd1))) begin
		wr_end <= 'd1;
	end
	else begin
		wr_end <= 'd0;
	end
end

endmodule

顶层模块

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Engineer    : Linest-5                                                         
/* File        : top_ddr3_init.v                                                         
/* Create      : 2022-09-15 09:58:59
/* Revise      : 2022-09-19 15:45:17                                                  
/* Module Name :                                                   
/* Description :                                                                          
/* Editor : sublime text3, tab size (2)                                                                                
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/

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;

ddr3_clock ddr3_clock_inst(
    // Clock out ports
    .clk_out1(sys_clk_in),     // output clk_out1
   // Clock in ports
    .clk_in1(sys_clk)
);      // input clk_in1

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_cmd),
    .app_en              (app_en),
    .app_addr            (app_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)
  );

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
    );

endmodule

testbench 设计

/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ 
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Engineer    : Linest-5                                                         
/* File        : tb_top_ddr3_init.v                                                         
/* Create      : 2022-09-15 10:10:36
/* Revise      : 2022-09-19 19:48:35                                                  
/* 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                    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;

initial begin
	sys_clk  = 'd1;
	rst_n   <= 'd0;
	#200
	rst_n   <= 'd1;
end

initial begin
	data_req     = 'd0;
    wr_data      = 'd0;
    wr_brust_len = 'd64;
    wr_start     = 'd0;
    wr_addr      = 'd0;
    wr_cmd       = 'd0;
	force ui_clk   = inst_top_ddr3_init.inst_ddr3_wr_ctrl.ui_clk;
	force rst      = inst_top_ddr3_init.inst_ddr3_wr_ctrl.rst;
	force data_req = inst_top_ddr3_init.inst_ddr3_wr_ctrl.data_req;
    force inst_top_ddr3_init.wr_brust_len = wr_brust_len;
    force inst_top_ddr3_init.wr_start     = wr_start;
    force inst_top_ddr3_init.wr_addr      = wr_addr;
    force inst_top_ddr3_init.wr_cmd       = wr_cmd;
    force inst_top_ddr3_init.wr_data      = wr_data;
end

initial begin
	#100
	gen_cmd();
end

always @(posedge ui_clk or posedge rst) begin
	if (rst) begin
		wr_data <= 'd0;
	end
	else if (wr_data == 'd63) begin
		wr_data <= 'd0;
	end
	else if (data_req) begin
		wr_data <= wr_data + 'd1;
	end
	else begin
		wr_data <= wr_data;
	end
end

// initial begin
// 	#100
// 	gen_data();
// end

task gen_cmd;
	begin
		@ (negedge rst);
		@ (posedge ui_clk);
		@ (posedge ui_clk);
		@ (posedge ui_clk);
		@ (posedge ui_clk);
		@ (posedge ui_clk);
		wr_start = 'd1;
		@ (posedge ui_clk);
		wr_start = 'd0;
	end
endtask

// task gen_data;
// 	integer i;
// 	begin
// 		@ (posedge data_req);
// 		for (i=0;i<64;i=i+1) begin
// 			wr_data = {96'd0,i[31:0]};
// 			@ (posedge ui_clk);
// 			if (data_req == 'd0) begin
// 				i = i - 1;
// 			end
// 		end
// 		wr_data = 'd0;
// 		@ (posedge ui_clk);
// 	end
// endtask

always #10 sys_clk = ~sys_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

仿真波形

先看总体的仿真波形,当 wr_start 信号拉高标志开始进行写指令的下发,根据wr_req 信号依次对写入的数据进行累加,最终完成写操作,将 wr_end 信号拉高。

【DDR3 控制器设计】(3)DDR3 的写操作设计_第7张图片

可以看到以下仿真图形,采用的是第二种写时序,先给出要写入的数据,然后再给出写命令以及要写入的地址,每次数据累加1,地址累加8(因为突发长度为8),当 data_req 拉低表示 FIFO 已满,停止接收数据请求,同时 app_rdy 拉低表示 MIG IP 未准备好接收数据,因此 app_addr 停止累加。

【DDR3 控制器设计】(3)DDR3 的写操作设计_第8张图片

当数据写完成后,可以看到一共写入的数据为0-63,地址累加至 504,最后写完成信号 wr_end 表示此次突发写操作完成。

【DDR3 控制器设计】(3)DDR3 的写操作设计_第9张图片

在控制台看到的打印信息也可以清晰的看出,进行写操作,初始写数据为128位的0,地址依次累加。

【DDR3 控制器设计】(3)DDR3 的写操作设计_第10张图片

在写完成后,可以看到最后写入的数据为 0000000000000000000000000000003f,即 128'd63,最后写入的地址为000001ff,即 29'd511,与设计的一致。

【DDR3 控制器设计】(3)DDR3 的写操作设计_第11张图片


汇总篇 

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

 

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

 

你可能感兴趣的:(总线接口协议,#,DDR,SDRAM,fpga开发,DDR3,MIG,Verilog,总线接口)