diff --git a/rtl/axi/ddr3_top_axi.v b/rtl/axi/ddr3_top_axi.v index e5ab389..cc37bc5 100644 --- a/rtl/axi/ddr3_top_axi.v +++ b/rtl/axi/ddr3_top_axi.v @@ -49,6 +49,7 @@ module ddr3_top_axi #( parameter[1:0] ECC_ENABLE = 0, // set to 1 or 2 to add ECC (1 = Side-band ECC per burst, 2 = Side-band ECC per 8 bursts , 3 = Inline ECC ) parameter[1:0] DIC = 2'b00, //Output Driver Impedance Control (2'b00 = RZQ/6, 2'b01 = RZQ/7, RZQ = 240ohms) (only change when you know what you are doing) parameter[2:0] RTT_NOM = 3'b011, //RTT Nominal (3'b000 = disabled, 3'b001 = RZQ/4, 3'b010 = RZQ/2 , 3'b011 = RZQ/6, RZQ = 240ohms) (only change when you know what you are doing) + parameter[1:0] SELF_REFRESH = 2'b00, // 0 = use i_user_self_refresh input, 1 = Self-refresh mode is enabled after 64 controller clock cycles of no requests, 2 = 128 cycles, 3 = 256 cycles parameter // The next parameters act more like a localparam (since user does not have to set this manually) but was added here to simplify port declaration DQ_BITS = 8, //device width (fixed to 8, if DDR3 is x16 then BYTE_LANES will be 2 while ) serdes_ratio = 4, // this controller is fixed as a 4:1 memory controller (CONTROLLER_CLK_PERIOD/DDR3_CLK_PERIOD = 4) @@ -130,11 +131,14 @@ module ddr3_top_axi #( output wire o_calib_complete, // // Debug outputs - output wire[31:0] o_debug1 + output wire[31:0] o_debug1, // output wire[31:0] o_debug2, // output wire[31:0] o_debug3, // output wire[(DQ_BITS*BYTE_LANES)/8-1:0] o_ddr3_debug_read_dqs_p, // output wire[(DQ_BITS*BYTE_LANES)/8-1:0] o_ddr3_debug_read_dqs_n + // + // User enabled self-refresh + input wire i_user_self_refresh ); wire wb_cyc; @@ -165,7 +169,8 @@ ddr3_top #( .SKIP_INTERNAL_TEST(SKIP_INTERNAL_TEST), // skip built-in self test (would require >2 seconds of internal test right after calibration) .ECC_ENABLE(ECC_ENABLE), // set to 1 or 2 to add ECC (1 = Side-band ECC per burst, 2 = Side-band ECC per 8 bursts , 3 = Inline ECC ) .DIC(DIC), // Output Driver Impedance Control (2'b00 = RZQ/6, 2'b01 = RZQ/7, RZQ = 240ohms) (only change when you know what you are doing) - .RTT_NOM(RTT_NOM) //RTT Nominal (3'b000 = disabled, 3'b001 = RZQ/4, 3'b010 = RZQ/2 , 3'b011 = RZQ/6, RZQ = 240ohms) (only change when you know what you are doing) + .RTT_NOM(RTT_NOM), //RTT Nominal (3'b000 = disabled, 3'b001 = RZQ/4, 3'b010 = RZQ/2 , 3'b011 = RZQ/6, RZQ = 240ohms) (only change when you know what you are doing) + .SELF_REFRESH(SELF_REFRESH) // Self-refresh options (0 = use i_user_self_refresh input, 1 = Self-refresh mode is enabled after 64 controller clock cycles of no requests, 2 = 128 cycles, 3 = 256 cycles) ) ddr3_top_inst ( //clock and reset @@ -222,11 +227,13 @@ ddr3_top #( .o_calib_complete(o_calib_complete), // // Debug outputs - .o_debug1(o_debug1) + .o_debug1(o_debug1), // .o_debug2(o_debug2), // .o_debug3(o_debug3), // .o_ddr3_debug_read_dqs_p(o_ddr3_debug_read_dqs_p), // .o_ddr3_debug_read_dqs_n(o_ddr3_debug_read_dqs_n) + // + .i_user_self_refresh(i_user_self_refresh) //////////////////////////////////// ); diff --git a/rtl/ddr3_controller.v b/rtl/ddr3_controller.v index 3f86357..fdb7db1 100644 --- a/rtl/ddr3_controller.v +++ b/rtl/ddr3_controller.v @@ -135,9 +135,11 @@ module ddr3_controller #( // Done Calibration pin output wire o_calib_complete, // Debug port - output wire [31:0] o_debug1 + output wire [31:0] o_debug1, // output wire [31:0] o_debug2, // output wire [31:0] o_debug3 + // User enabled self-refresh + input wire i_user_self_refresh ); @@ -150,8 +152,10 @@ module ddr3_controller #( CMD_WR = 4'b0100, // Write (A10-AP: 0 = no Auto-Precharge) (A12-BC#: 1 = Burst Length 8) CMD_RD = 4'b0101, //Read (A10-AP: 0 = no Auto-Precharge) (A12-BC#: 1 = Burst Length 8) CMD_NOP = 4'b0111, // No Operation - CMD_ZQC = 4'b0110; // ZQ Calibration (A10-AP: 0 = ZQ Calibration Short, 1 = ZQ Calibration Long) - + CMD_ZQC = 4'b0110, // ZQ Calibration (A10-AP: 0 = ZQ Calibration Short, 1 = ZQ Calibration Long) + CMD_SREF_EN = 4'b0001, + CMD_SREF_XT = 4'b0111; + localparam RST_DONE = 27, // Command bit that determines if reset seqeunce had aready finished. non-persistent (only needs to be toggled once), REF_IDLE = 27, // No refresh is about to start and no ongoing refresh. (same bit as RST_DONE) USE_TIMER = 26, // Command bit that determines if timer will be used (if delay is zero, USE_TIMER must be LOW) @@ -248,7 +252,11 @@ module ddr3_controller #( localparam DELAY_MAX_VALUE = ps_to_cycles(INITIAL_CKE_LOW); //Largest possible delay needed by the reset and refresh sequence localparam DELAY_COUNTER_WIDTH= $clog2(DELAY_MAX_VALUE); //Bitwidth needed by the maximum possible delay, this will be the delay counter width localparam CALIBRATION_DELAY = 2; // must be >= 2 - + localparam tXSDLL = nCK_to_cycles(512); // cycles (controller) Exit Self Refresh to commands requiring a locked DLL + localparam tXSDLL_tRFC = tXSDLL - ps_to_cycles(tRFC); // cycles (controller) Time before refresh after exit from self-refresh + localparam tCKE = max(3, ps_to_nCK(7500) ); // nCK CKE minimum pulse width + localparam tCKESR = nCK_to_cycles(tCKE + 1)+ 5; // cycles (controller) Minimum time that the DDR3 SDRAM must remain in Self-Refresh mode is tCKESR + localparam tCPDED = 1; // cycle (tCPDED is at most 2nCK but we make it to 1cycle or 4nCK) Command pass disable delay , required cycles of NOP after CKE low /*********************************************************************************************************************************************/ @@ -481,7 +489,8 @@ module ddr3_controller #( initial begin o_phy_bitslip = 0; end - reg cmd_odt_q = 0, cmd_odt, cmd_ck_en, cmd_reset_n; + reg cmd_odt_q = 0, cmd_odt, cmd_reset_n; + (* mark_debug = "true" *) reg cmd_ck_en; reg o_wb_stall_q = 1, o_wb_stall_d, o_wb_stall_calib = 1; reg precharge_slot_busy; reg activate_slot_busy; @@ -586,7 +595,8 @@ module ddr3_controller #( wire db_err_o; wire[wb_data_bits - 1:0] o_wb_data_q_decoded; /* verilator lint_on UNDRIVEN */ - + reg user_self_refresh_q; // registered i_user_self_refresh + // initial block for all regs initial begin o_wb_stall = 1; @@ -660,7 +670,7 @@ module ddr3_controller #( else read_rom_instruction = {5'b01000 , CMD_NOP , ps_to_cycles(POWER_ON_RESET_HIGH)}; //0. RESET# needs to be maintained low for minimum 200us with power-up initialization. CKE is pulled - //“Low” anytime before RESET# being de-asserted (min. time 10 ns). . + //“Low�? anytime before RESET# being de-asserted (min. time 10 ns). . 5'd1: if (MICRON_SIM) @@ -727,7 +737,7 @@ module ddr3_controller #( 5'd18: read_rom_instruction = {5'b01011, CMD_NOP, tMOD[DELAY_SLOT_WIDTH-1:0]}; //18. Delay of tMOD between MRS command to a non-MRS command excluding NOP and DES - // Perform first refresh and any subsequent refresh (so instruction 12 to 15 will be re-used for the refresh sequence) + // Perform first refresh and any subsequent refresh (so instruction 19 to 22 will be re-used for the refresh sequence) 5'd19: read_rom_instruction = {5'b01111, CMD_PRE, ps_to_cycles(tRP)}; //19. All banks must be precharged (A10-AP = high) and idle for a minimum of the precharge time tRP(min) before the Refresh Command can be applied. @@ -740,7 +750,22 @@ module ddr3_controller #( 5'd22: read_rom_instruction = {5'b01011, CMD_NOP, PRE_REFRESH_DELAY[DELAY_SLOT_WIDTH-1:0]}; // 22. Extra delay needed before starting the refresh sequence. - // (this already sets the wishbone stall high to make sure no user request is on-going when refresh seqeunce starts) + // (this already sets the wishbone stall high to make sure no user request is on-going when refresh seqeunce starts) + + 5'd23: read_rom_instruction = {5'b01111, CMD_PRE, ps_to_cycles(tRP)}; + // 23. All banks must be precharged (A10-AP = high) and idle for a minimum of the precharge time tRP(min) before the Self-Refresh Command can be applied. + + 5'd24: read_rom_instruction = {5'b01001, CMD_NOP, tCPDED[DELAY_SLOT_WIDTH-1:0]}; + // 24. CKE must go low to enter self-refresh, tCPDED cycles of NOP are required before CMD_SREF_EN + + 5'd25: read_rom_instruction = {5'b01001, CMD_SREF_EN, tCKESR[DELAY_SLOT_WIDTH-1:0]}; + // 25. Self-refresh entry + // JEDEC Standard No. 79-3E Page 79: The minimum time that the DDR3 SDRAM must remain in Self-Refresh mode is tCKESR + + 5'd26: read_rom_instruction = {5'b01011, CMD_SREF_XT, tXSDLL_tRFC[DELAY_SLOT_WIDTH-1:0]}; + // 26. From 25 (Self-refresh entry), wait until user-self_refresh is disabled then wait for tXSDLL - tRFC before going to 20 (Refresh) + // JEDEC Standard No. 79-3E Page 79: Before a command that requires a locked DLL can be applied, a delay of at least tXSDLL must be satisfied. + // JEDEC Standard No. 79-3E Page 80: Upon exit from Self-Refresh, the DDR3 SDRAM requires a minimum of one extra refresh command before it is put back into Self-Refresh Mode. default: read_rom_instruction = {5'b00011, CMD_NOP, {(DELAY_SLOT_WIDTH){1'b0}}}; endcase @@ -778,21 +803,45 @@ module ddr3_controller #( //delay_counter is 1 (for r/w calibration and prestall delay) //address will only move forward for these kinds of delay only //when skip_reset_seq_delay is toggled - else if(instruction[USE_TIMER] /*&& delay_counter != {(DELAY_COUNTER_WIDTH){1'b1}}*/ && !pause_counter) delay_counter <= delay_counter - 1; + else if(instruction[USE_TIMER] /*&& delay_counter != {(DELAY_COUNTER_WIDTH){1'b1}}*/ && !pause_counter && delay_counter != 0) delay_counter <= delay_counter - 1; //delay_counter of 1 means we will need to update the delay_counter next clock cycle (delay_counter of zero) so we need to retrieve //now the next instruction. The same thing needs to be done when current instruction does not need the timer delay. if(delay_counter == 1 || !instruction[USE_TIMER]/* || skip_reset_seq_delay*/) begin delay_counter_is_zero <= 1; instruction <= read_rom_instruction(instruction_address); - instruction_address <= (instruction_address == 5'd22)? 5'd19:instruction_address+1; //wrap back of address to repeat refresh sequence + if(instruction_address == 5'd22) begin // if user_self_refresh is disabled, wrap back to 19 (Precharge All before Refresh) + instruction_address <= 5'd19; + end + else if(instruction_address == 5'd26) begin // self-refresh exit always wraps back to 20 (Refresh) + instruction_address <= 5'd20; + end + else begin + instruction_address <= instruction_address + 5'd1; // just increment address + end end //we are now on the middle of a delay - else delay_counter_is_zero <=0; + else begin + delay_counter_is_zero <=0; + end + + if(instruction_address == 5'd22 && user_self_refresh_q) begin // if user_self_refresh is enabled, go straight to 23 + instruction_address <= 23; // go to Precharge All for Self-refresh + delay_counter_is_zero <= 1; + delay_counter <= 0; + instruction <= read_rom_instruction(instruction_address); + end + + //instruction[RST_DONE] is non-persistent thus we need to register it once it goes high reset_done <= instruction[RST_DONE]? 1'b1:reset_done; end end + + // register user-enabled self-refresh + always @(posedge i_controller_clk) begin + user_self_refresh_q <= i_user_self_refresh && (user_self_refresh_q || (instruction_address != 5'd26)) && final_calibration_done; //will not go high again if already at instruction_address 26 (self-refresh exit), only go high when calibration is done + end /*********************************************************************************************************************************************/ @@ -879,7 +928,7 @@ module ddr3_controller #( bank_active_row_q[index] <= bank_active_row_d[index]; end - if(instruction_address == 20) begin ///current instruction at precharge + if(instruction_address == 20 || instruction_address == 24) begin ///current instruction at precharge cmd_odt_q <= 1'b0; //all banks will be in idle after refresh for( index=0; index < (1<= 19 && f_read <= 22) ); //refresh sequence is only on instruction address 19,20,21,22 + assert((f_read >= 19 && f_read <= 26) ); //refresh sequence is only on instruction address 19,20,21,22 end // reset_done must retain high when it was already asserted once @@ -3562,7 +3635,7 @@ ALTERNATE_WRITE_READ: if(!o_wb_stall_calib) begin if(reset_done && f_read_inst[REF_IDLE]) begin assert(f_read == 21); end - + end end @@ -3575,9 +3648,18 @@ ALTERNATE_WRITE_READ: if(!o_wb_stall_calib) begin assert( a[DELAY_COUNTER_WIDTH - 1:0] > 0); end end - + // assertion on FSM calibration always @* begin + if(instruction_address == 19 || instruction_address == 23) begin //pre-stall delay before precharge all to finish all remaining requests + if(pause_counter == 1) begin // if there are still pending requests (pause_counter high) then delay_counter should still be at PRE_REFRESH_DELAY + assert(delay_counter == PRE_REFRESH_DELAY); + end + end + if(instruction_address >= 24 && instruction_address < 26) begin + assert(!pause_counter); // no pause counter from precharge to sel-refresh entry + end + if(instruction_address < 13) begin assert(state_calibrate == IDLE); end @@ -3595,6 +3677,7 @@ ALTERNATE_WRITE_READ: if(!o_wb_stall_calib) begin if(pause_counter) begin assert(delay_counter != 0); + // will fix this soon end if(state_calibrate > ISSUE_WRITE_1 && state_calibrate <= ANALYZE_DATA) begin @@ -3890,10 +3973,10 @@ ALTERNATE_WRITE_READ: if(!o_wb_stall_calib) begin end if(reset_done) begin - assert(cmd_d[PRECHARGE_SLOT][CMD_CKE] && cmd_d[PRECHARGE_SLOT][CMD_RESET_N]); //cke and rst_n should stay high when reset sequence is already done - assert(cmd_d[ACTIVATE_SLOT][CMD_CKE] && cmd_d[ACTIVATE_SLOT][CMD_RESET_N]); //cke and rst_n should stay high when reset sequence is already done - assert(cmd_d[READ_SLOT][CMD_CKE] && cmd_d[READ_SLOT][CMD_RESET_N]); //cke and rst_n should stay high when reset sequence is already done - assert(cmd_d[WRITE_SLOT][CMD_CKE] && cmd_d[WRITE_SLOT][CMD_RESET_N]); //cke and rst_n should stay high when reset sequence is already done + assert(cmd_d[PRECHARGE_SLOT][CMD_RESET_N]); //cke and rst_n should stay high when reset sequence is already done + assert(cmd_d[ACTIVATE_SLOT][CMD_RESET_N]); //cke and rst_n should stay high when reset sequence is already done + assert(cmd_d[READ_SLOT][CMD_RESET_N]); //cke and rst_n should stay high when reset sequence is already done + assert(cmd_d[WRITE_SLOT][CMD_RESET_N]); //cke and rst_n should stay high when reset sequence is already done end end if(state_calibrate == DONE_CALIBRATE) begin @@ -3908,7 +3991,7 @@ ALTERNATE_WRITE_READ: if(!o_wb_stall_calib) begin if(!f_empty) begin assert(state_calibrate == DONE_CALIBRATE); end - if(train_delay == 0 && state_calibrate == FINISH_READ) begin//remove + if(train_delay == 0 && state_calibrate == FINISH_READ) begin//fix this soon assume(f_sum_of_pending_acks == 0); end end @@ -4053,6 +4136,12 @@ ALTERNATE_WRITE_READ: if(!o_wb_stall_calib) begin end always @* begin assert(f_bank_status == bank_status_q); + if(instruction_address >= 25) begin // after precharge until end of refresh, all banks are idle + assert(bank_status_q == 0); + end + if(instruction_address == 23 && pause_counter) begin // if at PRE_REFRESH_DELAY and not yet done, then delay_counter should still be at original value + + end end (*keep*) reg[31:0] bank; @@ -4090,9 +4179,17 @@ ALTERNATE_WRITE_READ: if(!o_wb_stall_calib) begin f_bank_status_2 = f_bank_status_2 | (1< ISSUE_WRITE_1); - assert(instruction_address == 22 || instruction_address == 19); + assert(instruction_address == 22 || instruction_address == 19 || instruction_address == 23); end if(instruction_address < 13) begin @@ -4576,7 +4673,7 @@ ALTERNATE_WRITE_READ: if(!o_wb_stall_calib) begin assert(o_wb_stall_calib); end if(reset_done) begin - assert(instruction_address >= 19 && instruction_address <= 22); + assert(instruction_address >= 19 && instruction_address <= 26); end //delay_counter is zero at first clock of new instruction address, the actual delay_clock wil start at next clock cycle if(instruction_address == 19 && delay_counter != 0) begin diff --git a/rtl/ddr3_top.v b/rtl/ddr3_top.v index cf9a651..1a36040 100644 --- a/rtl/ddr3_top.v +++ b/rtl/ddr3_top.v @@ -48,6 +48,7 @@ module ddr3_top #( parameter[1:0] ECC_ENABLE = 0, // set to 1 or 2 to add ECC (1 = Side-band ECC per burst, 2 = Side-band ECC per 8 bursts , 3 = Inline ECC ) parameter[1:0] DIC = 2'b00, //Output Driver Impedance Control (2'b00 = RZQ/6, 2'b01 = RZQ/7, RZQ = 240ohms) (only change when you know what you are doing) parameter[2:0] RTT_NOM = 3'b011, //RTT Nominal (3'b000 = disabled, 3'b001 = RZQ/4, 3'b010 = RZQ/2 , 3'b011 = RZQ/6, RZQ = 240ohms) (only change when you know what you are doing) + parameter[1:0] SELF_REFRESH = 2'b00, // 0 = use i_user_self_refresh input, 1 = Self-refresh mode is enabled after 64 controller clock cycles of no requests, 2 = 128 cycles, 3 = 256 cycles parameter // The next parameters act more like a localparam (since user does not have to set this manually) but was added here to simplify port declaration DQ_BITS = 8, //device width (fixed to 8, if DDR3 is x16 then BYTE_LANES will be 2 while ) serdes_ratio = 4, // this controller is fixed as a 4:1 memory controller (CONTROLLER_CLK_PERIOD/DDR3_CLK_PERIOD = 4) @@ -108,11 +109,14 @@ module ddr3_top #( // Done Calibration pin output wire o_calib_complete, // Debug outputs - output wire[31:0] o_debug1 + output wire[31:0] o_debug1, // output wire[31:0] o_debug2, // output wire[31:0] o_debug3, // output wire[(DQ_BITS*BYTE_LANES)/8-1:0] o_ddr3_debug_read_dqs_p, // output wire[(DQ_BITS*BYTE_LANES)/8-1:0] o_ddr3_debug_read_dqs_n + // + // User enabled self-refresh + input wire i_user_self_refresh ); // Instantiation Template (DEFAULT VALUE IS FOR ARTY S7) @@ -207,6 +211,30 @@ ddr3_top #( wire write_leveling_calib; wire reset; + // logic for self-refresh + reg[8:0] refresh_counter = 0; + reg user_self_refresh; + // refresh counter + always @(posedge i_controller_clk) begin + if(i_wb_stb && i_wb_cyc) begin // if there is Wishbone request, then reset counter + refresh_counter <= 0; + end + else if(!o_wb_stall || user_self_refresh) begin // if no request (but not stalled) OR already on self-refresh, then increment counter + refresh_counter <= refresh_counter + 1; + end + end + // choose self-refresh options + always @* begin + case(SELF_REFRESH) + 2'b00: user_self_refresh = i_user_self_refresh; // use input i_user_self_refresh (high = enter self-refresh, low = exit self-refresh) + 2'b01: user_self_refresh = refresh_counter[6]; // Self-refresh mode is enabled after 64 controller clock cycles of no requests, then exit Self-refresh after another 64 controller clk cycles + 2'b10: user_self_refresh = refresh_counter[7]; // Self-refresh mode is enabled after 128 controller clock cycles of no requests, then exit Self-refresh after another 128 controller clk cycles + 2'b11: user_self_refresh = refresh_counter[8]; // Self-refresh mode is enabled after 256 controller clock cycles of no requests, then exit Self-refresh after another 256 controller clk cycles + endcase + end + + + //module instantiations ddr3_controller #( .CONTROLLER_CLK_PERIOD(CONTROLLER_CLK_PERIOD), //ps, clock period of the controller interface @@ -281,9 +309,11 @@ ddr3_top #( // Done Calibration pin .o_calib_complete(o_calib_complete), // Debug outputs - .o_debug1(o_debug1) + .o_debug1(o_debug1), // .o_debug2(o_debug2), // .o_debug3(o_debug3) + // User enabled self-refresh + .i_user_self_refresh(user_self_refresh) ); ddr3_phy #( @@ -341,6 +371,29 @@ ddr3_top #( .o_ddr3_debug_read_dqs_p(/*o_ddr3_debug_read_dqs_p*/), .o_ddr3_debug_read_dqs_n(/*o_ddr3_debug_read_dqs_n*/) ); - -endmodule + // display value of parameters for easy debugging + initial begin + $display("\nDDR3 TOP PARAMETERS:\n-----------------------------"); + $display("CONTROLLER_CLK_PERIOD = %0d", CONTROLLER_CLK_PERIOD); + $display("DDR3_CLK_PERIOD = %0d", DDR3_CLK_PERIOD); + $display("ROW_BITS = %0d", ROW_BITS); + $display("COL_BITS = %0d", COL_BITS); + $display("BA_BITS = %0d", BA_BITS); + $display("BYTE_LANES = %0d", BYTE_LANES); + $display("AUX_WIDTH = %0d", AUX_WIDTH); + $display("WB2_ADDR_BITS = %0d", WB2_ADDR_BITS); + $display("WB2_DATA_BITS = %0d", WB2_DATA_BITS); + $display("MICRON_SIM = %0d", MICRON_SIM); + $display("ODELAY_SUPPORTED = %0d", ODELAY_SUPPORTED); + $display("SECOND_WISHBONE = %0d", SECOND_WISHBONE); + $display("WB_ERROR = %0d", WB_ERROR); + $display("SKIP_INTERNAL_TEST = %0d", SKIP_INTERNAL_TEST); + $display("ECC_ENABLE = %0d", ECC_ENABLE); + $display("DIC = %0d", DIC); + $display("RTT_NOM = %0d", RTT_NOM); + $display("SELF_REFRESH = %0d", SELF_REFRESH); + $display("End of DDR3 TOP PARAMETERS\n-----------------------------"); + end + +endmodule diff --git a/testbench/ddr3_dimm_micron_sim.sv b/testbench/ddr3_dimm_micron_sim.sv index 5313875..3640673 100644 --- a/testbench/ddr3_dimm_micron_sim.sv +++ b/testbench/ddr3_dimm_micron_sim.sv @@ -64,7 +64,8 @@ module ddr3_dimm_micron_sim; localparam CONTROLLER_CLK_PERIOD = 10_000, //ps, period of clock input to this DDR3 controller module DDR3_CLK_PERIOD = 2500, //ps, period of clock input to DDR3 RAM device AUX_WIDTH = 16, // AUX lines - ECC_ENABLE = 0; // ECC enable + ECC_ENABLE = 0, // ECC enable + SELF_REFRESH = 2'b11; reg i_controller_clk, i_ddr3_clk, i_ref_clk, i_ddr3_clk_90; reg i_rst_n; @@ -107,7 +108,8 @@ module ddr3_dimm_micron_sim; wire o_wb2_stall; //1 = busy, cannot accept requests wire o_wb2_ack; //1 = read/write request has completed wire[$bits(ddr3_top.o_wb2_data)-1:0] o_wb2_data; //read data - + // User enabled self-refresh + reg i_user_self_refresh; wire clk_locked; `ifdef USE_CLOCK_WIZARD @@ -163,7 +165,9 @@ ddr3_top #( .ODELAY_SUPPORTED(ODELAY_SUPPORTED), //set to 1 if ODELAYE2 is supported .SECOND_WISHBONE(0), //set to 1 if 2nd wishbone for debugging is needed .ECC_ENABLE(ECC_ENABLE), // set to 1 or 2 to add ECC (1 = Side-band ECC per burst, 2 = Side-band ECC per 8 bursts , 3 = Inline ECC ) - .WB_ERROR(1) // set to 1 to support Wishbone error (asserts at ECC double bit error) + .WB_ERROR(1), // set to 1 to support Wishbone error (asserts at ECC double bit error) + .SKIP_INTERNAL_TEST(0), // skip built-in self test (would require >2 seconds of internal test right after calibration) + .SELF_REFRESH(SELF_REFRESH) // 0 = use i_user_self_refresh input, 1 = Self-refresh mode is enabled after 64 controller clock cycles of no requests, 2 = 128 cycles, 3 = 256 cycles ) ddr3_top ( //clock and reset @@ -211,9 +215,9 @@ ddr3_top #( .io_ddr3_dq(dq), .io_ddr3_dqs(dqs), .io_ddr3_dqs_n(dqs_n), - .o_ddr3_dm(ddr3_dm) - - //////////////////////////////////// + .o_ddr3_dm(ddr3_dm), + // User enabled self-refresh + .i_user_self_refresh(i_user_self_refresh) ); @@ -318,6 +322,7 @@ ddr3_top #( integer average_1, average_2, average_3, average_4; localparam MAX_READS = (2**COL_BITS)*(2**BA_BITS + 1)/8; //1 row = 2**(COL_BITS) addresses/8 burst = 128 words per row. Times 8 to pass all 8 banks initial begin + i_user_self_refresh = 0; //toggle reset for 1 slow clk @(posedge i_controller_clk) begin i_rst_n <= 0; @@ -342,7 +347,11 @@ ddr3_top #( i_rst_n <= 1; end wait(ddr3_top.ddr3_controller_inst.state_calibrate == ddr3_top.ddr3_controller_inst.DONE_CALIBRATE); - + + // test self refresh after calibration + // test self refresh + self_refresh(); + // test 1 phase 1: Write random word sequentially // write to row 1 number_of_op <= 0; @@ -380,8 +389,8 @@ ddr3_top #( if (!o_wb_stall) i_wb_stb <= 1'b0; end end - #1000_000; //rest here - + // test self refresh + self_refresh(); //Read sequentially address <= start_address; @@ -407,7 +416,8 @@ ddr3_top #( if (!o_wb_stall) i_wb_stb <= 1'b0; end end - #1000_000; //rest here + // test self refresh + self_refresh(); average_1 = ($time-time_started)/(number_of_op*1000); $display("\n--------------------------------\nDONE TEST 1: FIRST ROW\nNumber of Operations: %0d\nTime Started: %0d ns\nTime Done: %0d ns\nAverage Rate: %0d ns/request\n--------------------------------\n\n", @@ -454,7 +464,8 @@ ddr3_top #( if (!o_wb_stall) i_wb_stb <= 1'b0; end end - #1000_000; //rest here + // test self refresh + self_refresh(); // Read sequentially address <= start_address; @@ -480,7 +491,8 @@ ddr3_top #( if (!o_wb_stall) i_wb_stb <= 1'b0; end end - #1000_000; //rest here + // test self refresh + self_refresh(); average_2 = ($time-time_started)/(number_of_op*1000); $display("\n--------------------------------\nDONE TEST 1: MIDDLE ROW\nNumber of Operations: %0d\nTime Started: %0d ns\nTime Done: %0d ns\nAverage Rate: %0d ns/request\n--------------------------------\n\n", @@ -524,7 +536,8 @@ ddr3_top #( if (!o_wb_stall) i_wb_stb <= 1'b0; end end - #1000_000; //rest here + // test self refresh + self_refresh(); // Read sequentially address <= start_address; @@ -556,15 +569,14 @@ ddr3_top #( // i_wb_stb <= 0; // end // end - #1000_000; //rest here + // test self refresh + self_refresh(); average_3 = ($time-time_started)/(number_of_op*1000); $display("\n--------------------------------\nDONE TEST 1: LAST ROW\nNumber of Operations: %0d\nTime Started: %0d ns\nTime Done: %0d ns\nAverage Rate: %0d ns/request\n--------------------------------\n\n", number_of_op,time_started/1000, $time/1000, ($time-time_started)/(number_of_op*1000)); //#100_000; - - // Test 2:Random Access // write randomly address <= random_start; //this will just be used as the seed to generate a random number @@ -600,7 +612,9 @@ ddr3_top #( if (!o_wb_stall) i_wb_stb <= 1'b0; end end - #1000_000; //rest here + + // test self refresh + self_refresh(); // Read sequentially // Read the random words written at the random addresses @@ -637,8 +651,10 @@ ddr3_top #( average_4 = ($time-time_started)/(number_of_op*1000); $display("\n--------------------------------\nDONE TEST 2: RANDOM\nNumber of Operations: %0d\nTime Started: %0d ns\nTime Done: %0d ns\nAverage Rate: %0d ns/request\n--------------------------------\n\n", number_of_op,time_started/1000, $time/1000, ($time-time_started)/(number_of_op*1000)); - - #100_000; + + // test self refresh + self_refresh(); + // Test 3: Read from wishbone 2 (PHY) // Wishbone 2 i_wb2_cyc <= 0; //bus cycle active (1 = normal operation, 0 = all ongoing transaction are to be cancelled) @@ -697,7 +713,11 @@ ddr3_top #( end end - #1000_000; + #1000_000; //rest here + // test self refresh + self_refresh(); + #1000_000; // rest + $display("\n\n------- SUMMARY -------\nNumber of Writes = %0d\nNumber of Reads = %0d\nNumber of Success = %0d\nNumber of Fails = %0d\nNumber of Injected Errors = %0d\n", number_of_writes, number_of_reads,number_of_successful, number_of_failed, number_of_injected_errors); $display("\n\nTEST CALIBRATION\n[-]: write_test_address_counter = %0d", ddr3_top.ddr3_controller_inst.write_test_address_counter); @@ -707,6 +727,20 @@ ddr3_top #( $stop; end + task self_refresh; + if(SELF_REFRESH == 2'b00) begin + // test self refresh + @(posedge i_controller_clk) + i_user_self_refresh = 1; + #40_000_000; //40_000 ns of self-refresh + @(posedge i_controller_clk) + i_user_self_refresh = 0; + end + else begin + #10_000_000; // 10_000 ns of rest + end + endtask + //check read data initial begin start_read_address = 0; //start at first row @@ -986,4 +1020,3 @@ ddr3_top #( */ endmodule - diff --git a/testbench/ddr3_dimm_micron_sim_behav.wcfg b/testbench/ddr3_dimm_micron_sim_behav.wcfg index 0091286..b29ede8 100644 --- a/testbench/ddr3_dimm_micron_sim_behav.wcfg +++ b/testbench/ddr3_dimm_micron_sim_behav.wcfg @@ -11,15 +11,15 @@ - - - + + + - + - + Clocks and Reset label @@ -45,16 +45,37 @@ i_ref_clk - Calibration + Self-refresh label - - sb_err - sb_err + + i_user_self_refresh + i_user_self_refresh - - db_err - db_err + + user_self_refresh_q + user_self_refresh_q + + + o_ddr3_cke + o_ddr3_cke + + + refresh_counter[8:0] + refresh_counter[8:0] + UNSIGNEDDECRADIX + + + o_wb_stall + o_wb_stall + + + i_wb_stb + i_wb_stb + + + Calibration + label initial_calibration_done diff --git a/vivado_custom_ip/component.xml b/vivado_custom_ip/component.xml index adfc16f..77614c3 100644 --- a/vivado_custom_ip/component.xml +++ b/vivado_custom_ip/component.xml @@ -311,28 +311,6 @@ - - i_rst_n - - - - - - - RST - - - i_rst_n - - - - - - POLARITY - ACTIVE_LOW - - - i_controller_clk @@ -387,6 +365,28 @@ + + i_rst_n + + + + + + + RST + + + i_rst_n + + + + + + POLARITY + ACTIVE_LOW + + + ddr3 ddr3 @@ -561,7 +561,7 @@ viewChecksum - e9f63973 + 1dea7b87 @@ -577,7 +577,7 @@ viewChecksum - e9f63973 + 1dea7b87 @@ -591,7 +591,7 @@ viewChecksum - 30e22270 + ce7b9cf6 @@ -1578,6 +1578,29 @@ + + i_user_self_refresh + + in + + + wire + xilinx_anylanguagesynthesis + xilinx_anylanguagebehavioralsimulation + + + + 0 + + + + + + true + + + + @@ -1655,6 +1678,11 @@ Ecc Enable 0 + + SELF_REFRESH + Self-Refresh + 0 + DIC Dic @@ -1723,6 +1751,20 @@ ACTIVE_HIGH ACTIVE_LOW + + choice_pairs_933dc0fc + 0 + 1 + 2 + 3 + + + choice_pairs_96a879b9 + 0 + 1 + 2 + 3 + @@ -1778,7 +1820,7 @@ ../rtl/axi/ddr3_top_axi.v verilogSource - CHECKSUM_4123fcc3 + CHECKSUM_f4e2d855 @@ -1841,7 +1883,7 @@ xgui/uberddr3_axi_v1_0.tcl tclSource - CHECKSUM_30e22270 + CHECKSUM_ce7b9cf6 XGUI_VERSION_2 @@ -1935,7 +1977,7 @@ ECC_ENABLE ECC Enable - 0 + 0 DIC @@ -2057,6 +2099,11 @@ Component_Name uberddr3_axi_v1_0 + + SELF_REFRESH + Self-Refresh + 0 + @@ -2090,20 +2137,20 @@ uberddr3_axi_v1_0 package_project https://github.com/AngeloJacobo/UberDDR3 - 9 - 2024-10-19T05:33:11Z + 11 + 2024-11-24T08:00:34Z 2022.1 - + - - + + - + diff --git a/vivado_custom_ip/xgui/ddr3_top_axi_v1_0.tcl b/vivado_custom_ip/xgui/ddr3_top_axi_v1_0.tcl index a462c49..f5b2bf9 100644 --- a/vivado_custom_ip/xgui/ddr3_top_axi_v1_0.tcl +++ b/vivado_custom_ip/xgui/ddr3_top_axi_v1_0.tcl @@ -15,6 +15,7 @@ proc init_gui { IPINST } { ipgui::add_param $IPINST -name "DIC" -parent ${Page_0} ipgui::add_param $IPINST -name "DQ_BITS" -parent ${Page_0} ipgui::add_param $IPINST -name "ECC_ENABLE" -parent ${Page_0} + ipgui::add_param $IPINST -name "SELF_REFRESH" -parent ${Page_0} ipgui::add_param $IPINST -name "MICRON_SIM" -parent ${Page_0} ipgui::add_param $IPINST -name "ODELAY_SUPPORTED" -parent ${Page_0} ipgui::add_param $IPINST -name "ROW_BITS" -parent ${Page_0} @@ -142,6 +143,15 @@ proc validate_PARAM_VALUE.ECC_ENABLE { PARAM_VALUE.ECC_ENABLE } { return true } +proc update_PARAM_VALUE.SELF_REFRESH { PARAM_VALUE.SELF_REFRESH } { + # Procedure called to update SELF_REFRESH when any of the dependent parameters in the arguments change +} + +proc validate_PARAM_VALUE.SELF_REFRESH { PARAM_VALUE.SELF_REFRESH } { + # Procedure called to validate SELF_REFRESH + return true +} + proc update_PARAM_VALUE.MICRON_SIM { PARAM_VALUE.MICRON_SIM } { # Procedure called to update MICRON_SIM when any of the dependent parameters in the arguments change } @@ -353,6 +363,11 @@ proc update_MODELPARAM_VALUE.ECC_ENABLE { MODELPARAM_VALUE.ECC_ENABLE PARAM_VALU set_property value [get_property value ${PARAM_VALUE.ECC_ENABLE}] ${MODELPARAM_VALUE.ECC_ENABLE} } +proc update_MODELPARAM_VALUE.SELF_REFRESH { MODELPARAM_VALUE.SELF_REFRESH PARAM_VALUE.SELF_REFRESH } { + # Procedure called to set VHDL generic/Verilog parameter value(s) based on TCL parameter value + set_property value [get_property value ${PARAM_VALUE.SELF_REFRESH}] ${MODELPARAM_VALUE.SELF_REFRESH} +} + proc update_MODELPARAM_VALUE.DIC { MODELPARAM_VALUE.DIC PARAM_VALUE.DIC } { # Procedure called to set VHDL generic/Verilog parameter value(s) based on TCL parameter value set_property value [get_property value ${PARAM_VALUE.DIC}] ${MODELPARAM_VALUE.DIC} diff --git a/vivado_custom_ip/xgui/uberddr3_axi_v1_0.tcl b/vivado_custom_ip/xgui/uberddr3_axi_v1_0.tcl index e20ff24..4ec1d2e 100644 --- a/vivado_custom_ip/xgui/uberddr3_axi_v1_0.tcl +++ b/vivado_custom_ip/xgui/uberddr3_axi_v1_0.tcl @@ -19,8 +19,10 @@ proc init_gui { IPINST } { set_property tooltip {Width of bank address} ${BA_BITS} set BYTE_LANES [ipgui::add_param $IPINST -name "BYTE_LANES" -parent ${Page_0}] set_property tooltip {Number of byte lanes of DDR3 RAM in the FPGA board (e.g. x16 DDR3 will have 2 byte lanes)} ${BYTE_LANES} - set ECC_ENABLE [ipgui::add_param $IPINST -name "ECC_ENABLE" -parent ${Page_0}] - set_property tooltip {0 = DIsabled, 1 = Side-band ECC per burst, 2 = Side-band ECC per 8 bursts , 3 = Inline ECC} ${ECC_ENABLE} + set ECC_ENABLE [ipgui::add_param $IPINST -name "ECC_ENABLE" -parent ${Page_0} -widget comboBox] + set_property tooltip {Type of ECC (0,1,2,3)} ${ECC_ENABLE} + set SELF_REFRESH [ipgui::add_param $IPINST -name "SELF_REFRESH" -parent ${Page_0} -widget comboBox] + set_property tooltip {Enable option for self-refresh} ${SELF_REFRESH} set SKIP_INTERNAL_TEST [ipgui::add_param $IPINST -name "SKIP_INTERNAL_TEST" -parent ${Page_0}] set_property tooltip {Check to skip built-in self-test (check this if UberDDR3 will be connected to Microblaze)} ${SKIP_INTERNAL_TEST} set ODELAY_SUPPORTED [ipgui::add_param $IPINST -name "ODELAY_SUPPORTED" -parent ${Page_0}] @@ -284,6 +286,15 @@ proc validate_PARAM_VALUE.SECOND_WISHBONE { PARAM_VALUE.SECOND_WISHBONE } { return true } +proc update_PARAM_VALUE.SELF_REFRESH { PARAM_VALUE.SELF_REFRESH } { + # Procedure called to update SELF_REFRESH when any of the dependent parameters in the arguments change +} + +proc validate_PARAM_VALUE.SELF_REFRESH { PARAM_VALUE.SELF_REFRESH } { + # Procedure called to validate SELF_REFRESH + return true +} + proc update_PARAM_VALUE.SKIP_INTERNAL_TEST { PARAM_VALUE.SKIP_INTERNAL_TEST } { # Procedure called to update SKIP_INTERNAL_TEST when any of the dependent parameters in the arguments change } @@ -405,6 +416,11 @@ proc update_MODELPARAM_VALUE.ECC_ENABLE { MODELPARAM_VALUE.ECC_ENABLE PARAM_VALU set_property value [get_property value ${PARAM_VALUE.ECC_ENABLE}] ${MODELPARAM_VALUE.ECC_ENABLE} } +proc update_MODELPARAM_VALUE.SELF_REFRESH { MODELPARAM_VALUE.SELF_REFRESH PARAM_VALUE.SELF_REFRESH } { + # Procedure called to set VHDL generic/Verilog parameter value(s) based on TCL parameter value + set_property value [get_property value ${PARAM_VALUE.SELF_REFRESH}] ${MODELPARAM_VALUE.SELF_REFRESH} +} + proc update_MODELPARAM_VALUE.DIC { MODELPARAM_VALUE.DIC PARAM_VALUE.DIC } { # Procedure called to set VHDL generic/Verilog parameter value(s) based on TCL parameter value set_property value [get_property value ${PARAM_VALUE.DIC}] ${MODELPARAM_VALUE.DIC}