diff options
author | Wade Fife <wade.fife@ettus.com> | 2021-02-16 19:38:48 -0600 |
---|---|---|
committer | Wade Fife <wade.fife@ettus.com> | 2021-11-04 11:04:54 -0500 |
commit | a94ea11f00bba2c4227c9ab173eb4b6ef1049bad (patch) | |
tree | b0fcc3dcc26c42a2427b22a74cd6c0b903acb1b9 /fpga/usrp3/lib/rfnoc/sim | |
parent | f6ec1496486f14ce16042062bdddcec38fa316a5 (diff) | |
download | uhd-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')
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 |