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); Original-commit: fc895feacb8dde3b02c9a4eccb4b4f4a654f2881
This commit is contained in:
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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++;
|
||||
|
||||
@@ -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
|
||||
// ----------------------------------------
|
||||
|
||||
@@ -12,6 +12,7 @@ PkgTestExec.sv \
|
||||
test_exec.svh \
|
||||
sim_clock_gen.sv \
|
||||
PkgAxiStreamBfm.sv \
|
||||
PkgChdrData.sv \
|
||||
PkgChdrUtils.sv \
|
||||
PkgChdrBfm.sv \
|
||||
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;
|
||||
|
||||
+150
-123
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
|
||||
|
||||
+18
-45
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user