/*******************************************************************************
* 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