aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/sim/axi
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/lib/sim/axi')
-rw-r--r--fpga/usrp3/lib/sim/axi/axis_shift_register/Makefile49
-rw-r--r--fpga/usrp3/lib/sim/axi/axis_shift_register/axis_shift_register_tb.sv325
-rw-r--r--fpga/usrp3/lib/sim/axi/axis_width_conv/Makefile35
-rw-r--r--fpga/usrp3/lib/sim/axi/axis_width_conv/axis_width_conv_tb.sv322
4 files changed, 731 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/sim/axi/axis_shift_register/Makefile b/fpga/usrp3/lib/sim/axi/axis_shift_register/Makefile
new file mode 100644
index 000000000..8a65a6024
--- /dev/null
+++ b/fpga/usrp3/lib/sim/axi/axis_shift_register/Makefile
@@ -0,0 +1,49 @@
+#
+# Copyright 2018 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
+#-------------------------------------------------
+# Include makefiles and sources for the DUT and its dependencies
+include $(BASE_DIR)/../lib/axi/Makefile.srcs
+include $(BASE_DIR)/../lib/fifo/Makefile.srcs
+include $(BASE_DIR)/../lib/control/Makefile.srcs
+
+DESIGN_SRCS = $(abspath \
+$(AXI_SRCS) \
+$(FIFO_SRCS) \
+$(CONTROL_LIB_SRCS) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+# Define only one toplevel module
+SIM_TOP = axis_shift_register_tb
+
+# Add test bench, user design under test, and
+# additional user created files
+SIM_SRCS = $(abspath \
+axis_shift_register_tb.sv \
+)
+
+# MODELSIM_USER_DO = $(abspath wave.do)
+
+#-------------------------------------------------
+# 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/axi/axis_shift_register/axis_shift_register_tb.sv b/fpga/usrp3/lib/sim/axi/axis_shift_register/axis_shift_register_tb.sv
new file mode 100644
index 000000000..1ce868069
--- /dev/null
+++ b/fpga/usrp3/lib/sim/axi/axis_shift_register/axis_shift_register_tb.sv
@@ -0,0 +1,325 @@
+//
+// Copyright 2018 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 8
+
+`include "sim_exec_report.vh"
+`include "sim_clks_rsts.vh"
+`include "sim_axis_lib.svh"
+
+module axis_shift_register_tb();
+ `TEST_BENCH_INIT("axis_shift_register_tb", `NUM_TEST_CASES, `NS_PER_TICK);
+ localparam CLK_PERIOD = $ceil(1e9/100.0e6);
+ `DEFINE_CLK(clk, CLK_PERIOD, 50);
+ `DEFINE_RESET(reset, 0, 100);
+
+ localparam WIDTH = 32;
+ localparam integer NUM_INST = 4;
+
+ logic [WIDTH:0] i_tdata[0:NUM_INST-1];
+ logic i_tlast[0:NUM_INST-1], i_tvalid[0:NUM_INST-1];
+ wire i_tready[0:NUM_INST-1];
+ wire [WIDTH:0] o_tdata[0:NUM_INST-1];
+ wire o_tlast[0:NUM_INST-1], o_tvalid[0:NUM_INST-1];
+ logic o_tready[0:NUM_INST-1];
+ wire [15:0] stage_stb[0:NUM_INST-1], stage_eop[0:NUM_INST-1];
+ wire [WIDTH-1:0] sb_dout, sb_din;
+ wire sb_keep;
+ reg [WIDTH-1:0] sb_shreg[0:2];
+ always @(posedge clk) begin
+ if (stage_stb[2][0])
+ sb_shreg[0] <= sb_dout;
+ if (stage_stb[2][1])
+ sb_shreg[1] <= sb_shreg[0];
+ if (stage_stb[2][2])
+ sb_shreg[2] <= sb_shreg[1];
+ end
+ assign sb_din = sb_shreg[2];
+
+ axis_shift_register #(
+ .WIDTH(WIDTH), .NSPC(1), .LATENCY(2),
+ .SIDEBAND_DATAPATH(0), .GAPLESS(0),
+ .PIPELINE("NONE")
+ ) inst_0 (
+ .clk(clk), .reset(reset),
+ .s_axis_tdata(i_tdata[0]), .s_axis_tkeep(i_tdata[0][WIDTH]), .s_axis_tlast(i_tlast[0]),
+ .s_axis_tvalid(i_tvalid[0]), .s_axis_tready(i_tready[0]),
+ .m_axis_tdata(o_tdata[0]), .m_axis_tkeep(o_tdata[0][WIDTH]), .m_axis_tlast(o_tlast[0]),
+ .m_axis_tvalid(o_tvalid[0]), .m_axis_tready(o_tready[0]),
+ .stage_stb(stage_stb[0]), .stage_eop(stage_eop[0]),
+ .m_sideband_data(), .m_sideband_keep(), .s_sideband_data()
+ );
+
+ axis_shift_register #(
+ .WIDTH(WIDTH), .NSPC(1), .LATENCY(9),
+ .SIDEBAND_DATAPATH(0), .GAPLESS(0),
+ .PIPELINE("BOTH")
+ ) inst_1 (
+ .clk(clk), .reset(reset),
+ .s_axis_tdata(i_tdata[1]), .s_axis_tkeep(i_tdata[1][WIDTH]), .s_axis_tlast(i_tlast[1]),
+ .s_axis_tvalid(i_tvalid[1]), .s_axis_tready(i_tready[1]),
+ .m_axis_tdata(o_tdata[1]), .m_axis_tkeep(o_tdata[1][WIDTH]), .m_axis_tlast(o_tlast[1]),
+ .m_axis_tvalid(o_tvalid[1]), .m_axis_tready(o_tready[1]),
+ .stage_stb(stage_stb[1]), .stage_eop(stage_eop[1]),
+ .m_sideband_data(), .m_sideband_keep(), .s_sideband_data()
+ );
+
+ axis_shift_register #(
+ .WIDTH(WIDTH/2), .NSPC(2), .LATENCY(3),
+ .SIDEBAND_DATAPATH(1), .GAPLESS(0),
+ .PIPELINE("NONE")
+ ) inst_2 (
+ .clk(clk), .reset(reset),
+ .s_axis_tdata(i_tdata[2]), .s_axis_tkeep(i_tdata[2][WIDTH]), .s_axis_tlast(i_tlast[2]),
+ .s_axis_tvalid(i_tvalid[2]), .s_axis_tready(i_tready[2]),
+ .m_axis_tdata(o_tdata[2]), .m_axis_tkeep(o_tdata[2][WIDTH]), .m_axis_tlast(o_tlast[2]),
+ .m_axis_tvalid(o_tvalid[2]), .m_axis_tready(o_tready[2]),
+ .stage_stb(stage_stb[2]), .stage_eop(stage_eop[2]),
+ .m_sideband_data(sb_dout), .m_sideband_keep(sb_keep), .s_sideband_data(sb_din)
+ );
+
+ axis_shift_register #(
+ .WIDTH(WIDTH/8), .NSPC(8), .LATENCY(14),
+ .SIDEBAND_DATAPATH(0), .GAPLESS(1),
+ .PIPELINE("NONE")
+ ) inst_3 (
+ .clk(clk), .reset(reset),
+ .s_axis_tdata(i_tdata[3]), .s_axis_tkeep(i_tdata[3][WIDTH]), .s_axis_tlast(i_tlast[3]),
+ .s_axis_tvalid(i_tvalid[3]), .s_axis_tready(i_tready[3]),
+ .m_axis_tdata(o_tdata[3]), .m_axis_tkeep(o_tdata[3][WIDTH]), .m_axis_tlast(o_tlast[3]),
+ .m_axis_tvalid(o_tvalid[3]), .m_axis_tready(o_tready[3]),
+ .stage_stb(stage_stb[3]), .stage_eop(stage_eop[3]),
+ .m_sideband_data(), .m_sideband_keep(), .s_sideband_data()
+ );
+
+ logic [15:0] prev_stage_stb[0:NUM_INST-1];
+ initial begin : tb_main
+ string s;
+
+ `TEST_CASE_START("Wait for Reset");
+ for (int k = 0; k < NUM_INST; k=k+1) begin
+ o_tready[k] = 1'b1;
+ i_tvalid[k] = 1'b0;
+ prev_stage_stb[k] = 0;
+ end
+ while (reset) @(posedge clk);
+ `TEST_CASE_DONE(~reset);
+
+ `TEST_CASE_START("Check module with LATENCY=2, PIPELINE=NONE");
+ `ASSERT_ERROR(o_tvalid[0] == 1'b0, "Output incorrectly active when idle");
+ `ASSERT_ERROR(i_tready[0] == 1'b1, "Input was not ready when idle");
+ `ASSERT_ERROR(stage_stb[0][1:0] === 0, "Incorrect stage_stb when idle");
+ for (int j = 0; j < 2; j=j+1) begin
+ i_tdata[0] = j;
+ i_tlast[0] = j % 2;
+ i_tvalid[0] = 1'b1;
+ @(posedge clk);
+ i_tvalid[0] = 1'b0;
+ prev_stage_stb[0][1:0] = {prev_stage_stb[0][0], 1'b1};
+ `ASSERT_ERROR(stage_stb[0][1:0] == prev_stage_stb[0][1:0], "Incorrect stage_stb");
+ `ASSERT_ERROR(i_tready[0] == 1'b1, "Input was not ready during fill-up");
+ `ASSERT_ERROR(o_tvalid[0] == 1'b0, "Output incorrectly active during fill-up");
+ end
+ for (int j = 0; j < 2; j=j+1) begin
+ @(posedge clk);
+ `ASSERT_ERROR(o_tvalid[0] == 1'b1, "Output was not active");
+ `ASSERT_ERROR(o_tlast[0] == j % 2, "Incorrect tlast");
+ `ASSERT_ERROR(o_tdata[0] == j, "Incorrect tdata");
+ end
+ `TEST_CASE_DONE(1);
+
+ `TEST_CASE_START("Check module with LATENCY=9, PIPELINE=BOTH");
+ prev_stage_stb[1] = 0;
+ `ASSERT_ERROR(o_tvalid[1] == 1'b0, "Output incorrectly active when idle");
+ `ASSERT_ERROR(i_tready[1] == 1'b1, "Input was not ready when idle");
+ `ASSERT_ERROR(stage_stb[1][1:0] === 0, "Incorrect stage_stb when idle");
+ for (int j = 0; j < 9; j=j+1) begin
+ i_tdata[1] = -j;
+ i_tlast[1] = 1'b1;
+ i_tvalid[1] = 1'b1;
+ @(posedge clk);
+ i_tvalid[1] = 1'b0;
+ prev_stage_stb[1][8:0] = {prev_stage_stb[1][7:0], 1'b1};
+ `ASSERT_ERROR(stage_eop[1][8:0] == prev_stage_stb[1][8:0], "Incorrect stage_eop");
+ `ASSERT_ERROR(stage_stb[1][8:0] == prev_stage_stb[1][8:0], "Incorrect stage_stb");
+ `ASSERT_ERROR(i_tready[1] == 1'b1, "Input was not ready during fill-up");
+ `ASSERT_ERROR(o_tvalid[1] == 1'b0, "Output incorrectly active during fill-up");
+ end
+ for (int j = 0; j < 9; j=j+1) begin
+ @(posedge clk);
+ `ASSERT_ERROR(o_tvalid[1] == 1'b1, "Output was not active");
+ `ASSERT_ERROR(o_tlast[1] == 1'b1, "Incorrect tlast");
+ `ASSERT_ERROR(o_tdata[1] == -j, "Incorrect tdata");
+ end
+ @(posedge clk);
+ `TEST_CASE_DONE(1);
+
+ `TEST_CASE_START("Check module with LATENCY=9, PIPELINE=BOTH (backpressure)");
+ prev_stage_stb[1] = 0;
+ o_tready[1] = 1'b0;
+ `ASSERT_ERROR(o_tvalid[1] == 1'b0, "Output incorrectly active when idle");
+ `ASSERT_ERROR(i_tready[1] == 1'b1, "Input was not ready when idle");
+ `ASSERT_ERROR(stage_stb[1][1:0] === 0, "Incorrect stage_stb when idle");
+ for (int j = 0; j < 9; j=j+1) begin
+ i_tdata[1] = -j;
+ i_tlast[1] = 1'b1;
+ i_tvalid[1] = 1'b1;
+ @(posedge clk);
+ i_tvalid[1] = 1'b0;
+ prev_stage_stb[1][8:0] = {prev_stage_stb[1][7:0], 1'b1};
+ `ASSERT_ERROR(stage_stb[1][8:0] == prev_stage_stb[1][8:0], "Incorrect stage_stb");
+ `ASSERT_ERROR(stage_eop[1][8:0] == prev_stage_stb[1][8:0], "Incorrect stage_eop");
+ `ASSERT_ERROR(i_tready[1] == 1'b1, "Input was not ready during fill-up");
+ `ASSERT_ERROR(o_tvalid[1] == 1'b0, "Output incorrectly active during fill-up");
+ end
+ o_tready[1] = 1'b1;
+ for (int j = 0; j < 9; j=j+1) begin
+ @(posedge clk);
+ `ASSERT_ERROR(o_tvalid[1] == 1'b1, "Output was not active");
+ `ASSERT_ERROR(o_tlast[1] == 1'b1, "Incorrect tlast");
+ `ASSERT_ERROR(o_tdata[1] == -j, "Incorrect tdata");
+ end
+ @(posedge clk);
+ `TEST_CASE_DONE(1);
+
+ `TEST_CASE_START("Check module with LATENCY=9, PIPELINE=BOTH (steady-state)");
+ prev_stage_stb[1] = 0;
+ fork
+ begin
+ for (int j = 0; j < 50; j=j+1) begin
+ i_tdata[1] = j;
+ i_tlast[1] = j % 2;
+ i_tvalid[1] = 1'b1;
+ @(posedge clk);
+ i_tvalid[1] = 1'b0;
+ prev_stage_stb[1][8:0] = {prev_stage_stb[1][7:0], 1'b1};
+ `ASSERT_ERROR(stage_stb[1][8:0] == prev_stage_stb[1][8:0], "Incorrect stage_stb");
+ end
+ end
+ begin
+ o_tready[1] = 1'b1;
+ repeat(9) @(posedge clk);
+ for (int j = 0; j < 50; j=j+1) begin
+ @(posedge clk);
+ `ASSERT_ERROR(o_tvalid[1] == 1'b1, "Output was not active");
+ `ASSERT_ERROR(o_tlast[1] == j % 2, "Incorrect tlast");
+ `ASSERT_ERROR(o_tdata[1] == j, "Incorrect tdata");
+ end
+ @(posedge clk);
+ end
+ join
+ `TEST_CASE_DONE(1);
+
+ `TEST_CASE_START("Check module with LATENCY=9, PIPELINE=BOTH (gaps)");
+ prev_stage_stb[1] = 0;
+ fork
+ begin
+ for (int j = 0; j < 50; j=j+1) begin
+ i_tdata[1] = j;
+ i_tlast[1] = j % 2;
+ i_tvalid[1] = 1'b1;
+ @(posedge clk);
+ while (~i_tready[1]) @(posedge clk);
+ i_tvalid[1] = 1'b0;
+ end
+ end
+ begin
+ o_tready[1] = 1'b1;
+ repeat(9) @(posedge clk);
+ for (int j = 0; j < 20; j=j+1) begin
+ @(posedge clk);
+ `ASSERT_ERROR(o_tvalid[1] == 1'b1, "Output was not active");
+ `ASSERT_ERROR(o_tlast[1] == j % 2, "Incorrect tlast");
+ `ASSERT_ERROR(o_tdata[1] == j, "Incorrect tdata");
+ end
+ o_tready[1] = 1'b0;
+ repeat(4) @(posedge clk);
+ o_tready[1] = 1'b1;
+ for (int j = 20; j < 50; j=j+1) begin
+ @(posedge clk);
+ `ASSERT_ERROR(o_tvalid[1] == 1'b1, "Output was not active");
+ `ASSERT_ERROR(o_tlast[1] == j % 2, "Incorrect tlast");
+ `ASSERT_ERROR(o_tdata[1] == j, "Incorrect tdata");
+ end
+ @(posedge clk);
+ end
+ join
+ `TEST_CASE_DONE(1);
+
+ `TEST_CASE_START("Check module with LATENCY=3, SIDEBAND=1 (gaps)");
+ prev_stage_stb[2] = 0;
+ fork
+ begin
+ for (int j = 0; j < 50; j=j+1) begin
+ i_tdata[2] = j;
+ i_tlast[2] = j % 4 == 0;
+ i_tvalid[2] = 1'b1;
+ @(posedge clk);
+ while (~i_tready[2]) @(posedge clk);
+ i_tvalid[2] = 1'b0;
+ end
+ end
+ begin
+ o_tready[2] = 1'b1;
+ repeat(3) @(posedge clk);
+ for (int j = 0; j < 20; j=j+1) begin
+ @(posedge clk);
+ `ASSERT_ERROR(o_tvalid[2] == 1'b1, "Output was not active");
+ `ASSERT_ERROR(o_tlast[2] == j % 4 == 0, "Incorrect tlast");
+ `ASSERT_ERROR(o_tdata[2] == j, "Incorrect tdata");
+ end
+ o_tready[2] = 1'b0;
+ repeat(4) @(posedge clk);
+ o_tready[2] = 1'b1;
+ for (int j = 20; j < 50; j=j+1) begin
+ @(posedge clk);
+ `ASSERT_ERROR(o_tvalid[2] == 1'b1, "Output was not active");
+ `ASSERT_ERROR(o_tlast[2] == j % 4 == 0, "Incorrect tlast");
+ `ASSERT_ERROR(o_tdata[2] == j, "Incorrect tdata");
+ end
+ @(posedge clk);
+ end
+ join
+ `TEST_CASE_DONE(1);
+
+ `TEST_CASE_START("Check module with LATENCY=14, GAPLESS=1");
+ prev_stage_stb[3] = 0;
+ o_tready[3] = 1'b0;
+ fork
+ begin
+ for (int j = 0; j < 50; j=j+1) begin
+ i_tdata[3] = j;
+ i_tlast[3] = j % 4 == 0;
+ i_tvalid[3] = 1'b1;
+ @(posedge clk);
+ prev_stage_stb[3][13:0] = {prev_stage_stb[3][12:0], 1'b1};
+ `ASSERT_ERROR(stage_stb[3][13:0] == prev_stage_stb[3][13:0], "Incorrect stage_stb");
+ while (~i_tready[3]) @(posedge clk);
+ i_tvalid[3] = 1'b0;
+ if (j > 16) repeat($urandom_range(0, 15)) @(posedge clk);
+ `ASSERT_ERROR(stage_stb[3][13:0] == prev_stage_stb[3][13:0] || stage_stb[3][13:0] == 14'd0, "Incorrect stage_stb");
+ end
+ end
+ begin
+ for (int j = 0; j < 50 - 14; j=j+1) begin
+ while (~o_tvalid[3]) @(posedge clk);
+ @(negedge clk);
+ `ASSERT_ERROR(o_tvalid[3] == 1'b1, "Output was not active");
+ `ASSERT_ERROR(o_tlast[3] == j % 4 == 0, "Incorrect tlast");
+ `ASSERT_ERROR(o_tdata[3] == j, "Incorrect tdata");
+ o_tready[3] = 1'b1;
+ @(negedge clk);
+ o_tready[3] = 1'b0;
+ @(posedge clk);
+ end
+ end
+ join
+ `TEST_CASE_DONE(1);
+ `TEST_BENCH_DONE;
+ end
+endmodule
diff --git a/fpga/usrp3/lib/sim/axi/axis_width_conv/Makefile b/fpga/usrp3/lib/sim/axi/axis_width_conv/Makefile
new file mode 100644
index 000000000..2e7d0ba0c
--- /dev/null
+++ b/fpga/usrp3/lib/sim/axi/axis_width_conv/Makefile
@@ -0,0 +1,35 @@
+#
+# Copyright 2018 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
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+# Define only one toplevel module
+SIM_TOP = axis_width_conv_tb
+
+# Add test bench, user design under test, and
+# additional user created files
+SIM_SRCS = $(abspath \
+axis_width_conv_tb.sv \
+)
+
+# MODELSIM_USER_DO = $(abspath wave.do)
+
+#-------------------------------------------------
+# 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/axi/axis_width_conv/axis_width_conv_tb.sv b/fpga/usrp3/lib/sim/axi/axis_width_conv/axis_width_conv_tb.sv
new file mode 100644
index 000000000..0b1f4414c
--- /dev/null
+++ b/fpga/usrp3/lib/sim/axi/axis_width_conv/axis_width_conv_tb.sv
@@ -0,0 +1,322 @@
+//
+// Copyright 2018 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 13
+
+`include "sim_exec_report.vh"
+`include "sim_clks_rsts.vh"
+`include "sim_axis_lib.svh"
+
+module axis_width_conv_tb();
+ `TEST_BENCH_INIT("axis_width_conv_tb", `NUM_TEST_CASES, `NS_PER_TICK);
+
+ `DEFINE_CLK(clk50, 20.000, 50);
+ `DEFINE_CLK(clk200, 5.000, 50);
+ `DEFINE_LATE_START_CLK(clk166, 6.000, 50, 0.37, 0.01)
+ `DEFINE_RESET(areset, 0, 100);
+
+ localparam WORD_W = 8;
+ localparam integer NINST = 6;
+ localparam integer IN_WORDS[0:NINST-1] = {1, 4, 4, 8, 8, 17};
+ localparam integer OUT_WORDS[0:NINST-1] = {4, 1, 3, 2, 8, 19};
+ localparam integer MAX_IN_WORDS = 20;
+ localparam integer MAX_OUT_WORDS = 20;
+ localparam integer MAX_SPP = 100;
+
+ axis_master #(.DWIDTH((WORD_W+1)*IN_WORDS[0])) m0 (.clk(clk50));
+ axis_master #(.DWIDTH((WORD_W+1)*IN_WORDS[1])) m1 (.clk(clk50));
+ axis_master #(.DWIDTH((WORD_W+1)*IN_WORDS[2])) m2 (.clk(clk50));
+ axis_master #(.DWIDTH((WORD_W+1)*IN_WORDS[3])) m3 (.clk(clk50));
+ axis_master #(.DWIDTH((WORD_W+1)*IN_WORDS[4])) m4 (.clk(clk50));
+ axis_master #(.DWIDTH((WORD_W+1)*IN_WORDS[5])) m5 (.clk(clk50));
+
+ axis_slave #(.DWIDTH((WORD_W+1)*OUT_WORDS[0])) s0 (.clk(clk50));
+ axis_slave #(.DWIDTH((WORD_W+1)*OUT_WORDS[1])) s1 (.clk(clk50));
+ axis_slave #(.DWIDTH((WORD_W+1)*OUT_WORDS[2])) s2 (.clk(clk200));
+ axis_slave #(.DWIDTH((WORD_W+1)*OUT_WORDS[3])) s3 (.clk(clk166));
+ axis_slave #(.DWIDTH((WORD_W+1)*OUT_WORDS[4])) s4 (.clk(clk166));
+ axis_slave #(.DWIDTH((WORD_W+1)*OUT_WORDS[5])) s5 (.clk(clk50));
+
+ // An integer 1-to-4 upsizer
+ axis_width_conv #(
+ .WORD_W(WORD_W), .IN_WORDS(IN_WORDS[0]), .OUT_WORDS(OUT_WORDS[0]),
+ .SYNC_CLKS(1), .PIPELINE("INOUT")
+ ) dut0 (
+ .s_axis_aclk (clk50),
+ .s_axis_rst (areset),
+ .s_axis_tdata (m0.axis.tdata[(IN_WORDS[0]*WORD_W)-1:0]),
+ .s_axis_tkeep (m0.axis.tdata[IN_WORDS[0]*WORD_W+:IN_WORDS[0]]),
+ .s_axis_tlast (m0.axis.tlast),
+ .s_axis_tvalid(m0.axis.tvalid),
+ .s_axis_tready(m0.axis.tready),
+ .m_axis_aclk (clk50),
+ .m_axis_rst (areset),
+ .m_axis_tdata (s0.axis.tdata[(OUT_WORDS[0]*WORD_W)-1:0]),
+ .m_axis_tkeep (s0.axis.tdata[OUT_WORDS[0]*WORD_W+:OUT_WORDS[0]]),
+ .m_axis_tlast (s0.axis.tlast),
+ .m_axis_tvalid(s0.axis.tvalid),
+ .m_axis_tready(s0.axis.tready)
+ );
+
+ // An integer 4-to-1 downsizer
+ axis_width_conv #(
+ .WORD_W(WORD_W), .IN_WORDS(IN_WORDS[1]), .OUT_WORDS(OUT_WORDS[1]),
+ .SYNC_CLKS(1), .PIPELINE("IN")
+ ) dut1 (
+ .s_axis_aclk (clk50),
+ .s_axis_rst (areset),
+ .s_axis_tdata (m1.axis.tdata[(IN_WORDS[1]*WORD_W)-1:0]),
+ .s_axis_tkeep (m1.axis.tdata[IN_WORDS[1]*WORD_W+:IN_WORDS[1]]),
+ .s_axis_tlast (m1.axis.tlast),
+ .s_axis_tvalid(m1.axis.tvalid),
+ .s_axis_tready(m1.axis.tready),
+ .m_axis_aclk (clk50),
+ .m_axis_rst (areset),
+ .m_axis_tdata (s1.axis.tdata[(OUT_WORDS[1]*WORD_W)-1:0]),
+ .m_axis_tkeep (s1.axis.tdata[OUT_WORDS[1]*WORD_W+:OUT_WORDS[1]]),
+ .m_axis_tlast (s1.axis.tlast),
+ .m_axis_tvalid(s1.axis.tvalid),
+ .m_axis_tready(s1.axis.tready)
+ );
+
+ // A rational integer 4-to-3 downsizer
+ axis_width_conv #(
+ .WORD_W(WORD_W), .IN_WORDS(IN_WORDS[2]), .OUT_WORDS(OUT_WORDS[2]),
+ .SYNC_CLKS(0), .PIPELINE("OUT")
+ ) dut2 (
+ .s_axis_aclk (clk50),
+ .s_axis_rst (areset),
+ .s_axis_tdata (m2.axis.tdata[(IN_WORDS[2]*WORD_W)-1:0]),
+ .s_axis_tkeep (m2.axis.tdata[IN_WORDS[2]*WORD_W+:IN_WORDS[2]]),
+ .s_axis_tlast (m2.axis.tlast),
+ .s_axis_tvalid(m2.axis.tvalid),
+ .s_axis_tready(m2.axis.tready),
+ .m_axis_aclk (clk200),
+ .m_axis_rst (areset),
+ .m_axis_tdata (s2.axis.tdata[(OUT_WORDS[2]*WORD_W)-1:0]),
+ .m_axis_tkeep (s2.axis.tdata[OUT_WORDS[2]*WORD_W+:OUT_WORDS[2]]),
+ .m_axis_tlast (s2.axis.tlast),
+ .m_axis_tvalid(s2.axis.tvalid),
+ .m_axis_tready(s2.axis.tready)
+ );
+
+ // An integer 4-to-1 downsizer but with 2 words per cycle
+ axis_width_conv #(
+ .WORD_W(WORD_W), .IN_WORDS(IN_WORDS[3]), .OUT_WORDS(OUT_WORDS[3]),
+ .SYNC_CLKS(0), .PIPELINE("NONE")
+ ) dut3 (
+ .s_axis_aclk (clk50),
+ .s_axis_rst (areset),
+ .s_axis_tdata (m3.axis.tdata[(IN_WORDS[3]*WORD_W)-1:0]),
+ .s_axis_tkeep (m3.axis.tdata[IN_WORDS[3]*WORD_W+:IN_WORDS[3]]),
+ .s_axis_tlast (m3.axis.tlast),
+ .s_axis_tvalid(m3.axis.tvalid),
+ .s_axis_tready(m3.axis.tready),
+ .m_axis_aclk (clk166),
+ .m_axis_rst (areset),
+ .m_axis_tdata (s3.axis.tdata[(OUT_WORDS[3]*WORD_W)-1:0]),
+ .m_axis_tkeep (s3.axis.tdata[OUT_WORDS[3]*WORD_W+:OUT_WORDS[3]]),
+ .m_axis_tlast (s3.axis.tlast),
+ .m_axis_tvalid(s3.axis.tvalid),
+ .m_axis_tready(s3.axis.tready)
+ );
+
+ // A passthrough module (no up/down sizing) but with 8 words per cycle
+ axis_width_conv #(
+ .WORD_W(WORD_W), .IN_WORDS(IN_WORDS[4]), .OUT_WORDS(OUT_WORDS[4]),
+ .SYNC_CLKS(0), .PIPELINE("INOUT")
+ ) dut4 (
+ .s_axis_aclk (clk50),
+ .s_axis_rst (areset),
+ .s_axis_tdata (m4.axis.tdata[(IN_WORDS[4]*WORD_W)-1:0]),
+ .s_axis_tkeep (m4.axis.tdata[IN_WORDS[4]*WORD_W+:IN_WORDS[4]]),
+ .s_axis_tlast (m4.axis.tlast),
+ .s_axis_tvalid(m4.axis.tvalid),
+ .s_axis_tready(m4.axis.tready),
+ .m_axis_aclk (clk166),
+ .m_axis_rst (areset),
+ .m_axis_tdata (s4.axis.tdata[(OUT_WORDS[4]*WORD_W)-1:0]),
+ .m_axis_tkeep (s4.axis.tdata[OUT_WORDS[4]*WORD_W+:OUT_WORDS[4]]),
+ .m_axis_tlast (s4.axis.tlast),
+ .m_axis_tvalid(s4.axis.tvalid),
+ .m_axis_tready(s4.axis.tready)
+ );
+
+ // A rational integer 17-to-19 upsizer
+ axis_width_conv #(
+ .WORD_W(WORD_W), .IN_WORDS(IN_WORDS[5]), .OUT_WORDS(OUT_WORDS[5]),
+ .SYNC_CLKS(1), .PIPELINE("INOUT")
+ ) dut5 (
+ .s_axis_aclk (clk50),
+ .s_axis_rst (areset),
+ .s_axis_tdata (m5.axis.tdata[(IN_WORDS[5]*WORD_W)-1:0]),
+ .s_axis_tkeep (m5.axis.tdata[IN_WORDS[5]*WORD_W+:IN_WORDS[5]]),
+ .s_axis_tlast (m5.axis.tlast),
+ .s_axis_tvalid(m5.axis.tvalid),
+ .s_axis_tready(m5.axis.tready),
+ .m_axis_aclk (clk50),
+ .m_axis_rst (areset),
+ .m_axis_tdata (s5.axis.tdata[(OUT_WORDS[5]*WORD_W)-1:0]),
+ .m_axis_tkeep (s5.axis.tdata[OUT_WORDS[5]*WORD_W+:OUT_WORDS[5]]),
+ .m_axis_tlast (s5.axis.tlast),
+ .m_axis_tvalid(s5.axis.tvalid),
+ .m_axis_tready(s5.axis.tready)
+ );
+
+ // Push a test ramp packet into the specific instance of the module
+ // - words: The size of the packet in words
+ // - inst: The instance number of the module to send to
+ // - gaps: If 1 then insert bubble cycles randomly in the stream
+ task push_test_pkt(input integer words, input integer inst, input logic gaps);
+ begin
+ logic [(MAX_IN_WORDS*WORD_W)-1:0] data = 0;
+ logic [MAX_IN_WORDS-1:0] keep = 0;
+ logic last = 0;
+ integer nspc = IN_WORDS[inst];
+ integer lines = words/nspc;
+ integer residue = words % nspc;
+ if (residue != 0) begin
+ lines = lines + 1;
+ end
+ for (int l = 0; l < lines; l=l+1) begin
+ last = (l == lines-1);
+ for (int p = 0; p < nspc; p=p+1) begin
+ data[p*WORD_W+:WORD_W] = (l*nspc)+p;
+ end
+ keep = (last && (residue != 0)) ? (1<<residue)-1 : (1<<nspc)-1;
+ if (inst == 0)
+ m0.push_word({keep[IN_WORDS[0]-1:0], data[(IN_WORDS[0]*WORD_W)-1:0]}, last);
+ else if (inst == 1)
+ m1.push_word({keep[IN_WORDS[1]-1:0], data[(IN_WORDS[1]*WORD_W)-1:0]}, last);
+ else if (inst == 2)
+ m2.push_word({keep[IN_WORDS[2]-1:0], data[(IN_WORDS[2]*WORD_W)-1:0]}, last);
+ else if (inst == 3)
+ m3.push_word({keep[IN_WORDS[3]-1:0], data[(IN_WORDS[3]*WORD_W)-1:0]}, last);
+ else if (inst == 4)
+ m4.push_word({keep[IN_WORDS[4]-1:0], data[(IN_WORDS[4]*WORD_W)-1:0]}, last);
+ else
+ m5.push_word({keep[IN_WORDS[5]-1:0], data[(IN_WORDS[5]*WORD_W)-1:0]}, last);
+
+ // Keep tvalid deasserted randomly introduce gaps
+ if (gaps) begin
+ integer r = $urandom_range(0, 100);
+ if (r < 20) repeat(r) @(posedge clk50);
+ end
+// $display("PUSH(%02d)[%03d]: D{%0x}, K{%0b}, L{%0b}", inst, l, data, keep, last);
+ end
+ end
+ endtask
+
+ // Pull a test ramp packet from the specific instance of the module
+ // - words: The size of the packet in words
+ // - inst: The instance number of the module to send to
+ // - gaps: If 1 then insert bubble cycles randomly in the stream
+ // - ok: If 1 then all sanity checks have passed
+ task pull_test_pkt(input integer words, input integer inst, input logic gaps, output logic ok);
+ begin
+ logic [(MAX_OUT_WORDS*WORD_W)-1:0] pull_data = 0;
+ logic [MAX_OUT_WORDS-1:0] pull_keep = 0;
+ logic pull_last = 0;
+
+ logic [(MAX_OUT_WORDS*WORD_W)-1:0] data = 0;
+ logic [MAX_OUT_WORDS-1:0] keep = 0;
+ logic last = 0;
+ logic [(MAX_OUT_WORDS*WORD_W)-1:0] mask = 0;
+ integer nspc = OUT_WORDS[inst];
+ integer lines = words/nspc;
+ integer residue = words % nspc;
+ if (residue != 0) begin
+ lines = lines + 1;
+ end
+ for (int l = 0; l < lines; l=l+1) begin
+ last = (l == lines-1);
+ for (int p = 0; p < nspc; p=p+1) begin
+ data[p*WORD_W+:WORD_W] = (l*nspc)+p;
+ end
+ keep = (last && (residue != 0)) ? (1<<residue)-1 : (1<<nspc)-1;
+ mask = (last && (residue != 0)) ? (1<<(residue*WORD_W))-1 : (1<<(nspc*WORD_W))-1;
+ if (inst == 0)
+ s0.pull_word({pull_keep[OUT_WORDS[0]-1:0], pull_data[(OUT_WORDS[0]*WORD_W)-1:0]}, pull_last);
+ else if (inst == 1)
+ s1.pull_word({pull_keep[OUT_WORDS[1]-1:0], pull_data[(OUT_WORDS[1]*WORD_W)-1:0]}, pull_last);
+ else if (inst == 2)
+ s2.pull_word({pull_keep[OUT_WORDS[2]-1:0], pull_data[(OUT_WORDS[2]*WORD_W)-1:0]}, pull_last);
+ else if (inst == 3)
+ s3.pull_word({pull_keep[OUT_WORDS[3]-1:0], pull_data[(OUT_WORDS[3]*WORD_W)-1:0]}, pull_last);
+ else if (inst == 4)
+ s4.pull_word({pull_keep[OUT_WORDS[4]-1:0], pull_data[(OUT_WORDS[4]*WORD_W)-1:0]}, pull_last);
+ else
+ s5.pull_word({pull_keep[OUT_WORDS[5]-1:0], pull_data[(OUT_WORDS[5]*WORD_W)-1:0]}, pull_last);
+
+ // Keep tready deasserted randomly introduce gaps
+ if (gaps) begin
+ integer r = $urandom_range(0, 100);
+ if (r < 20) repeat(r) @(posedge clk50);
+ end
+ // Check data, keep and last
+ ok = ((data&mask) == (pull_data&mask)) && (keep == (pull_keep&((1<<nspc)-1))) && (last == pull_last);
+// $display("PULL(%02d)[%03d]: D{%0x =? %0x}, K{%0b =? %0b}, L{%0b =? %0b} ... %s",
+// inst, l, (data&mask), (pull_data&mask), keep, (pull_keep&((1<<nspc)-1)), last, pull_last, (ok?"OK":"FAIL"));
+ end
+ end
+ endtask
+
+ initial begin : tb_main
+ string s;
+
+ `TEST_CASE_START("Wait for Reset");
+ m0.reset();
+ m1.reset();
+ m2.reset();
+ m3.reset();
+ m4.reset();
+ m5.reset();
+ s0.reset();
+ s1.reset();
+ s2.reset();
+ s3.reset();
+ s4.reset();
+ s5.reset();
+ while (areset) @(posedge clk50);
+ `TEST_CASE_DONE(~areset);
+
+ // Iterate through two modes:
+ // - g == 0: Gapless send and receive. Transfer as fast as possible
+ // - g == 1: Sender and receiver AXI streams will have random bubbles
+ for (int g = 0; g < 2; g=g+1) begin
+ // Iterate through the various instances of the module
+ for (int i = 0; i < NINST; i=i+1) begin
+ $sformat(s, "Testing Instance %0d (%0s gaps)", i, (g==0?"without":"with"));
+ `TEST_CASE_START(s);
+ fork
+ begin
+ // Send packets of increasing size (up to MAX_SPP) to test all
+ // configurations of tkeep and tlast.
+ for (int n = 1; n <= MAX_SPP; n=n+1) begin
+ push_test_pkt(n, i, g);
+ end
+ end
+ begin
+ logic ok;
+ // Receive packets of increasing size and validate tlast, tkeep and tdata
+ for (int n = 1; n <= MAX_SPP; n=n+1) begin
+ pull_test_pkt(n, i, g, ok);
+ $sformat(s, "Packet Validation Failed! (Size=%0d)", n);
+ `ASSERT_ERROR(ok, s);
+ end
+ end
+ join
+ // Gaps to separate the tests in the wfm viewer
+ repeat (100) @(posedge clk50);
+ `TEST_CASE_DONE(1'b1);
+ end
+ end
+
+ `TEST_BENCH_DONE;
+ end
+endmodule