diff options
author | Wade Fife <wade.fife@ettus.com> | 2020-02-21 08:35:24 -0600 |
---|---|---|
committer | Wade Fife <wade.fife@ettus.com> | 2020-03-09 13:43:05 -0500 |
commit | fc895feacb8dde3b02c9a4eccb4b4f4a654f2881 (patch) | |
tree | 0325ce379611fae9e9902e0e9a5fd299c1c48243 | |
parent | 369594ef16d7b2d519940269d2af035cfe648f50 (diff) | |
download | uhd-fc895feacb8dde3b02c9a4eccb4b4f4a654f2881.tar.gz uhd-fc895feacb8dde3b02c9a4eccb4b4f4a654f2881.tar.bz2 uhd-fc895feacb8dde3b02c9a4eccb4b4f4a654f2881.zip |
sim: Parameterize chdr_word_t data type
This replaces chdr_word_t, which was a statically defined 64-bit data
type, with a paramaterizable data type that matches the defined CHDR_W.
Code that formerly referenced the chdr_word_t data type can now define
the data type for their desired CHDR_W and ITEM_W as follows:
// Define the CHDR word and item/sample data types
typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t;
typedef ChdrData #(CHDR_W, ITEM_W)::item_t item_t;
ITEM_W is optional when defining chdr_word_t if items are not
needed. Static methods in the ChdrData class also provide the ability to
convert between CHDR words and data items. For example:
// Convert CHDR data buffer to a buffer of samples
samples = ChdrData#(CHDR_W, ITEM_W)::chdr_to_item(data);
18 files changed, 423 insertions, 218 deletions
diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_tb.sv index 8ae72027e..e4753e92f 100644 --- a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_tb.sv +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_axi_ram_fifo/rfnoc_block_axi_ram_fifo_tb.sv @@ -83,6 +83,8 @@ module rfnoc_block_axi_ram_fifo_tb #( // Bus Functional Models //--------------------------------------------------------------------------- + typedef ChdrData #(CHDR_W)::chdr_word_t chdr_word_t; + RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk); AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0); AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0); diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_tb.sv index 86f64ab4c..e6f2c4d6b 100644 --- a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_tb.sv +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_ddc/rfnoc_block_ddc_tb.sv @@ -35,6 +35,7 @@ module rfnoc_block_ddc_tb(); // Block configuration localparam int CHDR_W = 64; + localparam int SAMP_W = 32; localparam int THIS_PORTID = 'h123; localparam int MTU = 8; localparam int NUM_PORTS = 1; @@ -60,6 +61,8 @@ module rfnoc_block_ddc_tb(); // Bus Functional Models //--------------------------------------------------------------------------- + typedef ChdrData #(CHDR_W, SAMP_W)::chdr_word_t chdr_word_t; + RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk); AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0); AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0); diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv index de54d5ee0..8151ed761 100644 --- a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_duc/rfnoc_block_duc_tb.sv @@ -35,6 +35,7 @@ module rfnoc_block_duc_tb(); // Block configuration localparam int CHDR_W = 64; + localparam int SAMP_W = 32; localparam int THIS_PORTID = 'h123; localparam int MTU = 8; localparam int NUM_PORTS = 1; @@ -49,6 +50,7 @@ module rfnoc_block_duc_tb(); bit rfnoc_chdr_clk; bit rfnoc_ctrl_clk; + bit ce_clk; sim_clock_gen #(CHDR_CLK_PER) rfnoc_chdr_clk_gen (.clk(rfnoc_chdr_clk), .rst()); sim_clock_gen #(CHDR_CLK_PER) rfnoc_ctrl_clk_gen (.clk(rfnoc_ctrl_clk), .rst()); @@ -59,6 +61,8 @@ module rfnoc_block_duc_tb(); // Bus Functional Models //--------------------------------------------------------------------------- + typedef ChdrData #(CHDR_W, SAMP_W)::chdr_word_t chdr_word_t; + RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk); AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0); AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0); diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft_tb.sv index 05b38ddeb..412963dc7 100644 --- a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft_tb.sv +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fft/rfnoc_block_fft_tb.sv @@ -30,6 +30,7 @@ module rfnoc_block_fft_tb(); // Block configuration localparam int NOC_ID = 32'hFF70_0000; localparam int CHDR_W = 64; + localparam int SAMP_W = 32; localparam int THIS_PORTID = 'h123; localparam int MTU = 10; localparam int NUM_PORTS = 1; @@ -60,6 +61,8 @@ module rfnoc_block_fft_tb(); // Bus Functional Models //--------------------------------------------------------------------------- + typedef ChdrData #(CHDR_W, SAMP_W)::chdr_word_t chdr_word_t; + RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk); AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0); AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0); diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter_tb.sv index 2a961b89f..fedb4f46b 100644 --- a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter_tb.sv +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_fir_filter/rfnoc_block_fir_filter_tb.sv @@ -36,6 +36,7 @@ module rfnoc_block_fir_filter_tb #( // DUT parameters to test localparam int CHDR_W = 64; + localparam int SAMP_W = 32; localparam int THIS_PORTID = 'h123; localparam int MTU = 8; // @@ -86,6 +87,8 @@ module rfnoc_block_fir_filter_tb #( // Bus Functional Models //--------------------------------------------------------------------------- + typedef ChdrData #(CHDR_W, SAMP_W)::chdr_word_t chdr_word_t; + RfnocBackendIf backend (rfnoc_chdr_clk, rfnoc_ctrl_clk); AxiStreamIf #(32) m_ctrl (rfnoc_ctrl_clk, 1'b0); AxiStreamIf #(32) s_ctrl (rfnoc_ctrl_clk, 1'b0); diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink_tb.sv index 699d53b11..192a8143b 100644 --- a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink_tb.sv +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_null_src_sink/rfnoc_block_null_src_sink_tb.sv @@ -24,6 +24,7 @@ module rfnoc_block_null_src_sink_tb; localparam [9:0] THIS_PORTID = 10'h17; localparam [15:0] THIS_EPID = 16'hDEAD; localparam int CHDR_W = 64; + localparam int ITEM_W = 32; localparam int SPP = 201; localparam int LPP = ((SPP+1)/2); localparam int NUM_PKTS = 50; @@ -48,6 +49,8 @@ module rfnoc_block_null_src_sink_tb; AxiStreamIf #(CHDR_W) s0_chdr (rfnoc_chdr_clk); // Optional data iface AxiStreamIf #(CHDR_W) s1_chdr (rfnoc_chdr_clk); // Optional data iface + typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t; + // Bus functional model for a software block controller RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl; @@ -128,7 +131,7 @@ module rfnoc_block_null_src_sink_tb; // Read status register and validate it blk_ctrl.reg_read(dut.REG_CTRL_STATUS, rvalue); `ASSERT_ERROR(rvalue[31:24] == 2, "Incorrect NIPC Value"); - `ASSERT_ERROR(rvalue[23:16] == 32, "Incorrect ITEM_W Value"); + `ASSERT_ERROR(rvalue[23:16] == ITEM_W, "Incorrect ITEM_W Value"); test.end_timeout(timeout); end test.end_test(); @@ -139,7 +142,7 @@ module rfnoc_block_null_src_sink_tb; repeat (NUM_PKTS) begin chdr_word_t rx_data[$]; int rx_bytes; - automatic ItemDataBuff #(logic[31:0]) tx_dbuff = new, rx_dbuff = new; + automatic ItemDataBuff #(logic[ITEM_W-1:0]) tx_dbuff = new, rx_dbuff = new; for (int i = 0; i < SPP; i++) tx_dbuff.put($urandom()); test.start_timeout(timeout, 5us, "Waiting for pkt to loop back"); @@ -176,7 +179,7 @@ module rfnoc_block_null_src_sink_tb; repeat (NUM_PKTS) begin chdr_word_t rx_data[$]; int rx_bytes; - automatic ItemDataBuff #(logic[31:0]) tx_dbuff = new; + automatic ItemDataBuff #(logic[ITEM_W-1:0]) tx_dbuff = new; for (int i = 0; i < SPP; i++) tx_dbuff.put($urandom()); test.start_timeout(timeout, 5us, "Waiting for pkt to loop back"); diff --git a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_tb.sv b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_tb.sv index 4438cae9e..553b0e33e 100644 --- a/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_tb.sv +++ b/fpga/usrp3/lib/rfnoc/blocks/rfnoc_block_radio/rfnoc_block_radio_tb.sv @@ -27,7 +27,6 @@ module rfnoc_block_radio_tb #( import PkgRfnocBlockCtrlBfm::*; import PkgAxisCtrlBfm::*; import PkgChdrBfm::*; - import PkgRfnocItemUtils::*; // Pull in radio register offsets and constants `include "rfnoc_block_radio_regs.vh" @@ -87,6 +86,9 @@ module rfnoc_block_radio_tb #( AxiStreamIf #(CHDR_W) m_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0); AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0); + typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t; + typedef ChdrData #(CHDR_W, ITEM_W)::item_t sample_t; + // Bus functional model for a software block controller RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl = new(backend, m_ctrl, s_ctrl); @@ -370,13 +372,13 @@ module rfnoc_block_radio_tb #( for (int i = 0; i < valid_words; i++) begin // Check each sample of the next chdr_word_t value for (int sub_sample = 0; sub_sample < $bits(chdr_word_t)/ITEM_W; sub_sample++) begin - chdr_word_t word; - word = data[i][ITEM_W*sub_sample +: ITEM_W]; // Work around Vivado 2018.3 issue + sample_t actual; + actual = data[i][ITEM_W*sub_sample +: ITEM_W]; // Work around Vivado 2018.3 issue `ASSERT_ERROR( - word == sample_val, + actual == sample_val, $sformatf( "Sample %0d (0x%X) didn't match expected value (0x%X)", - sample_count, data[i][ITEM_W*sub_sample +: ITEM_W], sample_val + sample_count, actual, sample_val ) ); sample_val++; diff --git a/fpga/usrp3/lib/rfnoc/sim/chdr_stream_endpoint_tb/chdr_stream_endpoint_tb.sv b/fpga/usrp3/lib/rfnoc/sim/chdr_stream_endpoint_tb/chdr_stream_endpoint_tb.sv index 0626ee447..34bf10707 100644 --- a/fpga/usrp3/lib/rfnoc/sim/chdr_stream_endpoint_tb/chdr_stream_endpoint_tb.sv +++ b/fpga/usrp3/lib/rfnoc/sim/chdr_stream_endpoint_tb/chdr_stream_endpoint_tb.sv @@ -45,6 +45,8 @@ module chdr_stream_endpoint_tb; localparam [9:0] PORT_A = 10'd1; localparam [9:0] PORT_B = 10'd2; + typedef ChdrData #(CHDR_W)::chdr_word_t chdr_word_t; + // ---------------------------------------- // DUT (and Crossbar) Instantiations // ---------------------------------------- diff --git a/fpga/usrp3/sim/rfnoc/Makefile.srcs b/fpga/usrp3/sim/rfnoc/Makefile.srcs index 38bb86655..a48d166b6 100644 --- a/fpga/usrp3/sim/rfnoc/Makefile.srcs +++ b/fpga/usrp3/sim/rfnoc/Makefile.srcs @@ -12,6 +12,7 @@ PkgTestExec.sv \ test_exec.svh \ sim_clock_gen.sv \ PkgAxiStreamBfm.sv \ +PkgChdrData.sv \ PkgChdrUtils.sv \ PkgChdrBfm.sv \ PkgAxisCtrlBfm.sv \ diff --git a/fpga/usrp3/sim/rfnoc/PkgAxisCtrlBfm.sv b/fpga/usrp3/sim/rfnoc/PkgAxisCtrlBfm.sv index 8c88792c7..7d7c01eff 100644 --- a/fpga/usrp3/sim/rfnoc/PkgAxisCtrlBfm.sv +++ b/fpga/usrp3/sim/rfnoc/PkgAxisCtrlBfm.sv @@ -29,7 +29,7 @@ package PkgAxisCtrlBfm; //------------ axis_ctrl_header_t header; - chdr_word_t timestamp; + chdr_timestamp_t timestamp; ctrl_op_word_t op_word; ctrl_word_t data[$]; @@ -86,7 +86,7 @@ package PkgAxisCtrlBfm; ref axis_ctrl_header_t header, ref ctrl_op_word_t op_word, ref ctrl_word_t data[$], - input chdr_word_t timestamp = 0 + input chdr_timestamp_t timestamp = 0 ); this.header = header; this.data = data; diff --git a/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv b/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv index fd0bd0048..5863bb598 100644 --- a/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv +++ b/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv @@ -21,14 +21,17 @@ package PkgChdrBfm; // CHDR Packet Class //--------------------------------------------------------------------------- - class ChdrPacket #(int BUS_WIDTH = 64); + class ChdrPacket #(int CHDR_W = 64); - typedef ChdrPacket #(BUS_WIDTH) ChdrPacket; + typedef ChdrPacket #(CHDR_W) ChdrPacket; + typedef ChdrData #(CHDR_W)::chdr_word_t chdr_word_t; - chdr_header_t header; - chdr_word_t timestamp; - chdr_word_t metadata[$]; - chdr_word_t data[$]; + const int BYTES_PER_CHDR_W = CHDR_W / 8; + + chdr_header_t header; + chdr_timestamp_t timestamp; + chdr_word_t metadata[$]; + chdr_word_t data[$]; extern function ChdrPacket copy(); extern function bit equal(ChdrPacket packet); @@ -39,12 +42,12 @@ package PkgChdrBfm; extern function void write_raw (ref chdr_header_t header, ref chdr_word_t data[$], input chdr_word_t metadata[$] = {}, - input chdr_word_t timestamp = 0, + input chdr_timestamp_t timestamp = 0, input int data_byte_length = -1); extern function void read_raw (output chdr_header_t header, output chdr_word_t data[$], output chdr_word_t metadata[$], - output chdr_word_t timestamp, + output chdr_timestamp_t timestamp, output int data_byte_length); extern function void write_stream_status(ref chdr_header_t header, ref chdr_str_status_t status); @@ -62,12 +65,12 @@ package PkgChdrBfm; ref chdr_ctrl_header_t ctrl_header, ref ctrl_op_word_t ctrl_op_word, ref ctrl_word_t ctrl_data[$], - input chdr_word_t ctrl_timestamp = 0); + input chdr_timestamp_t ctrl_timestamp = 0); extern function void read_ctrl (output chdr_header_t header, output chdr_ctrl_header_t ctrl_header, output ctrl_op_word_t ctrl_op_word, output ctrl_word_t ctrl_data[$], - output chdr_word_t ctrl_timestamp); + output chdr_timestamp_t ctrl_timestamp); // Helper methods @@ -79,6 +82,8 @@ package PkgChdrBfm; extern function string sprint_raw(); extern function string sprint_pretty(); + extern function bit chdr_word_queues_equal(ref chdr_word_t a[$], ref chdr_word_t b[$]); + endclass : ChdrPacket; @@ -88,14 +93,14 @@ package PkgChdrBfm; //--------------------------------------------------------------------------- class ChdrBfm #( - parameter int BUS_WIDTH = 64, + parameter int CHDR_W = 64, parameter int USER_WIDTH = 1 - ) extends AxiStreamBfm #(BUS_WIDTH, USER_WIDTH); + ) extends AxiStreamBfm #(CHDR_W, USER_WIDTH); - typedef ChdrPacket #(BUS_WIDTH) ChdrPacket; + typedef ChdrPacket #(CHDR_W) ChdrPacket; + typedef ChdrData #(CHDR_W)::chdr_word_t chdr_word_t; - // Number of 64-bit CHDR words per AXI word - const int CHDR_PER_BUS = BUS_WIDTH / $bits(chdr_word_t); + const int BYTES_PER_CHDR_W = CHDR_W / 8; // Default fields used by high-level transaction methods chdr_epid_t dst_epid; @@ -103,8 +108,8 @@ package PkgChdrBfm; extern function new ( - virtual AxiStreamIf #(BUS_WIDTH, USER_WIDTH).master master, - virtual AxiStreamIf #(BUS_WIDTH, USER_WIDTH).slave slave + virtual AxiStreamIf #(CHDR_W, USER_WIDTH).master master, + virtual AxiStreamIf #(CHDR_W, USER_WIDTH).slave slave ); @@ -210,7 +215,7 @@ package PkgChdrBfm; str = {str, $sformatf(" > chdr_str_command_t : %p\n", tmp_cmd)}; end else if (header.pkt_type == CHDR_CONTROL) begin chdr_header_t tmp_hdr; - chdr_word_t tmp_ts; + chdr_timestamp_t tmp_ts; chdr_ctrl_header_t tmp_ctrl_hdr; ctrl_op_word_t tmp_op_word; ctrl_word_t tmp_ctrl_data[$]; @@ -250,11 +255,11 @@ package PkgChdrBfm; // method to calculate the payload length based on the size of the data // array. function void ChdrPacket::write_raw ( - ref chdr_header_t header, - ref chdr_word_t data[$], - input chdr_word_t metadata[$] = {}, - input chdr_word_t timestamp = 0, - input int data_byte_length = -1 + ref chdr_header_t header, + ref chdr_word_t data[$], + input chdr_word_t metadata[$] = {}, + input chdr_timestamp_t timestamp = 0, + input int data_byte_length = -1 ); this.header = header; this.timestamp = timestamp; @@ -267,11 +272,11 @@ package PkgChdrBfm; int array_num_bytes; // Make sure number of words for data_byte_length matches data length - assert((data_byte_length+7) / 8 == data.size()) else begin + assert((data_byte_length+(BYTES_PER_CHDR_W-1)) / BYTES_PER_CHDR_W == data.size()) else begin $error("ChdrPacket::write_raw: data_byte_length doesn't correspond to number of words in data"); end - array_num_bytes = data.size() * $bits(chdr_word_t)/8; + array_num_bytes = data.size() * BYTES_PER_CHDR_W; this.header.length -= (array_num_bytes - data_byte_length); end endfunction : write_raw @@ -279,11 +284,11 @@ package PkgChdrBfm; // Read the contents of this packet function void ChdrPacket::read_raw ( - output chdr_header_t header, - output chdr_word_t data[$], - output chdr_word_t metadata[$], - output chdr_word_t timestamp, - output int data_byte_length + output chdr_header_t header, + output chdr_word_t data[$], + output chdr_word_t metadata[$], + output chdr_timestamp_t timestamp, + output int data_byte_length ); header = this.header; data = this.data; @@ -301,8 +306,8 @@ package PkgChdrBfm; header.pkt_type = CHDR_STRM_STATUS; // Update packet type in header this.header = header; data = {}; - for (int i = 0; i < $bits(status); i += $bits(chdr_word_t)) begin - data.push_back( status[i +: $bits(chdr_word_t)] ); + for (int i = 0; i < $bits(status); i += CHDR_W) begin + data.push_back( status[i +: CHDR_W] ); end update_lengths(); endfunction : write_stream_status @@ -315,17 +320,17 @@ package PkgChdrBfm; ); // Make sure it's a stream status packet assert(this.header.pkt_type == CHDR_STRM_STATUS) else begin - $error("ChdrPacket::read_status: Packet type is not CHDR_STRM_STATUS"); + $error("ChdrPacket::read_stream_status: Packet type is not CHDR_STRM_STATUS"); end // Make sure we have enough payload assert($bits(status) <= $bits(data)) else begin - $error("ChdrPacket::read_status: Not enough data for status payload"); + $error("ChdrPacket::read_stream_status: Not enough data for status payload"); end header = this.header; - for (int i = 0; i < $bits(status)/$bits(chdr_word_t); i++) begin - status[i*$bits(chdr_word_t) +: $bits(chdr_word_t)] = data[i]; + for (int i = 0; i < $bits(status)/CHDR_W; i++) begin + status[i*CHDR_W +: CHDR_W] = data[i]; end endfunction : read_stream_status @@ -338,8 +343,8 @@ package PkgChdrBfm; header.pkt_type = CHDR_STRM_CMD; // Update packet type in header this.header = header; data = {}; - for (int i = 0; i < $bits(command); i += $bits(chdr_word_t)) begin - data.push_back( command[i +: $bits(chdr_word_t)] ); + for (int i = 0; i < $bits(command); i += CHDR_W) begin + data.push_back( command[i +: CHDR_W] ); end update_lengths(); endfunction : write_stream_cmd @@ -352,17 +357,17 @@ package PkgChdrBfm; ); // Make sure it's a stream command packet assert(this.header.pkt_type == CHDR_STRM_CMD) else begin - $error("ChdrPacket::read_command: Packet type is not CHDR_STRM_CMD"); + $error("ChdrPacket::read_stream_cmd: Packet type is not CHDR_STRM_CMD"); end // Make sure we have enough payload assert($bits(command) <= $bits(data)) else begin - $error("ChdrPacket::read_command: Not enough data for command payload"); + $error("ChdrPacket::read_stream_cmd: Not enough data for command payload"); end header = this.header; - for (int i = 0; i < $bits(command)/$bits(chdr_word_t); i++) begin - command[i*$bits(chdr_word_t) +: $bits(chdr_word_t)] = data[i]; + for (int i = 0; i < $bits(command)/CHDR_W; i++) begin + command[i*CHDR_W +: CHDR_W] = data[i]; end endfunction : read_stream_cmd @@ -402,7 +407,7 @@ package PkgChdrBfm; header = this.header; - num_ops = data_bytes()/8 - 1; // Num words, minus one for the header + num_ops = data_bytes()/BYTES_PER_CHDR_W - 1; // Num words, minus one for the header // Make sure we have enough payload assert(1 + num_ops <= data.size()) else begin @@ -425,34 +430,49 @@ package PkgChdrBfm; ref chdr_ctrl_header_t ctrl_header, ref ctrl_op_word_t ctrl_op_word, ref ctrl_word_t ctrl_data[$], - input chdr_word_t ctrl_timestamp = 0 + input chdr_timestamp_t ctrl_timestamp = 0 ); bit partial_word; ctrl_word_t mandatory_data; + ChdrData #(CHDR_W, 64)::item_queue_t data64; + header.pkt_type = CHDR_CONTROL; // Update packet type in header this.header = header; - data = {}; + data64 = {}; // Insert word 0 of control payload - data.push_back(ctrl_header[63:0]); + data64.push_back(ctrl_header); // Insert word 1 of control payload, if timestamp is used if (ctrl_header.has_time) begin - data.push_back(ctrl_timestamp); + data64.push_back(ctrl_timestamp); + end + + // Make sure the amount of data passed matches the header + assert (ctrl_header.num_data == ctrl_data.size()) else begin + $error("ChdrPacket::write_ctrl: NumData doesn't match ctrl_data[] size"); end // Insert word 2 of control payload, the operation word // and first word of control data. mandatory_data = (ctrl_header.num_data > 0) ? ctrl_data[0] : '0; - data.push_back({mandatory_data, ctrl_op_word[31:0]}); + data64.push_back({mandatory_data, ctrl_op_word[31:0]}); // We have a half CHDR word if num_data is even partial_word = (ctrl_header.num_data[0] == '0); // Insert remaining data, if present for (int i = 1; i < ctrl_data.size(); i+=2) begin - data.push_back({(partial_word ? '0 : ctrl_data[i+1]), ctrl_data[i]}); + if (i == ctrl_data.size()-1) begin + // num_data must be even in this case, so last word is half filled + data64.push_back({ 32'b0, ctrl_data[i] }); + end else begin + data64.push_back({ ctrl_data[i+1], ctrl_data[i] }); + end end + // Convert from 64-bit words to CHDR_W-bit words + data = ChdrData #(CHDR_W, 64)::item_to_chdr(data64); + update_lengths(); endfunction : write_ctrl @@ -463,62 +483,71 @@ package PkgChdrBfm; output chdr_ctrl_header_t ctrl_header, output ctrl_op_word_t ctrl_op_word, output ctrl_word_t ctrl_data[$], - output chdr_word_t ctrl_timestamp + output chdr_timestamp_t ctrl_timestamp ); + ChdrData #(CHDR_W, 32)::item_queue_t data32; chdr_word_t chdr_op_word; + int ctrl_packet_size; int dptr = 0; // Make sure it's a stream status packet assert(this.header.pkt_type == CHDR_CONTROL) else begin - $error("ChdrPacket::read_status: Packet type is not CHDR_CONTROL"); + $error("ChdrPacket::read_ctrl: Packet type is not CHDR_CONTROL"); end - // Make sure we have enough payload - assert($bits(ctrl_header)+$bits(ctrl_op_word) <= $bits(data)) else begin - $error("ChdrPacket::read_status: Not enough data for status payload"); - end + // Convert packet to 32-bit words for easier parsing + data32 = ChdrData #(CHDR_W, 32)::chdr_to_item(data, data_bytes()); + // CHDR header header = this.header; - // Word 0 - ctrl_header[63:0] = data[dptr++]; + // Ctrl header + ctrl_header = { data32[dptr+1], data32[dptr] }; + dptr += 2; - // Word 1 - if (ctrl_header.has_time) begin - ctrl_timestamp = data[dptr++]; + // Make sure we have enough payload. Calculate expected size in 32-bit + // words. + ctrl_packet_size = 3 + ctrl_header.num_data; // header + op_word + data + if (ctrl_header.has_time) ctrl_packet_size += 2; // timestamp + assert(data32.size() < ctrl_packet_size) else begin + $error("ChdrPacket::read_ctrl: Not enough CHDR payload for control packet"); + end + assert(data32.size() > ctrl_packet_size) else begin + $warning("ChdrPacket::read_ctrl: Excess CHDR payload for control packet"); end - // Word 2, last 32-bits of control header and first word of control data - chdr_op_word = data[dptr++]; - ctrl_op_word = chdr_op_word[31:0]; - ctrl_data.delete(); - if (ctrl_header.num_data > 0) begin - ctrl_data[0] = chdr_op_word[63:32]; + // Timestamp (optional) + if(ctrl_header.has_time) begin + ctrl_timestamp = { data32[dptr+1], data32[dptr] }; + dptr += 2; end - // Copy any remaining data words - for (int i = dptr; i < data.size(); i++) begin - ctrl_data.push_back(data[i][31:0]); - if (i != data.size()-1 || ctrl_header.num_data[0] != 0) - ctrl_data.push_back(data[i][63:32]); + // Operation word + ctrl_op_word = data32[dptr++]; + + // Data words + ctrl_data = {}; + while (dptr < data32.size()) begin + ctrl_data.push_back(data32[dptr++]); end + endfunction : read_ctrl // Calculate the header size (including timestamp), in bytes, from the header // information. function int ChdrPacket::header_bytes(); - if (BUS_WIDTH == $bits(chdr_word_t) && header.pkt_type == CHDR_DATA_WITH_TS) begin - header_bytes = 2 * (BUS_WIDTH / 8); // Two words (header + timestamp) + if (CHDR_W == 64 && header.pkt_type == CHDR_DATA_WITH_TS) begin + header_bytes = 2 * BYTES_PER_CHDR_W; // Two words (header + timestamp) end else begin - header_bytes = (BUS_WIDTH / 8); // One word, regardless of timestamp + header_bytes = BYTES_PER_CHDR_W; // One word, regardless of timestamp end endfunction : header_bytes // Calculate the metadata size from the header information. function int ChdrPacket::mdata_bytes(); - mdata_bytes = header.num_mdata * BUS_WIDTH/8; + mdata_bytes = header.num_mdata * BYTES_PER_CHDR_W; endfunction : mdata_bytes @@ -535,15 +564,12 @@ package PkgChdrBfm; int num_mdata; // Calculate NumMData based on the size of metadata queue - num_mdata = metadata.size() / (BUS_WIDTH / $bits(chdr_word_t)); - if (metadata.size() % (BUS_WIDTH / $bits(chdr_word_t)) != 0) begin - num_mdata++; - end + num_mdata = metadata.size(); assert(num_mdata < 2**$bits(chdr_num_mdata_t)) else $fatal(1, "ChdrPacket::update_lengths(): Calculated NumMData exceeds maximum size"); // Calculate the Length field - num_bytes = data.size() * $bits(chdr_word_t) / 8; // Payload length + num_bytes = data.size() * BYTES_PER_CHDR_W; // Payload length num_bytes = num_bytes + header_bytes() + mdata_bytes(); // Payload + header length assert(num_bytes < 2**$bits(chdr_length_t)) else $fatal(1, "ChdrPacket::update_lengths(): Calculated Length exceeds maximum size"); @@ -554,6 +580,20 @@ package PkgChdrBfm; endfunction : update_lengths + // Returns 1 if the queues have the same contents, otherwise returns 0. This + // function is equivalent to (a == b), but this doesn't work correctly yet in + // Vivado 2018.3. + function automatic bit ChdrPacket::chdr_word_queues_equal(ref chdr_word_t a[$], ref chdr_word_t b[$]); + chdr_word_t x, y; + if (a.size() != b.size()) return 0; + foreach (a[i]) begin + x = a[i]; + y = b[i]; + if (x !== y) return 0; + end + return 1; + endfunction : chdr_word_queues_equal + //--------------------------------------------------------------------------- @@ -564,11 +604,11 @@ package PkgChdrBfm; // Class constructor. This must be given an interface for the master // connection and an interface for the slave connection. function ChdrBfm::new ( - virtual AxiStreamIf #(BUS_WIDTH, USER_WIDTH).master master, - virtual AxiStreamIf #(BUS_WIDTH, USER_WIDTH).slave slave + virtual AxiStreamIf #(CHDR_W, USER_WIDTH).master master, + virtual AxiStreamIf #(CHDR_W, USER_WIDTH).slave slave ); super.new(master, slave); - assert(BUS_WIDTH % 64 == 0) else begin + assert(CHDR_W % 64 == 0) else begin $fatal(1, "ChdrBfm::new: CHDR bus width must be a multiple of 64 bits"); end endfunction : new @@ -637,6 +677,7 @@ package PkgChdrBfm; enum int { ST_HEADER, ST_TIMESTAMP, ST_METADATA, ST_PAYLOAD } rx_state; data_t word; int num_rx_mdata; + int num_rx_bytes; ChdrPacket chdr_packet = new(); rx_state = ST_HEADER; @@ -646,12 +687,13 @@ package PkgChdrBfm; case (rx_state) ST_HEADER : begin + num_rx_bytes += BYTES_PER_CHDR_W; chdr_packet.header = word[63:0]; // Depending on the size of the word, we could have just the header // or both the header and the timestamp in this word. if (chdr_packet.header.pkt_type == CHDR_DATA_WITH_TS) begin - if ($bits(word) >= 128) begin + if (CHDR_W >= 128) begin chdr_packet.timestamp = word[127:64]; rx_state = ST_METADATA; end else begin @@ -667,33 +709,33 @@ package PkgChdrBfm; end end ST_TIMESTAMP : begin + num_rx_bytes += BYTES_PER_CHDR_W; chdr_packet.timestamp = word; rx_state = (chdr_packet.header.num_mdata > 0) ? ST_METADATA : ST_PAYLOAD; end ST_METADATA : begin - for(int w = 0; w < CHDR_PER_BUS; w++) begin - // Grab the next chdr_word_t worth of bits - //$display("Grabbing meta word %d (%016X)", w, word[w*$bits(chdr_word_t) +: $bits(chdr_word_t)]); - chdr_packet.metadata.push_back(word[w*$bits(chdr_word_t) +: $bits(chdr_word_t)]); - end + chdr_packet.metadata.push_back(word); num_rx_mdata++; + num_rx_bytes += BYTES_PER_CHDR_W; if (num_rx_mdata == chdr_packet.header.num_mdata) rx_state = ST_PAYLOAD; end ST_PAYLOAD : begin - for(int w = 0; w < CHDR_PER_BUS; w++) begin - // Grab the next chdr_word_t worth of bits - //$display("Grabbing data word %d (%016X)", w, word[w*$bits(chdr_word_t) +: $bits(chdr_word_t)]); - chdr_packet.data.push_back(word[w*$bits(chdr_word_t) +: $bits(chdr_word_t)]); - end + chdr_packet.data.push_back(word); + num_rx_bytes += BYTES_PER_CHDR_W; end endcase - end - assert(rx_state == ST_PAYLOAD) else begin + assert (rx_state == ST_PAYLOAD) else begin $error("ChdrBfm::axis_to_chdr: Malformed CHDR packet"); end + // Check length field, noting that the last word may be partially filled + assert (chdr_packet.header.length >= num_rx_bytes-(BYTES_PER_CHDR_W-1) && + chdr_packet.header.length <= num_rx_bytes) else begin + $error("ChdrBfm::axis_to_chdr: Incorrect CHDR length"); + end + return chdr_packet; endfunction : axis_to_chdr @@ -702,44 +744,37 @@ package PkgChdrBfm; // Convert a CHDR packet data structure to a an AXI-Stream packet data // structure. function ChdrBfm::AxisPacket ChdrBfm::chdr_to_axis (ChdrPacket chdr_packet); - int num_bus_words, num_chdr_words, expected_bus_words; + int num_words, expected_words; data_t bus_word = 0; AxisPacket axis_packet = new(); // Check that we have the right number of metadata words - num_chdr_words = chdr_packet.metadata.size(); - num_bus_words = num_chdr_words / CHDR_PER_BUS; - if (num_chdr_words % CHDR_PER_BUS != 0) num_bus_words++; - assert (num_bus_words == chdr_packet.header.num_mdata) else begin + assert (chdr_packet.metadata.size() == chdr_packet.header.num_mdata) else begin $error("ChdrBfm::chdr_to_axis: Packet metadata size doesn't match header NumMData field"); end // Calculate the number of words needed to represent this packet - num_bus_words = 0; - num_bus_words += chdr_packet.data.size() / CHDR_PER_BUS; - if (chdr_packet.data.size() % CHDR_PER_BUS != 0) num_bus_words++; - num_bus_words += chdr_packet.metadata.size() / CHDR_PER_BUS; - if (chdr_packet.metadata.size() % CHDR_PER_BUS != 0) num_bus_words++; - if (chdr_packet.header.pkt_type == CHDR_DATA_WITH_TS && CHDR_PER_BUS == 1) begin + num_words = chdr_packet.data.size() + chdr_packet.metadata.size(); + if (chdr_packet.header.pkt_type == CHDR_DATA_WITH_TS && CHDR_W == 64) begin // Add two words, one for header and one for timestamp - num_bus_words += 2; + num_words += 2; end else begin // Add one word only for header (which may or may not include a timestamp) - num_bus_words += 1; + num_words += 1; end // Calculate the number of words represented by the Length field - expected_bus_words = chdr_packet.header.length / (BUS_WIDTH/8); - if (chdr_packet.header.length % (BUS_WIDTH/8) != 0) expected_bus_words++; + expected_words = chdr_packet.header.length / BYTES_PER_CHDR_W; + if (chdr_packet.header.length % BYTES_PER_CHDR_W != 0) expected_words++; // Make sure length field matches actual packet length - assert (num_bus_words == expected_bus_words) else begin + assert (num_words == expected_words) else begin $error("ChdrBfm::chdr_to_axis: Packet size doesn't match header Length field"); end // Insert header bus_word[63:0] = chdr_packet.header; - if (BUS_WIDTH == 64) begin + if (CHDR_W == 64) begin axis_packet.data.push_back(bus_word); if (chdr_packet.header.pkt_type == CHDR_DATA_WITH_TS) begin // Insert timestamp @@ -754,21 +789,13 @@ package PkgChdrBfm; // Insert metadata while (chdr_packet.metadata.size() > 0) begin - bus_word = 0; - for (int w = 0; w < CHDR_PER_BUS; w++) begin - bus_word[w*$bits(chdr_word_t) +: $bits(chdr_word_t)] = chdr_packet.metadata.pop_front(); - if (chdr_packet.metadata.size() == 0) break; - end + bus_word = chdr_packet.metadata.pop_front(); axis_packet.data.push_back(bus_word); end // Insert payload while (chdr_packet.data.size() > 0) begin - bus_word = 0; - for (int word_count = 0; word_count < CHDR_PER_BUS; word_count++) begin - bus_word[word_count*64 +: 64] = chdr_packet.data.pop_front(); - if (chdr_packet.data.size() == 0) break; - end + bus_word = chdr_packet.data.pop_front(); axis_packet.data.push_back(bus_word); end diff --git a/fpga/usrp3/sim/rfnoc/PkgChdrData.sv b/fpga/usrp3/sim/rfnoc/PkgChdrData.sv new file mode 100644 index 000000000..a849346d6 --- /dev/null +++ b/fpga/usrp3/sim/rfnoc/PkgChdrData.sv @@ -0,0 +1,155 @@ +// +// Copyright 2020 Ettus Research, A National Instruments Company +// +// SPDX-License-Identifier: LGPL-3.0-or-later +// +// Module: PkgChdrData +// +// Description: This package defines the data types used to represent CHDR +// words and data samples in RFNoC as well as utilities for converting between +// them. +// + +package PkgChdrData; + + // Default value for ITEM_W needs to be 64 due to a bug in Vivado 2019.1. + class ChdrData #(int CHDR_W = 64, int ITEM_W = 64); + + // CHDR bus word (CHDR_W bits) + typedef logic [CHDR_W-1:0] chdr_word_t ; + typedef chdr_word_t chdr_word_queue_t[$]; + + // The item/sample word type (user-defined width, a multiple of 8 bits) + typedef logic [ITEM_W-1:0] item_t ; + typedef item_t item_queue_t[$]; + + + function new(); + assert ((ITEM_W % 8 == 0) && (CHDR_W % ITEM_W == 0)) else begin + $fatal(1, "RfnocData::new: Invalid CHDR_W and/or ITEM_W"); + end + endfunction + + + // Convert a queue of items to a queue of CHDR words + static function chdr_word_queue_t item_to_chdr(ref item_queue_t items); + int items_per_word = CHDR_W / ITEM_W; + int num_chdr_words = ((items.size() + items_per_word - 1) / items_per_word); + chdr_word_queue_t chdr_words; + chdr_word_t word; + + for (int i = 0; i < num_chdr_words; i++) begin + for (int j = 0; j < items_per_word; j++) begin + word[j*ITEM_W +: ITEM_W] = items[i*items_per_word + j]; + end + chdr_words.push_back(word); + end + return chdr_words; + endfunction : item_to_chdr + + + // Convert a queue of CHDR words to a queue of items. The optional + // num_bytes argument indicates how many bytes are in the item queue that + // you want to convert. + static function item_queue_t chdr_to_item( + ref chdr_word_queue_t chdr_words, + input int num_bytes = -1 + ); + int items_per_word = CHDR_W / ITEM_W; + int bytes_left; + item_queue_t item_words; + + if (num_bytes < 0) bytes_left = chdr_words.size() * (CHDR_W / 8); + else bytes_left = num_bytes; + + foreach (chdr_words[i]) begin + for (int j = 0; j < items_per_word; j++) begin + if (bytes_left > 0) begin + item_words.push_back(chdr_words[i][j*ITEM_W +: ITEM_W]); + bytes_left -= (ITEM_W/8); + end + end + end + + assert (bytes_left == 0) else begin + $warning("ChdrData::chdr_to_item: num_bytes is not a multiple of items"); + end + + return item_words; + endfunction : chdr_to_item + + + // Return a string representation of the contents of the CHDR queue + static function string chdr_string( + ref chdr_word_queue_t chdr_words, + input string format = "%X" + ); + string str = $sformatf("%0d-bit CHDR words (%0d words or %0d bytes):\n", + CHDR_W, chdr_words.size(), chdr_words.size() * (CHDR_W/8)); + foreach (chdr_words[i]) begin + str = { str, $sformatf({"%5d> ", format, "\n"}, i, chdr_words[i]) }; + end + return str; + endfunction : chdr_string + + + // Return a string representation of the contents of the item queue + static function string item_string( + ref item_queue_t items, + input string format = "%X" + ); + string str = $sformatf("%0d-bit items (%0d items or %0d bytes)\n", + ITEM_W, items.size(), items.size() * (ITEM_W/8)); + foreach (items[i]) begin + str = { str, $sformatf({"%5d> ", format, "\n"}, i, items[i]) }; + end + return str; + endfunction : item_string + + + // Display a string representation of the contents of a CHDR word queue + static function void print_chdr( + ref chdr_word_queue_t chdr_words, + input string format = "%X" + ); + $display(chdr_string(chdr_words, format)); + endfunction : print_chdr + + + // Display a string representation of the contents of an item queue + static function void print_item( + ref item_queue_t items, + input string format = "%X" + ); + $display(item_string(items, format)); + endfunction : print_item + + + // Check if the contents of two CHDR queues is equal + static function bit chdr_equal( + ref chdr_word_queue_t left, + ref chdr_word_queue_t right + ); + if (left.size() != right.size()) return 0; + for (int i = 0; i < left.size(); i++) begin + if (left[i] != right[i]) return 0; + end + return 1; + endfunction : chdr_equal + + + // Check if the contents of two item queues is equal + static function bit item_equal( + ref item_queue_t left, + ref item_queue_t right + ); + if (left.size() != right.size()) return 0; + for (int i = 0; i < left.size(); i++) begin + if (left[i] != right[i]) return 0; + end + return 1; + endfunction : item_equal + + endclass + +endpackage : PkgChdrData diff --git a/fpga/usrp3/sim/rfnoc/PkgChdrIfaceBfm.sv b/fpga/usrp3/sim/rfnoc/PkgChdrIfaceBfm.sv index 513910ee7..0b372684c 100644 --- a/fpga/usrp3/sim/rfnoc/PkgChdrIfaceBfm.sv +++ b/fpga/usrp3/sim/rfnoc/PkgChdrIfaceBfm.sv @@ -16,11 +16,11 @@ package PkgChdrIfaceBfm; typedef struct packed { - chdr_vc_t vc; - logic eob; - logic eov; - logic has_time; - chdr_word_t timestamp; + chdr_vc_t vc; + chdr_eob_t eob; + chdr_eov_t eov; + bit has_time; + chdr_timestamp_t timestamp; } packet_info_t; diff --git a/fpga/usrp3/sim/rfnoc/PkgChdrUtils.sv b/fpga/usrp3/sim/rfnoc/PkgChdrUtils.sv index 5f7abb46f..31c6984d7 100644 --- a/fpga/usrp3/sim/rfnoc/PkgChdrUtils.sv +++ b/fpga/usrp3/sim/rfnoc/PkgChdrUtils.sv @@ -13,16 +13,20 @@ package PkgChdrUtils; + import PkgChdrData::*; + + //--------------------------------------------------------------------------- // Type Definitions //--------------------------------------------------------------------------- - // CHDR Definitions - // ---------------- + //---------------------- + // AXIS-CHDR Definitions + //---------------------- - // The fundamental unit of the CHDR bus, which is always a multiple of 64-bits - typedef logic [63:0] chdr_word_t; - typedef chdr_word_t chdr_word_queue_t[$]; + // Expose the CHDR word and item/sample data types and methods in the + // ChdrData class. The width of these types is a class parameter. + export PkgChdrData::ChdrData; // CHDR header fields typedef enum bit [2:0] { @@ -37,10 +41,13 @@ package PkgChdrUtils; } chdr_pkt_type_t; // CHDR Packet Type typedef bit [ 5:0] chdr_vc_t; // CHDR Virtual Channel field + typedef bit [ 0:0] chdr_eob_t; // CHDR End of Burst field + typedef bit [ 0:0] chdr_eov_t; // CHDR End of Vector field typedef bit [ 4:0] chdr_num_mdata_t; // CHDR Num Metadata field typedef bit [15:0] chdr_seq_num_t; // CHDR SeqNum field typedef bit [15:0] chdr_length_t; // CHDR Length field typedef bit [15:0] chdr_epid_t; // CHDR EPID field + typedef bit [63:0] chdr_timestamp_t; // CHDR Timestamp field // CHDR Context Field Identifiers typedef enum bit [3:0] { @@ -50,8 +57,9 @@ package PkgChdrUtils; CONTEXT_FIELD_MDATA = 4'd3 } chdr_context_type_t; + //---------------------- // AXIS-Ctrl Definitions - // --------------------- + //---------------------- // The fundamental unit of the AXIS-Ctrl (control) bus, which is always 32 bits typedef logic [31:0] ctrl_word_t; @@ -82,9 +90,9 @@ package PkgChdrUtils; typedef bit [3:0] ctrl_byte_en_t; // AXIS-Ctrl ByteEnable field typedef bit [19:0] ctrl_address_t; // AXIS-Ctrl Address field - + //------------------------------- // CHDR Type-Specific Definitions - // ------------------------------ + //------------------------------- // CHDR Status packet fields typedef enum bit [3:0] { @@ -141,8 +149,8 @@ package PkgChdrUtils; // CHDR packet header typedef struct packed { chdr_vc_t vc; - bit eob; - bit eov; + chdr_eob_t eob; + chdr_eov_t eov; chdr_pkt_type_t pkt_type; chdr_num_mdata_t num_mdata; chdr_seq_num_t seq_num; @@ -237,39 +245,4 @@ package PkgChdrUtils; } chdr_mgmt_t; - - //--------------------------------------------------------------------------- - // Functions - //--------------------------------------------------------------------------- - - // Returns 1 if the queues have the same contents, otherwise returns 0. This - // function is equivalent to (a == b), but this doesn't work correctly yet in - // Vivado 2018.3. - function automatic bit chdr_word_queues_equal(ref chdr_word_t a[$], ref chdr_word_t b[$]); - chdr_word_t x, y; - if (a.size() != b.size()) return 0; - foreach (a[i]) begin - x = a[i]; - y = b[i]; - if (x !== y) return 0; - end - return 1; - endfunction : chdr_word_queues_equal - - - // Returns 1 if the queues have the same contents, otherwise returns 0. This - // function is equivalent to (a == b), but this doesn't work correctly yet in - // Vivado 2018.3. - function automatic bit chdr_mgmt_op_queues_equal(ref chdr_mgmt_op_t a[$], ref chdr_mgmt_op_t b[$]); - chdr_mgmt_op_t x, y; - if (a.size() != b.size()) return 0; - foreach (a[i]) begin - x = a[i]; - y = b[i]; - if (x !== y) return 0; - end - return 1; - endfunction : chdr_mgmt_op_queues_equal - - endpackage : PkgChdrUtils diff --git a/fpga/usrp3/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv b/fpga/usrp3/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv index 722299bb6..ac2bf00ec 100644 --- a/fpga/usrp3/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv +++ b/fpga/usrp3/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv @@ -96,13 +96,15 @@ package PkgRfnocBlockCtrlBfm; class RfnocBlockCtrlBfm #(CHDR_W = 64); local virtual RfnocBackendIf.master backend; - local CtrlIfaceBfm ctrl; - local ChdrIfaceBfm #(CHDR_W) m_data[$]; - local ChdrIfaceBfm #(CHDR_W) s_data[$]; + local CtrlIfaceBfm ctrl; + local ChdrIfaceBfm #(CHDR_W) m_data[$]; + local ChdrIfaceBfm #(CHDR_W) s_data[$]; local bit running; localparam CMD_PROP_CYC = 5; + typedef ChdrData #(CHDR_W)::chdr_word_t chdr_word_t; + // Class constructor to create a new BFM instance. // // backend: Interface for the backend signals of a block diff --git a/fpga/usrp3/sim/rfnoc/PkgRfnocItemUtils.sv b/fpga/usrp3/sim/rfnoc/PkgRfnocItemUtils.sv index 2c671ad93..f827d2363 100644 --- a/fpga/usrp3/sim/rfnoc/PkgRfnocItemUtils.sv +++ b/fpga/usrp3/sim/rfnoc/PkgRfnocItemUtils.sv @@ -5,11 +5,14 @@ // // Module: PkgRfnocItemUtils // -// Description: This package contains utilities to handle and process items. +// Description: This package contains classes to handle and process CHDR items +// (e.g., samples) and to convert between CHDR words and items. It includes the +// following classes: // -// - ItemDataBuff: A class that holds a collection of items of arbitrary width -// To can convert to and from a CHDR vector -// - ItemDataBuffQueue: A queue of ItemDataBuff buffers +// - ItemDataBuff: A class that holds a collection of items of arbitrary +// width and functions to convert to and from a CHDR vector. +// +// - ItemDataBuffQueue: A queue of ItemDataBuff buffers // package PkgRfnocItemUtils; @@ -20,14 +23,20 @@ package PkgRfnocItemUtils; // Item Data Buffer //--------------------------------------------------------------------------- // - // This class items of an arbitrary type + // This is a class of "items" of an arbitrary type // //--------------------------------------------------------------------------- - class ItemDataBuff #(type data_t); - typedef ItemDataBuff #(data_t) ItemDataBuffQueue[$]; + + class ItemDataBuff #(type item_t = logic [31:0], int CHDR_W = 64); + + localparam ITEM_W = $bits(item_t); + + // Redefine these types from PkgChdrUtils to workaround Vivado 2019.1 bug + typedef logic [CHDR_W-1:0] chdr_word_t; + typedef chdr_word_t chdr_word_queue_t[$]; // Store data in the user-specified format - local data_t buff[$]; + local item_t buff[$]; // Create a new empty buffer function new(); @@ -36,7 +45,7 @@ package PkgRfnocItemUtils; // Get the bitwidth of a item in this buffer function int item_w(); - return $size(data_t); + return ITEM_W; endfunction : item_w // Delete the contents of this buffer @@ -55,48 +64,46 @@ package PkgRfnocItemUtils; endfunction : get_bytes // Get the i'th element in this buffer - function data_t get(int i); + function item_t get(int i); return buff[i]; endfunction : get // Put and element in this buffer. If i is negative // then put it at the end - function void put(data_t d, int i = -1); + function void put(item_t d, int i = -1); if (i < 0) buff.push_back(d); else buff.insert(i, d); endfunction : put - // Convert the contents of this buffer to a CHDR payload - // A CHDR payload can be transmitted using the block controller - // BFM. + // Convert the contents of this buffer to a CHDR payload A CHDR payload can + // be transmitted using the block controller BFM. function chdr_word_queue_t to_chdr_payload(); - int samps_per_word = $size(chdr_word_t) / $size(data_t); + int samps_per_word = CHDR_W / ITEM_W; int num_chdr_lines = ((buff.size() + samps_per_word - 1) / samps_per_word); chdr_word_t chdr_w_vtr[$]; for (int i = 0; i < num_chdr_lines; i++) begin chdr_word_t tmp = 'x; for (int j = 0; j < samps_per_word; j++) begin - tmp[j*$size(data_t) +: $size(data_t)] = buff[i*samps_per_word + j]; + tmp[j*ITEM_W +: ITEM_W] = buff[i*samps_per_word + j]; end chdr_w_vtr.push_back(tmp); end return chdr_w_vtr; endfunction : to_chdr_payload - // Populate this buffer using a CHDR payload - // A CHDR payload can be received using the block controller - // BFM. + // Populate this buffer using a CHDR payload A CHDR payload can be received + // using the block controller BFM. function void from_chdr_payload(chdr_word_queue_t chdr_w_vtr, int bytes); - int samps_per_word = $size(chdr_word_t) / $size(data_t); + int samps_per_word = CHDR_W / ITEM_W; int bytes_left = bytes; buff.delete(); foreach (chdr_w_vtr[i]) begin for (int j = 0; j < samps_per_word; j++) begin if (bytes_left > 0) begin - buff.push_back(chdr_w_vtr[i][j*$size(data_t) +: $size(data_t)]); - bytes_left -= ($size(data_t)/8); + buff.push_back(chdr_w_vtr[i][j*ITEM_W +: ITEM_W]); + bytes_left -= (ITEM_W/8); end end end @@ -112,7 +119,7 @@ package PkgRfnocItemUtils; end return str; end else begin - string str = $sformatf("ItemDataBuff (%0d-bit)\n", $size(data_t)); + string str = $sformatf("ItemDataBuff (%0d-bit)\n", ITEM_W); foreach (buff[i]) begin str = { str, $sformatf({"%5d> ", format, "\n"}, i, buff[i]) }; end @@ -127,7 +134,7 @@ package PkgRfnocItemUtils; // Check if the contents of two buffers is equal function bit equal( - ItemDataBuff #(data_t) rhs + ItemDataBuff #(item_t) rhs ); if (this.size() != rhs.size()) return 0; for (int i = 0; i < this.size(); i++) begin @@ -139,8 +146,16 @@ package PkgRfnocItemUtils; endclass - class ItemDataBuffQueue #(type data_t); - local ItemDataBuff #(data_t) queue[$]; + //--------------------------------------------------------------------------- + // Item Data Buffer Queue + //--------------------------------------------------------------------------- + // + // This is a queue of item buffers + // + //--------------------------------------------------------------------------- + + class ItemDataBuffQueue #(type item_t = logic [31:0], int CHDR_W = 64); + local ItemDataBuff #(item_t, CHDR_W) queue[$]; // Create a new empty queue function new(); @@ -158,13 +173,13 @@ package PkgRfnocItemUtils; endfunction : size // Get the i'th element in this buffer - function ItemDataBuff #(data_t) get(int i); + function ItemDataBuff #(item_t) get(int i); return queue[i]; endfunction : get // Put an element in this buffer. If i is negative // then put it at the end - function void put(ItemDataBuff #(data_t) buff, int i = -1); + function void put(ItemDataBuff #(item_t) buff, int i = -1); if (i < 0) queue.push_back(buff); else @@ -181,10 +196,10 @@ package PkgRfnocItemUtils; int handle = $fopen(filename, "r"); queue.delete(); while ($fgets(line, handle) > 0) begin - data_t word = 'x; + item_t word = 'x; int buff_i = word_i++ / max_buff_size; if (queue.size() < buff_i + 1) begin - ItemDataBuff #(data_t) buff = new; + ItemDataBuff #(item_t) buff = new; queue.push_back(buff); end if ($sscanf(line, "%x", word) > 0) begin diff --git a/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain_tb.sv b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain_tb.sv index 1f76563a8..2b0c892f7 100644 --- a/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain_tb.sv +++ b/host/examples/rfnoc-example/fpga/rfnoc_block_gain/rfnoc_block_gain_tb.sv @@ -27,11 +27,12 @@ module rfnoc_block_gain_tb; localparam [ 9:0] THIS_PORTID = 10'h123; localparam [31:0] NOC_ID = 32'h00000B16; localparam int CHDR_W = 64; + localparam int ITEM_W = 32; localparam int NUM_PORTS_I = 1; localparam int NUM_PORTS_O = 1; localparam int MTU = 13; localparam int SPP = 64; - localparam int PKT_SIZE_BYTES = SPP * 4; // Assumes 4 bytes per sample + localparam int PKT_SIZE_BYTES = SPP * (ITEM_W/8); localparam int STALL_PROB = 25; // Default BFM stall probability localparam real CHDR_CLK_PER = 5.0; // 200 MHz localparam real CTRL_CLK_PER = 25.0; // 40 MHz @@ -64,6 +65,10 @@ module rfnoc_block_gain_tb; // Block Controller BFM RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl = new(backend, m_ctrl, s_ctrl); + // CHDR word and item/sample data types + typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t; + typedef ChdrData #(CHDR_W, ITEM_W)::item_t item_t; + // Connect block controller to BFMs for (genvar i = 0; i < NUM_PORTS_I; i++) begin : gen_bfm_input_connections initial begin diff --git a/host/utils/rfnoc_blocktool/templates/rfnoc_block_template_tb.sv.mako b/host/utils/rfnoc_blocktool/templates/rfnoc_block_template_tb.sv.mako index 4ec1d1b93..ef535f166 100644 --- a/host/utils/rfnoc_blocktool/templates/rfnoc_block_template_tb.sv.mako +++ b/host/utils/rfnoc_blocktool/templates/rfnoc_block_template_tb.sv.mako @@ -28,6 +28,7 @@ module rfnoc_block_${config['module_name']}_tb; localparam [ 9:0] THIS_PORTID = 10'h123; localparam [31:0] NOC_ID = 32'h${format(config['noc_id'], "08X")}; localparam int CHDR_W = ${config['chdr_width']}; + localparam int ITEM_W = 32; %if 'parameters' in config: %for param, value in config['parameters'].items(): localparam int ${'{:<15}'.format(param)} = ${value}; @@ -37,7 +38,7 @@ module rfnoc_block_${config['module_name']}_tb; localparam int NUM_PORTS_O = ${func.num_ports_out_str()}; localparam int MTU = 13; localparam int SPP = 64; - localparam int PKT_SIZE_BYTES = SPP * 4; // Assumes 4 bytes per sample + localparam int PKT_SIZE_BYTES = SPP * (ITEM_W/8); localparam int STALL_PROB = 25; // Default BFM stall probability localparam real CHDR_CLK_PER = 5.0; // 200 MHz localparam real CTRL_CLK_PER = 25.0; // 40 MHz @@ -85,6 +86,10 @@ module rfnoc_block_${config['module_name']}_tb; // Block Controller BFM RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl = new(backend, m_ctrl, s_ctrl); + // CHDR word and item/sample data types + typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t; + typedef ChdrData #(CHDR_W, ITEM_W)::item_t item_t; + // Connect block controller to BFMs for (genvar i = 0; i < NUM_PORTS_I; i++) begin : gen_bfm_input_connections initial begin |