上一节 【DDR】基于Verilog的DDR控制器的简单实现(二)——写操作
本文继续以美光(Micron)公司生产的DDR3芯片MT41J512M8RH-093(芯片手册)为例,说明DDR芯片的读操作过程。下图为读操作指令格式(来自P122 Table 72: READ Command Summary),与写操作指令格式区别在于WE#位。
DDR的读操作过程类似于写操作过程,在读特定row前需要使用ACTIVATE指令激活,在读完毕后需要显式或隐式使用PRECHARGE预充电。主要区别在于读操作的DQS与DQ由DDR芯片产生,DDR控制器需要根据DQS时钟对DQ采样。一次读操作的波形图如下(来自P163 Figure 69: READ Latency),在READ指令发出后经过CL+AL个周期,DQ数据线上将有连续数据输出。其中CL(CAS latency)、AL的值为初始化阶段写入MR0,MR1寄存器的值。
读过程DQS和DQ总线上的时序约束如图(来自P170 Figure 79: Data Output Timing – tDQSQ and Data Valid Window),DQS作为DQ的时钟信号,在DQ数据开始输出前tRPRE时钟周期开始输出低电平。DQ数据线上的多个bit只有在Data valid的一段时间内同时有效。
读过程主要涉及的时序约束如下(来自P94)
* tDQSQ <75ps ;DQ信号同时有效开始相对DQS边沿的延后时间
* tQH >0.38 tCK ;DQ信号同时有效结束相对DQS边沿的延后时间
* tRTP >max(7.5ns,4CK) ;READ指令后相邻PRECHARGE指令最小时间间隔
仿真时将时钟周期取为最小时钟周期0.938ns,对应时钟频率1066.099MHz。tDQSS取为0,此时DQS时钟与clk时钟相同,每次连续写操作只对8个地址中的首个数据,最终得到的DDR3写数据代码如下:
`timescale 1ns / 1ps
//
// Company:
// Engineer: wjh776a68
//
// Create Date: 01/15/2024 15:25:15 PM
// Design Name:
// Module Name: micron_ddr_rdbyte
// Project Name:
// Target Devices: VU9P
// Tool Versions: 2017.4
// Description:
//
// Dependencies:
//
// Revision:
// Revision 0.01 - File Created
// Additional Comments:
//
//// ddr3 x8 4Gb MT41J512M8RH-093
/****************************
* DDR3L-2133 https://www.micron.com/-/media/client/global/documents/products/data-sheet/dram/ddr3/4gb_ddr3l.pdf?rev=305217e2f9bd4ef48d7c6f353dfc064c
* CK(MIN) 0.938 ns
* CL 14 CK
* RCD(MIN) 14 CK
* RC(MIN) 50 CK
* RAS(MIN) 36 CK
* RP(MIN) 14 CK
* FAW 27 CK
* RRD 6 CK
* RFC 279CK
* XPR >max(5CK, RFC+10ns)
* MRD >4CK
* MOD >max(12CK, 15ns)
* ZQinit <max(512nCK, 640ns)
* DLLK >512CK
*
* tDQSS =(-0.27, 0.27)CK P94
* tDSS >0.18CK
* tWPRE >0.9CK
* tWPST >0.3CK
* tDS >55ps
* tDH >60ps
* tRAS >33ns
* tWR >15ns
*
* tDQSQ <75ps P94
* tQH >0.38 tCK
* tRTP >max(7.5ns,4CK)
* command all p118
* initial waveform p137
*
* COMMAND | NOP | MRS_1 | MRS_2 | MRS_3 | MRS_4 | ZQCL
* ddr_cke | 1 1 | 1 1 | 1 1 | 1 1 | 1 1 | 1 1
* ddr_dqs_en | 0 | | | | |
* ddr_dq_en | 0 | | | | |
* ddr_cs_n | 0 | 0 | 0 | 0 | 0 | 0
* ddr_ras_n | 1 | 0 | 0 | 0 | 0 | 1
* ddr_cas_n | 1 | 0 | 0 | 0 | 0 | 1
* ddr_we_n | 1 | 0 | 0 | 0 | 0 | 0
* ddr_ba | vvv | 010 | 011 | 001 | 000 |
* ddr_addr | vvv | 28 | 00 | 44 | 124 | a[10]
* ddr_odt | 0 | | | | |
*
* ACT : open a row in a bank, do PRECHANGE after open other row in the same bank P161
* PRECHARGE : access for the same bank is avaliable for 'RP after PRECHARGE *
* WRITE COMMAND (P122 Table 73)
* COMMAND | WR | WRS4 | WRS8 | WRAP | WRAPS4| WRAPS8
* ddr_cke | 1 1 | 1 1 | 1 1 | 1 1 | 1 1 | 1 1
* ddr_dqs_en | - | | | | |
* ddr_dq_en | - | | | | |
* ddr_cs_n | 0 | 0 | 0 | 0 | 0 | 0
* ddr_ras_n | 1 | 1 | 1 | 1 | 1 | 1
* ddr_cas_n | 0 | 0 | 0 | 0 | 0 | 0
* ddr_we_n | 0 | 0 | 0 | 0 | 0 | 0
* ddr_ba | BA | BA | BA | BA | BA | BA
* ddr_addr12 | v | 0 | 1 | v | 0 | 1
* ddr_addr10 | 0 | 0 | 0 | 1 | 1 | 1
* ddr_addr | CA | | | | |
* ddr_odt | - | | | | |
* ddr_dq | data write to mem
* ddr_dm | 0 : dq valid ; 1 : dq invalid, write skip that byte/column
*
* READ COMMAND (P122 Table 72)
* COMMAND | RD | RDS4 | RDS8 | RDAP | RDAPS4| RDAPS8
* ddr_cke | 1 1 | 1 1 | 1 1 | 1 1 | 1 1 | 1 1
* ddr_dqs_en | - | | | | |
* ddr_dq_en | - | | | | |
* ddr_cs_n | 0 | 0 | 0 | 0 | 0 | 0
* ddr_ras_n | 1 | 1 | 1 | 1 | 1 | 1
* ddr_cas_n | 0 | 0 | 0 | 0 | 0 | 0
* ddr_we_n | 1 | 1 | 1 | 1 | 1 | 1
* ddr_ba | BA | BA | BA | BA | BA | BA
* ddr_addr12 | v | 0 | 1 | v | 0 | 1
* ddr_addr10 | 0 | 0 | 0 | 1 | 1 | 1
* ddr_addr | CA | | | | |
* ddr_odt | - | | | | |
* ddr_dq | data read from mem
* ddr_dm | -
*
***************************************************************/
module micron_ddr_rdbyte #(parameter CLK_FREQ = 1066.099, // MHzparameter _1MS_CYCLE = 10.0**-3 / (1.0 / (CLK_FREQ * 10**6)),parameter _1US_CYCLE = 10.0**-6 / (1.0 / (CLK_FREQ * 10**6)),parameter integer INITIAL_CYCLE = 200 * _1US_CYCLE,parameter integer INITIAL_STABLE_CYCLE = 500 * _1US_CYCLE,parameter integer FREE_CYCLE = _1US_CYCLE
) (output reg [15:0] ddr_addr,output reg [2:0] ddr_ba,output reg ddr_cas_n,output reg [0:0] ddr_ck_n,output reg [0:0] ddr_ck_p,output reg [0:0] ddr_cke,output reg [0:0] ddr_cs_n,output reg [0:0] ddr_dm,inout [7:0] ddr_dq,inout [0:0] ddr_dqs_n,inout [0:0] ddr_dqs_p,output reg [0:0] ddr_odt,output reg ddr_ras_n,output reg ddr_reset_n,output reg ddr_we_n,input [28:0] s_wraddr, // 4Gb -> 512MB -> 512M -> 2^29// 3BA 16RA 10CAinput [7:0] s_wrdata,input s_wrstrb,input s_wren,input [28:0] s_rdaddr,output [7:0] s_rddata,input s_rden,input clk);localparam [27:0] NOP_CMD = {11'b11110111000, 16'h0000, 1'b0};
localparam [27:0] MRS1_CMD = {11'b11110000010, 16'h0028, 1'b0}; //MR2 tCWL=10 A5-A3
localparam [27:0] MRS2_CMD = {11'b11110000011, 16'h0000, 1'b0}; //MR3
localparam [27:0] MRS3_CMD = {11'b11110000001, 16'h0044, 1'b0}; //MR1 tAL=0 A4-A3
localparam [27:0] MRS4_CMD = {11'b11110000000, 16'h0124, 1'b0}; //MR0 tCAS=14CK A6-A4,A2
localparam [27:0] ZQCL_CMD = {11'b11110110000, 16'h0400, 1'b0};localparam [27:0] ACT_CMD = {11'b11110011000, 16'h0000, 1'b0}; // ba ralocalparam [27:0] PRE_CMD = {11'b11110010000, 16'h0000, 1'b0}; // balocalparam [27:0] WR_CMD = {11'b11110100000, 16'h0000, 1'b0}; // dqs_o dq_o ba ca
localparam [27:0] RD_CMD = {11'b11110101000, 16'h0000, 1'b0}; // dqs_i dq_i ba careg ddr_cke_p1, ddr_cke_p2;
reg ddr_dqs_o, ddr_dqs_en;
wire ddr_dqs_i;
reg [7:0] ddr_dq_o_r_p1, ddr_dq_o_r_p2;
wire [7:0] ddr_dq_o_r;
wire [7:0] ddr_dq_o, ddr_dq_i;
wire [7:0] dq_i_r_p1, dq_i_r_p2;
wire [7:0] dq_i_r;
reg ddr_dq_en;
reg ddr_dm_o_r_p1, ddr_dm_o_r_p2;
wire ddr_dm_o_r;
wire ddr_dm_o; wire [2:0] wraddr_ba_s;
wire [15:0] wraddr_ra_s;
wire [9:0] wraddr_ca_s;
wire [7:0] wrdata_s;
wire wrstrb_s;
wire wren_s;wire [2:0] rdaddr_ba_s;
wire [15:0] rdaddr_ra_s;
wire [9:0] rdaddr_ca_s;
wire [7:0] rddata_s;
// wire rdstrb_s;
wire rden_s;reg [2:0] wraddr_ba_r;
reg [15:0] wraddr_ra_r;
reg [9:0] wraddr_ca_r;
reg [7:0] wrdata_r;
reg wrstrb_r;
reg wren_r;reg [2:0] rdaddr_ba_r;
reg [15:0] rdaddr_ra_r;
reg [9:0] rdaddr_ca_r;
reg [7:0] rddata_r;
// reg rdstrb_r;
reg rden_r;OBUFDS OBUFDS_ck (.O(ddr_ck_p), // 1-bit output: Diff_p output (connect directly to top-level port).OB(ddr_ck_n), // 1-bit output: Diff_n output (connect directly to top-level port).I(clk) // 1-bit input: Buffer input);IOBUFDS #(.DQS_BIAS("FALSE") // (FALSE, TRUE))IOBUFDS_dqs_inst (.O(ddr_dqs_i), // 1-bit output: Buffer output.I(ddr_dqs_o), // 1-bit input: Buffer input.IO(ddr_dqs_p), // 1-bit inout: Diff_p inout (connect directly to top-level port).IOB(ddr_dqs_n), // 1-bit inout: Diff_n inout (connect directly to top-level port).T(ddr_dqs_en) // 1-bit input: 3-state enable input);generateOBUF OBUF_dm_inst (.O(ddr_dm), // 1-bit output: Buffer output (connect directly to top-level port).I(ddr_dm_o) // 1-bit input: Buffer input);IDELAYCTRL #(.SIM_DEVICE("ULTRASCALE") // Must be set to "ULTRASCALE" )IDELAYCTRL_dm_o_inst (.RDY(), // 1-bit output: Ready output.REFCLK(clk), // 1-bit input: Reference clock input.RST(1'b0) // 1-bit input: Active high reset input. Asynchronous assert, synchronous deassert to// REFCLK.);ODELAYE3 #(.CASCADE("NONE"), // Cascade setting (MASTER, NONE, SLAVE_END, SLAVE_MIDDLE).DELAY_FORMAT("TIME"), // (COUNT, TIME).DELAY_TYPE("FIXED"), // Set the type of tap delay line (FIXED, VARIABLE, VAR_LOAD).DELAY_VALUE(55), // Output delay tap setting (ps).IS_CLK_INVERTED(1'b0), // Optional inversion for CLK.IS_RST_INVERTED(1'b0), // Optional inversion for RST.REFCLK_FREQUENCY(1066.098), // IDELAYCTRL clock input frequency in MHz (200.0-2667.0)..SIM_DEVICE("ULTRASCALE"), // Set the device version (ULTRASCALE, ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,// ULTRASCALE_PLUS_ES2).UPDATE_MODE("ASYNC") // Determines when updates to the delay will take effect (ASYNC, MANUAL, SYNC))ODELAYE3_dm_o_inst (.CASC_OUT(), // 1-bit output: Cascade delay output to IDELAY input cascade.CNTVALUEOUT(), // 9-bit output: Counter value output.DATAOUT(ddr_dm_o), // 1-bit output: Delayed data from ODATAIN input port.CASC_IN(1'b0), // 1-bit input: Cascade delay input from slave IDELAY CASCADE_OUT.CASC_RETURN(1'b0), // 1-bit input: Cascade delay returning from slave IDELAY DATAOUT.CE(1'b1), // 1-bit input: Active high enable increment/decrement input.CLK(clk), // 1-bit input: Clock input.CNTVALUEIN(9'b0), // 9-bit input: Counter value input.EN_VTC(1'b1), // 1-bit input: Keep delay constant over VT.INC(1'b0), // 1-bit input: Increment/Decrement tap delay input.LOAD(1'b0), // 1-bit input: Load DELAY_VALUE input.ODATAIN(ddr_dm_o_r), // 1-bit input: Data input.RST(1'b0) // 1-bit input: Asynchronous Reset to the DELAY_VALUE);ODDRE1 #(.IS_C_INVERTED(1'b1), // Optional inversion for C.IS_D1_INVERTED(1'b0), // Unsupported, do not use.IS_D2_INVERTED(1'b0), // Unsupported, do not use.SRVAL(1'b0) // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1))ODDRE1_dm_o_inst (.Q(ddr_dm_o_r), // 1-bit output: Data output to IOB.C(clk), // 1-bit input: High-speed clock input.D1(ddr_dm_o_r_p1), // 1-bit input: Parallel data input 1.D2(ddr_dm_o_r_p2), // 1-bit input: Parallel data input 2.SR(1'b0) // 1-bit input: Active High Async Reset);for (genvar i = 0; i < 8; i++) beginIOBUF IOBUF_dq_inst (.O(ddr_dq_i[i]), // 1-bit output: Buffer output.I(ddr_dq_o[i]), // 1-bit input: Buffer input.IO(ddr_dq[i]), // 1-bit inout: Buffer inout (connect directly to top-level port).T(ddr_dq_en) // 1-bit input: 3-state enable input);IDELAYCTRL #(.SIM_DEVICE("ULTRASCALE") // Must be set to "ULTRASCALE" )IDELAYCTRL_dq_o_inst (.RDY(), // 1-bit output: Ready output.REFCLK(clk), // 1-bit input: Reference clock input.RST(1'b0) // 1-bit input: Active high reset input. Asynchronous assert, synchronous deassert to// REFCLK.);ODELAYE3 #(.CASCADE("NONE"), // Cascade setting (MASTER, NONE, SLAVE_END, SLAVE_MIDDLE).DELAY_FORMAT("TIME"), // (COUNT, TIME).DELAY_TYPE("FIXED"), // Set the type of tap delay line (FIXED, VARIABLE, VAR_LOAD).DELAY_VALUE(55), // Output delay tap setting (ps).IS_CLK_INVERTED(1'b0), // Optional inversion for CLK.IS_RST_INVERTED(1'b0), // Optional inversion for RST.REFCLK_FREQUENCY(1066.098), // IDELAYCTRL clock input frequency in MHz (200.0-2667.0)..SIM_DEVICE("ULTRASCALE"), // Set the device version (ULTRASCALE, ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,// ULTRASCALE_PLUS_ES2).UPDATE_MODE("ASYNC") // Determines when updates to the delay will take effect (ASYNC, MANUAL, SYNC))ODELAYE3_dq_o_inst (.CASC_OUT(), // 1-bit output: Cascade delay output to IDELAY input cascade.CNTVALUEOUT(), // 9-bit output: Counter value output.DATAOUT(ddr_dq_o[i]), // 1-bit output: Delayed data from ODATAIN input port.CASC_IN(1'b0), // 1-bit input: Cascade delay input from slave IDELAY CASCADE_OUT.CASC_RETURN(1'b0), // 1-bit input: Cascade delay returning from slave IDELAY DATAOUT.CE(1'b1), // 1-bit input: Active high enable increment/decrement input.CLK(clk), // 1-bit input: Clock input.CNTVALUEIN(9'b0), // 9-bit input: Counter value input.EN_VTC(1'b1), // 1-bit input: Keep delay constant over VT.INC(1'b0), // 1-bit input: Increment/Decrement tap delay input.LOAD(1'b0), // 1-bit input: Load DELAY_VALUE input.ODATAIN(ddr_dq_o_r[i]), // 1-bit input: Data input.RST(1'b0) // 1-bit input: Asynchronous Reset to the DELAY_VALUE);ODDRE1 #(.IS_C_INVERTED(1'b1), // Optional inversion for C.IS_D1_INVERTED(1'b0), // Unsupported, do not use.IS_D2_INVERTED(1'b0), // Unsupported, do not use.SRVAL(1'b0) // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1))ODDRE1_dq_o_inst (.Q(ddr_dq_o_r[i]), // 1-bit output: Data output to IOB.C(clk), // 1-bit input: High-speed clock input.D1(ddr_dq_o_r_p1[i]), // 1-bit input: Parallel data input 1.D2(ddr_dq_o_r_p2[i]), // 1-bit input: Parallel data input 2.SR(1'b0) // 1-bit input: Active High Async Reset);IDDRE1 #(.DDR_CLK_EDGE("SAME_EDGE"), // IDDRE1 mode (OPPOSITE_EDGE, SAME_EDGE, SAME_EDGE_PIPELINED).IS_CB_INVERTED(1'b1), // Optional inversion for CB.IS_C_INVERTED(1'b1) // Optional inversion for C)IDDRE1_dq_inst (.Q1(dq_i_r_p1[i]), // 1-bit output: Registered parallel output 1.Q2(dq_i_r_p2[i]), // 1-bit output: Registered parallel output 2.C(ddr_dqs_i), // 1-bit input: High-speed clock.CB(~ddr_dqs_i), // 1-bit input: Inversion of High-speed clock C.D(dq_i_r[i]), // 1-bit input: Serial Data Input.R(1'b0) // 1-bit input: Active High Async Reset);IDELAYE3 #(.CASCADE("NONE"), // Cascade setting (MASTER, NONE, SLAVE_END, SLAVE_MIDDLE).DELAY_FORMAT("TIME"), // Units of the DELAY_VALUE (COUNT, TIME).DELAY_SRC("IDATAIN"), // Delay input (DATAIN, IDATAIN).DELAY_TYPE("FIXED"), // Set the type of tap delay line (FIXED, VARIABLE, VAR_LOAD).DELAY_VALUE(75), // Input delay value setting.IS_CLK_INVERTED(1'b1), // Optional inversion for CLK.IS_RST_INVERTED(1'b0), // Optional inversion for RST.REFCLK_FREQUENCY(1066.098), // IDELAYCTRL clock input frequency in MHz (200.0-2667.0).SIM_DEVICE("ULTRASCALE"), // Set the device version (ULTRASCALE, ULTRASCALE_PLUS, ULTRASCALE_PLUS_ES1,// ULTRASCALE_PLUS_ES2).UPDATE_MODE("ASYNC") // Determines when updates to the delay will take effect (ASYNC, MANUAL, SYNC))IDELAYE3_dq_inst (.CASC_OUT(), // 1-bit output: Cascade delay output to ODELAY input cascade.CNTVALUEOUT(), // 9-bit output: Counter value output.DATAOUT(dq_i_r[i]), // 1-bit output: Delayed data output.CASC_IN(1'b0), // 1-bit input: Cascade delay input from slave ODELAY CASCADE_OUT.CASC_RETURN(1'b0), // 1-bit input: Cascade delay returning from slave ODELAY DATAOUT.CE(1'b1), // 1-bit input: Active high enable increment/decrement input.CLK(ddr_dqs_i), // 1-bit input: Clock input.CNTVALUEIN(9'b0), // 9-bit input: Counter value input.DATAIN(1'b0), // 1-bit input: Data input from the logic.EN_VTC(1'b0), // 1-bit input: Keep delay constant over VT.IDATAIN(ddr_dq_i[i]), // 1-bit input: Data input from the IOBUF.INC(1'b0), // 1-bit input: Increment / Decrement tap delay input.LOAD(1'b0), // 1-bit input: Load DELAY_VALUE input.RST(1'b0) // 1-bit input: Asynchronous Reset to the DELAY_VALUE); endendgenerateODDRE1 #(.IS_C_INVERTED(1'b1), // Optional inversion for C.IS_D1_INVERTED(1'b0), // Unsupported, do not use.IS_D2_INVERTED(1'b0), // Unsupported, do not use.SRVAL(1'b0) // Initializes the ODDRE1 Flip-Flops to the specified value (1'b0, 1'b1))ODDRE1_cke_inst (.Q(ddr_cke), // 1-bit output: Data output to IOB.C(clk), // 1-bit input: High-speed clock input.D1(ddr_cke_p1), // 1-bit input: Parallel data input 1.D2(ddr_cke_p2), // 1-bit input: Parallel data input 1.SR(1'b0) // 1-bit input: Active High Async Reset);//OBUFDS OBUFDS_dqs (
// .O(ddr_dqs_p), // 1-bit output: Diff_p output (connect directly to top-level port)
// .OB(ddr_dqs_n), // 1-bit output: Diff_n output (connect directly to top-level port)
// .I(ddr_dqs) // 1-bit input: Buffer input
// );reg [15:0] ddr_addr_r;reg [2:0] ddr_ba_r;reg ddr_cas_n_r;reg [0:0] ddr_cs_n_r;// reg [0:0] ddr_dm_r; // no refreg [0:0] ddr_odt_r;reg ddr_ras_n_r;reg ddr_we_n_r;reg ddr_dq_en_r; reg ddr_dqs_en_r; always @(negedge clk) beginddr_addr <= ddr_addr_r ;ddr_ba <= ddr_ba_r ;ddr_cas_n <= ddr_cas_n_r ;ddr_cs_n <= ddr_cs_n_r ;// ddr_dm <= ddr_dm_r ;ddr_odt <= ddr_odt_r ;ddr_ras_n <= ddr_ras_n_r ;ddr_we_n <= ddr_we_n_r ;ddr_dq_en <= ddr_dq_en_r;ddr_dqs_en <= ddr_dqs_en_r;endreg [5:0] cs = 0, ns;reg [31:0] initial_cnt = 0;reg [31:0] initial_stable_cnt = 0;reg [31:0] freerun_cnt = 0;reg [5:0] trcd_cnt = 0;reg [5:0] twl_cnt = 0;reg [5:0] trl_cnt = 0;reg [5:0] twr_cnt = 0;reg [5:0] tRTP_cnt = 0;reg [5:0] burst_cnt = 0;reg [3:0] initial_cmd_ptr = 0;reg [27:0] initial_cmd_seq[0:5] = '{NOP_CMD, MRS1_CMD, MRS2_CMD, MRS3_CMD, MRS4_CMD, ZQCL_CMD};reg initial_finish = 0;always @(negedge clk) begincs <= ns;endalways @(*) begincase (cs)0: beginif (initial_cnt == INITIAL_CYCLE) begin // wait 200usns = 1;end else beginns = 0;endend1: begin // wait 500us if (initial_stable_cnt == INITIAL_STABLE_CYCLE) begin // wait 500usns = 2;end else beginns = 1;endend2: beginns = 3;end3: beginif (freerun_cnt == FREE_CYCLE) beginif (initial_finish) beginns = 4;end else beginns = 2;endend else beginns = 3;endend4: beginif (wren_s) beginns = 5; // Goto ACTIVATE max(tRC, tRRD, tFAW/4), same bank ACTIVATE at least tRC, different bank ACTIVATE at least tRRD, no more than four bank ACT during tFAW (P161 Fig.67)end else if (rden_s) beginns = 10; // READ Operation tCCD consecutive read end else beginns = 4;endend5: begin // WRITE ACTIVATEif (trcd_cnt == 14) beginns = 6; // WRITE/READ Operation prior to tRCD after ACTend else beginns = 5;end end6: begin // WRITE WRITE-WRITE delay tCCD, DATA provided at posedge of DQS with latency AL+CWL in MR0 and MR2 , tWTR tWR P174 P176if (twl_cnt == 10) begin // tCWL + tAL = 10 ddr_dq_o_r has extra one cycle delay -> 10 - 2ns = 7;end else beginns = 6;endend7: begin // WRITE DATA WL latency
// if (ddr_dqs_i) begin // tDQSS = 0if (burst_cnt == 4) begin // double burst == 8ns = 8;end else beginns = 7;end// end else begin// ns = 7;// endend8: begin // POSTEAMBLE if (twr_cnt == 16) beginns = 9; // Write to ReCharge tWR >15ns ACT to ReCharge tRAS>33nsend else beginns = 8;endend9: begin // READ PRECHARGEns = 4;end10: begin // READ ACTIVATEif (trcd_cnt == 14) beginns = 11; // WRITE/READ Operation prior to tRCD after ACTend else beginns = 10;end end11: begin // READ P166 Nonconsecutive READif (trl_cnt == 14 + 3) begin // AL + CL + ddr_pipe_delay(idelay,iddrns = 12;end else beginns = 11;endend12: begin // READ DATA CL latencyif (burst_cnt == 4) beginns = 13;end else beginns = 12;endend13: begin // READ POSTAMBLEif (tRTP_cnt == 7) beginns = 14; // Read to ReCharge tRTP>max(7.5ns,4CK) ACT to ReCharge tRAS>33nsend else beginns = 13;endend14: begin // READ PRECHARGEns = 4; enddefault: beginns = 0;endendcaseendalways @(negedge clk) begincase (ns)0: begininitial_cnt <= initial_cnt + 1;enddefault: begininitial_cnt <= 0;endendcaseendalways @(negedge clk) begincase (ns)1: begininitial_stable_cnt <= initial_stable_cnt + 1;enddefault: begininitial_stable_cnt <= 0;endendcaseendalways @(negedge clk) begincase (ns)3: beginfreerun_cnt <= freerun_cnt + 1;enddefault: beginfreerun_cnt <= 0;endendcaseendalways @(negedge clk) begincase (ns)2: beginif (initial_cmd_ptr == 6 - 1) begininitial_finish <= 1;end else begininitial_finish <= 0;endinitial_cmd_ptr <= initial_cmd_ptr + 1;endendcaseendinitial begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en, ddr_dq_en, ddr_cs_n, ddr_ras_n, ddr_cas_n, ddr_we_n, ddr_ba, ddr_addr, ddr_odt} <= NOP_CMD;{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= NOP_CMD;endalways @(negedge clk) begincase (ns)5: begintrcd_cnt <= trcd_cnt + 1;end10: begintrcd_cnt <= trcd_cnt + 1;enddefault: begintrcd_cnt <= 0;endendcaseendalways @(negedge clk) begincase (ns)6: begintwl_cnt <= twl_cnt + 1;enddefault: begintwl_cnt <= 0;endendcaseendalways @(negedge clk) begincase (ns)11: begintrl_cnt <= trl_cnt + 1;enddefault: begintrl_cnt <= 0;endendcaseendalways @(negedge clk) begincase (ns)8: begintwr_cnt <= twr_cnt + 1;enddefault: begintwr_cnt <= 0;endendcaseendalways @(negedge clk) begincase (ns)13: begintRTP_cnt <= tRTP_cnt + 1;enddefault: begintRTP_cnt <= 0;endendcaseendalways @(negedge clk) begincase (ns)7: beginburst_cnt <= burst_cnt + 1; // writeend12: beginburst_cnt <= burst_cnt + 1; // readenddefault: beginburst_cnt <= 0;endendcaseendassign {wraddr_ba_s, wraddr_ra_s, wraddr_ca_s} = s_wraddr;assign wrdata_s = s_wrdata;assign wrstrb_s = s_wrstrb;assign wren_s = s_wren;assign {rdaddr_ba_s, rdaddr_ra_s, rdaddr_ca_s} = s_rdaddr;assign s_rddata = rddata_r;// assign rdstrb_s = s_rdstrb;assign rden_s = s_rden;always @(negedge clk) begincase (ns)5: beginif (trcd_cnt == 0) begin{wraddr_ba_r, wraddr_ra_r, wraddr_ca_r} = s_wraddr;wrdata_r <= s_wrdata;wrstrb_r <= s_wrstrb;wren_r <= s_wren;endend10: beginif (trcd_cnt == 0) begin{rdaddr_ba_r, rdaddr_ra_r, rdaddr_ca_r} = s_rdaddr;// rddata_r <= s_rddata;// rdstrb_r <= s_rdstrb;rden_r <= s_rden;endendendcaseendalways @(negedge clk) begincase (ns)0: beginddr_reset_n <= 1'b0;{ddr_cke_p2, ddr_cke_p1} <= 2'b0;ddr_dqs_en_r <= 1'b1;ddr_dq_en_r <= 1'b1;end1: beginddr_reset_n <= 1'b1;{ddr_cke_p2, ddr_cke_p1} <= 2'b0;ddr_dqs_en_r <= 1'b1;ddr_dq_en_r <= 1'b1;end2: begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= initial_cmd_seq[initial_cmd_ptr];end3: begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= NOP_CMD;end4: begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= NOP_CMD;end5: beginif (trcd_cnt == 0) begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= {8'b11110011, wraddr_ba_s, wraddr_ra_s, 1'b0}; // ACT_CMD | end else begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= NOP_CMD;endend6: beginif (twl_cnt == 0) begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= {8'b11010100, wraddr_ba_r, 6'b0, wraddr_ca_r, 1'b0}; // WR_CMD | end else begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= {8'b11010111, 3'b000, 16'h0000, 1'b0}; //enable dqs clock NOP_CMD | endend7: beginif (burst_cnt == 0) beginddr_dqs_en_r <= 0;ddr_dq_en_r <= 0;ddr_dq_o_r_p1 <= wrdata_r;ddr_dq_o_r_p2 <= 0;ddr_dm_o_r_p1 <= ~wrstrb_r;ddr_dm_o_r_p2 <= 1;end else beginddr_dqs_en_r <= 0;ddr_dq_en_r <= 0;ddr_dq_o_r_p1 <= 0;ddr_dq_o_r_p2 <= 0;ddr_dm_o_r_p1 <= 1;ddr_dm_o_r_p2 <= 1;endend8: beginif (twr_cnt == 0) beginddr_dqs_en_r <= 0;ddr_dq_en_r <= 0;end else beginddr_dqs_en_r <= 1;ddr_dq_en_r <= 1;endddr_dq_o_r_p1 <= 0;ddr_dq_o_r_p2 <= 0;ddr_dm_o_r_p1 <= 1;ddr_dm_o_r_p2 <= 1;end9: begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= {8'b11110010, wraddr_ba_r, 16'h0000, 1'b0}; // PRECHARGE single bank PRE_CMD | end10: begin // READ ACTIVATEif (trcd_cnt == 0) begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= {8'b11110011, rdaddr_ba_s, rdaddr_ra_s, 1'b0}; // ACT_CMD | end else begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= NOP_CMD;endend11: begin // READ CMDif (trl_cnt == 0) begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= {8'b11110101, rdaddr_ba_r, 6'b0, rdaddr_ca_r, 1'b0}; // RD_CMD | end else begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= {8'b11110111, 3'b000, 16'h0000, 1'b0}; //enable dqs clock NOP_CMD | endend12: begin // READ DATA // tDQSQ(MAX) tQHif (burst_cnt == 0) beginrddata_r <= dq_i_r_p1;end else beginrddata_r <= 'bx;endend13: begin // READ POSTAMBLErddata_r <= 'bz;end14: begin{ddr_cke_p2, ddr_cke_p1, ddr_dqs_en_r, ddr_dq_en_r, ddr_cs_n_r, ddr_ras_n_r, ddr_cas_n_r, ddr_we_n_r, ddr_ba_r, ddr_addr_r, ddr_odt_r} <= {8'b11110010, rdaddr_ba_r, 16'h0000, 1'b0}; // PRECHARGE single bank PRE_CMD | enddefault: beginddr_reset_n <= 1'b1;endendcaseendalways @(*) beginddr_dqs_o = clk;endendmodule
上述代码在Vivado 2017.4中进行了仿真测试,可替换ddr示例工程中的example_top自行仿真。
对应激励如下。
initial begin# 900000000;repeat(100) @(negedge sys_clk_i);s_wren <= 1;s_wraddr <= {3'b110, 16'h4444, 10'h0};s_wrdata <= 8'h66;s_wrstrb <= 1'b1;@(negedge sys_clk_i);s_wren <= 0;repeat(100) @(negedge sys_clk_i);s_rden <= 1;s_rdaddr <= {3'b110, 16'h4444, 10'h0};@(negedge sys_clk_i);s_rden <= 0;
end