aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/sim
diff options
context:
space:
mode:
authorWade Fife <wade.fife@ettus.com>2020-02-24 13:41:20 -0600
committerWade Fife <wade.fife@ettus.com>2020-03-09 13:43:05 -0500
commit6f42ab7c1c962bfc425b6790fe41b2c14769e561 (patch)
tree5e278cc8bb3aba7898ed3ae4bb100e69fe17a7a5 /fpga/usrp3/sim
parentbb6293bc60ccaf13632f9367617e3dfb4a057f9a (diff)
downloaduhd-6f42ab7c1c962bfc425b6790fe41b2c14769e561.tar.gz
uhd-6f42ab7c1c962bfc425b6790fe41b2c14769e561.tar.bz2
uhd-6f42ab7c1c962bfc425b6790fe41b2c14769e561.zip
sim: Add ChdrIfaceBfm test
Diffstat (limited to 'fpga/usrp3/sim')
-rw-r--r--fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv5
-rw-r--r--fpga/usrp3/sim/rfnoc/PkgChdrIfaceBfm.sv11
-rw-r--r--fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/ChdrIfaceBfm_all_tb.sv53
-rw-r--r--fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/ChdrIfaceBfm_tb.sv579
-rw-r--r--fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/Makefile32
5 files changed, 675 insertions, 5 deletions
diff --git a/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv b/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv
index 5863bb598..908447806 100644
--- a/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv
+++ b/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv
@@ -569,8 +569,9 @@ package PkgChdrBfm;
$fatal(1, "ChdrPacket::update_lengths(): Calculated NumMData exceeds maximum size");
// Calculate the Length field
- num_bytes = data.size() * BYTES_PER_CHDR_W; // Payload length
- num_bytes = num_bytes + header_bytes() + mdata_bytes(); // Payload + header length
+ num_bytes = header_bytes() + // Header
+ num_mdata * BYTES_PER_CHDR_W + // Metadata
+ data.size() * BYTES_PER_CHDR_W; // Payload
assert(num_bytes < 2**$bits(chdr_length_t)) else
$fatal(1, "ChdrPacket::update_lengths(): Calculated Length exceeds maximum size");
diff --git a/fpga/usrp3/sim/rfnoc/PkgChdrIfaceBfm.sv b/fpga/usrp3/sim/rfnoc/PkgChdrIfaceBfm.sv
index 243987d68..a25f29d18 100644
--- a/fpga/usrp3/sim/rfnoc/PkgChdrIfaceBfm.sv
+++ b/fpga/usrp3/sim/rfnoc/PkgChdrIfaceBfm.sv
@@ -50,10 +50,15 @@ package PkgChdrIfaceBfm;
virtual AxiStreamIf #(CHDR_W).master m_chdr,
virtual AxiStreamIf #(CHDR_W).slave s_chdr,
input int max_payload_length = 2**$bits(chdr_length_t),
- input int ticks_per_word = CHDR_W/32
+ input int ticks_per_word = CHDR_W/ITEM_W
);
super.new(m_chdr, s_chdr);
this.seq_num = 0;
+
+ assert (CHDR_W % ITEM_W == 0) else begin
+ $fatal(1, "ChdrIfaceBfm::new: CHDR_W must be a multiple of ITEM_W");
+ end
+
set_max_payload_length(max_payload_length);
set_ticks_per_word(ticks_per_word);
endfunction : new
@@ -189,7 +194,7 @@ package PkgChdrIfaceBfm;
if (data_bytes < 0) data_bytes = data.size() * BYTES_PER_CHDR_W;
// Make sure there's not too much metadata for this number of packets
- assert(metadata.size()*$bits(chdr_word_t) < num_pkts * 2**$bits(chdr_num_mdata_t) * CHDR_W) else
+ assert(metadata.size() <= num_pkts * (2**$bits(chdr_num_mdata_t)-1)) else
$fatal(1, "ChdrIfaceBfm::send: Too much metadata for this send request");
// Send the data, one packet at a time.
@@ -204,7 +209,7 @@ package PkgChdrIfaceBfm;
payload_length = data_bytes - (num_pkts-1) * max_payload_length;
first_dword = i*max_payload_length/BYTES_PER_CHDR_W;
last_dword = data.size()-1;
- first_mword = i*(2**$bits(chdr_num_mdata_t) * CHDR_W / $bits(chdr_word_t));
+ first_mword = i*(2**$bits(chdr_num_mdata_t)-1);
last_mword = metadata.size()-1;
end else begin
// A full-sized packet, not the last
diff --git a/fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/ChdrIfaceBfm_all_tb.sv b/fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/ChdrIfaceBfm_all_tb.sv
new file mode 100644
index 000000000..30d72050c
--- /dev/null
+++ b/fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/ChdrIfaceBfm_all_tb.sv
@@ -0,0 +1,53 @@
+//
+// Copyright 2020 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: ChdrIfaceBfm_all_tb
+//
+// Description:
+//
+// This is the top-level testbench that instantiates multiple instances of
+// the ChdrIfaceBfm_tb testbench to test different parameters.
+//
+
+module ChdrIfaceBfm_all_tb();
+
+ `include "test_exec.svh"
+
+ //---------------------------------------------------------------------------
+ // Test Definitions
+ //---------------------------------------------------------------------------
+
+ typedef struct {
+ int CHDR_W;
+ int ITEM_W;
+ } test_config_t;
+
+ localparam NUM_TESTS = 8;
+
+ localparam test_config_t tests[NUM_TESTS] = '{
+ '{ CHDR_W: 64, ITEM_W: 8 },
+ '{ CHDR_W: 64, ITEM_W: 16 },
+ '{ CHDR_W: 64, ITEM_W: 32 },
+ '{ CHDR_W: 64, ITEM_W: 64 },
+ '{ CHDR_W: 128, ITEM_W: 8 },
+ '{ CHDR_W: 128, ITEM_W: 16 },
+ '{ CHDR_W: 128, ITEM_W: 32 },
+ '{ CHDR_W: 128, ITEM_W: 64 }
+ };
+
+
+ //---------------------------------------------------------------------------
+ // DUT Instances
+ //---------------------------------------------------------------------------
+
+ genvar i;
+ for (i = 0; i < NUM_TESTS; i++) begin : gen_test_config
+ ChdrIfaceBfm_tb #(
+ .CHDR_W (tests[i].CHDR_W),
+ .ITEM_W (tests[i].ITEM_W)
+ ) ChdrIfaceBfm_tb_i ();
+ end : gen_test_config
+
+endmodule : ChdrIfaceBfm_all_tb
diff --git a/fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/ChdrIfaceBfm_tb.sv b/fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/ChdrIfaceBfm_tb.sv
new file mode 100644
index 000000000..bf771ac49
--- /dev/null
+++ b/fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/ChdrIfaceBfm_tb.sv
@@ -0,0 +1,579 @@
+//
+// Copyright 2020 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: ChdrIfaceBfm_tb
+//
+// Description: This is the testbench for the ChdrIfaceBfm class.
+//
+
+module ChdrIfaceBfm_tb #(
+ parameter int CHDR_W = 64,
+ parameter int ITEM_W = 32,
+ parameter int SPP = 64
+);
+
+ `include "test_exec.svh"
+
+ import PkgTestExec::*;
+ import PkgChdrUtils::*;
+ import PkgChdrIfaceBfm::*;
+
+
+ //---------------------------------------------------------------------------
+ // Simulation Constants
+ //---------------------------------------------------------------------------
+
+ localparam bit VERBOSE = 0; // Set to 1 for more display output
+
+ localparam realtime CLOCK_PER = 10.0ns;
+
+ localparam int WPP = SPP * ITEM_W / CHDR_W; // CHDR words per packet
+ localparam int BYTES_PER_ITEM = ITEM_W/8;
+ localparam int MAX_PYLD_BYTES = SPP * BYTES_PER_ITEM;
+ localparam int MAX_PACKETS = 3;
+
+
+ //---------------------------------------------------------------------------
+ // Clocks and Resets
+ //---------------------------------------------------------------------------
+
+ bit rfnoc_chdr_clk;
+
+ sim_clock_gen #(CLOCK_PER) rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst());
+
+
+ //---------------------------------------------------------------------------
+ // CHDR Types
+ //---------------------------------------------------------------------------
+
+ typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t;
+ typedef ChdrData #(CHDR_W, ITEM_W)::item_t item_t;
+
+ ChdrData #(CHDR_W, ITEM_W) chdr_data;
+
+
+ //---------------------------------------------------------------------------
+ // BFM
+ //---------------------------------------------------------------------------
+
+ AxiStreamIf #(CHDR_W) chdr_ifc (rfnoc_chdr_clk, 1'b0);
+
+ // Loop the CHDR BFM back to itself
+ ChdrIfaceBfm #(CHDR_W, ITEM_W) bfm = new(chdr_ifc, chdr_ifc, MAX_PYLD_BYTES);
+
+
+ //---------------------------------------------------------------------------
+ // Data Structures
+ //---------------------------------------------------------------------------
+
+ typedef struct {
+ chdr_word_t data[$];
+ int data_bytes;
+ chdr_word_t metadata[$];
+ packet_info_t pkt_info;
+ } packet_t;
+
+ typedef enum int {
+ TEST_RECV, TEST_RECV_ADV, TEST_NUM_ITEMS, TEST_EOB, TEST_EOV
+ } test_variant_t;
+
+
+ //---------------------------------------------------------------------------
+ // Utilities
+ //---------------------------------------------------------------------------
+
+ // Rand#(WIDTH)::rand_logic() returns a WIDTH-bit random number. We avoid
+ // std::randomize() due to license requirements and limited tool support.
+ class Rand #(WIDTH = 32);
+
+ static function logic [WIDTH-1:0] rand_logic();
+ logic [WIDTH-1:0] result;
+ int num_rand32 = (WIDTH + 31) / 32;
+ for (int i = 0; i < num_rand32; i++) begin
+ result = {result, $urandom()};
+ end
+ return result;
+ endfunction : rand_logic
+
+ endclass : Rand
+
+
+ // Generate a random packet, in the form of a random packet_t structure.
+ //
+ // packets: Generate multiple packets worth of data if 1, generate 1
+ // packet if 0.
+ // items: Make the data a multiple of items if 1, a of bytes if 0.
+ //
+ function automatic packet_t rand_pkt(bit packets = 0, bit items = 0);
+ packet_t packet;
+ int num_mdata;
+ int num_packets;
+
+ // Decide how many packets we're going to generate
+ if (packets) num_packets = $urandom_range(1, MAX_PACKETS);
+ else num_packets = 1;
+
+ // Decide how much metadata, assuming we can split it across multiple
+ // packets.
+ num_mdata = $urandom_range(0, num_packets*(31));
+
+ // Randomize the rest of the packet info
+ packet.pkt_info = Rand#($bits(packet.pkt_info))::rand_logic();
+
+ // Decide how much data we're going to send. The last packet will be a
+ // random length, all preceding packets will be full length.
+ if (items) packet.data_bytes = $urandom_range(1, SPP) * BYTES_PER_ITEM;
+ else packet.data_bytes = $urandom_range(1, SPP*BYTES_PER_ITEM);
+ packet.data_bytes += (num_packets-1) * MAX_PYLD_BYTES;
+
+ // Generate random data and metadata
+ packet.data = {};
+ for (int bytes = 0; bytes < packet.data_bytes; bytes += (CHDR_W/8))
+ packet.data.push_back(Rand#(CHDR_W)::rand_logic());
+ packet.metadata = {};
+ for (int words = 0; words < num_mdata; words++)
+ packet.metadata.push_back(Rand#(CHDR_W)::rand_logic());
+
+ // Zero the timestamp if there's no time, since that's what the BFM will
+ // return in that case for the time.
+ if (packet.pkt_info.has_time == 0) packet.pkt_info.timestamp = 0;
+
+ return packet;
+ endfunction : rand_pkt
+
+
+ //---------------------------------------------------------------------------
+ // Test Tasks
+ //---------------------------------------------------------------------------
+
+ task test_send(test_variant_t test_type);
+ packet_t send_pkt, recv_pkt;
+
+ // Generate a random packet
+ send_pkt = rand_pkt(0, 0);
+ if (VERBOSE) begin
+ $display("test_send: data_bytes = %04d, num_mdata = %02d, pkt_info = %p",
+ send_pkt.data_bytes, send_pkt.metadata.size(), send_pkt.pkt_info);
+ end
+
+ // Send then receive it
+ bfm.send(send_pkt.data, send_pkt.data_bytes, send_pkt.metadata, send_pkt.pkt_info);
+
+ if (test_type == TEST_RECV_ADV) begin
+ bfm.recv_adv(recv_pkt.data, recv_pkt.data_bytes, recv_pkt.metadata, recv_pkt.pkt_info);
+
+ // Check if the metadata and packet info matches what we sent
+ `ASSERT_ERROR(chdr_data.chdr_equal(send_pkt.metadata, recv_pkt.metadata), "Metadata did not match");
+ `ASSERT_ERROR(send_pkt.pkt_info == recv_pkt.pkt_info, "Packet info did not match");
+ end else begin
+ bfm.recv(recv_pkt.data, recv_pkt.data_bytes);
+ end
+
+ // Check if we received the data what we sent
+ `ASSERT_ERROR(chdr_data.chdr_equal(send_pkt.data, recv_pkt.data), "Data did not match");
+ `ASSERT_ERROR(send_pkt.data_bytes == recv_pkt.data_bytes, "Data byte length did not match");
+ endtask : test_send
+
+
+ task test_send_items();
+ packet_t send_pkt, recv_pkt;
+ item_t send_items[$], recv_items[$];
+
+ // Generate a random packet
+ send_pkt = rand_pkt(0, 1);
+ if (VERBOSE) begin
+ $display("test_send_items: data_bytes = %04d, num_mdata = %02d, pkt_info = %p",
+ send_pkt.data_bytes, send_pkt.metadata.size(), send_pkt.pkt_info);
+ end
+
+ // Send then receive it, converting between CHDR and item words
+ send_items = chdr_data.chdr_to_item(send_pkt.data, send_pkt.data_bytes);
+ bfm.send_items(send_items, send_pkt.metadata, send_pkt.pkt_info);
+ bfm.recv_items_adv(recv_items, recv_pkt.metadata, recv_pkt.pkt_info);
+
+ // Check if we received what we sent
+ `ASSERT_ERROR(chdr_data.item_equal(send_items, recv_items), "Data did not match");
+ `ASSERT_ERROR(chdr_data.chdr_equal(send_pkt.metadata, recv_pkt.metadata), "Metadata did not match");
+ `ASSERT_ERROR(send_pkt.pkt_info == recv_pkt.pkt_info, "Packet info did not match");
+ endtask : test_send_items
+
+
+ task test_send_packets();
+ packet_t send_pkt, recv_pkt;
+ int num_packets;
+ int data_index, mdata_index;
+ packet_info_t pkt_info;
+
+ // Generate a random packet
+ send_pkt = rand_pkt(1, 0);
+ if (VERBOSE) begin
+ $display("test_send_packets: data_bytes = %04d, num_mdata = %02d, pkt_info = %p",
+ send_pkt.data_bytes, send_pkt.metadata.size(), send_pkt.pkt_info);
+ end
+
+ // Send the packet data, all at once
+ bfm.send_packets(send_pkt.data, send_pkt.data_bytes, send_pkt.metadata, send_pkt.pkt_info);
+
+ // Receive and check all the generated packets
+ num_packets = (send_pkt.data_bytes + MAX_PYLD_BYTES - 1) / MAX_PYLD_BYTES;
+ data_index = 0;
+ mdata_index = 0;
+ pkt_info = send_pkt.pkt_info;
+ pkt_info.eob = 0; // EOB/EOV should only be set for last packet
+ pkt_info.eov = 0;
+ for (int pkt_count = 0; pkt_count < num_packets; pkt_count++) begin
+ int exp_byte_length;
+ int exp_word_length;
+ int exp_num_mdata;
+ chdr_word_t temp_queue[$];
+
+ // Calculate the length of the next packet
+ if (pkt_count < num_packets-1) begin
+ exp_byte_length = MAX_PYLD_BYTES;
+ end else begin
+ // Last packet's length is whatever is left
+ exp_byte_length = send_pkt.data_bytes - pkt_count * MAX_PYLD_BYTES;
+ end
+ exp_word_length = (exp_byte_length + (CHDR_W/8) - 1) / (CHDR_W/8);
+
+ // Calculate how much metadata we have left for the next packet
+ exp_num_mdata = send_pkt.metadata.size() - (pkt_count * 31);
+ if (exp_num_mdata > 31) exp_num_mdata = 31; // Up to 31 words per packet
+ if (exp_num_mdata < 0) exp_num_mdata = 0; // No less than 0
+
+ // Receive the next packet
+ bfm.recv_adv(recv_pkt.data, recv_pkt.data_bytes, recv_pkt.metadata, recv_pkt.pkt_info);
+
+ // Check the data length of the received packet
+ `ASSERT_ERROR(
+ exp_byte_length == recv_pkt.data_bytes,
+ $sformatf(
+ "Length of packet %0d didn't match (received %0d, expected %0d)",
+ pkt_count, recv_pkt.data_bytes, exp_byte_length
+ )
+ );
+
+ // Check the data contents
+ temp_queue = send_pkt.data[data_index:data_index+exp_word_length-1];
+ `ASSERT_ERROR(
+ chdr_data.chdr_equal(temp_queue, recv_pkt.data),
+ "Data did not match"
+ );
+
+ // Check the metadata contents
+ if (exp_num_mdata > 0) begin
+ temp_queue = send_pkt.metadata[mdata_index:mdata_index+exp_num_mdata-1];
+ `ASSERT_ERROR(
+ chdr_data.chdr_equal(temp_queue, recv_pkt.metadata),
+ "Metadata did not match"
+ );
+ end
+
+ // Check the pkt_info
+ if (pkt_count < num_packets-1) begin
+ // Not the last packet
+ `ASSERT_ERROR(
+ pkt_info == recv_pkt.pkt_info,
+ $sformatf("Packet info did not match on packet %0d", pkt_count)
+ );
+ end else begin
+ // This is the last packet
+ pkt_info.eob = send_pkt.pkt_info.eob;
+ pkt_info.eov = send_pkt.pkt_info.eov;
+ `ASSERT_ERROR(
+ pkt_info == recv_pkt.pkt_info,
+ $sformatf("Packet info did not match on packet %0d (last packet)", pkt_count)
+ );
+ end
+
+ // Update counters for next iteration
+ if (pkt_info.has_time) pkt_info.timestamp += exp_word_length * CHDR_W/ITEM_W;
+ data_index += exp_word_length;
+ mdata_index += exp_num_mdata;
+ end
+
+ endtask : test_send_packets
+
+
+ task test_send_packets_items();
+ packet_t send_pkt, recv_pkt;
+ int num_packets;
+ int data_index, mdata_index;
+ packet_info_t pkt_info;
+ item_t send_items[$], recv_items[$];
+
+ // Generate a random packet
+ send_pkt = rand_pkt(1, 1);
+ if (VERBOSE) begin
+ $display("test_send_packets_items: data_bytes = %04d, num_mdata = %02d, pkt_info = %p",
+ send_pkt.data_bytes, send_pkt.metadata.size(), send_pkt.pkt_info);
+ end
+
+ // Send the packet data, all at once
+ send_items = chdr_data.chdr_to_item(send_pkt.data, send_pkt.data_bytes);
+ bfm.send_packets_items(send_items, send_pkt.metadata, send_pkt.pkt_info);
+
+ // Receive and check all the generated packets
+ num_packets = (send_pkt.data_bytes + MAX_PYLD_BYTES - 1) / MAX_PYLD_BYTES;
+ data_index = 0;
+ mdata_index = 0;
+ pkt_info = send_pkt.pkt_info;
+ pkt_info.eob = 0; // EOB/EOV should only be set for last packet
+ pkt_info.eov = 0;
+ for (int pkt_count = 0; pkt_count < num_packets; pkt_count++) begin
+ int exp_byte_length;
+ int exp_word_length;
+ int exp_num_mdata;
+ chdr_word_t temp_queue[$];
+
+ // Calculate the length of the next packet
+ if (pkt_count < num_packets-1) begin
+ exp_byte_length = MAX_PYLD_BYTES;
+ end else begin
+ // Last packet's length is whatever is left
+ exp_byte_length = send_pkt.data_bytes - pkt_count * MAX_PYLD_BYTES;
+ end
+ exp_word_length = (exp_byte_length + (CHDR_W/8) - 1) / (CHDR_W/8);
+
+ // Calculate how much metadata we have left for the next packet
+ exp_num_mdata = send_pkt.metadata.size() - (pkt_count * 31);
+ if (exp_num_mdata > 31) exp_num_mdata = 31; // Up to 31 words per packet
+ if (exp_num_mdata < 0) exp_num_mdata = 0; // No less than 0
+
+ // Receive the next packet
+ bfm.recv_items_adv(recv_items, recv_pkt.metadata, recv_pkt.pkt_info);
+ recv_pkt.data_bytes = recv_items.size() * (ITEM_W/8);
+ recv_pkt.data = chdr_data.item_to_chdr(recv_items);
+
+ // Check the data length of the received packet
+ `ASSERT_ERROR(
+ exp_byte_length == recv_pkt.data_bytes,
+ $sformatf(
+ "Length of packet %0d didn't match (received %0d, expected %0d)",
+ pkt_count, recv_pkt.data_bytes, exp_byte_length
+ )
+ );
+
+ // Check the data contents
+ temp_queue = send_pkt.data[data_index:data_index+exp_word_length-1];
+ `ASSERT_ERROR(
+ chdr_data.chdr_equal(temp_queue, recv_pkt.data),
+ "Data did not match"
+ );
+
+ // Check the metadata contents
+ if (exp_num_mdata > 0) begin
+ temp_queue = send_pkt.metadata[mdata_index:mdata_index+exp_num_mdata-1];
+ `ASSERT_ERROR(
+ chdr_data.chdr_equal(temp_queue, recv_pkt.metadata),
+ "Metadata did not match"
+ );
+ end
+
+ // Check the pkt_info
+ if (pkt_count < num_packets-1) begin
+ // Not the last packet
+ `ASSERT_ERROR(
+ pkt_info == recv_pkt.pkt_info,
+ $sformatf("Packet info did not match on packet %0d", pkt_count)
+ );
+ end else begin
+ // This is the last packet
+ pkt_info.eob = send_pkt.pkt_info.eob;
+ pkt_info.eov = send_pkt.pkt_info.eov;
+ `ASSERT_ERROR(
+ pkt_info == recv_pkt.pkt_info,
+ $sformatf("Packet info did not match on packet %0d (last packet)", pkt_count)
+ );
+ end
+
+ // Update counters for next iteration
+ if (pkt_info.has_time) pkt_info.timestamp += exp_word_length * CHDR_W/ITEM_W;
+ data_index += exp_word_length;
+ mdata_index += exp_num_mdata;
+ end
+
+ endtask : test_send_packets_items
+
+
+ task test_recv_packets_items(test_variant_t test_type);
+ packet_t send_pkt, recv_pkt;
+ item_t send_items[$], recv_items[$];
+ chdr_word_t recv_metadata[$];
+
+ // Generate a random packet
+ send_pkt = rand_pkt(1, 1);
+ if (VERBOSE) begin
+ $display("test_recv_packets_items: data_bytes = %04d, num_mdata = %02d, pkt_info = %p",
+ send_pkt.data_bytes, send_pkt.metadata.size(), send_pkt.pkt_info);
+ end
+
+ // Set the flags, if needed
+ case (test_type)
+ TEST_EOB :
+ send_pkt.pkt_info.eob = 1;
+ TEST_EOV :
+ send_pkt.pkt_info.eov = 1;
+ endcase
+
+ // Send the packet data, all at once
+ send_items = chdr_data.chdr_to_item(send_pkt.data, send_pkt.data_bytes);
+ bfm.send_packets_items(send_items, send_pkt.metadata, send_pkt.pkt_info);
+
+ // Receive the data, all at once
+ case (test_type)
+ TEST_NUM_ITEMS :
+ bfm.recv_packets_items(
+ recv_items, send_items.size());
+ TEST_EOB :
+ bfm.recv_packets_items(
+ recv_items, /* num_samps */, 1, 0);
+ TEST_EOV :
+ bfm.recv_packets_items
+ (recv_items, /* num_samps */, 0, 1);
+ endcase
+
+ // Check if we received what we sent
+ `ASSERT_ERROR(chdr_data.item_equal(send_items, recv_items), "Data did not match");
+
+ endtask : test_recv_packets_items
+
+
+ task test_recv_packets_items_adv(test_variant_t test_type);
+ packet_t send_pkt, recv_pkt;
+ item_t send_items[$], recv_items[$];
+ chdr_word_t recv_metadata[$];
+
+ // Generate a random packet
+ send_pkt = rand_pkt(1, 1);
+ if (VERBOSE) begin
+ $display("test_recv_packets_items_adv: data_bytes = %04d, num_mdata = %02d, pkt_info = %p",
+ send_pkt.data_bytes, send_pkt.metadata.size(), send_pkt.pkt_info);
+ end
+
+ // Set the flags, if needed
+ case (test_type)
+ TEST_EOB :
+ send_pkt.pkt_info.eob = 1;
+ TEST_EOV :
+ send_pkt.pkt_info.eov = 1;
+ endcase
+
+ // Send the packet data, all at once
+ send_items = chdr_data.chdr_to_item(send_pkt.data, send_pkt.data_bytes);
+ bfm.send_packets_items(send_items, send_pkt.metadata, send_pkt.pkt_info);
+
+ // Receive the data, all at once
+ case (test_type)
+ TEST_NUM_ITEMS :
+ bfm.recv_packets_items_adv(
+ recv_items, recv_pkt.metadata, recv_pkt.pkt_info, send_items.size());
+ TEST_EOB :
+ bfm.recv_packets_items_adv(
+ recv_items, recv_pkt.metadata, recv_pkt.pkt_info, /* num_samps */, 1, 0);
+ TEST_EOV :
+ bfm.recv_packets_items_adv
+ (recv_items, recv_pkt.metadata, recv_pkt.pkt_info, /* num_samps */, 0, 1);
+ endcase
+
+ // Check if we received what we sent
+ `ASSERT_ERROR(chdr_data.item_equal(send_items, recv_items), "Data did not match");
+ `ASSERT_ERROR(chdr_data.chdr_equal(send_pkt.metadata, recv_pkt.metadata), "Metadata did not match");
+ `ASSERT_ERROR(send_pkt.pkt_info == recv_pkt.pkt_info, "Packet info did not match");
+
+ endtask : test_recv_packets_items_adv
+
+
+ //---------------------------------------------------------------------------
+ // Main Test Process
+ //---------------------------------------------------------------------------
+
+ initial begin : tb_main
+ string tb_name;
+
+ //-------------------------------------------------------------------------
+ // Initialization
+ //-------------------------------------------------------------------------
+
+ // Generate a string for the name of this instance of the testbench
+ tb_name = $sformatf(
+ "ChdrIfaceBfm_tb\nCHDR_W = %0d, ITEM_W = %0d",
+ CHDR_W, ITEM_W
+ );
+
+ // We're not testing flow control, only correct data generate and
+ // extraction, so there's not need to stall on the CHDR interface.
+ bfm.set_slave_stall_prob(0);
+ bfm.set_master_stall_prob(0);
+
+ // Start the BFM runnings
+ bfm.run();
+
+ test.start_tb(tb_name, 100ms);
+
+
+ //-------------------------------------------------------------------------
+ // Test Sequences
+ //-------------------------------------------------------------------------
+
+ test.start_test("Test send() / recv()", 10ms);
+ for (int i = 0; i < 1000; i++) test_send(TEST_RECV);
+ test.end_test();
+
+ test.start_test("Test send() / recv_adv()", 10ms);
+ for (int i = 0; i < 1000; i++) test_send(TEST_RECV_ADV);
+ test.end_test();
+
+ test.start_test("Test send_items() / recv_items_adv()", 10ms);
+ for (int i = 0; i < 1000; i++) test_send_items();
+ test.end_test();
+
+ test.start_test("Test send_packets() / recv_adv()", 10ms);
+ for (int i = 0; i < 1000; i++) test_send_packets();
+ test.end_test();
+
+ test.start_test("Test send_packets_items() / recv_items_adv()", 10ms);
+ for (int i = 0; i < 1000; i++) test_send_packets_items();
+ test.end_test();
+
+ test.start_test("Test send_packets_items() / recv_packets_items(num_items)", 10ms);
+ for (int i = 0; i < 1000; i++) test_recv_packets_items(TEST_NUM_ITEMS);
+ test.end_test();
+
+ test.start_test("Test send_packets_items() / recv_packets_items(eob)", 10ms);
+ for (int i = 0; i < 1000; i++) test_recv_packets_items(TEST_EOB);
+ test.end_test();
+
+ test.start_test("Test send_packets_items() / recv_packets_items(eov)", 10ms);
+ for (int i = 0; i < 1000; i++) test_recv_packets_items(TEST_EOV);
+ test.end_test();
+
+ test.start_test("Test send_packets_items() / recv_packets_items_adv(num_items)", 10ms);
+ for (int i = 0; i < 1000; i++) test_recv_packets_items_adv(TEST_NUM_ITEMS);
+ test.end_test();
+
+ test.start_test("Test send_packets_items() / recv_packets_items_adv(eob)", 10ms);
+ for (int i = 0; i < 1000; i++) test_recv_packets_items_adv(TEST_EOB);
+ test.end_test();
+
+ test.start_test("Test send_packets_items() / recv_packets_items_adv(eov)", 10ms);
+ for (int i = 0; i < 1000; i++) test_recv_packets_items_adv(TEST_EOV);
+ test.end_test();
+
+ // Make sure we don't get any more packets. Wait for more than a packet of
+ // worth of time the check if we've received anything.
+ #(CLOCK_PER * WPP * 8);
+ `ASSERT_ERROR(bfm.num_received() == 0, "Received unexpected packets");
+
+ // 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
+ rfnoc_chdr_clk_gen.kill();
+
+ end
+
+endmodule : ChdrIfaceBfm_tb
diff --git a/fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/Makefile b/fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/Makefile
new file mode 100644
index 000000000..a4931f945
--- /dev/null
+++ b/fpga/usrp3/sim/rfnoc/test/ChdrIfaceBfm/Makefile
@@ -0,0 +1,32 @@
+#
+# Copyright 2020 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 = ChdrIfaceBfm_tb
+
+# Add test bench, user design under test, and
+# additional user created files
+SIM_SRCS = \
+$(abspath ChdrIfaceBfm_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