aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/lib/rfnoc/sim
diff options
context:
space:
mode:
authorWade Fife <wade.fife@ettus.com>2021-02-16 19:38:48 -0600
committerWade Fife <wade.fife@ettus.com>2021-11-04 11:04:54 -0500
commita94ea11f00bba2c4227c9ab173eb4b6ef1049bad (patch)
treeb0fcc3dcc26c42a2427b22a74cd6c0b903acb1b9 /fpga/usrp3/lib/rfnoc/sim
parentf6ec1496486f14ce16042062bdddcec38fa316a5 (diff)
downloaduhd-a94ea11f00bba2c4227c9ab173eb4b6ef1049bad.tar.gz
uhd-a94ea11f00bba2c4227c9ab173eb4b6ef1049bad.tar.bz2
uhd-a94ea11f00bba2c4227c9ab173eb4b6ef1049bad.zip
fpga: rfnoc: Add RFNoC CHDR resize module
Diffstat (limited to 'fpga/usrp3/lib/rfnoc/sim')
-rw-r--r--fpga/usrp3/lib/rfnoc/sim/chdr_resize_tb/Makefile39
-rw-r--r--fpga/usrp3/lib/rfnoc/sim/chdr_resize_tb/chdr_resize_all_tb.sv46
-rw-r--r--fpga/usrp3/lib/rfnoc/sim/chdr_resize_tb/chdr_resize_tb.sv666
3 files changed, 751 insertions, 0 deletions
diff --git a/fpga/usrp3/lib/rfnoc/sim/chdr_resize_tb/Makefile b/fpga/usrp3/lib/rfnoc/sim/chdr_resize_tb/Makefile
new file mode 100644
index 000000000..813ae2b13
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/sim/chdr_resize_tb/Makefile
@@ -0,0 +1,39 @@
+#
+# Copyright 2021 Ettus Research, a National Instruments Brand
+#
+# 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_preample after defining BASE_DIR
+include $(BASE_DIR)/../tools/make/viv_sim_preamble.mak
+
+#-------------------------------------------------
+# Design Specific
+#-------------------------------------------------
+
+DESIGN_SRCS += $(abspath \
+$(abspath ../../utils/chdr_convert_up.v) \
+$(abspath ../../utils/chdr_convert_down.v) \
+$(abspath ../../utils/chdr_resize.v) \
+)
+
+#-------------------------------------------------
+# Testbench Specific
+#-------------------------------------------------
+SIM_TOP = chdr_resize_all_tb
+SIM_SRCS = \
+$(abspath chdr_resize_tb.sv) \
+$(abspath chdr_resize_all_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/lib/rfnoc/sim/chdr_resize_tb/chdr_resize_all_tb.sv b/fpga/usrp3/lib/rfnoc/sim/chdr_resize_tb/chdr_resize_all_tb.sv
new file mode 100644
index 000000000..997a6fa09
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/sim/chdr_resize_tb/chdr_resize_all_tb.sv
@@ -0,0 +1,46 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_resize_all_tb
+//
+// Description:
+//
+// Top-level testbench for chdr_resize, testing different configurations of
+// the module.
+//
+
+module chdr_resize_all_tb;
+ // Up-size CHDR bus
+ chdr_resize_tb #(.I_CHDR_W( 64), .O_CHDR_W(128), .PIPELINE("NONE" )) chdr_resize_tb_00();
+ chdr_resize_tb #(.I_CHDR_W( 64), .O_CHDR_W(256), .PIPELINE("IN" )) chdr_resize_tb_01();
+ chdr_resize_tb #(.I_CHDR_W(128), .O_CHDR_W(256), .PIPELINE("OUT" )) chdr_resize_tb_02();
+ chdr_resize_tb #(.I_CHDR_W(128), .O_CHDR_W(512), .PIPELINE("INOUT")) chdr_resize_tb_03();
+ chdr_resize_tb #(.I_CHDR_W(256), .O_CHDR_W(512), .PIPELINE("NONE" )) chdr_resize_tb_04();
+ // Down-size CHDR bus
+ chdr_resize_tb #(.I_CHDR_W(128), .O_CHDR_W( 64), .PIPELINE("NONE" )) chdr_resize_tb_10();
+ chdr_resize_tb #(.I_CHDR_W(256), .O_CHDR_W( 64), .PIPELINE("IN" )) chdr_resize_tb_11();
+ chdr_resize_tb #(.I_CHDR_W(256), .O_CHDR_W(128), .PIPELINE("OUT" )) chdr_resize_tb_12();
+ chdr_resize_tb #(.I_CHDR_W(512), .O_CHDR_W(128), .PIPELINE("INOUT")) chdr_resize_tb_13();
+ chdr_resize_tb #(.I_CHDR_W(512), .O_CHDR_W(256), .PIPELINE("NONE" )) chdr_resize_tb_14();
+
+ // Up-size CHDR encoding (keep bus width)
+ chdr_resize_tb #(.I_CHDR_W( 64), .O_CHDR_W(128), .I_DATA_W( 64), .O_DATA_W( 64), .PIPELINE("NONE" )) chdr_resize_tb_20();
+ chdr_resize_tb #(.I_CHDR_W( 64), .O_CHDR_W(512), .I_DATA_W( 64), .O_DATA_W( 64), .PIPELINE("IN" )) chdr_resize_tb_21();
+ chdr_resize_tb #(.I_CHDR_W(128), .O_CHDR_W(256), .I_DATA_W(128), .O_DATA_W(128), .PIPELINE("OUT" )) chdr_resize_tb_22();
+ chdr_resize_tb #(.I_CHDR_W(128), .O_CHDR_W(512), .I_DATA_W(128), .O_DATA_W(128), .PIPELINE("INOUT")) chdr_resize_tb_23();
+ chdr_resize_tb #(.I_CHDR_W(256), .O_CHDR_W(512), .I_DATA_W(256), .O_DATA_W(256), .PIPELINE("NONE" )) chdr_resize_tb_24();
+ // Down-size CHDR encoding (keep bus width)
+ chdr_resize_tb #(.I_CHDR_W(128), .O_CHDR_W( 64), .I_DATA_W( 64), .O_DATA_W( 64), .PIPELINE("NONE" )) chdr_resize_tb_30();
+ chdr_resize_tb #(.I_CHDR_W(512), .O_CHDR_W( 64), .I_DATA_W( 64), .O_DATA_W( 64), .PIPELINE("IN" )) chdr_resize_tb_31();
+ chdr_resize_tb #(.I_CHDR_W(256), .O_CHDR_W(128), .I_DATA_W(128), .O_DATA_W(128), .PIPELINE("OUT" )) chdr_resize_tb_32();
+ chdr_resize_tb #(.I_CHDR_W(512), .O_CHDR_W(128), .I_DATA_W(128), .O_DATA_W(128), .PIPELINE("INOUT")) chdr_resize_tb_33();
+ chdr_resize_tb #(.I_CHDR_W(512), .O_CHDR_W(256), .I_DATA_W(256), .O_DATA_W(256), .PIPELINE("NONE" )) chdr_resize_tb_34();
+
+ // No resize (pass through)
+ chdr_resize_tb #(.I_CHDR_W(512), .O_CHDR_W(512), .PIPELINE("NONE" )) chdr_resize_tb_40();
+ chdr_resize_tb #(.I_CHDR_W(256), .O_CHDR_W(256), .PIPELINE("IN" )) chdr_resize_tb_41();
+ chdr_resize_tb #(.I_CHDR_W(128), .O_CHDR_W(128), .PIPELINE("OUT" )) chdr_resize_tb_42();
+ chdr_resize_tb #(.I_CHDR_W( 64), .O_CHDR_W( 64), .PIPELINE("INOUT")) chdr_resize_tb_43();
+endmodule : chdr_resize_all_tb
diff --git a/fpga/usrp3/lib/rfnoc/sim/chdr_resize_tb/chdr_resize_tb.sv b/fpga/usrp3/lib/rfnoc/sim/chdr_resize_tb/chdr_resize_tb.sv
new file mode 100644
index 000000000..63b314abd
--- /dev/null
+++ b/fpga/usrp3/lib/rfnoc/sim/chdr_resize_tb/chdr_resize_tb.sv
@@ -0,0 +1,666 @@
+//
+// Copyright 2021 Ettus Research, a National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: chdr_resize_tb
+//
+// Description:
+//
+// Testbench for chdr_resize.
+//
+
+`default_nettype none
+
+
+module chdr_resize_tb #(
+ parameter I_CHDR_W = 64,
+ parameter O_CHDR_W = 128,
+ parameter I_DATA_W = I_CHDR_W,
+ parameter O_DATA_W = O_CHDR_W,
+ parameter USER_W = 4,
+ parameter PIPELINE = "NONE"
+);
+
+ // Include macros and time declarations for use with PkgTestExec
+ `include "test_exec.svh"
+
+ `define MIN(A, B) ((A)<(B)?(A):(B))
+ `define DIV_CEIL(N, D) (((N)+(D)-1)/(D)) // ceiling(N/D)
+
+ import PkgTestExec::*;
+ import PkgChdrUtils::*;
+ import PkgChdrBfm::*;
+ import PkgRandom::*;
+
+ // Clock periods
+ localparam real CLK_PERIOD = 10.0;
+
+ // Widths for BFMs to use
+ localparam ITEM_W = 8;
+
+ // Test parameters
+ localparam NUM_PACKETS = 1000; // Number of packets to test
+ localparam MAX_MDATA_WORDS = 31; // Maximum number of metadata words (31
+ // is the max supported by CHDR).
+ localparam MAX_PYLD_WORDS = 64; // Maximum number of payload words.
+ localparam USE_RANDOM = 1; // Use random vs. sequential data.
+ localparam DEBUG = 0; // Display extra debug info
+
+
+ //---------------------------------------------------------------------------
+ // Clocks and Resets
+ //---------------------------------------------------------------------------
+
+ bit clk;
+ bit rst;
+
+ sim_clock_gen #(.PERIOD(CLK_PERIOD), .AUTOSTART(0))
+ clk_gen (.clk(clk), .rst(rst));
+
+
+ //---------------------------------------------------------------------------
+ // Bus Functional Models
+ //---------------------------------------------------------------------------
+
+ // Connections to DUT as interfaces:
+ AxiStreamIf #(I_CHDR_W) m_chdr (clk, 1'b0);
+ AxiStreamIf #(O_CHDR_W) s_chdr (clk, 1'b0);
+
+ // CHDR BFMs. Because the input and output have different CHDR widths, we
+ // need two BFMs, one for each CHDR width.
+ ChdrBfm #(I_CHDR_W) m_bfm = new(m_chdr, null);
+ ChdrBfm #(O_CHDR_W) s_bfm = new(null, s_chdr);
+
+ // CHDR data types
+ typedef ChdrPacket #(I_CHDR_W)::ChdrPacket_t IChdrPacket_t;
+ typedef ChdrPacket #(O_CHDR_W)::ChdrPacket_t OChdrPacket_t;
+ typedef ChdrData #(I_CHDR_W, ITEM_W)::chdr_word_t i_chdr_word_t;
+ typedef ChdrData #(O_CHDR_W, ITEM_W)::chdr_word_t o_chdr_word_t;
+
+
+ //---------------------------------------------------------------------------
+ // Device Under Test (DUT)
+ //---------------------------------------------------------------------------
+ //
+ // To simplify the testbench we randomly generate CHDR packets, resize them,
+ // then change their size back. This tests up-sizing and down-sizing
+ // simultaneously and simplifies output checking. It's possible, however,
+ // that we make a mistake up-sizing, then undo that mistake when downsizing,
+ // so there is some risk to this simplification.
+ //
+ //---------------------------------------------------------------------------
+
+ wire [I_DATA_W-1:0] i_data_tdata;
+ wire [ USER_W-1:0] i_data_tuser;
+ wire i_data_tlast;
+ wire i_data_tvalid;
+ wire i_data_tready;
+
+ wire [O_DATA_W-1:0] o_data_tdata;
+ wire [ USER_W-1:0] o_data_tuser;
+ wire o_data_tlast;
+ wire o_data_tvalid;
+ wire o_data_tready;
+
+ chdr_resize #(
+ .I_CHDR_W (I_CHDR_W),
+ .O_CHDR_W (O_CHDR_W),
+ .I_DATA_W (I_DATA_W),
+ .O_DATA_W (O_DATA_W),
+ .USER_W (USER_W),
+ .PIPELINE (PIPELINE)
+ ) chdr_resize_i (
+ .clk (clk),
+ .rst (rst),
+ .i_chdr_tdata (i_data_tdata),
+ .i_chdr_tuser (i_data_tuser),
+ .i_chdr_tlast (i_data_tlast),
+ .i_chdr_tvalid (i_data_tvalid),
+ .i_chdr_tready (i_data_tready),
+ .o_chdr_tdata (o_data_tdata),
+ .o_chdr_tuser (o_data_tuser),
+ .o_chdr_tlast (o_data_tlast),
+ .o_chdr_tvalid (o_data_tvalid),
+ .o_chdr_tready (o_data_tready)
+ );
+
+
+ //---------------------------------------------------------------------------
+ // Bus Resizer for DUT
+ //---------------------------------------------------------------------------
+ //
+ // Resize the DUT inputs/outputs to match match the widths used by our BFMs.
+ //
+ //---------------------------------------------------------------------------
+
+ localparam IN_WORD_W = `MIN(I_CHDR_W, I_DATA_W);
+ localparam OUT_WORD_W = `MIN(O_CHDR_W, O_DATA_W);
+ localparam OUT_KEEP_W = O_CHDR_W/OUT_WORD_W;
+
+ wire [I_CHDR_W-1:0] i_chdr_tdata;
+ wire i_chdr_tlast;
+ wire i_chdr_tvalid;
+ wire i_chdr_tready;
+
+ wire [ O_CHDR_W-1:0] o_chdr_tdata_unmasked;
+ wire [ O_CHDR_W-1:0] o_chdr_tdata;
+ wire [OUT_KEEP_W-1:0] o_chdr_tkeep;
+ wire o_chdr_tlast;
+ wire o_chdr_tvalid;
+ wire o_chdr_tready;
+
+ axis_width_conv #(
+ .WORD_W (IN_WORD_W),
+ .IN_WORDS (I_CHDR_W/IN_WORD_W),
+ .OUT_WORDS (I_DATA_W/IN_WORD_W),
+ .SYNC_CLKS (1),
+ .PIPELINE ("NONE")
+ ) axis_width_conv_in (
+ .s_axis_aclk (clk),
+ .s_axis_rst (rst),
+ .s_axis_tdata (i_chdr_tdata),
+ .s_axis_tkeep ('1),
+ .s_axis_tlast (i_chdr_tlast),
+ .s_axis_tvalid (i_chdr_tvalid),
+ .s_axis_tready (i_chdr_tready),
+ .m_axis_aclk (clk),
+ .m_axis_rst (rst),
+ .m_axis_tdata (i_data_tdata),
+ .m_axis_tkeep (),
+ .m_axis_tlast (i_data_tlast),
+ .m_axis_tvalid (i_data_tvalid),
+ .m_axis_tready (i_data_tready)
+ );
+
+ axis_width_conv #(
+ .WORD_W (OUT_WORD_W),
+ .IN_WORDS (O_DATA_W/OUT_WORD_W),
+ .OUT_WORDS (O_CHDR_W/OUT_WORD_W),
+ .SYNC_CLKS (1),
+ .PIPELINE ("NONE")
+ ) axis_width_conv_out (
+ .s_axis_aclk (clk),
+ .s_axis_rst (rst),
+ .s_axis_tdata (o_data_tdata),
+ .s_axis_tkeep ('1),
+ .s_axis_tlast (o_data_tlast),
+ .s_axis_tvalid (o_data_tvalid),
+ .s_axis_tready (o_data_tready),
+ .m_axis_aclk (clk),
+ .m_axis_rst (rst),
+ .m_axis_tdata (o_chdr_tdata_unmasked),
+ .m_axis_tkeep (o_chdr_tkeep),
+ .m_axis_tlast (o_chdr_tlast),
+ .m_axis_tvalid (o_chdr_tvalid),
+ .m_axis_tready (o_chdr_tready)
+ );
+
+
+ // Invalidate the bits we shouldn't be keeping by changing them to X. This
+ // ensures we aren't checking bits that aren't there and thinking they're OK
+ // because they happen to be 0.
+ reg [O_CHDR_W-1:0] o_chdr_keep_mask;
+
+ always_comb begin
+ for (int w = 0; w < OUT_KEEP_W; w++) begin
+ if (o_chdr_tkeep[w] == 1'b1) begin
+ o_chdr_keep_mask[w*OUT_WORD_W+:OUT_WORD_W] = {OUT_WORD_W{1'b1}};
+ end else begin
+ o_chdr_keep_mask[w*OUT_WORD_W+:OUT_WORD_W] = {OUT_WORD_W{1'bX}};
+ end
+ end
+ end
+
+ // This mask operation leaves the bits we're keeping unmodified but changes
+ // the ones we aren't keeping to X.
+ assign o_chdr_tdata = o_chdr_tdata_unmasked & o_chdr_keep_mask;
+
+
+ //---------------------------------------------------------------------------
+ // BFM Connections
+ //---------------------------------------------------------------------------
+
+ // Input
+ assign i_chdr_tdata = m_chdr.tdata;
+ assign i_chdr_tlast = m_chdr.tlast;
+ assign i_chdr_tvalid = m_chdr.tvalid;
+ assign m_chdr.tready = i_chdr_tready;
+
+ // Output
+ assign s_chdr.tdata = o_chdr_tdata;
+ assign s_chdr.tlast = o_chdr_tlast;
+ assign s_chdr.tvalid = o_chdr_tvalid;
+ assign o_chdr_tready = s_chdr.tready;
+
+
+ //---------------------------------------------------------------------------
+ // Debug Monitors
+ //---------------------------------------------------------------------------
+ //
+ // Display packet info as packets go in/out of the DUT to make packets easier
+ // to find in the simulator.
+ //
+ //---------------------------------------------------------------------------
+
+ if (DEBUG) begin
+ bit i_chdr_sop = 1;
+ bit mid_chdr_sop = 1;
+ bit o_chdr_sop = 1;
+
+ always @(posedge clk) begin
+ chdr_header_t header;
+
+ if (i_chdr_tvalid && i_chdr_tready) begin
+ if (i_chdr_sop) begin
+ header = i_chdr_tdata;
+ $display("In packet, @%0t: 0x%16X %p", $realtime, header, header);
+ i_chdr_sop <= 0;
+ end
+ i_chdr_sop <= i_chdr_tlast;
+ end
+
+ if (o_chdr_tvalid && o_chdr_tready) begin
+ if (o_chdr_sop) begin
+ header = o_chdr_tdata;
+ $display("Out packet, @%0t: 0x%16X %p", $realtime, header, header);
+ o_chdr_sop <= 0;
+ end
+ o_chdr_sop <= o_chdr_tlast;
+ end
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // TUSER Generation
+ //---------------------------------------------------------------------------
+ //
+ // Create a simple counter on TUSER than increments for each packet.
+ //
+ //---------------------------------------------------------------------------
+
+ reg [USER_W-1:0] i_user_count = 0;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ i_user_count <= 0;
+ end else if (i_data_tvalid && i_data_tready) begin
+ if (i_data_tlast) begin
+ i_user_count <= i_user_count + 1;
+ end
+ end
+ end
+
+ // Ensure that i_data_tuser is only valid during the packet to guarantee the
+ // DUT doesn't sample it outside that.
+ assign i_data_tuser =
+ (i_data_tvalid && i_data_tready) ? i_user_count : 'X;
+
+
+ //---------------------------------------------------------------------------
+ // TUSER Checking
+ //---------------------------------------------------------------------------
+ //
+ // Verify that each output packet has the expected count on TUSER.
+ //
+ //---------------------------------------------------------------------------
+
+ reg [USER_W-1:0] o_user_count = 0;
+
+ always @(posedge clk) begin
+ if (rst) begin
+ o_user_count <= 0;
+ end else if (o_data_tvalid && o_data_tready) begin
+ `ASSERT_ERROR(
+ o_data_tuser == o_user_count,
+ "TUSER output doesn't match expected count."
+ );
+ if (o_data_tlast) begin
+ o_user_count <= o_user_count+1;
+ end
+ end
+ end
+
+
+ //---------------------------------------------------------------------------
+ // Helper Logic
+ //---------------------------------------------------------------------------
+
+ // Convert the input packet to the output packet, based on the configured
+ // I_CHDR_W and O_CHDR_W. Returns the expected output packet. This function
+ // does to the input ChdrPacket what the DUT is supposed to do to the CHDR
+ // AXI-Stream packet.
+ function automatic OChdrPacket_t convert_packet(IChdrPacket_t i_packet);
+ OChdrPacket_t o_packet = new;
+ int num_mdata;
+ int max_num_mdata = 2**$bits(o_packet.header.num_mdata)-1;
+
+ // Check if we're doing any conversion or resizing
+ if (I_CHDR_W == O_CHDR_W && I_CHDR_W == I_DATA_W && O_CHDR_W == O_DATA_W) begin
+ // We don't modify the packet at all in this case. We should just pass it
+ // through.
+ o_packet = OChdrPacket_t'(i_packet.copy());
+ return o_packet;
+ end
+
+ //---------------------------------
+ // Update the header
+
+ o_packet.header = i_packet.header;
+
+ // NumMData
+ if (I_CHDR_W > O_CHDR_W) begin
+ // Make sure there isn't too much metadata for a smaller CHDR_W packet
+ num_mdata = i_packet.header.num_mdata * I_CHDR_W / O_CHDR_W;
+ if (num_mdata > max_num_mdata) num_mdata = max_num_mdata;
+ end else begin
+ // Round up to the nearest whole O_CHDR_W word
+ num_mdata = `DIV_CEIL(i_packet.header.num_mdata * I_CHDR_W, O_CHDR_W);
+ end
+ o_packet.header.num_mdata = num_mdata;
+
+ // Length
+ o_packet.header.length =
+ // Header
+ (O_CHDR_W/8) +
+ // Timestamp (goes in same word as header, unless O_CHDR_W is 64-bit)
+ ((o_packet.header.pkt_type == CHDR_DATA_WITH_TS && O_CHDR_W == 64) ? (O_CHDR_W/8) : 0) +
+ // Metadata
+ (O_CHDR_W/8)*o_packet.header.num_mdata +
+ // Payload data. We expect the length of the output packet to be a
+ // multiple of CHDR_W for management packets.
+ ((o_packet.header.pkt_type == CHDR_MANAGEMENT) ?
+ `DIV_CEIL(i_packet.data_bytes(), I_CHDR_W/8) * (O_CHDR_W/8) : i_packet.data_bytes());
+
+ // Timestamp
+ o_packet.timestamp = i_packet.timestamp;
+
+ //---------------------------------
+ // Copy the data and metadata
+
+ if (I_CHDR_W > O_CHDR_W) begin
+ // Drop any metadata beyond what CHDR allows
+ o_packet.metadata = ChdrData#(I_CHDR_W, O_CHDR_W)::chdr_to_item(i_packet.metadata,
+ num_mdata * O_CHDR_W/8);
+ // Drop any O_CHDR_W words that aren't part of the payload
+ o_packet.data = ChdrData#(I_CHDR_W, O_CHDR_W)::chdr_to_item(i_packet.data,
+ `DIV_CEIL(i_packet.data_bytes(), O_CHDR_W/8) * O_CHDR_W/8);
+ end else begin
+ o_chdr_word_t last_mdata;
+ o_packet.metadata = ChdrData#(O_CHDR_W, I_CHDR_W)::item_to_chdr(i_packet.metadata);
+ o_packet.data = ChdrData#(O_CHDR_W, I_CHDR_W)::item_to_chdr(i_packet.data);
+
+ // When I_CHDR_W < O_CHDR_W, the number of metadata bytes might not be a
+ // multiple of O_CHDR_W. The DUT should zero these extra bytes.
+ if (o_packet.metadata.size() > 0) begin
+ last_mdata = o_packet.metadata[$];
+ for (int i = 0; i < o_packet.mdata_bytes()-i_packet.mdata_bytes(); i++) begin
+ last_mdata[$bits(o_chdr_word_t)-i*8-1 -: 8] = 8'd0;
+ end
+ o_packet.metadata[$] = last_mdata;
+ end
+ end
+
+ //---------------------------------
+ // Copy management packet data
+
+ if (o_packet.header.pkt_type == CHDR_MANAGEMENT) begin
+ // Management packets don't get serialized, so we just copy each word,
+ // ignoring the upper bits.
+ o_packet.data = {};
+ for (int i = 0; i < i_packet.data.size(); i++) begin
+ if (i == 0) begin
+ // Update the CHDR width in the management header word
+ o_chdr_word_t word;
+ word = i_packet.data[i];
+ word[47:45] = translate_chdr_w(O_CHDR_W);
+ o_packet.data.push_back(word);
+ end else begin
+ o_packet.data.push_back(i_packet.data[i]);
+ end
+ end
+ end
+
+ return o_packet;
+ endfunction : convert_packet
+
+
+ // Compare the output packet to what we expect the output packet to be.
+ function automatic string compare_packets(
+ input OChdrPacket_t packet,
+ input OChdrPacket_t exp_packet
+ );
+ string msg = "";
+
+ // Check that the headers match (including num_mdata and length)
+ if(packet.header != exp_packet.header) begin
+ msg = { msg, "Headers do not match\n" };
+ end
+
+ // Check that the timestamps match
+ if (exp_packet.header.pkt_type == CHDR_DATA_WITH_TS) begin
+ if(packet.timestamp != exp_packet.timestamp) begin
+ msg = { msg, "Timestamps do not match\n" };
+ end
+ end
+
+ // Check that the metadata matches
+ if(!exp_packet.chdr_word_queues_equal(
+ packet.metadata, exp_packet.metadata, packet.mdata_bytes())
+ ) begin
+ msg = { msg, "Metadata does not match\n" };
+ end
+
+ // Check that the payloads match
+ if(!exp_packet.chdr_word_queues_equal(
+ packet.data, exp_packet.data, exp_packet.data_bytes())
+ ) begin
+ msg = { msg, "Payloads do not match\n" };
+ end
+
+ return msg;
+ endfunction : compare_packets
+
+
+ //---------------------------------------------------------------------------
+ // Tests
+ //---------------------------------------------------------------------------
+
+ // Perform a randomized test with the given stall probabilities in the input
+ // and output ports.
+ task automatic test_random(int in_stall_prob, int out_stall_prob);
+ longint word_count = 0;
+ bit enable_input = 0;
+ string msg;
+ mailbox #(IChdrPacket_t) packets = new;
+
+ msg = $sformatf("Test Random Packets (%0d%%, %0d%%)",
+ in_stall_prob, out_stall_prob);
+ test.start_test(msg, 10ms);
+
+ m_bfm.set_master_stall_prob(in_stall_prob);
+ s_bfm.set_slave_stall_prob(out_stall_prob);
+
+ fork
+ //-------------------------------
+ // Input Process
+ //-------------------------------
+
+ begin : input_process
+ IChdrPacket_t chdr_packet = new;
+ i_chdr_word_t data[$];
+ i_chdr_word_t mdata[$];
+ chdr_header_t header;
+ chdr_timestamp_t timestamp;
+ int data_byte_length;
+ int count;
+
+ repeat (NUM_PACKETS) begin
+ //-------------------------------
+ // Generate a random packet
+ //-------------------------------
+
+ // Start with a random header. Make sure the packet type is legal.
+ do begin
+ header = Rand#($bits(chdr_header_t))::rand_logic();
+ end while (header.pkt_type == CHDR_RESERVED_0 || header.pkt_type == CHDR_RESERVED_1);
+
+ // Generate timestamp
+ if (header.pkt_type == CHDR_DATA_WITH_TS) begin
+ if (USE_RANDOM) timestamp = Rand#($bits(chdr_header_t))::rand_logic();
+ else timestamp = 64'h0123456789ABCDEF;
+ end else begin
+ timestamp = 0;
+ end
+
+ // Generate random metadata (50% chance of no metadata)
+ mdata = {};
+ if ($urandom_range(0, 1)) begin
+ count = 0;
+ repeat ($urandom_range(1, MAX_MDATA_WORDS)) begin
+ if (USE_RANDOM) mdata.push_back(Rand#(I_CHDR_W)::rand_logic());
+ else mdata.push_back(64'hA1000000 + count++);
+ end
+ end
+
+ // Generate random data (always at least one word)
+ data = {};
+ count = 0;
+ repeat ($urandom_range(1, MAX_PYLD_WORDS)) begin
+ if (USE_RANDOM) data.push_back(Rand#(I_CHDR_W)::rand_logic());
+ else data.push_back(64'hB2000000 + count++);
+ end
+
+ // Calculate the size of data minus one word, in bytes
+ data_byte_length = (data.size()-1) * (I_CHDR_W/8);
+ // Add from 1 byte to a full word of bytes to test partially filling
+ // the last word.
+ if (header.pkt_type == CHDR_MANAGEMENT) begin
+ // For management packets, the spec is not explicit about whether
+ // the length must include the padding of the last word. We assume
+ // the worst case, that either case is possible and we expect the
+ // DUT to handle both correctly.
+ data_byte_length += $urandom_range(1, I_CHDR_W/64) * 8;
+ end else begin
+ data_byte_length += $urandom_range(1, I_CHDR_W/8);
+ end
+
+ // Build packet
+ chdr_packet.write_raw(
+ header,
+ data,
+ mdata,
+ timestamp,
+ data_byte_length
+ );
+
+ // Queue the packet
+ m_bfm.put_chdr(chdr_packet);
+
+ // Queue up what we sent for the output process to check
+ packets.put(chdr_packet.copy());
+ end
+ $display("Done inputting packets.");
+ end : input_process
+
+ //-------------------------------
+ // Output Process
+ //-------------------------------
+
+ begin : output_process
+ IChdrPacket_t i_packet;
+ OChdrPacket_t o_packet;
+ OChdrPacket_t exp_packet;
+ int packet_count;
+ string msg;
+
+ repeat (NUM_PACKETS) begin
+ s_bfm.get_chdr(o_packet);
+ packets.get(i_packet);
+ exp_packet = convert_packet(i_packet);
+ msg = compare_packets(o_packet, exp_packet);
+ if(msg != "") begin
+ $display("Sent packet:");
+ i_packet.print(0);
+ $display("Received packet:");
+ o_packet.print(0);
+ $display("Expected packet:");
+ exp_packet.print(0);
+ `ASSERT_ERROR(0, $sformatf(
+ "Output packet is incorrect for the following reasons:\n%s", msg)
+ );
+ end
+ end
+ $display("Done processing packets.");
+ end : output_process
+ join
+
+ test.end_test();
+
+ endtask : test_random
+
+
+ //---------------------------------------------------------------------------
+ // Main Test Process
+ //---------------------------------------------------------------------------
+
+ initial begin : tb_main
+ string msg;
+ string tb_name;
+
+ tb_name = $sformatf( {
+ "chdr_resize_tb\n",
+ "I_CHDR_W = %03d\n",
+ "O_CHDR_W = %03d\n",
+ "I_DATA_W = %03d\n",
+ "O_DATA_W = %03d\n",
+ "PIPLINE = %s" },
+ I_CHDR_W, O_CHDR_W, I_DATA_W, O_DATA_W, PIPELINE
+ );
+ test.start_tb(tb_name, 100ms);
+
+ // Don't start the clocks until after start_tb() returns. This ensures that
+ // the clocks aren't toggling while other instances of this testbench are
+ // running, which speeds up simulation time.
+ clk_gen.start();
+
+ // Start the BFM
+ m_bfm.run();
+ s_bfm.run();
+
+ //--------------------------------
+ // Reset
+ //--------------------------------
+
+ test.start_test("Reset", 10us);
+ clk_gen.reset();
+ if (rst) @rst;
+ test.end_test();
+
+ //--------------------------------
+ // Test Sequences
+ //--------------------------------
+
+ test_random(50, 50); // Test 50% push-back
+ test_random( 0, 0); // Test no push-back
+ test_random(50, 0); // Test for underflow
+ test_random( 0, 50); // Test for overflow
+
+ //--------------------------------
+ // Finish Up
+ //--------------------------------
+
+ // End the TB, but don't $finish, since we don't want to kill other
+ // instances of this testbench that may be running.
+ test.end_tb(0);
+
+ // Kill the clocks to end this instance of the testbench
+ clk_gen.kill();
+
+ end : tb_main
+
+endmodule : chdr_resize_tb
+
+
+`default_nettype wire