diff options
Diffstat (limited to 'fpga/usrp3/lib/sim/io_cap_gen')
8 files changed, 1642 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/sim/io_cap_gen/cap_pattern_verifier/Makefile b/fpga/usrp3/lib/sim/io_cap_gen/cap_pattern_verifier/Makefile new file mode 100644 index 000000000..d80b63901 --- /dev/null +++ b/fpga/usrp3/lib/sim/io_cap_gen/cap_pattern_verifier/Makefile @@ -0,0 +1,53 @@ +# +# Copyright 2015 Ettus Research LLC +# Copyright 2016 Ettus Research, a National Instruments Company +# +# SPDX-License-Identifier: LGPL-3.0-or-later +# + +#------------------------------------------------- +# Top-of-Makefile +#------------------------------------------------- +# Define BASE_DIR to point to the "top" dir +BASE_DIR = $(abspath ../../../../top) +# Include viv_sim_preamble after defining BASE_DIR +include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak + +#------------------------------------------------- +# Design Specific +#------------------------------------------------- +# Define part using PART_ID (<device>/<package>/<speedgrade>) +ARCH = kintex7 +PART_ID = xc7k410t/ffg900/-2 + +include $(BASE_DIR)/../lib/control/Makefile.srcs + +DESIGN_SRCS = $(abspath \ +$(CONTROL_LIB_SRCS) \ +) + +DESIGN_SRCS += $(abspath \ +../../../io_cap_gen/cap_pattern_verifier.v \ +) + +#------------------------------------------------- +# Testbench Specific +#------------------------------------------------- +include $(BASE_DIR)/../sim/general/Makefile.srcs +include $(BASE_DIR)/../sim/axi/Makefile.srcs + +# Define only one toplevel module +SIM_TOP = cap_pattern_verifier_tb + +SIM_SRCS = \ +$(abspath cap_pattern_verifier_tb.sv) \ +$(SIM_GENERAL_SRCS) \ +$(SIM_AXI_SRCS) + +#------------------------------------------------- +# Bottom-of-Makefile +#------------------------------------------------- +# Include all simulator specific makefiles here +# Each should define a unique target to simulate +# e.g. xsim, vsim, etc and a common "clean" target +include $(BASE_DIR)/../tools/make/viv_simulator.mak diff --git a/fpga/usrp3/lib/sim/io_cap_gen/cap_pattern_verifier/cap_pattern_verifier_tb.sv b/fpga/usrp3/lib/sim/io_cap_gen/cap_pattern_verifier/cap_pattern_verifier_tb.sv new file mode 100644 index 000000000..53aae3719 --- /dev/null +++ b/fpga/usrp3/lib/sim/io_cap_gen/cap_pattern_verifier/cap_pattern_verifier_tb.sv @@ -0,0 +1,199 @@ +// +// Copyright 2015 Ettus Research LLC +// Copyright 2015 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// + + +`timescale 1ns/1ps +`define NS_PER_TICK 1 +`define NUM_TEST_CASES 10 + +`include "sim_clks_rsts.vh" +`include "sim_exec_report.vh" +`include "sim_axis_lib.svh" + + +module cap_pattern_verifier_tb(); + `TEST_BENCH_INIT("cap_pattern_verifier_tb",`NUM_TEST_CASES,`NS_PER_TICK) + + // Define all clocks and resets + `DEFINE_CLK(clk, 5, 50) //100MHz sys_clk to generate DDR3 clocking + `DEFINE_RESET(rst, 0, 100) //100ns for GSR to deassert + + axis_master data0 (.clk(clk)); + axis_master data1 (.clk(clk)); + assign data0.axis.tready = 1; + assign data1.axis.tready = 1; + + wire [31:0] count0, errors0, count1, errors1; + wire locked0, failed0, locked1, failed1; + + cap_pattern_verifier #( + .WIDTH(14), + .PATTERN("RAMP"), + .RAMP_START(14'h0000), + .RAMP_STOP(14'h3FFF), + .RAMP_INCR(14'h0001), + .NTH_CYCLE(1) + ) dut0 ( + .clk(clk), + .rst(rst), + .valid(data0.axis.tvalid), + .data(data0.axis.tdata[13:0]), + .count(count0), + .errors(errors0), + .locked(locked0), + .failed(failed0) + ); + + cap_pattern_verifier #( + .WIDTH(14), + .PATTERN("RAMP"), + .RAMP_START(14'h0100), + .RAMP_STOP(14'h0FFF), + .RAMP_INCR(14'h0001), + .NTH_CYCLE(1) + ) dut1 ( + .clk(clk), + .rst(rst), + .valid(data1.axis.tvalid), + .data(data1.axis.tdata[13:0]), + .count(count1), + .errors(errors1), + .locked(locked1), + .failed(failed1) + ); + + //------------------------------------------ + //Main thread for testbench execution + //------------------------------------------ + initial begin : tb_main + localparam ASYNC_RST_LEN = 10; + localparam OUTPUT_LATENCY = 2; + + `TEST_CASE_START("Wait for reset"); + while (rst) @(posedge clk); + @(posedge clk); + `TEST_CASE_DONE((rst==0)); + + repeat (10) @(posedge clk); + + `TEST_CASE_START("Check reset state"); + `ASSERT_ERROR(count0==0,"Invalid state: count"); + `ASSERT_ERROR(errors0==0,"Invalid state: errors"); + `ASSERT_ERROR(~locked0,"Invalid state: locked"); + `ASSERT_ERROR(~failed0,"Invalid state: failed"); + `TEST_CASE_DONE(1); + + `TEST_CASE_START("Simple ramp"); + data0.push_ramp_pkt(100, 64'd0, 64'h1); + repeat (OUTPUT_LATENCY) @(posedge clk); + `ASSERT_ERROR(count0==100,"Invalid state: count"); + `ASSERT_ERROR(errors0==0,"Invalid state: errors"); + `ASSERT_ERROR(locked0,"Invalid state: locked"); + `ASSERT_ERROR(~failed0,"Invalid state: failed"); + `TEST_CASE_DONE(1); + + rst <= 1; + @(posedge clk); + rst <= 0; + repeat (ASYNC_RST_LEN) @(posedge clk); + + `TEST_CASE_START("Multiple ramp iterations"); + data0.push_ramp_pkt(65536, 64'd0, 64'h1); + repeat (OUTPUT_LATENCY) @(posedge clk); + `ASSERT_ERROR(count0==65536,"Invalid state: count"); + `ASSERT_ERROR(errors0==0,"Invalid state: errors"); + `ASSERT_ERROR(locked0,"Invalid state: locked"); + `ASSERT_ERROR(~failed0,"Invalid state: failed"); + `TEST_CASE_DONE(1); + + #(`NS_PER_TICK*0.3333) + rst <= 1; + #(`NS_PER_TICK*3.25) + rst <= 0; + repeat (ASYNC_RST_LEN+1) @(posedge clk); + + `TEST_CASE_START("Simple ramp after async reset"); + data0.push_ramp_pkt(100, 64'd0, 64'h1); + repeat (OUTPUT_LATENCY) @(posedge clk); + `ASSERT_ERROR(count0==100,"Invalid state: count"); + `ASSERT_ERROR(errors0==0,"Invalid state: errors"); + `ASSERT_ERROR(locked0,"Invalid state: locked"); + `ASSERT_ERROR(~failed0,"Invalid state: failed"); + `TEST_CASE_DONE(1); + + rst <= 1; + @(posedge clk); + rst <= 0; + repeat (ASYNC_RST_LEN) @(posedge clk); + + `TEST_CASE_START("Simple failure"); + data0.push_ramp_pkt(9, 64'd0, 64'h1); + data0.push_ramp_pkt(100, 64'd10, 64'h1); + repeat (OUTPUT_LATENCY) @(posedge clk); + `ASSERT_ERROR(count0==109,"Invalid state: count"); + `ASSERT_ERROR(errors0==1,"Invalid state: errors"); + `ASSERT_ERROR(locked0,"Invalid state: locked"); + `ASSERT_ERROR(failed0,"Invalid state: failed"); + `TEST_CASE_DONE(1); + + `TEST_CASE_START("Late start success"); + data1.push_ramp_pkt(4096, 64'd0, 64'h1); + repeat (OUTPUT_LATENCY) @(posedge clk); + `ASSERT_ERROR(count1==4096-256,"Invalid state: count"); + `ASSERT_ERROR(errors1==0,"Invalid state: errors"); + `ASSERT_ERROR(locked1,"Invalid state: locked"); + `ASSERT_ERROR(~failed1,"Invalid state: failed"); + `TEST_CASE_DONE(1); + + rst <= 1; + @(posedge clk); + rst <= 0; + repeat (ASYNC_RST_LEN) @(posedge clk); + + `TEST_CASE_START("Late failure overshoot"); + data1.push_ramp_pkt(4097, 64'd0, 64'h1); + repeat (OUTPUT_LATENCY) @(posedge clk); + `ASSERT_ERROR(count1==4097-256,"Invalid state: count"); + `ASSERT_ERROR(errors1==1,"Invalid state: errors"); + `ASSERT_ERROR(locked1,"Invalid state: locked"); + `ASSERT_ERROR(failed1,"Invalid state: failed"); + `TEST_CASE_DONE(1); + + `TEST_CASE_START("Metastable data in reset"); + @(posedge clk); + rst <= 1; + data0.push_word(14'hXXXX, 1'b0); + data0.push_word(14'hXXXX, 1'b1); + repeat (OUTPUT_LATENCY) @(posedge clk); + `ASSERT_ERROR(count0==0,"Invalid state: count"); + `ASSERT_ERROR(errors0==0,"Invalid state: errors"); + `ASSERT_ERROR(~locked0,"Invalid state: locked"); + `ASSERT_ERROR(~failed0,"Invalid state: failed"); + `TEST_CASE_DONE(1); + + `TEST_CASE_START("Metastable data out of reset"); + rst <= 0; + repeat (ASYNC_RST_LEN) @(posedge clk); + data0.push_word(14'h0000, 1'b0); + data0.push_word(14'h0001, 1'b0); + data0.push_word(14'h0002, 1'b0); + data0.push_word(14'hXXXX, 1'b0); + data0.push_word(14'hXXXX, 1'b0); + data0.push_word(14'h0005, 1'b0); + repeat (OUTPUT_LATENCY) @(posedge clk); + `ASSERT_ERROR(count0==6,"Invalid state: count"); + `ASSERT_ERROR(errors0==2,"Invalid state: errors"); + `ASSERT_ERROR(locked0,"Invalid state: locked"); + `ASSERT_ERROR(failed0,"Invalid state: failed"); + `TEST_CASE_DONE(1); + + `TEST_BENCH_DONE; + + end + +endmodule diff --git a/fpga/usrp3/lib/sim/io_cap_gen/cat_io_lvds/cat_io_lvds_dual_mode_tb.v b/fpga/usrp3/lib/sim/io_cap_gen/cat_io_lvds/cat_io_lvds_dual_mode_tb.v new file mode 100644 index 000000000..ecb744086 --- /dev/null +++ b/fpga/usrp3/lib/sim/io_cap_gen/cat_io_lvds/cat_io_lvds_dual_mode_tb.v @@ -0,0 +1,780 @@ +// +// Copyright 2016 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: cat_io_lvds_dual_mode_tb +// +// Description: Testbench for cat_io_lvds_dual_mode. +// + +`timescale 1ns/1ps + +module cat_io_lvds_dual_mode_tb(); + + localparam CLK_PERIOD = 10; + localparam CLK200_PERIOD = 2.5; + + localparam USE_CLOCK_IDELAY = 1; + localparam USE_DATA_IDELAY = 1; + localparam DATA_IDELAY_MODE = "FIXED"; + localparam CLOCK_IDELAY_MODE = "FIXED"; + localparam INPUT_CLOCK_DELAY = 16; + localparam INPUT_DATA_DELAY = 0; + localparam USE_CLOCK_ODELAY = 1; + localparam USE_DATA_ODELAY = 1; + localparam DATA_ODELAY_MODE = "FIXED"; + localparam CLOCK_ODELAY_MODE = "FIXED"; + localparam OUTPUT_CLOCK_DELAY = 31; + localparam OUTPUT_DATA_DELAY = 0; + + reg [8*19:0] test_status; + reg check_enabled; // Controls when output checking is performed + + reg clk = 0; + reg rx_clk = 0; + reg clk200 = 0; + + reg reset; + reg mimo; + reg tx_ch; + reg [5:0] rx_d; + reg rx_frame; + reg [7:0] rx_count = 0; + + // Each channel's data begins with a unique identifier (A../B.. or C../D..) + // followed by a count, which should always be sequential. + wire [11:0] i0 = { 4'hA, rx_count }; + wire [11:0] q0 = { 4'hB, rx_count }; + wire [11:0] i1 = { 4'hC, rx_count }; + wire [11:0] q1 = { 4'hD, rx_count }; + + wire radio_clk; + + reg [11:0] tx_i0; + reg [11:0] tx_q0; + reg [11:0] tx_i1; + reg [11:0] tx_q1; + + wire [11:0] rx_i0; + wire [11:0] rx_q0; + wire [11:0] rx_i1; + wire [11:0] rx_q1; + + wire rx_aligned; + + wire tx_clk_p, tx_clk_n; + wire tx_frame_p, tx_frame_n; + wire [5:0] tx_d_p, tx_d_n; + + reg [4:0] ctrl_in_data_delay; + reg [4:0] ctrl_in_clk_delay; + reg ctrl_ld_in_data_delay; + reg ctrl_ld_in_clk_delay; + + reg [4:0] ctrl_out_data_delay; + reg [4:0] ctrl_out_clk_delay; + reg ctrl_ld_out_data_delay; + reg ctrl_ld_out_clk_delay; + + + //--------------------------------------------------------------------------- + // Clock Generation + //--------------------------------------------------------------------------- + + // IODELAYCTRL reference clock + always #(CLK200_PERIOD) clk200 = ~clk200; + + // Create an internal clock we'll use to drive the data + always #(CLK_PERIOD) clk = ~clk; + + // RF interface clock. Half the rate of clk and out of phase + always @(negedge clk) rx_clk <= ~rx_clk; + + + //--------------------------------------------------------------------------- + // Tasks + //--------------------------------------------------------------------------- + + // Output a single burst of 2*len samples. In MIMO mode, this consists of len + // samples on each channel. In SISO mode, this consists of 2*len samples on + // the same channel. + task Burst; + input [31:0] len; + input do_mimo; + begin + repeat(len) + begin + mimo <= do_mimo; + + // Channel 0 sample + @(posedge clk); + rx_d <= i0[11:6]; + rx_frame <= 1; + @(posedge clk); + rx_d <= q0[11:6]; + rx_frame <= 1; + @(posedge clk); + rx_d <= i0[5:0]; + rx_frame <= do_mimo; + @(posedge clk); + rx_d <= q0[5:0]; + rx_frame <= do_mimo; + + // Channel 1 sample / Second channel 0 sample + @(posedge clk); + rx_d <= i1[11:6]; + rx_frame <= ~do_mimo; + @(posedge clk); + rx_d <= q1[11:6]; + rx_frame <= ~do_mimo; + @(posedge clk); + rx_d <= i1[5:0]; + rx_frame <= 0; + @(posedge clk); + rx_d <= q1[5:0]; + rx_frame <= 0; + + rx_count <= rx_count + 1; + end + end + endtask // Burst + + + // Test receiving/transmitting 2*len samples, checking len-2 for correctness. + // The output is checked by the Tx and Rx Output Checkers below. We have to + // be a little bit careful when we enable output checking, because it takes a + // few clock cycles for data to propagate through, and we don't want to check + // the outputs when the outputs are not valid. + task TestBurst; + input [31:0] len; + input do_mimo; + begin + if (len <= 2) begin + $display("ERROR @%0t in %m: In TestBurst, len must be > 2", $time); + $finish; + end + + // Input several bursts, to fill the pipeline and cause results on the + // outputs before we start checking. + Burst(1, do_mimo); + + // Enable output checking + check_enabled <= 1'b1; + + // Do the requested length, minus 1 + Burst(len-2, do_mimo); + + // Disable output checking + check_enabled <= 1'b0; + + // Give an extra output to allow data to propagate to the output + Burst(1, do_mimo); + end + endtask // TestBurst + + + //--------------------------------------------------------------------------- + // Test Procedure + //--------------------------------------------------------------------------- + + initial + begin + // Initial values + check_enabled <= 1'b0; + test_status <= "Reset"; + reset = 1; + mimo = 1; + ctrl_in_clk_delay = INPUT_CLOCK_DELAY; + ctrl_in_data_delay = INPUT_DATA_DELAY; + ctrl_ld_in_data_delay = 1'b0; + ctrl_ld_in_clk_delay = 1'b0; + ctrl_out_clk_delay = OUTPUT_CLOCK_DELAY; + ctrl_out_data_delay = OUTPUT_DATA_DELAY; + ctrl_ld_out_data_delay = 1'b0; + ctrl_ld_out_clk_delay = 1'b0; + repeat(10) @(negedge rx_clk); + reset = 0; + @(negedge rx_clk); + + //----------------------------------------------------------------------- + // Test Changing Delays + + test_status <= "Load IO delays"; + + if (CLOCK_IDELAY_MODE == "VAR_LOAD") begin + ctrl_ld_in_clk_delay = 1'b1; + @(negedge rx_clk); + ctrl_ld_in_clk_delay = 1'b0; + @(negedge rx_clk); + end + + if (DATA_IDELAY_MODE == "VAR_LOAD") begin + ctrl_ld_in_data_delay = 1'b1; + @(negedge rx_clk); + ctrl_ld_in_data_delay = 1'b0; + @(negedge rx_clk); + end + + if (CLOCK_ODELAY_MODE == "VAR_LOAD") begin + ctrl_ld_out_clk_delay = 1'b1; + @(negedge rx_clk); + ctrl_ld_out_clk_delay = 1'b0; + @(negedge rx_clk); + end + + if (DATA_ODELAY_MODE == "VAR_LOAD") begin + ctrl_ld_out_data_delay = 1'b1; + @(negedge rx_clk); + ctrl_ld_out_data_delay = 1'b0; + @(negedge rx_clk); + end + + //----------------------------------------------------------------------- + // Startup + + test_status <= "Startup"; + + // Pump a few clock cycles to get things started (flush out X values) + Burst(2,1); + + //----------------------------------------------------------------------- + // Test MIMO + + // Input data until the Rx circuit aligns + test_status <= "Wait align 1"; + while (!rx_aligned) begin + Burst(1,1); + end + + // Input some new samples + test_status <= "Burst 1 (MIMO)"; + TestBurst(30, 1); + + // Reset and do another burst + test_status <= "Reset 2"; + reset = 1; + repeat(20) @(negedge rx_clk); + reset = 0; + repeat(2) @(negedge rx_clk); + + // Input data until the Rx circuit aligns + test_status <= "Wait align 2"; + while (!rx_aligned) begin + Burst(1,1); + end + + // Input some new samples + test_status <= "Burst 2 (MIMO)"; + TestBurst(23, 1); + + //----------------------------------------------------------------------- + // Test SISO (transmit channel 0) + + tx_ch <= 1'b0; + + // Reset and do another burst + test_status <= "Reset 3"; + reset = 1; + repeat(20) @(negedge rx_clk); + reset = 0; + repeat(2) @(negedge rx_clk); + + // Input data until the Rx circuit aligns in SISO mode + test_status <= "Wait align 3"; + while (!rx_aligned) begin + Burst(1,0); + end + + // Test SISO mode + test_status <= "Burst 3 (SISO, Ch 0)"; + TestBurst(25, 0); + + // Reset and do another burst + test_status <= "Reset 4"; + reset = 1; + repeat(20) @(negedge rx_clk); + reset = 0; + repeat(2) @(negedge rx_clk); + + // Input data until the Rx circuit aligns in SISO mode + test_status <= "Wait align 4"; + while (!rx_aligned) begin + Burst(1,0); + end + + // Test SISO mode + test_status <= "Burst 4 (SISO, Ch 0)"; + TestBurst(27, 0); + + //----------------------------------------------------------------------- + // Test SISO (transmit channel 1) + + tx_ch <= 1'b1; + + // Reset and do another burst + test_status <= "Reset 5"; + reset = 1; + repeat(20) @(negedge rx_clk); + reset = 0; + repeat(2) @(negedge rx_clk); + + // Input data until the Rx circuit aligns in SISO mode + test_status <= "Wait align 5"; + while (!rx_aligned) begin + Burst(1,0); + end + + // Test SISO mode + test_status <= "Burst 5 (SISO, Ch 1)"; + TestBurst(25, 0); + + // Reset and do another burst + test_status <= "Reset 6"; + reset = 1; + repeat(20) @(negedge rx_clk); + reset = 0; + repeat(2) @(negedge rx_clk); + + // Input data until the Rx circuit aligns in SISO mode + test_status <= "Wait align 6"; + while (!rx_aligned) begin + Burst(1,0); + end + + // Test SISO mode + test_status <= "Burst 6 (SISO, Ch 1)"; + TestBurst(27, 0); + + //----------------------------------------------------------------------- + // Done + + test_status <= "Finished"; + repeat(50) @(negedge rx_clk); + + $finish; + end + + + //--------------------------------------------------------------------------- + // Rx Output Checker + //--------------------------------------------------------------------------- + // + // In MIMO mode, we expect to see: + // + // rx_i0: A00, A01, A02, A03, ... + // rx_q0: B00, B01, B02, B03, ... + // rx_i1: C00, C01, C02, C03, ... + // rx_q1: D00, D01, D02, D03, ... + // + // In SISO mode, we expect to see (with twice the clock rate): + // + // rx_i0: A00, C00, A01, C01, ... + // rx_q0: B00, D00, B01, D01, ... + // rx_i1: A00, C00, A01, C01, ... + // rx_q1: B00, D00, B01, D01, ... + // + //--------------------------------------------------------------------------- + + reg first_rx_check = 1'b1; + reg [11:0] rx_i0_del1, rx_i0_del2; + reg [11:0] rx_q0_del1, rx_q0_del2; + reg [11:0] rx_i1_del1, rx_i1_del2; + reg [11:0] rx_q1_del1, rx_q1_del2; + + always @(posedge radio_clk) + begin + if (check_enabled) begin + if (!first_rx_check) begin + + if (mimo) begin + + // Check prefix for channel 0 + if (rx_i0[11:8] != 4'hA || rx_q0[11:8] != 4'hB) begin + $display("ERROR @%0t in %m: Rx channel 0 didn't have expected A/B prefix in MIMO mode", $time); + $finish; + end + + // Check prefix for channel 1 + if (rx_i1[11:8] != 4'hC || rx_q1[11:8] != 4'hD) begin + $display("ERROR @%0t in %m: Rx channel 1 didn't have expected C/D in MIMO mode", $time); + $finish; + end + + // All outputs should have the same count in MIMO mode + if (! (rx_i0[7:0] == rx_q0[7:0] && + rx_i0[7:0] == rx_i1[7:0] && + rx_i0[7:0] == rx_q1[7:0]) ) begin + $display("ERROR @%0t in %m: Rx data counts didn't match on all outputs in MIMO mode", $time); + $finish; + end + + // Make sure the count increments + if (rx_i0[7:0] != rx_i0_del1[7:0] + 8'd1 || rx_q0[7:0] != rx_q0_del1[7:0] + 8'd1 || + rx_i1[7:0] != rx_i1_del1[7:0] + 8'd1 || rx_q1[7:0] != rx_q1_del1[7:0] + 8'd1) begin + $display("ERROR @%0t in %m: Rx data count didn't increment as expected", $time); + $finish; + end + + end else begin // if (mimo) + + // In SISO mode, both outputs should be the same + if (rx_i0 != rx_i1 || rx_q0 != rx_q1) begin + $display("ERROR @%0t in %m: Rx channel 0 and 1 don't match in SISO mode", $time); + $finish; + end + + // Check channel 0 prefix. No need to check channel 1, since we + // already checked that the channels match. + if (!((rx_i0[11:8] == 4'hA && rx_q0[11:8] == 4'hB) || + (rx_i0[11:8] == 4'hC && rx_q0[11:8] == 4'hD))) begin + $display("ERROR @%0t in %m: Rx data didn't have expected A/B or C/D prefix in SISO mode", $time); + $finish; + end + + // Make sure we're alternating between channel data. No need to check + // channel 1, since we already checked that the channels match. + if (!((rx_i0[11:8] == 4'hA && rx_i0_del1[11:8] == 4'hC) || + (rx_i0[11:8] == 4'hC && rx_i0_del1[11:8] == 4'hA) || + (rx_q0[11:8] == 4'hB && rx_q0_del1[11:8] == 4'hD) || + (rx_q0[11:8] == 4'hD && rx_q0_del1[11:8] == 4'hB))) begin + $display("ERROR @%0t in %m: Rx data not toggling between channel data in SISO mode", $time); + $finish; + end + + // Make sure the counts are the same for both I and Q. No need to + // check channel 1, since we already checked that the channels match. + if (rx_i0[7:0] != rx_q0[7:0]) begin + $display("ERROR @%0t in %m: Rx data counts didn't match on all outputs in SISO mode", $time); + $finish; + end + + // Make sure the count increments every other clock cycle. No need to + // check channel 1, since we already checked that the channels match. + if (!( + rx_i0[7:0] != rx_i0_del2[7:0] + 8'd1 && (rx_i0[7:0] == rx_i0_del1[7:0] || rx_i0[7:0] == rx_i0_del1[7:0] + 8'd1) && + rx_q0[7:0] != rx_q0_del2[7:0] + 8'd1 && (rx_q0[7:0] == rx_q0_del1[7:0] || rx_q0[7:0] == rx_q0_del1[7:0] + 8'd1) + )) begin + $display("ERROR @%0t in %m: Rx data count didn't increment as expected", $time); + $finish; + end + + end // if (mimo) + end // if (!first_rx_check) + + // Make sure we've captured at least one set of values, so we have a + // previous set to look back to. + first_rx_check <= 1'b0; + + end else begin // if (check_enabled) + first_rx_check <= 1'b1; + end // if (check_enabled) + + // Save values seen this cycle + rx_i0_del1 <= rx_i0; + rx_q0_del1 <= rx_q0; + rx_i1_del1 <= rx_i1; + rx_q1_del1 <= rx_q1; + rx_i0_del2 <= rx_i0_del2; + rx_q0_del2 <= rx_q0_del2; + rx_i1_del2 <= rx_i1_del2; + rx_q1_del2 <= rx_q1_del2; + end + + + //--------------------------------------------------------------------------- + // Tx Output Checker + //--------------------------------------------------------------------------- + // + // The code implements a loopback, so the output should match the input. In + // SISO mode, however, the frame signal may not be aligned. + // + //--------------------------------------------------------------------------- + + reg first_tx_check; + reg [11:0] tx_i0_del1; + reg [11:0] tx_q0_del1; + reg [11:0] tx_i1_del1; + reg [11:0] tx_q1_del1; + reg tx_frame_del1; + + reg [11:0] tx_i0_check; + reg [11:0] tx_q0_check; + reg [11:0] tx_i1_check; + reg [11:0] tx_q1_check; + reg [7:0] tx_frame_check; + + + always @(posedge tx_clk_p) + begin + tx_frame_del1 <= tx_frame_p; + end + + + always @(posedge tx_clk_p) + begin + if (tx_frame_p && !tx_frame_del1) begin + //----------------------------------------------------------------------- + // Grab two samples from the output, starting at frame boundary + //----------------------------------------------------------------------- + + // Channel 0 sample + tx_i0_check[11:6] <= tx_d_p; + tx_frame_check[7] <= tx_frame_p; + @(posedge tx_clk_n); + tx_q0_check[11:6] <= tx_d_p; + tx_frame_check[6] <= tx_frame_p; + @(posedge tx_clk_p); + tx_i0_check[5:0] <= tx_d_p; + tx_frame_check[5] <= tx_frame_p; + @(posedge tx_clk_n); + tx_q0_check[5:0] <= tx_d_p; + tx_frame_check[4] <= tx_frame_p; + + // Channel 1 sample / Second channel 0 sample + @(posedge tx_clk_p); + tx_i1_check[11:6] <= tx_d_p; + tx_frame_check[3] <= tx_frame_p; + @(posedge tx_clk_n); + tx_q1_check[11:6] <= tx_d_p; + tx_frame_check[2] <= tx_frame_p; + @(posedge tx_clk_p); + tx_i1_check[5:0] <= tx_d_p; + tx_frame_check[1] <= tx_frame_p; + @(posedge tx_clk_n); + tx_q1_check[5:0] <= tx_d_p; + tx_frame_check[0] <= tx_frame_p; + + #1 // Minimum delay for *_check registers to update in simulation + + if (check_enabled) begin + if (!first_tx_check) begin + + if (mimo) begin + //----------------------------------------------------------------- + // Check MIMO output + //----------------------------------------------------------------- + + // Check that the frame signal is correct + if (tx_frame_check != 8'b11110000) begin + $display("ERROR @%0t in %m: Tx frame was not correct in MIMO mode", $time); + $finish; + end + + // Check prefix for channel 0 + if (tx_i0_check[11:8] != 4'hA || tx_q0_check[11:8] != 4'hB) begin + $display("ERROR @%0t in %m: Tx channel 0 didn't have expected A/B prefix in MIMO mode", $time); + $finish; + end + + // Check prefix for channel 1 + if (tx_i1_check[11:8] != 4'hC || tx_q1_check[11:8] != 4'hD) begin + $display("ERROR @%0t in %m: Tx channel 1 didn't have expected C/D in MIMO mode", $time); + $finish; + end + + // All outputs should have the same count in MIMO mode + if (! (tx_i0_check[7:0] == tx_q0_check[7:0] && + tx_i0_check[7:0] == tx_i1_check[7:0] && + tx_i0_check[7:0] == tx_q1_check[7:0]) ) begin + $display("ERROR @%0t in %m: Rx data counts didn't match on all outputs in MIMO mode", $time); + $finish; + end + + // Make sure the count increments + if (tx_i0_check[7:0] != tx_i0_del1[7:0] + 8'd1 || tx_q0_check[7:0] != tx_q0_del1[7:0] + 8'd1 || + tx_i1_check[7:0] != tx_i1_del1[7:0] + 8'd1 || tx_q1_check[7:0] != tx_q1_del1[7:0] + 8'd1) begin + $display("ERROR @%0t in %m: Rx data count didn't increment as expected", $time); + $finish; + end + + end else begin + //----------------------------------------------------------------- + // Check SISO Output + //----------------------------------------------------------------- + + // Check that the frame signal is correct + if (tx_frame_check != 8'b11001100) begin + $display("ERROR @%0t in %m: Tx frame was not correct in SISO mode", $time); + $finish; + end + + + // In SISO mode, the data we get depends on which channel is + // selected. + // + // Channel 0: Channel 1: + // ...,A01,B01,A02,B02,... OR ...,C01,D01,C02,D02,... + // + // So we should receive + // + // A01 A03 A05 + // ... B01 B03 B05 ... + // A02 B04 A06 + // B02 B04 A07 + // + // or + // C01 C03 C05 + // ... D01 D03 D05 ... + // C02 C04 C06 + // D02 D04 D07 + // + + // Check prefixes + if (!( + // Either A,B on channel 0 or C,D on channel 1 + ((tx_ch == 0 && + tx_i0_check[11:8] == 4'hA && + tx_q0_check[11:8] == 4'hB) || + (tx_ch == 1 && + tx_i0_check[11:8] == 4'hC && + tx_q0_check[11:8] == 4'hD)) && + // Samples 0 and 1 prefixes equal samples 2 and 3 prefixes + (tx_i0_check[11:8] == tx_i1_check[11:8] && + tx_q0_check[11:8] == tx_q1_check[11:8]) + )) begin + $display("ERROR @%0t in %m: Tx channel didn't have expected prefixes in SISO mode", $time); + $finish; + end + + // Check that the data count matches between samples + if (!( + tx_i0_check[7:0] == tx_q0_check[7:0] && + tx_i1_check[7:0] == tx_q1_check[7:0] && + tx_i0_check[7:0] == tx_i1_check[7:0] - 8'd1 + )) begin + $display("ERROR @%0t in %m: Tx channel data counts didn't correlate in SISO mode", $time); + $finish; + end + + // Make sure the count increments form one burst to the next + if (tx_i0_check[7:0] != tx_i0_del1[7:0] + 8'd2 || + tx_q0_check[7:0] != tx_q0_del1[7:0] + 8'd2 || + tx_i1_check[7:0] != tx_i1_del1[7:0] + 8'd2 || + tx_q1_check[7:0] != tx_q1_del1[7:0] + 8'd2) begin + $display("ERROR @%0t in %m: Tx data count didn't increment as expected", $time); + $finish; + end + + end + + end else begin // if (!first_tx_check) + // Make sure we've captured at least one set of values, so we have a + // previous set to look back to. + first_tx_check <= 1'b0; + end // if (!first_tx_check) + + // Save values seen this cycle + tx_i0_del1 <= tx_i0_check; + tx_q0_del1 <= tx_q0_check; + tx_i1_del1 <= tx_i1_check; + tx_q1_del1 <= tx_q1_check; + + end else begin // if (check_enabled) + first_tx_check <= 1'b1; + + end // if (check_enabled) + + end // if (tx_frame_p && !tx_frame_del1) + + end + + + //--------------------------------------------------------------------------- + // Tx Input Data Generation + //--------------------------------------------------------------------------- + // + // Input a known data pattern similar to the Rx patten. + // + // I0: A01 A02 A03 + // Q0: ... B01 B02 B03 ... + // I1: C01 C02 C03 + // Q1: D01 D02 D03 + // + //--------------------------------------------------------------------------- + + reg [7:0] tx_count = 0; + + // Loop the Rx interface of DUT back to its Tx interface + always @(posedge radio_clk) begin + tx_i0 <= { 4'hA, tx_count }; + tx_q0 <= { 4'hB, tx_count }; + tx_i1 <= { 4'hC, tx_count }; + tx_q1 <= { 4'hD, tx_count }; + tx_count <= tx_count + 7'd1; + end + + + //--------------------------------------------------------------------------- + // DUT + //--------------------------------------------------------------------------- + + cat_io_lvds_dual_mode #( + .INVERT_FRAME_RX (0), + .INVERT_DATA_RX (6'b00_0000), + .INVERT_FRAME_TX (0), + .INVERT_DATA_TX (6'b00_0000), + .USE_CLOCK_IDELAY (USE_CLOCK_IDELAY), + .USE_DATA_IDELAY (USE_DATA_IDELAY), + .DATA_IDELAY_MODE (DATA_IDELAY_MODE), + .CLOCK_IDELAY_MODE (CLOCK_IDELAY_MODE), + .INPUT_CLOCK_DELAY (INPUT_CLOCK_DELAY), + .INPUT_DATA_DELAY (INPUT_DATA_DELAY), + .USE_CLOCK_ODELAY (USE_CLOCK_ODELAY), + .USE_DATA_ODELAY (USE_DATA_ODELAY), + .DATA_ODELAY_MODE (DATA_ODELAY_MODE), + .CLOCK_ODELAY_MODE (CLOCK_ODELAY_MODE), + .OUTPUT_CLOCK_DELAY (OUTPUT_CLOCK_DELAY), + .OUTPUT_DATA_DELAY (OUTPUT_DATA_DELAY) + ) cat_io_lvds_dual_mode_dut ( + .rst (reset), + .clk200 (clk200), + + // Data and frame timing + .a_mimo (mimo), + .a_tx_ch (tx_ch), + + // Delay control interface + .ctrl_clk (rx_clk), + // + .ctrl_in_data_delay (ctrl_in_data_delay), + .ctrl_in_clk_delay (ctrl_in_clk_delay), + .ctrl_ld_in_data_delay (ctrl_ld_in_data_delay), + .ctrl_ld_in_clk_delay (ctrl_ld_in_clk_delay), + // + .ctrl_out_data_delay (ctrl_out_data_delay), + .ctrl_out_clk_delay (ctrl_out_clk_delay), + .ctrl_ld_out_data_delay (ctrl_ld_out_data_delay), + .ctrl_ld_out_clk_delay (ctrl_ld_out_clk_delay), + + // Baseband sample interface + .radio_clk (radio_clk), + .rx_aligned (rx_aligned), + // + .rx_i0 (rx_i0), + .rx_q0 (rx_q0), + .rx_i1 (rx_i1), + .rx_q1 (rx_q1), + // + .tx_i0 (tx_i0), + .tx_q0 (tx_q0), + .tx_i1 (tx_i1), + .tx_q1 (tx_q1), + + // Catalina interface + .rx_clk_p (rx_clk), + .rx_clk_n (~rx_clk), + .rx_frame_p (rx_frame), + .rx_frame_n (~rx_frame), + .rx_d_p (rx_d), + .rx_d_n (~rx_d), + // + .tx_clk_p (tx_clk_p), + .tx_clk_n (tx_clk_n), + .tx_frame_p (tx_frame_p), + .tx_frame_n (tx_frame_n), + .tx_d_p (tx_d_p), + .tx_d_n (tx_d_n) + ); + +endmodule // cat_io_lvds_dual_mode_tb diff --git a/fpga/usrp3/lib/sim/io_cap_gen/cat_io_lvds/cat_io_lvds_tb.v b/fpga/usrp3/lib/sim/io_cap_gen/cat_io_lvds/cat_io_lvds_tb.v new file mode 100644 index 000000000..91cd6f938 --- /dev/null +++ b/fpga/usrp3/lib/sim/io_cap_gen/cat_io_lvds/cat_io_lvds_tb.v @@ -0,0 +1,350 @@ +// +// Copyright 2016 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: cat_io_lvds_tb +// +// Description: Testbench for cat_io_lvds. +// + +`timescale 1ns/1ps + +module cat_io_lvds_tb(); + + localparam CLK_PERIOD = 10; + localparam CLK200_PERIOD = 2.5; + + localparam FRAME_SAMPLE = 0; + + localparam USE_CLOCK_IDELAY = 1; + localparam USE_DATA_IDELAY = 1; + localparam DATA_IDELAY_MODE = "VAR_LOAD"; + localparam CLOCK_IDELAY_MODE = "VAR_LOAD"; + localparam INPUT_CLOCK_DELAY = 0; + localparam INPUT_DATA_DELAY = 0; + localparam USE_CLOCK_ODELAY = 1; + localparam USE_DATA_ODELAY = 1; + localparam DATA_ODELAY_MODE = "VAR_LOAD"; + localparam CLOCK_ODELAY_MODE = "VAR_LOAD"; + localparam OUTPUT_CLOCK_DELAY = 0; + localparam OUTPUT_DATA_DELAY = 0; + + reg [8*19:0] test_status; + + reg clk = 0; + reg rx_clk = 0; + reg clk200 = 0; + + reg reset; + reg mimo; + reg [5:0] rx_d; + reg rx_frame; + reg [7:0] count; + +// initial $dumpfile("catcap_ddr_lvds_tb.vcd"); +// initial $dumpvars(0,catcap_ddr_lvds_tb); + + wire [11:0] i0 = {4'hA,count}; + wire [11:0] q0 = {4'hB,count}; + wire [11:0] i1 = {4'hC,count}; + wire [11:0] q1 = {4'hD,count}; + + wire radio_clk; + + reg [11:0] tx_i0; + reg [11:0] tx_q0; + reg [11:0] tx_i1; + reg [11:0] tx_q1; + + wire [11:0] rx_i0; + wire [11:0] rx_q0; + wire [11:0] rx_i1; + wire [11:0] rx_q1; + + wire rx_aligned; + + wire tx_clk_p, tx_clk_n; + wire tx_frame_p, tx_frame_n; + wire [5:0] tx_d_p, tx_d_n; + + reg [4:0] ctrl_in_data_delay; + reg [4:0] ctrl_in_clk_delay; + reg ctrl_ld_in_data_delay; + reg ctrl_ld_in_clk_delay; + + reg [4:0] ctrl_out_data_delay; + reg [4:0] ctrl_out_clk_delay; + reg ctrl_ld_out_data_delay; + reg ctrl_ld_out_clk_delay; + + + //--------------------------------------------------------------------------- + // Clock Generation + //--------------------------------------------------------------------------- + + // IODELAYCTRL reference clock + always #(CLK200_PERIOD) clk200 = ~clk200; + + // Create an internal clock we'll use to drive the data + always #(CLK_PERIOD) clk = ~clk; + + // RF interface clock. Half the rate of clk and out of phase + always @(negedge clk) rx_clk <= ~rx_clk; + + + //--------------------------------------------------------------------------- + // Tasks + //--------------------------------------------------------------------------- + + task BURST; + input [31:0] len; + input do_mimo; + begin + count <= 0; + repeat(len) + begin + mimo <= do_mimo; + + // Channel 0 sample + @(posedge clk); + rx_d <= i0[11:6]; + rx_frame <= 1; + @(posedge clk); + rx_d <= q0[11:6]; + rx_frame <= 1; + @(posedge clk); + rx_d <= i0[5:0]; + rx_frame <= ~FRAME_SAMPLE; + @(posedge clk); + rx_d <= q0[5:0]; + rx_frame <= ~FRAME_SAMPLE; + + if (do_mimo) begin + // Channel 1 sample + @(posedge clk); + rx_d <= i1[11:6]; + rx_frame <= FRAME_SAMPLE; + @(posedge clk); + rx_d <= q1[11:6]; + rx_frame <= FRAME_SAMPLE; + @(posedge clk); + rx_d <= i1[5:0]; + rx_frame <= 0; + @(posedge clk); + rx_d <= q1[5:0]; + rx_frame <= 0; + end else begin + if (!FRAME_SAMPLE) begin + // When we frame every two samples (one from each channel), in + // MIMO mode, we should only grab channel 0. So input garbage on + // channel 1 to make sure the data doesn't get used. + @(posedge clk); + rx_d <= 6'bXXXXXX; + rx_frame <= FRAME_SAMPLE; + @(posedge clk); + rx_d <= 6'bXXXXXX; + rx_frame <= FRAME_SAMPLE; + @(posedge clk); + rx_d <= 6'bXXXXXX; + rx_frame <= 0; + @(posedge clk); + rx_d <= 6'bXXXXXX; + rx_frame <= 0; + end else begin + // When every sample is framed, we might sync align to either + // channel (no way to tell them apart), so input the channel 1 + // data, but we should only see one channel's data duplicated on + // both outputs. + @(posedge clk); + rx_d <= i1[11:6]; + rx_frame <= FRAME_SAMPLE; + @(posedge clk); + rx_d <= q1[11:6]; + rx_frame <= FRAME_SAMPLE; + @(posedge clk); + rx_d <= i1[5:0]; + rx_frame <= 0; + @(posedge clk); + rx_d <= q1[5:0]; + rx_frame <= 0; + end + end + + count <= count + 1; + end + end + endtask // BURST + + + //--------------------------------------------------------------------------- + // Test Procedure + //--------------------------------------------------------------------------- + + initial + begin + // Initial values + test_status <= "Reset"; + reset = 1; + mimo = 1; + ctrl_in_data_delay = 5'd0; + ctrl_in_clk_delay = 5'd8; + ctrl_ld_in_data_delay = 1'b0; + ctrl_ld_in_clk_delay = 1'b0; + ctrl_out_data_delay = 5'd0; + ctrl_out_clk_delay = 5'd16; + ctrl_ld_out_data_delay = 1'b0; + ctrl_ld_out_clk_delay = 1'b0; + repeat(10) @(negedge rx_clk); + reset = 0; + @(negedge rx_clk); + + // Load new input delay values + test_status <= "Load input delays"; + ctrl_ld_in_data_delay = 1'b1; + ctrl_ld_in_clk_delay = 1'b1; + @(negedge rx_clk); + ctrl_ld_in_data_delay = 1'b0; + ctrl_ld_in_clk_delay = 1'b0; + + // Load new output delay values + test_status <= "Load output delays"; + ctrl_ld_out_data_delay = 1'b1; + ctrl_ld_out_clk_delay = 1'b1; + @(negedge rx_clk); + ctrl_ld_out_data_delay = 1'b0; + ctrl_ld_out_clk_delay = 1'b0; + + // Input data until the Rx circuit aligns + test_status <= "Wait align"; + while (!rx_aligned) begin + BURST(1,1); + end + + // Input some new samples + test_status <= "Burst 1 (MIMO)"; + BURST(30, 1); + + // Reset and do another burst + test_status <= "Reset 2"; + reset = 1; + repeat(20) @(negedge rx_clk); + reset = 0; + repeat(2) @(negedge rx_clk); + + // Input data until the Rx circuit aligns + test_status <= "Wait align 2"; + while (!rx_aligned) begin + BURST(1,1); + end + + // Input some new samples + test_status <= "Burst 2 (MIMO)"; + BURST(30, 1); + + // Reset and do another burst + test_status <= "Reset 3"; + reset = 1; + repeat(20) @(negedge rx_clk); + reset = 0; + repeat(2) @(negedge rx_clk); + + // Input data until the Rx circuit aligns in SISO mode + test_status <= "Wait align 3"; + while (!rx_aligned) begin + BURST(1,0); + end + + // Switch to SISO mode + test_status <= "Burst 3 (SISO)"; + BURST(25,0); + + repeat(50) @(negedge rx_clk); + + + $finish; + end + + + //--------------------------------------------------------------------------- + // DUT + //--------------------------------------------------------------------------- + + // Loop the Rx interface of cat_io_lvds back to its Tx interface + always @(posedge radio_clk) begin + tx_i0 = rx_i0; + tx_q0 = rx_q0; + tx_i1 = rx_i1; + tx_q1 = rx_q1; + end + + cat_io_lvds #( + .INVERT_FRAME_RX (0), + .INVERT_DATA_RX (6'b00_0000), + .INVERT_FRAME_TX (0), + .INVERT_DATA_TX (6'b00_0000), + .USE_CLOCK_IDELAY (USE_CLOCK_IDELAY ), + .USE_DATA_IDELAY (USE_DATA_IDELAY ), + .DATA_IDELAY_MODE (DATA_IDELAY_MODE ), + .CLOCK_IDELAY_MODE (CLOCK_IDELAY_MODE ), + .INPUT_CLOCK_DELAY (INPUT_CLOCK_DELAY ), + .INPUT_DATA_DELAY (INPUT_DATA_DELAY ), + .USE_CLOCK_ODELAY (USE_CLOCK_ODELAY ), + .USE_DATA_ODELAY (USE_DATA_ODELAY ), + .DATA_ODELAY_MODE (DATA_ODELAY_MODE ), + .CLOCK_ODELAY_MODE (CLOCK_ODELAY_MODE ), + .OUTPUT_CLOCK_DELAY (OUTPUT_CLOCK_DELAY), + .OUTPUT_DATA_DELAY (OUTPUT_DATA_DELAY ) + ) cat_io_lvds_i0 ( + .rst (reset), + .clk200 (clk200), + + // Data and frame timing + .mimo (mimo), + .frame_sample (FRAME_SAMPLE[0]), + + // Delay control interface + .ctrl_clk (rx_clk), + // + .ctrl_in_data_delay (ctrl_in_data_delay), + .ctrl_in_clk_delay (ctrl_in_clk_delay), + .ctrl_ld_in_data_delay (ctrl_ld_in_data_delay), + .ctrl_ld_in_clk_delay (ctrl_ld_in_clk_delay), + // + .ctrl_out_data_delay (ctrl_out_data_delay), + .ctrl_out_clk_delay (ctrl_out_clk_delay), + .ctrl_ld_out_data_delay (ctrl_ld_out_data_delay), + .ctrl_ld_out_clk_delay (ctrl_ld_out_clk_delay), + + // Baseband sample interface + .radio_clk (radio_clk), + .radio_clk_2x (), + .rx_aligned (rx_aligned), + // + .rx_i0 (rx_i0), + .rx_q0 (rx_q0), + .rx_i1 (rx_i1), + .rx_q1 (rx_q1), + // + .tx_i0 (tx_i0), + .tx_q0 (tx_q0), + .tx_i1 (tx_i1), + .tx_q1 (tx_q1), + + // Catalina interface + .rx_clk_p (rx_clk), + .rx_clk_n (~rx_clk), + .rx_frame_p (rx_frame), + .rx_frame_n (~rx_frame), + .rx_d_p (rx_d), + .rx_d_n (~rx_d), + // + .tx_clk_p (tx_clk_p), + .tx_clk_n (tx_clk_n), + .tx_frame_p (tx_frame_p), + .tx_frame_n (tx_frame_n), + .tx_d_p (tx_d_p), + .tx_d_n (tx_d_n) + ); + +endmodule // cat_io_lvds_tb diff --git a/fpga/usrp3/lib/sim/io_cap_gen/catcap/catcap_tb.build b/fpga/usrp3/lib/sim/io_cap_gen/catcap/catcap_tb.build new file mode 100755 index 000000000..827ab0628 --- /dev/null +++ b/fpga/usrp3/lib/sim/io_cap_gen/catcap/catcap_tb.build @@ -0,0 +1,21 @@ + +#!/bin/sh + +rm -rf isim* +rm -rf catcap_tb +rm -rf fuse* +\ +# --sourcelibdir ../../models \ + +vlogcomp \ + --sourcelibext .v \ + --sourcelibdir ../../coregen \ + --sourcelibdir ../../control_lib \ + --sourcelibdir . \ + --sourcelibdir $XILINX/verilog/src \ + --sourcelibdir $XILINX/verilog/src/unisims \ + --work work \ + catcap_tb.v + + +fuse -o catcap_tb catcap_tb
\ No newline at end of file diff --git a/fpga/usrp3/lib/sim/io_cap_gen/catcap/catcap_tb.v b/fpga/usrp3/lib/sim/io_cap_gen/catcap/catcap_tb.v new file mode 100644 index 000000000..f300cc32f --- /dev/null +++ b/fpga/usrp3/lib/sim/io_cap_gen/catcap/catcap_tb.v @@ -0,0 +1,115 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +`timescale 1ns/1ps + +module catcap_tb(); + + wire GSR, GTS; + glbl glbl( ); + + reg clk = 0; + reg ddrclk = 0; + reg reset = 1; + + always #100 clk = ~clk; + always @(negedge clk) ddrclk <= ~ddrclk; + + initial $dumpfile("catcap_tb.vcd"); + initial $dumpvars(0,catcap_tb); + + wire [11:0] i0 = {4'hA,count}; + wire [11:0] q0 = {4'hB,count}; + wire [11:0] i1 = {4'hC,count}; + wire [11:0] q1 = {4'hD,count}; + + reg mimo; + reg [11:0] pins; + reg frame; + reg [7:0] count; + + initial + begin + #1000 reset = 0; + MIMO_BURST(4); + MIMO_BURST(5); + BURST(4); + BURST(5); + #2000; + $finish; + end + + task BURST; + input [7:0] len; + begin + frame <= 0; + @(posedge clk); + @(posedge clk); + mimo <= 0; + @(posedge clk); + @(posedge clk); + @(posedge clk); + @(posedge ddrclk); + count <= 0; + repeat(len) + begin + @(posedge clk); + pins <= i0; + frame <= 1; + @(posedge clk); + pins <= q0; + frame <= 0; + count <= count + 1; + end + end + endtask // BURST + + task MIMO_BURST; + input [7:0] len; + begin + frame <= 0; + @(posedge clk); + @(posedge clk); + mimo <= 1; + @(posedge clk); + @(posedge clk); + @(posedge clk); + @(posedge ddrclk); + count <= 0; + repeat(len) + begin + @(posedge clk); + pins <= i0; + frame <= 1; + @(posedge clk); + pins <= q0; + @(posedge clk); + pins <= i1; + frame <= 0; + @(posedge clk); + pins <= q1; + count <= count + 1; + end + @(posedge clk); + @(posedge clk); + end + endtask // MIMO_BURST + + wire rx_clk, rx_strobe; + wire [11:0] i0o,i1o,q0o,q1o; + + catcap_ddr_cmos catcap + (.data_clk(ddrclk), + .reset(reset), + .mimo(mimo), + .rx_frame(frame), + .rx_d(pins), + .rx_clk(rx_clk), + .rx_strobe(rx_strobe), + .i0(i0o),.q0(q0o), + .i1(i1o),.q1(q1o)); + +endmodule // hb_chain_tb diff --git a/fpga/usrp3/lib/sim/io_cap_gen/catgen/catgen_tb.build b/fpga/usrp3/lib/sim/io_cap_gen/catgen/catgen_tb.build new file mode 100755 index 000000000..072495479 --- /dev/null +++ b/fpga/usrp3/lib/sim/io_cap_gen/catgen/catgen_tb.build @@ -0,0 +1,21 @@ + +#!/bin/sh + +rm -rf isim* +rm -rf catgen_tb +rm -rf fuse* +\ +# --sourcelibdir ../../models \ + +vlogcomp \ + --sourcelibext .v \ + --sourcelibdir ../../coregen \ + --sourcelibdir ../../control_lib \ + --sourcelibdir . \ + --sourcelibdir $XILINX/verilog/src \ + --sourcelibdir $XILINX/verilog/src/unisims \ + --work work \ + catgen_tb.v + + +fuse -o catgen_tb catgen_tb
\ No newline at end of file diff --git a/fpga/usrp3/lib/sim/io_cap_gen/catgen/catgen_tb.v b/fpga/usrp3/lib/sim/io_cap_gen/catgen/catgen_tb.v new file mode 100644 index 000000000..e55f2de32 --- /dev/null +++ b/fpga/usrp3/lib/sim/io_cap_gen/catgen/catgen_tb.v @@ -0,0 +1,103 @@ +// +// Copyright 2014 Ettus Research LLC +// Copyright 2018 Ettus Research, a National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +`timescale 1ns/1ps + +module catgen_tb(); + + wire GSR, GTS; + glbl glbl( ); + + reg clk = 0; + reg reset = 1; + wire ddrclk; + + always #100 clk = ~clk; + + initial $dumpfile("catgen_tb.vcd"); + initial $dumpvars(0,catgen_tb); + + wire [11:0] pins; + wire frame; + + reg mimo; + reg [7:0] count; + reg tx_strobe; + + wire [11:0] i0 = {4'hA,count}; + wire [11:0] q0 = {4'hB,count}; + wire [11:0] i1 = {4'hC,count}; + wire [11:0] q1 = {4'hD,count}; + + initial + begin + #1000 reset = 0; + BURST(4); + BURST(5); + MIMO_BURST(4); + MIMO_BURST(5); + #2000; + $finish; + end + + task BURST; + input [7:0] len; + + begin + tx_strobe <= 0; + mimo <= 0; + count <= 0; + @(posedge clk); + @(posedge clk); + repeat(len) + begin + tx_strobe <= 1; + @(posedge clk); + count <= count + 1; + end + tx_strobe <= 0; + @(posedge clk); + @(posedge clk); + @(posedge clk); + end + endtask // BURST + + task MIMO_BURST; + input [7:0] len; + + begin + tx_strobe <= 0; + mimo <= 1; + count <= 0; + @(posedge clk); + @(posedge clk); + repeat(len) + begin + tx_strobe <= 1; + @(posedge clk); + tx_strobe <= 0; + @(posedge clk); + count <= count + 1; + end + tx_strobe <= 0; + @(posedge clk); + @(posedge clk); + @(posedge clk); + end + endtask // BURST + + catgen_ddr_cmos catgen + (.data_clk(ddrclk), + .reset(reset), + .mimo(mimo), + .tx_frame(frame), + .tx_d(pins), + .tx_clk(clk), + .tx_strobe(tx_strobe), + .i0(i0),.q0(q0), + .i1(i1),.q1(q1)); + +endmodule // hb_chain_tb |