aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/top/e31x/sim
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/top/e31x/sim')
-rw-r--r--fpga/usrp3/top/e31x/sim/dram_test/Makefile65
-rw-r--r--fpga/usrp3/top/e31x/sim/dram_test/dram_test_tb.sv121
-rw-r--r--fpga/usrp3/top/e31x/sim/e310_io_tb/Makefile40
-rw-r--r--fpga/usrp3/top/e31x/sim/e310_io_tb/e310_io_tb.sv230
-rwxr-xr-xfpga/usrp3/top/e31x/sim/e3x0/catcap_ddr_cmos/catcap_tb.build21
-rw-r--r--fpga/usrp3/top/e31x/sim/e3x0/catcap_ddr_cmos/catcap_tb.v114
-rwxr-xr-xfpga/usrp3/top/e31x/sim/e3x0/catgen_ddr_cmos/catgen_tb.build21
-rw-r--r--fpga/usrp3/top/e31x/sim/e3x0/catgen_ddr_cmos/catgen_tb.v102
8 files changed, 714 insertions, 0 deletions
diff --git a/fpga/usrp3/top/e31x/sim/dram_test/Makefile b/fpga/usrp3/top/e31x/sim/dram_test/Makefile
new file mode 100644
index 000000000..164f956f3
--- /dev/null
+++ b/fpga/usrp3/top/e31x/sim/dram_test/Makefile
@@ -0,0 +1,65 @@
+#
+# Copyright 2015 Ettus Research LLC
+#
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+# Define BASE_DIR to point to the "top" dir
+BASE_DIR = $(abspath ../../..)
+# Include viv_sim_preample after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+# Define part using PART_ID (<device>/<package>/<speedgrade>)
+ARCH = zynq
+PART_ID= xc7z020/clg484/-1
+
+# Include makefiles and sources for the DUT and its dependencies
+include $(BASE_DIR)/../lib/fifo/Makefile.srcs
+include $(BASE_DIR)/../lib/axi/Makefile.srcs
+include $(BASE_DIR)/../lib/control/Makefile.srcs
+
+DESIGN_SRCS = $(abspath \
+$(FIFO_SRCS) \
+$(AXI_SRCS) \
+$(CONTROL_LIB_SRCS) \
+)
+
+#-------------------------------------------------
+# IP Specific
+#-------------------------------------------------
+# If simulation contains IP, define the IP_DIR and point
+# it to the base level IP directory
+IP_DIR = ../../ip
+
+# Include makefiles and sources for all IP components
+# *after* defining the IP_DIR
+include $(IP_DIR)/mig_7series_0/Makefile.inc
+
+DESIGN_SRCS += $(abspath \
+$(IP_MIG_7SERIES_0_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+# Define only one toplevel module
+SIM_TOP = dram_test_tb
+
+SIM_SRCS = \
+$(abspath dram_test_tb.sv) \
+$(IP_MIG_7SERIES_0_SIM_OUTS) \
+$(IP_MIG_7SERIES_TG_SRCS) \
+$(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/top/e31x/sim/dram_test/dram_test_tb.sv b/fpga/usrp3/top/e31x/sim/dram_test/dram_test_tb.sv
new file mode 100644
index 000000000..e447c80a6
--- /dev/null
+++ b/fpga/usrp3/top/e31x/sim/dram_test/dram_test_tb.sv
@@ -0,0 +1,121 @@
+//
+// Copyright 2016 Ettus Research
+//
+
+
+`timescale 1ns/1ps
+`define SIM_TIMEOUT_US 10000
+`define NS_PER_TICK 1
+`define NUM_TEST_CASES 3
+
+`define SIM_RUNTIME_US 100
+
+`include "sim_clks_rsts.vh"
+`include "sim_exec_report.vh"
+
+module dram_test_tb();
+ `TEST_BENCH_INIT("dram_test_tb",`NUM_TEST_CASES,`NS_PER_TICK)
+
+ // Define all clocks and resets
+ `DEFINE_CLK(sys_clk, 10, 50) //100MHz sys_clk to generate DDR3 clocking
+ `DEFINE_CLK(ref_clk, 5, 50) //200MHz ref_clk to generate DDR3 clocking
+ `DEFINE_RESET(sys_rst, 0, 250000) //100ns for GSR to deassert
+
+ // Initialize DUT
+ wire calib_complete;
+
+ wire [15:0] ddr3_dq; // Data pins. Input for Reads; Output for Writes.
+ wire [1:0] ddr3_dqs_n; // Data Strobes. Input for Reads; Output for Writes.
+ wire [1:0] ddr3_dqs_p;
+ wire [14:0] ddr3_addr; // Address
+ wire [2:0] ddr3_ba; // Bank Address
+ wire ddr3_ras_n; // Row Address Strobe.
+ wire ddr3_cas_n; // Column address select
+ wire ddr3_we_n; // Write Enable
+ wire ddr3_reset_n; // SDRAM reset pin.
+ wire [0:0] ddr3_ck_p; // Differential clock
+ wire [0:0] ddr3_ck_n;
+ wire [0:0] ddr3_cke; // Clock Enable
+ wire [0:0] ddr3_cs_n; // Chip Select
+ wire [3:0] ddr3_dm; // Data Mask [3] = UDM.U26; [2] = LDM.U26;
+ wire [0:0] ddr3_odt; // On-Die termination enable.
+
+ ddr3_model #(
+ .DEBUG(1) //Disable verbose prints
+ ) sdram_i0 (
+ .rst_n (ddr3_reset_n),
+ .ck (ddr3_ck_p),
+ .ck_n (ddr3_ck_n),
+ .cke (ddr3_cke),
+ .cs_n (1'b0),
+ .ras_n (ddr3_ras_n),
+ .cas_n (ddr3_cas_n),
+ .we_n (ddr3_we_n),
+ .dm_tdqs (ddr3_dm[1:0]),
+ .ba (ddr3_ba),
+ .addr (ddr3_addr),
+ .dq (ddr3_dq[15:0]),
+ .dqs (ddr3_dqs_p[1:0]),
+ .dqs_n (ddr3_dqs_n[1:0]),
+ .tdqs_n (), // Unused on x16
+ .odt (ddr3_odt)
+ );
+
+ example_top inst_example_top
+ (
+ .ddr3_dq (ddr3_dq),
+ .ddr3_dqs_n (ddr3_dqs_n),
+ .ddr3_dqs_p (ddr3_dqs_p),
+ .ddr3_addr (ddr3_addr),
+ .ddr3_ba (ddr3_ba),
+ .ddr3_ras_n (ddr3_ras_n),
+ .ddr3_cas_n (ddr3_cas_n),
+ .ddr3_we_n (ddr3_we_n),
+ .ddr3_reset_n (ddr3_reset_n),
+ .ddr3_ck_p (ddr3_ck_p),
+ .ddr3_ck_n (ddr3_ck_n),
+ .ddr3_cke (ddr3_cke),
+ .ddr3_dm (ddr3_dm),
+ .ddr3_odt (ddr3_odt),
+ .sys_clk_i (sys_clk),
+ .clk_ref_i (ref_clk),
+ .tg_compare_error (tg_compare_error),
+ .init_calib_complete (calib_complete),
+ .sys_rst (sys_rst)
+ );
+
+ //
+ //Make sure we catch the error condition
+ //
+ reg tg_compare_error_reg;
+ always @ (posedge sys_clk)
+ if (sys_rst)
+ tg_compare_error_reg = 1'b0;
+ else
+ tg_compare_error_reg = tg_compare_error | tg_compare_error_reg;
+
+ //------------------------------------------
+ //Main thread for testbench execution
+ //------------------------------------------
+
+ initial begin : tb_main
+
+ `TEST_CASE_START("Wait for reset");
+ while (sys_rst) @(posedge sys_clk);
+ `TEST_CASE_DONE((~sys_rst));
+
+ repeat (200) @(posedge sys_clk);
+
+ `TEST_CASE_START("Wait for initial calibration to complete");
+ while (calib_complete !== 1'b1) @(posedge sys_clk);
+ `TEST_CASE_DONE(calib_complete);
+
+ `TEST_CASE_START("Run for a while, then check for error");
+ repeat (2_000_000) @(posedge sys_clk);
+ `ASSERT_ERROR(tg_compare_error_reg == 1'b0, "Test generator reported error");
+ `TEST_CASE_DONE(1'b1);
+ `TEST_BENCH_DONE;
+
+ end
+
+endmodule
diff --git a/fpga/usrp3/top/e31x/sim/e310_io_tb/Makefile b/fpga/usrp3/top/e31x/sim/e310_io_tb/Makefile
new file mode 100644
index 000000000..bf4922c21
--- /dev/null
+++ b/fpga/usrp3/top/e31x/sim/e310_io_tb/Makefile
@@ -0,0 +1,40 @@
+#
+# Copyright 2015 Ettus Research LLC
+#
+
+#-------------------------------------------------
+# Top-of-Makefile
+#-------------------------------------------------
+# Define BASE_DIR to point to the "top" dir
+BASE_DIR = $(abspath ../../..)
+# Include viv_sim_preample after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+# Define part using PART_ID (<device>/<package>/<speedgrade>)
+ARCH = zynq
+PART_ID= xc7z020/clg484/-1
+
+DESIGN_SRCS = $(abspath ../../e310_io.v) \
+ $(abspath $(addprefix $(BASE_DIR)/../lib/control/, \
+ synchronizer.v \
+ synchronizer_impl.v))
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+# Define only one toplevel module
+SIM_TOP = e310_io_tb
+
+SIM_SRCS = \
+$(abspath e310_io_tb.sv)
+
+#-------------------------------------------------
+# 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/top/e31x/sim/e310_io_tb/e310_io_tb.sv b/fpga/usrp3/top/e31x/sim/e310_io_tb/e310_io_tb.sv
new file mode 100644
index 000000000..66e086644
--- /dev/null
+++ b/fpga/usrp3/top/e31x/sim/e310_io_tb/e310_io_tb.sv
@@ -0,0 +1,230 @@
+//
+// Copyright 2015 Ettus Research
+//
+// Test bench for E310 I/O interface to AD9361.
+
+`timescale 1ns/1ps
+`define SIM_TIMEOUT_US 20
+`define NS_PER_TICK 1
+`define NUM_TEST_CASES 6
+
+`include "sim_clks_rsts.vh"
+`include "sim_exec_report.vh"
+
+module e310_io_tb();
+ `TEST_BENCH_INIT("e310_io_tb",`NUM_TEST_CASES,`NS_PER_TICK)
+
+ // Define all clocks and resets
+ `DEFINE_CLK(rx_clk, 16.27, 50) // ~61.44 MHz clock from AD9361
+ `DEFINE_RESET(areset, 0, 100) // 100ns reset
+
+ reg mimo;
+ wire radio_clk, radio_rst;
+ wire [11:0] rx_i0, rx_i1, rx_q0, rx_q1;
+ wire rx_stb;
+ reg [11:0] tx_i0, tx_i1, tx_q0, tx_q1;
+ wire tx_stb;
+ reg rx_frame;
+ reg [11:0] rx_data;
+ wire tx_clk;
+ wire tx_frame;
+ wire [11:0] tx_data;
+ e310_io e310_io (
+ .areset(areset),
+ .mimo(mimo),
+ .radio_clk(radio_clk),
+ .radio_rst(radio_rst),
+ .rx_i0(rx_i0),
+ .rx_q0(rx_q0),
+ .rx_i1(rx_i1),
+ .rx_q1(rx_q1),
+ .rx_stb(rx_stb),
+ .tx_i0(tx_i0),
+ .tx_q0(tx_q0),
+ .tx_i1(tx_i1),
+ .tx_q1(tx_q1),
+ .tx_stb(tx_stb),
+ .rx_clk(rx_clk),
+ .rx_frame(rx_frame),
+ .rx_data(rx_data),
+ .tx_clk(tx_clk),
+ .tx_frame(tx_frame),
+ .tx_data(tx_data));
+
+ /********************************************************
+ ** Test Bench
+ ********************************************************/
+ initial begin : tb_main
+ mimo <= 1'b0;
+ tx_i0 <= 'd0;
+ tx_q0 <= 'd0;
+ tx_i1 <= 'd0;
+ tx_q1 <= 'd0;
+ rx_data <= 'd0;
+ rx_frame <= 1'b0;
+ `TEST_CASE_START("Wait for reset");
+ while (areset) @(posedge radio_clk);
+ `TEST_CASE_DONE((~areset));
+
+ repeat (10) @(posedge radio_clk);
+
+ `TEST_CASE_START("Test RX channel 0,1");
+ mimo <= 1'b0;
+ rx_data <= 'd0;
+ repeat (10) @(posedge radio_clk);
+ fork
+ begin
+ for (int i = 1; i < 64; i = i + 2) begin
+ @(posedge radio_clk);
+ rx_frame <= 1'b1;
+ rx_data <= i;
+ @(negedge radio_clk);
+ rx_frame <= 1'b0;
+ rx_data <= i+1;
+ end
+ end
+ begin
+ while ({rx_i0, rx_q0} == 24'd0) @(posedge radio_clk);
+ for (int i = 1; i < 64; i = i + 2) begin
+ // RX should be replicated across both ports
+ `ASSERT_ERROR(rx_i0 == i, "RX0 I incorrect!");
+ `ASSERT_ERROR(rx_q0 == i+1, "RX0 Q incorrect!");
+ `ASSERT_ERROR(rx_i1 == i, "RX1 I incorrect!");
+ `ASSERT_ERROR(rx_q1 == i+1, "RX1 Q incorrect!");
+ @(posedge radio_clk);
+ end
+ end
+ join
+ `TEST_CASE_DONE(1);
+
+ `TEST_CASE_START("Test RX channels 0 & 1 (MIMO mode)");
+ mimo <= 1'b1;
+ rx_frame <= 1'b0;
+ rx_data <= 'd0;
+ repeat (10) @(posedge radio_clk);
+ fork
+ begin
+ for (int i = 1; i < 64; i = i + 2) begin
+ @(posedge radio_clk);
+ rx_frame <= ~rx_frame;
+ rx_data <= i;
+ @(negedge radio_clk);
+ rx_data <= i+1;
+ end
+ end
+ begin
+ while ({rx_i0, rx_q0} == 24'd0) @(posedge radio_clk);
+ for (int i = 1; i < 32; i = i + 4) begin
+ // RX should be replicated across both ports
+ `ASSERT_ERROR(rx_i0 == i, "RX0 I incorrect!");
+ `ASSERT_ERROR(rx_q0 == i+1, "RX0 Q incorrect!");
+ @(posedge radio_clk);
+ `ASSERT_ERROR(rx_i1 == i+2, "RX1 I incorrect!");
+ `ASSERT_ERROR(rx_q1 == i+3, "RX1 Q incorrect!");
+ @(posedge radio_clk);
+ end
+ end
+ join
+ `TEST_CASE_DONE(1);
+
+ `TEST_CASE_START("Test TX channel 0");
+ mimo <= 1'b0;
+ tx_i0 <= 'd0;
+ tx_q0 <= 'd0;
+ tx_i1 <= 'd0;
+ tx_q1 <= 'd0;
+ repeat (10) @(posedge radio_clk);
+ // TX0
+ fork
+ begin
+ for (int i = 1; i < 64; i = i + 2) begin
+ tx_i0 <= i;
+ tx_q0 <= i+1;
+ @(posedge radio_clk);
+ end
+ end
+ begin
+ while (tx_data == 12'd0) @(posedge tx_clk);
+ for (int i = 1; i < 64; i = i + 2) begin
+ // RX should be replicated across both ports
+ `ASSERT_ERROR(tx_data == i, "TX0 I data incorrect!");
+ `ASSERT_ERROR(tx_frame == 1'b1, "TX frame incorrect");
+ @(negedge tx_clk);
+ `ASSERT_ERROR(tx_data == i+1, "TX0 Q data incorrect!");
+ `ASSERT_ERROR(tx_frame == 1'b0, "TX frame incorrect");
+ @(posedge tx_clk);
+ end
+ end
+ join
+ `TEST_CASE_DONE(1);
+
+ `TEST_CASE_START("Test TX channel 1");
+ mimo <= 1'b0;
+ tx_i0 <= 'd0;
+ tx_q0 <= 'd0;
+ tx_i1 <= 'd0;
+ tx_q1 <= 'd0;
+ repeat (10) @(posedge radio_clk);
+ fork
+ begin
+ for (int i = 1; i < 64; i = i + 2) begin
+ tx_i1 <= i;
+ tx_q1 <= i+1;
+ @(posedge radio_clk);
+ end
+ end
+ begin
+ while (tx_data == 12'd0) @(posedge tx_clk);
+ for (int i = 1; i < 64; i = i + 2) begin
+ `ASSERT_ERROR(tx_data == i, "TX1 I data incorrect!");
+ `ASSERT_ERROR(tx_frame == 1'b1, "TX frame incorrect");
+ @(negedge tx_clk);
+ `ASSERT_ERROR(tx_data == i+1, "TX1 Q data incorrect!");
+ `ASSERT_ERROR(tx_frame == 1'b0, "TX frame incorrect");
+ @(posedge tx_clk);
+ end
+ end
+ join
+ `TEST_CASE_DONE(1);
+
+ `TEST_CASE_START("Test TX channel 0 & 1 (MIMO)");
+ mimo <= 1'b1;
+ tx_i0 <= 'd0;
+ tx_q0 <= 'd0;
+ tx_i1 <= 'd0;
+ tx_q1 <= 'd0;
+ repeat (10) @(posedge radio_clk);
+ fork
+ begin
+ for (int i = 1; i < 32; i = i + 4) begin
+ tx_i0 <= i;
+ tx_q0 <= i+1;
+ tx_i1 <= i+2;
+ tx_q1 <= i+3;
+ @(posedge radio_clk);
+ while (tx_stb) @(posedge radio_clk);
+ end
+ end
+ begin
+ while (tx_data == 12'd0) @(posedge tx_clk);
+ for (int i = 1; i < 32; i = i + 4) begin
+ `ASSERT_ERROR(tx_data == i, "TX0 I data incorrect!");
+ `ASSERT_ERROR(tx_frame == 1'b1, "TX frame incorrect");
+ @(negedge tx_clk);
+ `ASSERT_ERROR(tx_data == i+1, "TX0 Q data incorrect!");
+ `ASSERT_ERROR(tx_frame == 1'b1, "TX frame incorrect");
+ @(posedge tx_clk);
+ `ASSERT_ERROR(tx_data == i+2, "TX1 I data incorrect!");
+ `ASSERT_ERROR(tx_frame == 1'b0, "TX frame incorrect");
+ @(negedge tx_clk);
+ `ASSERT_ERROR(tx_data == i+3, "TX1 Q data incorrect!");
+ `ASSERT_ERROR(tx_frame == 1'b0, "TX frame incorrect");
+ @(posedge tx_clk);
+ end
+ end
+ join
+ `TEST_CASE_DONE(1);
+ `TEST_BENCH_DONE;
+ end
+
+endmodule
diff --git a/fpga/usrp3/top/e31x/sim/e3x0/catcap_ddr_cmos/catcap_tb.build b/fpga/usrp3/top/e31x/sim/e3x0/catcap_ddr_cmos/catcap_tb.build
new file mode 100755
index 000000000..759549e4b
--- /dev/null
+++ b/fpga/usrp3/top/e31x/sim/e3x0/catcap_ddr_cmos/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 ../../../top/e300/coregen \
+ --sourcelibdir ../../control_lib \
+ --sourcelibdir ../../../top/e300/ \
+ --sourcelibdir $XILINX/verilog/src \
+ --sourcelibdir $XILINX/verilog/src/unisims \
+ --work work \
+ catcap_tb.v
+
+
+fuse -o catcap_tb catcap_tb
diff --git a/fpga/usrp3/top/e31x/sim/e3x0/catcap_ddr_cmos/catcap_tb.v b/fpga/usrp3/top/e31x/sim/e3x0/catcap_ddr_cmos/catcap_tb.v
new file mode 100644
index 000000000..4e05e6ed4
--- /dev/null
+++ b/fpga/usrp3/top/e31x/sim/e3x0/catcap_ddr_cmos/catcap_tb.v
@@ -0,0 +1,114 @@
+//
+// Copyright 2014 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/top/e31x/sim/e3x0/catgen_ddr_cmos/catgen_tb.build b/fpga/usrp3/top/e31x/sim/e3x0/catgen_ddr_cmos/catgen_tb.build
new file mode 100755
index 000000000..6512340f1
--- /dev/null
+++ b/fpga/usrp3/top/e31x/sim/e3x0/catgen_ddr_cmos/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 ../../../top/e300/coregen \
+ --sourcelibdir ../../control_lib \
+ --sourcelibdir ../../../top/e300 \
+ --sourcelibdir $XILINX/verilog/src \
+ --sourcelibdir $XILINX/verilog/src/unisims \
+ --work work \
+ catgen_tb.v
+
+
+fuse -o catgen_tb catgen_tb
diff --git a/fpga/usrp3/top/e31x/sim/e3x0/catgen_ddr_cmos/catgen_tb.v b/fpga/usrp3/top/e31x/sim/e3x0/catgen_ddr_cmos/catgen_tb.v
new file mode 100644
index 000000000..070364b41
--- /dev/null
+++ b/fpga/usrp3/top/e31x/sim/e3x0/catgen_ddr_cmos/catgen_tb.v
@@ -0,0 +1,102 @@
+//
+// Copyright 2014 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