赞
踩
上一节 【DDR】基于Verilog的DDR控制器的简单实现(一)——初始化
本文继续以美光(Micron)公司生产的DDR3芯片MT41J512M8RH-093(芯片手册)为例,说明DDR芯片的写操作过程。
DDR芯片内部存储如下图(来源P16 512 Meg x 8 Functional Block Diagram),由多个存储单元构成的阵列构成,每个存储单元大小(即DDR_DQ引脚位宽)为1个字节,DDR芯片每次读写操作对Column低3位相同的8个存储单元连续进行,即Burst操作(可通过设置MR0寄存器的BL位设置为BL8(每次连续操作8个字节)或BC4(每次连续操作4个字节),具体见P142 Figure 54: Mode Register 0 (MR0) Definitions)。
在存储单元的寻址上采用Bank、Row、Column的方式,MT41J512M8RH-093芯片共有8个Bank,65536个Row,以及128x8个Column(读写操作将Column低3位相同的8个Column视为一组)。
下图展示了DDR芯片的工作状态机(来自 P12 Fig.2 Simplified State Diagram),在DDR芯片初始化过程完成后,DDR芯片进入空闲状态,在该状态下DDR控制器可发送指令进行ZQ重新校准、刷新、激活等操作。可以看到,DDR芯片在进行读写(Reading、Writing)操作前需要进行激活(Activating)操作,在读写操作结束后需要进行预充电(PreCharging)操作。
激活操作的指令格式可从(P118 Table 70: Truth Table – Command)找到,它作用于指定Bank的指定Row中的所有Column。每个Bank同时只能存在一个激活状态的Row(P121),通过预充电操作可以取消Row的激活状态。
预充电操作包含单bank预充电(Single-bank PRECHARGE)和全bank预充电(PRECHARGE all banks)两种类型。其中单bank预充电需要指定bank的类型。
写操作主要需要用到的指令如下。(P122 Table 73: WRITE Command Summary、P118 Table 70: Truth Table – Command)
在写指令中,CA的[2:0]位无论如何变化,写入数据的顺序均从Column0到7顺序进行。不同于读操作时CA[2:0]能够决定读出数据的顺序,类似于AXI的WRAP(P143 Table 76: Burst Order)。
除了上面涉及到的信号外,在WRITE指令发出后,还需要DDR_DQ [7:0]用于提供写入存储单元的数据,DDR_DM用于告知写入数据是否有效(低电平有效),DDR_DQS用于提供写入存储单元数据的时钟。
在写操作过程中主要关注下面几个时序参数。(来自P94 Table 59: Electrical Characteristics and AC Operating Conditions for Speed Extensions (Continued))
* tDQSS =(-0.27, 0.27)CK ;DQS相对CK的相位差范围
* tDSS >0.18CK
* tWPRE >0.9CK ;DQS的preamble
* tWPST >0.3CK ;DQS的postamble
* tDS >55ps ;DQ的建立时间,这里使用ODELAY实现
* tDH >60ps ;DQ的保持时间
* tRAS >33ns ;在对一行Row的激活指令结束后需要等待tRAS时间才能使用预充电指令
* tWR >15ns ;写恢复时间,在写指令结束后需要等待tWR时间才能使用预充电指令
一次完整的写过程波形如下(P176 Figure 86: WRITE Burst),当WRITE指令发出后,在tWL±tDQSS的时间范围内(tAL与tCWL的时间由初始化阶段写入MR1和MR2的对应值决定,tDQSS可从Table 59查到),需要在DQ、DM信号线上输出写入对应Column的数据,同时在DQS信号线上输出写入数据的时钟。这里展示的是突发长度为8的操作。为了确保DQ写入的数据能够被DDR存储,DQS时钟信号需要在DQ第一个数据出现前tWPRE就已经出现,在DQ最后一个数据结束后tWPST结束。此外每个DQ数据需要满足相对于DQS时钟的建立时间tDS和保持时间tDH要求(P183 Figure 96: Data Input Timing)。
仿真时将时钟周期取为最小时钟周期0.938ns,对应时钟频率1066.099MHz。tDQSS取为0,此时DQS时钟与clk时钟相同,每次连续写操作只对8个地址中的首个数据,最终得到的DDR3写数据代码如下:
`timescale 1ns / 1ps // // Company: // Engineer: wjh776a68 // // Create Date: 01/09/2024 08:45:15 PM // Design Name: // Module Name: micron_ddr_wrbyte // 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 * * 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 ***************************************************************/ module micron_ddr_wrbyte #( parameter CLK_FREQ = 1066.099, // MHz parameter _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 10CA input [7:0] s_wrdata, input s_wrstrb, input s_wren, 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=5 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 localparam [27:0] ZQCL_CMD = {11'b11110110000, 16'h0400, 1'b0}; localparam [27:0] ACT_CMD = {11'b11110011000, 16'h0000, 1'b0}; // ba ra localparam [27:0] PRE_CMD = {11'b11110010000, 16'h0000, 1'b0}; // ba localparam [27:0] WR_CMD = {11'b11110100000, 16'h0000, 1'b0}; // dqs_o dq_o ba ca reg 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; 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; 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; 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 ); generate OBUF 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++) begin IOBUF 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 ); end endgenerate 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_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 ref reg [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) begin ddr_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; end reg [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] twr_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) begin cs <= ns; end always @(*) begin case (cs) 0: begin if (initial_cnt == INITIAL_CYCLE) begin // wait 200us ns = 1; end else begin ns = 0; end end 1: begin // wait 500us if (initial_stable_cnt == INITIAL_STABLE_CYCLE) begin // wait 500us ns = 2; end else begin ns = 1; end end 2: begin ns = 3; end 3: begin if (freerun_cnt == FREE_CYCLE) begin if (initial_finish) begin ns = 4; end else begin ns = 2; end end else begin ns = 3; end end 4: begin if (s_wren) begin ns = 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 begin ns = 4; end end 5: begin // ACTIVATE if (trcd_cnt == 14) begin ns = 6; // WRITE/READ Operation prior to tRCD after ACT end else begin ns = 5; end end 6: begin // WRITE WRITE-WRITE delay tCCD, DATA provided at posedge of DQS with latency AL+CWL in MR0 and MR2 , tWTR tWR P174 P176 if (twl_cnt == 5 + 5) begin // tCWL + tAL = 5 ddr_dq_o_r has extra one cycle delay -> 5 - 2 ns = 7; end else begin ns = 6; end end 7: begin // WRITE DATA WL latency // if (ddr_dqs_i) begin // tDQSS = 0 if (burst_cnt == 4) begin // double burst == 8 ns = 8; end else begin ns = 7; end // end else begin // ns = 7; // end end 8: begin // POSTEAMBLE if (twr_cnt == 16) begin ns = 9; // Write to ReCharge tWR >15ns ACT to ReCharge tRAS>33ns end else begin ns = 8; end end 9: begin // PRECHARGE ns = 4; end default: begin ns = 0; end endcase end always @(negedge clk) begin case (ns) 0: begin initial_cnt <= initial_cnt + 1; end default: begin initial_cnt <= 0; end endcase end always @(negedge clk) begin case (ns) 1: begin initial_stable_cnt <= initial_stable_cnt + 1; end default: begin initial_stable_cnt <= 0; end endcase end always @(negedge clk) begin case (ns) 3: begin freerun_cnt <= freerun_cnt + 1; end default: begin freerun_cnt <= 0; end endcase end always @(negedge clk) begin case (ns) 2: begin if (initial_cmd_ptr == 6 - 1) begin initial_finish <= 1; end else begin initial_finish <= 0; end initial_cmd_ptr <= initial_cmd_ptr + 1; end endcase end initial 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; end always @(negedge clk) begin case (ns) 5: begin trcd_cnt <= trcd_cnt + 1; end default: begin trcd_cnt <= 0; end endcase end always @(negedge clk) begin case (ns) 6: begin twl_cnt <= twl_cnt + 1; end default: begin twl_cnt <= 0; end endcase end always @(negedge clk) begin case (ns) 8: begin twr_cnt <= twr_cnt + 1; end default: begin twr_cnt <= 0; end endcase end always @(negedge clk) begin case (ns) 7: begin burst_cnt <= burst_cnt + 1; end default: begin burst_cnt <= 0; end endcase end assign {wraddr_ba_s, wraddr_ra_s, wraddr_ca_s} = s_wraddr; assign wrdata_s = s_wrdata; assign wrstrb_s = s_wrstrb; always @(negedge clk) begin case (ns) 5: begin if (trcd_cnt == 0) begin {wraddr_ba_r, wraddr_ra_r, wraddr_ca_r} = s_wraddr; wrdata_r <= s_wrdata; wrstrb_r <= s_wrstrb; end end endcase end always @(negedge clk) begin case (ns) 0: begin ddr_reset_n <= 1'b0; {ddr_cke_p2, ddr_cke_p1} <= 2'b0; ddr_dqs_en_r <= 1'b1; ddr_dq_en_r <= 1'b1; end 1: begin ddr_reset_n <= 1'b1; {ddr_cke_p2, ddr_cke_p1} <= 2'b0; ddr_dqs_en_r <= 1'b1; ddr_dq_en_r <= 1'b1; end 2: 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]; end 3: 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; end 4: 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; end 5: begin if (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; end end 6: begin if (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 | end end 7: begin if (burst_cnt == 0) begin ddr_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 begin ddr_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; end end 8: begin if (twr_cnt == 0) begin ddr_dqs_en_r <= 0; ddr_dq_en_r <= 0; end else begin ddr_dqs_en_r <= 1; ddr_dq_en_r <= 1; end ddr_dq_o_r_p1 <= 0; ddr_dq_o_r_p2 <= 0; ddr_dm_o_r_p1 <= 1; ddr_dm_o_r_p2 <= 1; end 9: 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 | end default: begin ddr_reset_n <= 1'b1; end endcase end always @(*) begin ddr_dqs_o = clk; end endmodule
上述代码在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'h1};
s_wrdata <= 8'h66;
s_wrstrb <= 1'b1;
@(negedge sys_clk_i);
s_wren <= 0;
end
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。