diff --git a/rtl/ddr3_controller.v b/rtl/ddr3_controller.v index 4aadb1c..c7080d8 100644 --- a/rtl/ddr3_controller.v +++ b/rtl/ddr3_controller.v @@ -52,7 +52,7 @@ module ddr3_controller #( input wire[wb_sel_bits - 1:0] i_wb_sel, //byte strobe for write (1 = write the byte) input wire i_aux, //for AXI-interface compatibility (given upon strobe) // Wishbone outputs - output reg o_wb_stall, //1 = busy, cannot accept requests + output reg o_wb_stall, //1 = busy, cannot accept requests output reg o_wb_ack, //1 = read/write request has completed output reg[wb_data_bits - 1:0] o_wb_data, //read data, for a 4:1 controller data width is 8 times the number of pins on the device output reg o_aux //for AXI-interface compatibility (returned upon ack) @@ -75,11 +75,11 @@ module ddr3_controller #( CMD_ZQC = 4'b0110; // ZQ Calibration (A10-AP: 0 = ZQ Calibration Short, 1 = ZQ Calibration Long) 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) - A10_CONTROL = 25, //Command bit that determines if A10 AutoPrecharge will be high - CLOCK_EN = 24, //Clock-enable to DDR3 - RESET_N = 23; //Reset_n to DDR3 + 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) + A10_CONTROL = 25, //Command bit that determines if A10 AutoPrecharge will be high + CLOCK_EN = 24, //Clock-enable to DDR3 + RESET_N = 23; //Reset_n to DDR3 // ddr3_metadata partitioning localparam CMD_LEN = 4 + BA_BITS + ROW_BITS, //4 is the width of a single ddr3 command (precharge,actvate, etc.) plus bank bits plus row bits @@ -313,18 +313,17 @@ module ddr3_controller #( //clear bank_status and bank_active_row to zero - integer index; initial begin for(integer index=0; index< (1<= b) max = a; @@ -767,45 +766,45 @@ module ddr3_controller #( `ifndef YOSYS ///YOSYS: System task `$display' called with invalid/unsupported format specifier initial begin - $display("Test ns_to_cycles() function:"); - $display("\tns_to_cycles(15) = 3 = %0d [exact]", ns_to_cycles(15) ); - $display("\tns_to_cycles(14.5) = 3 = %0d [round-off]", ns_to_cycles(14.5) ); - $display("\tns_to_cycles(11) = 3 = %0d [round-up]\n", ns_to_cycles(11) ); - - $display("Test nCK_to_cycles() function:"); - $display("\tns_to_cycles(16) = 4 = %0d [exact]", nCK_to_cycles(16) ); - $display("\tns_to_cycles(15) = 4 = %0d [round-off]", nCK_to_cycles(15) ); - $display("\tns_to_cycles(13) = 4 = %0d [round-up]\n", nCK_to_cycles(13) ); - - $display("Test ns_to_nCK() function:"); - $display("\tns_to_cycles(15) = 12 = %0d [exact]", ns_to_nCK(15) ); - $display("\tns_to_cycles(14.875) = 12 = %0d [round-off]", ns_to_nCK(14.875) ); - $display("\tns_to_cycles(13.875) = 12 = %0d [round-up]", ns_to_nCK(13.875) ); - $display("\tns_to_nCK(tRCD) = 11 = %0d [WRONG]", ns_to_nCK(tRCD)); - $display("\ttRTP = 7.5 = %f ", tRTP); - $display("\tns_to_nCK(tRTP) = 6= %f [WRONG]\n", ns_to_nCK(tRTP) ); - - $display("Test nCK_to_ns() function:"); - $display("\tns_to_cycles(4) = 5 = %0d [exact]", nCK_to_ns(4) ); - $display("\tns_to_cycles(14.875) = 4 = %0d [round-off]", nCK_to_ns(3) ); - $display("\tns_to_cycles(13.875) = 7 = %0d [round-up]\n", nCK_to_ns(5) ); - - $display("Test nCK_to_ns() function:"); - $display("\tns_to_cycles(4) = 5 = %0d [exact]", nCK_to_ns(4) ); - $display("\tns_to_cycles(14.875) = 4 = %0d [round-off]", nCK_to_ns(3) ); - $display("\tns_to_cycles(13.875) = 7 = %0d [round-up]\n", nCK_to_ns(5) ); - - $display("Test added_delay() function:"); - $display("\tadded_delay(0) = 0xffffffff = 0x%h", added_delay(0) ); - $display("\tadded_delay(1) = 0xfffffffe = 0x%h", added_delay(1) ); - $display("\tadded_delay(2) = 0xfffffffc = 0x%h", added_delay(2) ); - $display("\tadded_delay(3) = 0xfffffff8 = 0x%h\n", added_delay(3) ); - - $display("Test $floor() function:"); - $display("\t$floor(5/2) = 2.5 = %0d", $floor(5/2) ); - $display("\t$floor(9/4) = 2.25 = %0d", $floor(9/4) ); - $display("\t$floor(9/4) = 2 = %0d", $floor(8/4) ); - $display("\t$floor(9/5) = 1.8 = %0d\n", $floor(9/5) ); + $display("Test ns_to_cycles() function:"); + $display("\tns_to_cycles(15) = 3 = %0d [exact]", ns_to_cycles(15) ); + $display("\tns_to_cycles(14.5) = 3 = %0d [round-off]", ns_to_cycles(14.5) ); + $display("\tns_to_cycles(11) = 3 = %0d [round-up]\n", ns_to_cycles(11) ); + + $display("Test nCK_to_cycles() function:"); + $display("\tns_to_cycles(16) = 4 = %0d [exact]", nCK_to_cycles(16) ); + $display("\tns_to_cycles(15) = 4 = %0d [round-off]", nCK_to_cycles(15) ); + $display("\tns_to_cycles(13) = 4 = %0d [round-up]\n", nCK_to_cycles(13) ); + + $display("Test ns_to_nCK() function:"); + $display("\tns_to_cycles(15) = 12 = %0d [exact]", ns_to_nCK(15) ); + $display("\tns_to_cycles(14.875) = 12 = %0d [round-off]", ns_to_nCK(14.875) ); + $display("\tns_to_cycles(13.875) = 12 = %0d [round-up]", ns_to_nCK(13.875) ); + $display("\tns_to_nCK(tRCD) = 11 = %0d [WRONG]", ns_to_nCK(tRCD)); + $display("\ttRTP = 7.5 = %f ", tRTP); + $display("\tns_to_nCK(tRTP) = 6= %f [WRONG]\n", ns_to_nCK(tRTP) ); + + $display("Test nCK_to_ns() function:"); + $display("\tns_to_cycles(4) = 5 = %0d [exact]", nCK_to_ns(4) ); + $display("\tns_to_cycles(14.875) = 4 = %0d [round-off]", nCK_to_ns(3) ); + $display("\tns_to_cycles(13.875) = 7 = %0d [round-up]\n", nCK_to_ns(5) ); + + $display("Test nCK_to_ns() function:"); + $display("\tns_to_cycles(4) = 5 = %0d [exact]", nCK_to_ns(4) ); + $display("\tns_to_cycles(14.875) = 4 = %0d [round-off]", nCK_to_ns(3) ); + $display("\tns_to_cycles(13.875) = 7 = %0d [round-up]\n", nCK_to_ns(5) ); + + $display("Test added_delay() function:"); + $display("\tadded_delay(0) = 0xffffffff = 0x%h", added_delay(0) ); + $display("\tadded_delay(1) = 0xfffffffe = 0x%h", added_delay(1) ); + $display("\tadded_delay(2) = 0xfffffffc = 0x%h", added_delay(2) ); + $display("\tadded_delay(3) = 0xfffffff8 = 0x%h\n", added_delay(3) ); + + $display("Test $floor() function:"); + $display("\t$floor(5/2) = 2.5 = %0d", $floor(5/2) ); + $display("\t$floor(9/4) = 2.25 = %0d", $floor(9/4) ); + $display("\t$floor(9/4) = 2 = %0d", $floor(8/4) ); + $display("\t$floor(9/5) = 1.8 = %0d\n", $floor(9/5) ); $display("\nDELAY_COUNTER_WIDTH = %0d", DELAY_COUNTER_WIDTH); $display("DELAY_SLOT_WIDTH = %0d", DELAY_SLOT_WIDTH); @@ -817,22 +816,22 @@ module ddr3_controller #( $display("wb_sel_bits = %0d\n\n",wb_sel_bits); $display("request_row_width = %0d = %0d", ROW_BITS, $bits(i_wb_addr[ (ROW_BITS + BA_BITS + COL_BITS- $clog2(serdes_ratio*2) - 1) : (BA_BITS + COL_BITS- $clog2(serdes_ratio*2)) ])); $display("request_col_width = %0d = %0d", COL_BITS, $bits({ i_wb_addr[(COL_BITS- $clog2(serdes_ratio*2)-1):0], {{$clog2(serdes_ratio*2)}{1'b0}} })); - $display("request_bank_width = %0d = %0d", BA_BITS, $bits(i_wb_addr[(BA_BITS + COL_BITS- $clog2(serdes_ratio*2) - 1) : (COL_BITS- $clog2(serdes_ratio*2))])); + $display("request_bank_width = %0d = %0d", BA_BITS, $bits(i_wb_addr[(BA_BITS + COL_BITS- $clog2(serdes_ratio*2) - 1) : (COL_BITS- $clog2(serdes_ratio*2))])); $display("READ_SLOT = %0d", READ_SLOT); $display("WRITE_SLOT = %0d", WRITE_SLOT); $display("ANTICIPATE_ACTIVATE_SLOT = %0d", ANTICIPATE_ACTIVATE_SLOT); $display("ANTICIPATE_PRECHARGE_SLOT = %0d", ANTICIPATE_PRECHARGE_SLOT); - - $display("\n\nDELAYS:"); - $display("\tns_to_nCK(tRCD): %0d", ns_to_nCK(tRCD)); - $display("\tns_to_nCK(tRP): %0d", ns_to_nCK(tRP)); - $display("\tns_to_nCK(tRTP): %0d", ns_to_nCK(tRTP)); - $display("\ttCCD: %0d", tCCD); - $display("\t(CL_nCK + tCCD + 3'd2 - CWL_nCK): %0d", (CL_nCK + tCCD + 3'd2 - CWL_nCK)); - $display("\t(CWL_nCK + 3'd4 + ns_to_nCK(tWR)): %0d", (CWL_nCK + 3'd4 + ns_to_nCK(tWR))); - $display("\t(CWL_nCK + 3'd4 + ns_to_nCK(tWTR)): %0d", (CWL_nCK + 3'd4 + ns_to_nCK(tWTR))); - $display("\t$signed(4'b1100)>>>4: %b", $signed(4'b1100) >>> 4); + + $display("\n\nDELAYS:"); + $display("\tns_to_nCK(tRCD): %0d", ns_to_nCK(tRCD)); + $display("\tns_to_nCK(tRP): %0d", ns_to_nCK(tRP)); + $display("\tns_to_nCK(tRTP): %0d", ns_to_nCK(tRTP)); + $display("\ttCCD: %0d", tCCD); + $display("\t(CL_nCK + tCCD + 3'd2 - CWL_nCK): %0d", (CL_nCK + tCCD + 3'd2 - CWL_nCK)); + $display("\t(CWL_nCK + 3'd4 + ns_to_nCK(tWR)): %0d", (CWL_nCK + 3'd4 + ns_to_nCK(tWR))); + $display("\t(CWL_nCK + 3'd4 + ns_to_nCK(tWTR)): %0d", (CWL_nCK + 3'd4 + ns_to_nCK(tWTR))); + $display("\t$signed(4'b1100)>>>4: %b", $signed(4'b1100) >>> 4); end `endif @@ -878,10 +877,10 @@ module ddr3_controller #( assert(f_addr == instruction_address); //f_addr is the shadow of instruction_address (thus f_addr is the address of NEXT instruction) f_read_inst = read_rom_instruction(f_read); //f_read is the address of CURRENT instruction assert(f_read_inst == read_rom_instruction(f_read)); // needed for induction to make sure the engine will not create his own instruction - if(f_addr == 0) begin - f_read_inst = INITIAL_RESET_INSTRUCTION; //will only happen at the very start: f_addr (0) -> f_read (0) where we are reading the initial reset instruction and not the rom - end - assert(f_read_inst == instruction); // f_read_inst is the shadow of current instruction + if(f_addr == 0) begin + f_read_inst = INITIAL_RESET_INSTRUCTION; //will only happen at the very start: f_addr (0) -> f_read (0) where we are reading the initial reset instruction and not the rom + end + assert(f_read_inst == instruction); // f_read_inst is the shadow of current instruction end // main assertions for the reset sequence @@ -924,22 +923,22 @@ module ddr3_controller #( end //sanity checking for the comment "delay_counter will be zero AT NEXT CLOCK CYCLE when counter is now one" - if($past(delay_counter) == 1) begin - assert(delay_counter == 0 && delay_counter_is_zero); - end + if($past(delay_counter) == 1) begin + assert(delay_counter == 0 && delay_counter_is_zero); + end //assert the relationship between the stages FOR RESET SEQUENCE - if(!reset_done) begin - if(f_addr == 0) begin - assert(f_read == 0); //will only happen at the very start: f_addr (0) -> f_read (0) - end - else if(f_read == 0) begin - assert(f_addr <= 1); //will only happen at the very first two cycles: f_addr (1) -> f_read (0) or f_addr (0) -> f_read (0) - end - //else if($past(reset_done)) assert(f_read == $past(f_read)); //reset instruction does not repeat after reaching end address thus it must saturate when pipeline reaches end - else begin - assert(f_read + 1 == f_addr); //address increments continuously - end - assert($past(f_read) <= 14); //only instruction address 0-to-13 is for reset sequence (reset_done is asserted at address 14) + if(!reset_done) begin + if(f_addr == 0) begin + assert(f_read == 0); //will only happen at the very start: f_addr (0) -> f_read (0) + end + else if(f_read == 0) begin + assert(f_addr <= 1); //will only happen at the very first two cycles: f_addr (1) -> f_read (0) or f_addr (0) -> f_read (0) + end + //else if($past(reset_done)) assert(f_read == $past(f_read)); //reset instruction does not repeat after reaching end address thus it must saturate when pipeline reaches end + else begin + assert(f_read + 1 == f_addr); //address increments continuously + end + assert($past(f_read) <= 14); //only instruction address 0-to-13 is for reset sequence (reset_done is asserted at address 14) end //assert the relationship between the stages FOR REFRESH SEQUENCE @@ -970,7 +969,7 @@ module ddr3_controller #( always @* begin //there MUST BE no instruction which USE_TIMER is high but delay is zero since it can cause the logic to lock-up (delay must be at least 1) if(a[USE_TIMER]) begin - assert( a[DELAY_COUNTER_WIDTH - 1:0] > 0); + assert( a[DELAY_COUNTER_WIDTH - 1:0] > 0); end end @@ -995,44 +994,43 @@ module ddr3_controller #( //create a formal assertion that says during refresh ack should be low always //make an assertion that there will be no request pending before actual refresh starts at instruction 4'd12 - - reg[24:0] f_wb_inputs[13:0]; - reg[4:0] f_index = 0; - reg[5:0] f_counter = 0; - initial begin - f_wb_inputs[0] = {1'b0, {14'd0,3'd1, 7'd0}}; //read - f_wb_inputs[1] = {1'b0, {14'd0,3'd1, 7'd8}}; //read on same bank (tCCD) - f_wb_inputs[2] = {1'b1, {14'd0,3'd1, 7'd16}}; //write on same bank (tRTW) - f_wb_inputs[3] = {1'b1, {14'd0,3'd1, 7'd24}}; //write on same bank (tCCD) - f_wb_inputs[4] = {1'b0, {14'd0,3'd2, 7'd0}}; //read on different bank - f_wb_inputs[5] = {1'b1, {14'd0,3'd2, 7'd8}}; //write on same bank (tRTW) - f_wb_inputs[6] = {1'b1, {14'd0,3'd1, 7'd32}}; //write on different bank (already activated) - f_wb_inputs[7] = {1'b1, {14'd0,3'd1, 7'd40}}; //write (tCCD) - f_wb_inputs[8] = {1'b1, {14'd1,3'd2, 7'd0}}; //write on different bank (already activated but wrong row) - f_wb_inputs[9] = {1'b1, {14'd1,3'd2, 7'd8}}; //write (tCCD) - f_wb_inputs[10] = {1'b1, {14'd1,3'd2, 7'd16}}; //write (tCCD) - f_wb_inputs[11] = {1'b0, {14'd2,3'd2, 7'd24}}; //read (same bank but wrong row so precharge first) - f_wb_inputs[12] = {1'b0, {14'd2,3'd2, 7'd32}}; //read (tCCD) - f_wb_inputs[13] = {1'b0, {14'd2,3'd2, 7'd40}}; //read (tCCD) - end - always @(posedge i_clk) begin - if(o_wb_ack) begin - f_index <= f_index + 1; - f_counter <= 0; - end - else begin - f_counter <= f_counter + 1; - end - end - - always @* begin - assume(i_wb_cyc == 1); - assume(i_wb_stb == 1); - if(f_index>1) assume(i_rst_n); - assume(i_wb_we == f_wb_inputs[f_index][24]); - assume(i_wb_addr == f_wb_inputs[f_index][23:0]); - cover(f_index == 3); - end + + reg[24:0] f_wb_inputs[13:0]; + reg[4:0] f_index = 0; + reg[5:0] f_counter = 0; + initial begin + f_wb_inputs[0] = {1'b0, {14'd0,3'd1, 7'd0}}; //read + f_wb_inputs[1] = {1'b0, {14'd0,3'd1, 7'd8}}; //read on same bank (tCCD) + f_wb_inputs[2] = {1'b1, {14'd0,3'd1, 7'd16}}; //write on same bank (tRTW) + f_wb_inputs[3] = {1'b1, {14'd0,3'd1, 7'd24}}; //write on same bank (tCCD) + f_wb_inputs[4] = {1'b0, {14'd0,3'd2, 7'd0}}; //read on different bank + f_wb_inputs[5] = {1'b1, {14'd0,3'd2, 7'd8}}; //write on same bank (tRTW) + f_wb_inputs[6] = {1'b1, {14'd0,3'd1, 7'd32}}; //write on different bank (already activated) + f_wb_inputs[7] = {1'b1, {14'd0,3'd1, 7'd40}}; //write (tCCD) + f_wb_inputs[8] = {1'b1, {14'd1,3'd2, 7'd0}}; //write on different bank (already activated but wrong row) + f_wb_inputs[9] = {1'b1, {14'd1,3'd2, 7'd8}}; //write (tCCD) + f_wb_inputs[10] = {1'b1, {14'd1,3'd2, 7'd16}}; //write (tCCD) + f_wb_inputs[11] = {1'b0, {14'd2,3'd2, 7'd24}}; //read (same bank but wrong row so precharge first) + f_wb_inputs[12] = {1'b0, {14'd2,3'd2, 7'd32}}; //read (tCCD) + f_wb_inputs[13] = {1'b0, {14'd2,3'd2, 7'd40}}; //read (tCCD) + end + always @(posedge i_clk) begin + if(o_wb_ack) begin + f_index <= f_index + 1; + f_counter <= 0; + end + else begin + f_counter <= f_counter + 1; + end + end + + always @* begin + assume(i_wb_cyc == 1); + assume(i_wb_stb == 1); + if(f_index>1) assume(i_rst_n); + assume(i_wb_we == f_wb_inputs[f_index][24]); + assume(i_wb_addr == f_wb_inputs[f_index][23:0]); + cover(f_index == 3); + end `endif endmodule -