/******************************************************************************* * Module: ddrc_test01_testbench * Date:2014-05-20 * Author: Andrey Filippov * Description: testbench for ddrc_test01 * * Copyright (c) 2014 Elphel, Inc. * ddrc_test01_testbench.v is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * ddrc_test01_testbench.v is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . *******************************************************************************/ `timescale 1ns/1ps module ddrc_test01_testbench #( parameter PHASE_WIDTH = 8, parameter SLEW_DQ = "SLOW", parameter SLEW_DQS = "SLOW", parameter SLEW_CMDA = "SLOW", parameter SLEW_CLK = "SLOW", parameter IBUF_LOW_PWR = "TRUE", parameter real REFCLK_FREQUENCY = 300.0, parameter HIGH_PERFORMANCE_MODE = "FALSE", parameter CLKIN_PERIOD = 10, //ns >1.25, 600= 2.5ns), 3'b001 6CK (1.875ns <= tCK < 2.5ns) mr3 <= ddr3_mr3 ( 1'h0, // mpr; // MPR mode: 0 - normal, 1 - dataflow from MPR 2'h0); // [1:0] mpr_rf; // MPR read function: 2'b00: predefined pattern 0101... cmd_addr <= BASEADDR_CMD0; wait (~CLK); data <= encode_seq_word( mr2[14:0], // [14:0] phy_addr_in; mr2[17:15], // [ 2:0] phy_bank_in; //TODO: debug! 3'b111, // [ 2:0] phy_rcw_in; // {ras,cas,we}, positive 1'b0, // phy_odt_in; // may be optimized? 1'b0, // phy_cke_inv; // may be optimized? 1'b0, // phy_sel_in; // first/second half-cycle, oter will be nop (cke+odt applicable to both) 1'b0, // phy_dq_en_in; 1'b0, // phy_dqs_ddrc_sequenceren_in; 1'b0, // phy_dci_en_in; // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) 1'b0, // phy_buf_wr; // connect to external buffer 1'b0); // phy_buf_rd; // connect to external buffer wait (CLK); axi_write_single(cmd_addr, data); cmd_addr <= cmd_addr + 4; wait (~CLK); // data <= encode_seq_skip(2,0); data <= encode_seq_skip(1,0); // 6 cycles between mrs commands wait (CLK); axi_write_single(cmd_addr, data); cmd_addr <= cmd_addr + 4; wait (~CLK); data <= encode_seq_word( mr3[14:0], // [14:0] phy_addr_in; mr3[17:15], // [ 2:0] phy_bank_in; //TODO: debug! 3'b111, // [ 2:0] phy_rcw_in; // {ras,cas,we}, positive 1'b0, // phy_odt_in; // may be optimized? 1'b0, // phy_cke_inv; // may be optimized? 1'b0, // phy_sel_in; // first/second half-cycle, other will be nop (cke+odt applicable to both) 1'b0, // phy_dq_eddrc_sequencern_in; 1'b0, // phy_dqs_en_in; 1'b0, // phy_dci_en_in; // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) 1'b0, // phy_buf_wr; // connect to external buffer 1'b0); // phy_buf_rd; // connect to external buffer wait (CLK); axi_write_single(cmd_addr, data); cmd_addr <= cmd_addr + 4; wait (~CLK); // data <= encode_seq_skip(2,0); // TODO: function - does not check arguments number data <= encode_seq_skip(0,0); // 5 cycles between mrs commands (next command has phy_sel_in == 1) wait (CLK); axi_write_single(cmd_addr, data); cmd_addr <= cmd_addr + 4; wait (~CLK); data <= encode_seq_word( mr1[14:0], // [14:0] phy_addr_in; mr1[17:15], // [ 2:0] phy_bank_in; //TODO: debug! 3'b111, // [ 2:0] phy_rcw_in; // {ras,cas,we}, positive 1'b0, // phy_odt_in; // may be optimized? 1'b0, // phy_cke_inv; // may be optimized? 1'b1, // phy_sel_in == 1 (test); // first/second half-cycle, 1'b0, // phy_dq_en_in; 1'b0, // phy_dqs_en_in; 1'b0, // phy_dci_en_in; // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) 1'b0, // phy_buf_wr; // connect to external buffer 1'b0); // phy_buf_rd; // connect to external buffer wait (CLK); axi_write_single(cmd_addr, data); cmd_addr <= cmd_addr + 4; wait (~CLK); data <= encode_seq_skip(2,0); // 7 cycles between mrs commands ( prev. command had phy_sel_in == 1) wait (CLK); axi_write_single(cmd_addr, data); cmd_addr <= cmd_addr + 4; wait (~CLK); data <= encode_seq_word( mr0[14:0], // [14:0] phy_addr_in; mr0[17:15], // [ 2:0] phy_bank_in; //TODO: debug! 3'b111, // [ 2:0] phy_rcw_in; // {ras,cas,we}, positive 1'b0, // phy_odt_in; // may be optimized? 1'b0, // phy_cke_inv; // may be optimized? 1'b0, // phy_sel_in; // first/second half-cycle, other will be nop (cke+odt applicable to both) 1'b0, // phy_dq_en_in; 1'b0, // phy_dqs_en_in; 1'b0, // phy_dci_en_in; // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) 1'b0, // phy_buf_wr; // connect to external buffer 1'b0); // phy_buf_rd; // connect to external buffer wait (CLK); axi_write_single(cmd_addr, data); cmd_addr <= cmd_addr + 4; wait (~CLK); data <= encode_seq_skip(10,1); // sequence done bit, skip length is ignored wait (CLK); axi_write_single(cmd_addr, data); cmd_addr <= cmd_addr + 4; // TODO: Function of function does not work - debug /* axi_write_single(cmd_addr, encode_seq_word( mr2[14:0], // [14:0] phy_addr_in; mr2[17:15], // [ 2:0] phy_bank_in; //TODO: debug! 3'b111, // [ 2:0] phy_rcw_in; // {ras,cas,we}, positive 1'b0, // phy_odt_in; // may be optimized? 1'b0, // phy_cke_inv; // may be optimized? 1'b0, // phy_sel_in; // first/second half-cycle, other will be nop (cke+odt applicable to both) 1'b0, // phy_dq_en_in; 1'b0, // phy_dqs_en_in; 1'b0, // phy_dci_en_in; // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) 1'b0, // phy_buf_wr; // connect to external buffer 1'b0)); // phy_buf_rd; // connect to external buffer */ end endtask function [31:0] encode_seq_word; input [14:0] phy_addr_in; // also provides pause length when the command is NOP input [ 2:0] phy_bank_in; input [ 2:0] phy_rcw_in; // {ras,cas,we} input phy_odt_in; // may be optimized? input phy_cke_inv; // invert CKE input phy_sel_in; // fitst/second half-cycle, oter will be nop (cke+odt applicable to both) input phy_dq_en_in; input phy_dqs_en_in; input phy_dci_en_in; // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) input phy_buf_wr; // connect to external buffer input phy_buf_rd; // connect to external buffer begin encode_seq_word={ phy_addr_in[14:0], phy_bank_in[2:0], phy_rcw_in[2:0], // {ras,cas,we}, positive logic (3'b0 - NOP) phy_odt_in, // may be optimized? phy_cke_inv, // invert CKE phy_sel_in, // first/second half-cycle, other will be nop (cke+odt applicable to both) phy_dq_en_in, //phy_dq_tri_in, // tristate DQ lines (internal timing sequencer for 0->1 and 1->0) phy_dqs_en_in, //phy_dqs_tri_in, // tristate DQS lines (internal timing sequencer for 0->1 and 1->0) phy_dci_en_in, //phy_dci_in, // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) phy_buf_wr, // connect to external buffer (but only if not paused) phy_buf_rd, // connect to external buffer (but only if not paused) 3'h0 // Reserved for future use }; end endfunction // parameter CMD_PAUSE_BITS= 6, // numer of (address) bits to encode pause // parameter CMD_DONE_BIT= 6 // bit number (address) to signal sequence done function [31:0] encode_seq_skip; input [CMD_PAUSE_BITS-1:0] skip; input done; begin encode_seq_skip={ {14-CMD_DONE_BIT{1'b0}}, done, skip[CMD_PAUSE_BITS-1:0], 3'b0, //phy_bank_in[2:0], 3'b0, // phy_rcw_in[2:0], // {ras,cas,we} 1'b0, // phy_odt_in, // may be optimized? 1'b0, // phy_cke_in, // may be optimized? 1'b0, // phy_sel_in, // fitst/second half-cycle, oter will be nop (cke+odt applicable to both) 1'b0, // phy_dq_en_in, //phy_dq_tri_in, // tristate DQ lines (internal timing sequencer for 0->1 and 1->0) 1'b0, // phy_dqs_en_in, //phy_dqs_tri_in, // tristate DQS lines (internal timing sequencer for 0->1 and 1->0) 1'b0, // phy_dci_en_in, //phy_dci_in, // DCI disable, both DQ and DQS lines (internal logic and timing sequencer for 0->1 and 1->0) 1'b0, // phy_buf_wr, // connect to external buffer (but only if not paused) 1'b0, // phy_buf_rd, // connect to external buffer (but only if not paused) 3'h0 // Reserved for future use }; end endfunction function [ADDRESS_NUMBER+2:0] ddr3_mr0; input pd; // precharge power down 0 - dll off (slow exit), 1 - dll on (fast exit) input [2:0] wr; // write recovery: // 3'b000: 16 // 3'b001: 5 // 3'b010: 6 // 3'b011: 7 // 3'b100: 8 // 3'b101: 10 // 3'b110: 12 // 3'b111: 14 input dll_rst; // 1 - dll reset (self clearing bit) input [3:0] cl; // CAS latency: // 0000: reserved // 0010: 5 // 0100: 6 // 0110: 7 // 1000: 8 // 1010: 9 // 1100: 10 // 1110: 11 // 0001: 12 // 0011: 13 // 0101: 14 input bt; // read burst type: 0 sequential (nibble), 1 - interleaved input [1:0] bl; // burst length: // 2'b00 - fixed BL8 // 2'b01 - 4 or 8 on-the-fly by A12 // 2'b10 - fixed BL4 (chop) // 2'b11 - reserved begin ddr3_mr0 = { 3'b0, {ADDRESS_NUMBER-13{1'b0}}, pd, // MR0.12 wr, // MR0.11_9 dll_rst, // MR0.8 1'b0, // MR0.7 cl[3:1], // MR0.6_4 bt, // MR0.3 cl[0], // MR0.2 bl[1:0]}; // MR0.1_0 end endfunction function [ADDRESS_NUMBER+2:0] ddr3_mr1; input qoff; // output enable: 0 - DQ, DQS operate in normal mode, 1 - DQ, DQS are disabled input tdqs; // termination data strobe (for x8 devices) 0 - disabled, 1 - enabled input [2:0] rtt; // on-die termination resistance: // 3'b000 - disabled // 3'b001 - RZQ/4 (60 Ohm) // 3'b010 - RZQ/2 (120 Ohm) // 3'b011 - RZQ/6 (40 Ohm) // 3'b100 - RZQ/12(20 Ohm) // 3'b101 - RZQ/8 (30 Ohm) // 3'b11x - reserved input wlev; // write leveling input [1:0] ods; // output drive strength: // 2'b00 - RZQ/6 - 40 Ohm // 2'b01 - RZQ/7 - 34 Ohm // 2'b1x - reserved input [1:0] al; // additive latency: // 2'b00 - disabled (AL=0) // 2'b01 - AL=CL-1; // 2'b10 - AL=CL-2 // 2'b11 - reserved input dll; // 0 - DLL enabled (normal), 1 - DLL disabled begin ddr3_mr1 = { 3'h1, {ADDRESS_NUMBER-13{1'b0}}, qoff, // MR1.12 tdqs, // MR1.11 1'b0, // MR1.10 rtt[2], // MR1.9 1'b0, // MR1.8 wlev, // MR1.7 rtt[1], // MR1.6 ods[1], // MR1.5 al[1:0], // MR1.4_3 rtt[0], // MR1.2 ods[0], // MR1.1 dll}; // MR1.0 end endfunction function [ADDRESS_NUMBER+2:0] ddr3_mr2; input [1:0] rtt_wr; // Dynamic ODT : // 2'b00 - disabled // 2'b01 - RZQ/4 = 60 Ohm // 2'b10 - RZQ/2 = 120 Ohm // 2'b11 - reserved input srt; // Self-refresh temperature 0 - normal (0-85C), 1 - extended (<=95C) input asr; // Auto self-refresh 0 - disabled (manual), 1 - enabled (auto) input [2:0] cwl; // CAS write latency: // 3'b000 5CK ( tCK >= 2.5ns) // 3'b001 6CK (1.875ns <= tCK < 2.5ns) // 3'b010 7CK (1.5ns <= tCK < 1.875ns) // 3'b011 8CK (1.25ns <= tCK < 1.5ns) // 3'b100 9CK (1.071ns <= tCK < 1.25ns) // 3'b101 10CK (0.938ns <= tCK < 1.071ns) // 3'b11x reserved begin ddr3_mr2 = { 3'h2, {ADDRESS_NUMBER-11{1'b0}}, rtt_wr[1:0], // MR2.10_9 1'b0, // MR2.8 srt, // MR2.7 asr, // MR2.6 cwl[2:0], // MR2.5_3 3'b0}; // MR2.2_0 end endfunction function [ADDRESS_NUMBER+2:0] ddr3_mr3; input mpr; // MPR mode: 0 - normal, 1 - dataflow from MPR input [1:0] mpr_rf; // MPR read function: // 2'b00: predefined pattern 0101... // 2'b1x, 2'bx1 - reserved begin ddr3_mr3 = { 3'h3, {ADDRESS_NUMBER-3{1'b0}}, mpr, // MR3.2 mpr_rf[1:0]}; // MR3.1_0 end endfunction reg [7:0] target_phase=0; // initialize delays task axi_set_delays; integer i; begin for (i=0;i<19;i=i+1) begin axi_write_single(BASEADDRESS_LANE0 + (i<<2), (DLY_LANE0 >> (i<<3)) & 32'hff); end for (i=0;i<19;i=i+1) begin axi_write_single(BASEADDRESS_LANE1 + (i<<2), (DLY_LANE1 >> (i<<3)) & 32'hff); end for (i=0;i<32;i=i+1) begin axi_write_single(BASEADDRESS_CMDA + (i<<2), (DLY_CMDA >> (i<<3)) & 32'hff); end axi_write_single(BASEADDR_DLY_SET, 0); // set all dealys - remove after fixed axi_set_phase axi_set_phase(DLY_PHASE); axi_write_single(BASEADDR_DLY_SET, 0); // set all dealys end endtask task axi_set_phase; input [PHASE_WIDTH-1:0] phase; begin axi_write_single(BASEADDRESS_PHASE, {{(32-PHASE_WIDTH){1'b0}},phase}); target_phase <= phase; end endtask /* assign rdata={21'b0,run_busy,locked,ps_rdy,ps_out[7:0]}; */ task wait_phase_shifter_ready; begin read_status; while (((registered_rdata & PSHIFTER_RDY_MASK) == 0) || (((registered_rdata ^ {24'h0,target_phase}) & 'hff) != 0)) read_status; end endtask task read_status; begin read_and_wait(BASEADDR_STATUS); end endtask /* // read memory task test_axi_2; integer i; //,j; begin axi_set_rd_lag(0); for (i=0;i<1024;i=i+16) begin axi_read_addr( i, // id i<<2, // addr 4'hf, // len 1 // burst type - increment ); if ((i==256) || (i==384)) begin repeat (512) @(posedge CLK) ; end if (( i & 'h7f)==0) begin if (( i & 'h80)==0) axi_set_rd_lag(1); else axi_set_rd_lag(0); end end // assign aaa=bbb; // task internals were not parsed end endtask task read_status; begin read_and_wait(BASEADDR_STATUS); end endtask */ // Low-level tasks task axi_set_rd_lag; input [3:0] lag; begin @(posedge CLK); RD_LAG <= lag; end endtask task axi_set_b_lag; input [3:0] lag; begin @(posedge CLK); B_LAG <= lag; end endtask reg [11:0] GLOBAL_WRITE_ID=0; reg [11:0] GLOBAL_READ_ID=0; task read_and_wait; input [31:0] address; begin axi_read_addr( GLOBAL_READ_ID, // id address & 32'hfffffffc, // addr 4'h0, // len - single 1 // burst type - increment ); GLOBAL_READ_ID <= GLOBAL_READ_ID+1; wait (!CLK && rvalid && rready); wait (CLK); registered_rdata <= rdata; end endtask task axi_write_single; // address in bytes, not words input [31:0] address; input [31:0] data; begin `ifdef DEBUG_WR_SINGLE $display("axi_write_single %h:%h @ %t",address,data,$time); `endif axi_write_addr_data( GLOBAL_WRITE_ID, // id // address << 2, // addr address & 32'hfffffffc, // addr data, 4'h0, // len - single 1, // burst type - increment 1'b1, // data_en 4'hf, // wstrb 1'b1 // last ); GLOBAL_WRITE_ID <= GLOBAL_WRITE_ID+1; end endtask task axi_write_addr_data; input [11:0] id; input [31:0] addr; input [31:0] data; input [ 3:0] len; input [ 1:0] burst; input data_en; // if 0 - do not send data, only address input [ 3:0] wstrb; input last; reg data_sent; // wire data_sent_d; // assign #(.1) data_sent_d= data_sent; begin wait (!CLK && AW_READY); AWID_IN_r <= id; AWADDR_IN_r <= addr; AWLEN_IN_r <= len; AWSIZE_IN_r <= 3'b010; AWBURST_IN_r <= burst; AW_SET_CMD_r <= 1'b1; if (data_en && W_READY) begin WID_IN_r <= id; WDATA_IN_r <= data; WSTRB_IN_r <= wstrb; WLAST_IN_r <= last; W_SET_CMD_r <= 1'b1; data_sent <= 1'b1; end else begin data_sent <= 1'b0; end DEBUG1 <=1'b1; wait (CLK); DEBUG1 <=1'b0; AWID_IN_r <= 'hz; AWADDR_IN_r <= 'hz; AWLEN_IN_r <= 'hz; AWSIZE_IN_r <= 'hz; AWBURST_IN_r <= 'hz; AW_SET_CMD_r <= 1'b0; DEBUG2 <=1'b1; if (data_sent) begin WID_IN_r <= 'hz; WDATA_IN_r <= 'hz; WSTRB_IN_r <= 'hz; WLAST_IN_r <= 'hz; W_SET_CMD_r <= 1'b0; end // Now sent data if it was not sent simultaneously with the address if (data_en && !data_sent) begin DEBUG3 <=1'b1; wait (!CLK && W_READY); DEBUG3 <=1'b0; WID_IN_r <= id; WDATA_IN_r <= data; WSTRB_IN_r <= wstrb; WLAST_IN_r <= last; W_SET_CMD_r <= 1'b1; wait (CLK); DEBUG3 <=1'bx; WID_IN_r <= 'hz; WDATA_IN_r <= 'hz; WSTRB_IN_r <= 'hz; WLAST_IN_r <= 'hz; W_SET_CMD_r <= 1'b0; end DEBUG2 <=1'b0; #0.1; data_sent <= 1'b0; #0.1; end endtask // SuppressWarnings VEditor - not yet used task axi_write_data; input [11:0] id; input [31:0] data; input [ 3:0] wstrb; input last; begin wait (!CLK && W_READY); WID_IN_r <= id; WDATA_IN_r <= data; WSTRB_IN_r <= wstrb; WLAST_IN_r <= last; W_SET_CMD_r <= 1'b1; wait (CLK); WID_IN_r <= 12'hz; WDATA_IN_r <= 'hz; WSTRB_IN_r <= 4'hz; WLAST_IN_r <= 1'bz; W_SET_CMD_r <= 1'b0; #0.1; end endtask task axi_read_addr;`ifndef IVERILOG (* dont_touch = "true" *) `endif input [11:0] id; input [31:0] addr; input [ 3:0] len; input [ 1:0] burst; begin wait (!CLK && AR_READY); ARID_IN_r <= id; ARADDR_IN_r <= addr; ARLEN_IN_r <= len; ARSIZE_IN_r <= 3'b010; ARBURST_IN_r <= burst; AR_SET_CMD_r <= 1'b1; wait (CLK); ARID_IN_r <= 12'hz; ARADDR_IN_r <= 'hz; ARLEN_IN_r <= 4'hz; ARSIZE_IN_r <= 3'hz; ARBURST_IN_r <= 2'hz; AR_SET_CMD_r <= 1'b0; end endtask endmodule