sim: Add item support to RFNoC simulation
This adds variants of the send and recv methods in RfnocBlockCtrlBfm and ChdrIfaceBfm that input/output items instead of CHDR words. Original-commit: aebcaea5eb4e28c5c2a1c78b5e8dd42e5d1247b6
This commit is contained in:
@@ -92,7 +92,7 @@ module rfnoc_block_axi_ram_fifo_tb #(
|
|||||||
AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
|
AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
|
||||||
|
|
||||||
// Bus functional model for a software block controller
|
// Bus functional model for a software block controller
|
||||||
RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl =
|
RfnocBlockCtrlBfm #(CHDR_W) blk_ctrl =
|
||||||
new(backend, m_ctrl, s_ctrl);
|
new(backend, m_ctrl, s_ctrl);
|
||||||
|
|
||||||
// Connect block controller to BFMs
|
// Connect block controller to BFMs
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ module rfnoc_block_ddc_tb();
|
|||||||
AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
|
AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
|
||||||
|
|
||||||
// Bus functional model for a software block controller
|
// Bus functional model for a software block controller
|
||||||
RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl =
|
RfnocBlockCtrlBfm #(CHDR_W, SAMP_W) blk_ctrl =
|
||||||
new(backend, m_ctrl, s_ctrl);
|
new(backend, m_ctrl, s_ctrl);
|
||||||
|
|
||||||
// Connect block controller to BFMs
|
// Connect block controller to BFMs
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ module rfnoc_block_duc_tb();
|
|||||||
AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
|
AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
|
||||||
|
|
||||||
// Bus functional model for a software block controller
|
// Bus functional model for a software block controller
|
||||||
RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl =
|
RfnocBlockCtrlBfm #(CHDR_W, SAMP_W) blk_ctrl =
|
||||||
new(backend, m_ctrl, s_ctrl);
|
new(backend, m_ctrl, s_ctrl);
|
||||||
|
|
||||||
// Connect block controller to BFMs
|
// Connect block controller to BFMs
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ module rfnoc_block_fft_tb();
|
|||||||
AxiStreamIf #(CHDR_W) s_chdr (rfnoc_chdr_clk, 1'b0);
|
AxiStreamIf #(CHDR_W) s_chdr (rfnoc_chdr_clk, 1'b0);
|
||||||
|
|
||||||
// Bus functional model for a software block controller
|
// Bus functional model for a software block controller
|
||||||
RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl =
|
RfnocBlockCtrlBfm #(CHDR_W, SAMP_W) blk_ctrl =
|
||||||
new(backend, m_ctrl, s_ctrl);
|
new(backend, m_ctrl, s_ctrl);
|
||||||
|
|
||||||
// Connect block controller to BFMs
|
// Connect block controller to BFMs
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ module rfnoc_block_fir_filter_tb #(
|
|||||||
AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
|
AxiStreamIf #(CHDR_W) s_chdr [NUM_PORTS] (rfnoc_chdr_clk, 1'b0);
|
||||||
|
|
||||||
// Bus functional model for a software block controller
|
// Bus functional model for a software block controller
|
||||||
RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl = new(backend, m_ctrl, s_ctrl);
|
RfnocBlockCtrlBfm #(CHDR_W, SAMP_W) blk_ctrl = new(backend, m_ctrl, s_ctrl);
|
||||||
|
|
||||||
// Connect block controller to BFMs
|
// Connect block controller to BFMs
|
||||||
for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_connections
|
for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_connections
|
||||||
|
|||||||
@@ -90,7 +90,7 @@ module rfnoc_block_radio_tb #(
|
|||||||
typedef ChdrData #(CHDR_W, ITEM_W)::item_t sample_t;
|
typedef ChdrData #(CHDR_W, ITEM_W)::item_t sample_t;
|
||||||
|
|
||||||
// Bus functional model for a software block controller
|
// Bus functional model for a software block controller
|
||||||
RfnocBlockCtrlBfm #(.CHDR_W(CHDR_W)) blk_ctrl = new(backend, m_ctrl, s_ctrl);
|
RfnocBlockCtrlBfm #(CHDR_W, ITEM_W) blk_ctrl = new(backend, m_ctrl, s_ctrl);
|
||||||
|
|
||||||
// Connect block controller to BFMs
|
// Connect block controller to BFMs
|
||||||
for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_connections
|
for (genvar i = 0; i < NUM_PORTS; i++) begin : gen_bfm_connections
|
||||||
|
|||||||
+208
-13
@@ -24,7 +24,17 @@ package PkgChdrIfaceBfm;
|
|||||||
} packet_info_t;
|
} packet_info_t;
|
||||||
|
|
||||||
|
|
||||||
class ChdrIfaceBfm #(CHDR_W = 64) extends ChdrBfm #(CHDR_W);
|
class ChdrIfaceBfm #(CHDR_W = 64, ITEM_W = 32) extends ChdrBfm #(CHDR_W);
|
||||||
|
|
||||||
|
// Redefine the ChdrPacket and chdr_word_t data types from ChdrBfm due to a
|
||||||
|
// bug in Vivado 2019.1.
|
||||||
|
typedef ChdrPacket #(CHDR_W) ChdrPacket;
|
||||||
|
typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t;
|
||||||
|
typedef ChdrData #(CHDR_W, ITEM_W)::item_t item_t;
|
||||||
|
|
||||||
|
localparam int BYTES_PER_CHDR_W = CHDR_W/8;
|
||||||
|
localparam int BYTES_PER_ITEM_W = ITEM_W/8;
|
||||||
|
|
||||||
chdr_seq_num_t seq_num; // Sequence number
|
chdr_seq_num_t seq_num; // Sequence number
|
||||||
|
|
||||||
protected int max_payload_length; // Maximum number of payload bytes per packet
|
protected int max_payload_length; // Maximum number of payload bytes per packet
|
||||||
@@ -55,7 +65,7 @@ package PkgChdrIfaceBfm;
|
|||||||
// max_length: Maximum payload length in bytes for each packet
|
// max_length: Maximum payload length in bytes for each packet
|
||||||
//
|
//
|
||||||
function void set_max_payload_length(int max_payload_length);
|
function void set_max_payload_length(int max_payload_length);
|
||||||
assert (max_payload_length % (CHDR_W/8) == 0) else begin
|
assert (max_payload_length % BYTES_PER_CHDR_W == 0) else begin
|
||||||
$fatal(1, "ChdrIfaceBfm::set_max_payload_length: max_payload_length must be a multiple of CHDR_W in bytes");
|
$fatal(1, "ChdrIfaceBfm::set_max_payload_length: max_payload_length must be a multiple of CHDR_W in bytes");
|
||||||
end
|
end
|
||||||
this.max_payload_length = max_payload_length;
|
this.max_payload_length = max_payload_length;
|
||||||
@@ -122,9 +132,28 @@ package PkgChdrIfaceBfm;
|
|||||||
endtask : send
|
endtask : send
|
||||||
|
|
||||||
|
|
||||||
// Send data as one or more CHDR data packets. The input data and metadata
|
// Send a CHDR data packet, filling the payload with items.
|
||||||
// is automatically broken into max_payload_length'd packets. If multiple
|
//
|
||||||
// packets are needed, EOB and EOV are only applied to the last packet.
|
// items: Data items to insert into the CHDR packet.
|
||||||
|
// metadata: Metadata words to insert into the CHDR packet. Omit this
|
||||||
|
// argument (or set to an empty array) to not include metadata.
|
||||||
|
// pkt_info: Data structure containing packet header information.
|
||||||
|
//
|
||||||
|
task send_items (
|
||||||
|
input item_t items[$],
|
||||||
|
input chdr_word_t metadata[$] = {},
|
||||||
|
input packet_info_t pkt_info = 0
|
||||||
|
);
|
||||||
|
chdr_word_t data[$];
|
||||||
|
data = ChdrData#(CHDR_W, ITEM_W)::item_to_chdr(items);
|
||||||
|
send(data, items.size()*BYTES_PER_ITEM_W, metadata, pkt_info);
|
||||||
|
endtask : send_items
|
||||||
|
|
||||||
|
|
||||||
|
// Send data as one or more CHDR data packets. The input data and metadata
|
||||||
|
// is automatically broken into max_payload_length'd packets. The
|
||||||
|
// timestamp, if present, is set for the first packet and updated for
|
||||||
|
// subsequent packets. The EOB and EOV are only applied to the last packet.
|
||||||
//
|
//
|
||||||
// data: Data words to insert into the CHDR packet.
|
// data: Data words to insert into the CHDR packet.
|
||||||
// data_bytes: The number of data bytes in the CHDR packet. This
|
// data_bytes: The number of data bytes in the CHDR packet. This
|
||||||
@@ -153,10 +182,12 @@ package PkgChdrIfaceBfm;
|
|||||||
chdr_word_t temp_data[$];
|
chdr_word_t temp_data[$];
|
||||||
chdr_word_t temp_mdata[$];
|
chdr_word_t temp_mdata[$];
|
||||||
|
|
||||||
num_pkts = $ceil(real'(data.size()*($bits(chdr_word_t)/8)) / max_payload_length);
|
num_pkts = $ceil(real'(data.size()*BYTES_PER_CHDR_W) / max_payload_length);
|
||||||
pkt_type = pkt_info.has_time ? CHDR_DATA_WITH_TS : CHDR_DATA_NO_TS;
|
pkt_type = pkt_info.has_time ? CHDR_DATA_WITH_TS : CHDR_DATA_NO_TS;
|
||||||
timestamp = pkt_info.timestamp;
|
timestamp = pkt_info.timestamp;
|
||||||
|
|
||||||
|
if (data_bytes < 0) data_bytes = data.size() * BYTES_PER_CHDR_W;
|
||||||
|
|
||||||
// Make sure there's not too much metadata for this number of packets
|
// Make sure there's not too much metadata for this number of packets
|
||||||
assert(metadata.size()*$bits(chdr_word_t) < num_pkts * 2**$bits(chdr_num_mdata_t) * CHDR_W) else
|
assert(metadata.size()*$bits(chdr_word_t) < num_pkts * 2**$bits(chdr_num_mdata_t) * CHDR_W) else
|
||||||
$fatal(1, "ChdrIfaceBfm::send: Too much metadata for this send request");
|
$fatal(1, "ChdrIfaceBfm::send: Too much metadata for this send request");
|
||||||
@@ -170,8 +201,8 @@ package PkgChdrIfaceBfm;
|
|||||||
// The last packet, which may or may not be full-sized
|
// The last packet, which may or may not be full-sized
|
||||||
eob = pkt_info.eob;
|
eob = pkt_info.eob;
|
||||||
eov = pkt_info.eov;
|
eov = pkt_info.eov;
|
||||||
payload_length = (data_bytes < 0) ? data_bytes : data_bytes % max_payload_length;
|
payload_length = data_bytes - (num_pkts-1) * max_payload_length;
|
||||||
first_dword = i*max_payload_length/($bits(chdr_word_t)/8);
|
first_dword = i*max_payload_length/BYTES_PER_CHDR_W;
|
||||||
last_dword = data.size()-1;
|
last_dword = data.size()-1;
|
||||||
first_mword = i*(2**$bits(chdr_num_mdata_t) * CHDR_W / $bits(chdr_word_t));
|
first_mword = i*(2**$bits(chdr_num_mdata_t) * CHDR_W / $bits(chdr_word_t));
|
||||||
last_mword = metadata.size()-1;
|
last_mword = metadata.size()-1;
|
||||||
@@ -180,10 +211,10 @@ package PkgChdrIfaceBfm;
|
|||||||
eob = 1'b0;
|
eob = 1'b0;
|
||||||
eov = 1'b0;
|
eov = 1'b0;
|
||||||
payload_length = max_payload_length;
|
payload_length = max_payload_length;
|
||||||
first_dword = (i+0)*max_payload_length / ($bits(chdr_word_t)/8);
|
first_dword = (i+0)*max_payload_length / BYTES_PER_CHDR_W;
|
||||||
last_dword = (i+1)*max_payload_length / ($bits(chdr_word_t)/8) - 1;
|
last_dword = (i+1)*max_payload_length / BYTES_PER_CHDR_W - 1;
|
||||||
first_mword = (i+0)*(2**$bits(chdr_num_mdata_t) * CHDR_W / $bits(chdr_word_t));
|
first_mword = (i+0)*(2**$bits(chdr_num_mdata_t)-1);
|
||||||
last_mword = (i+1)*(2**$bits(chdr_num_mdata_t) * CHDR_W / $bits(chdr_word_t)) - 1;
|
last_mword = (i+1)*(2**$bits(chdr_num_mdata_t)-1) - 1;
|
||||||
last_mword = last_mword > metadata.size() ? metadata.size() : last_mword;
|
last_mword = last_mword > metadata.size() ? metadata.size() : last_mword;
|
||||||
end
|
end
|
||||||
|
|
||||||
@@ -216,11 +247,33 @@ package PkgChdrIfaceBfm;
|
|||||||
put_chdr(chdr_packet);
|
put_chdr(chdr_packet);
|
||||||
|
|
||||||
// Update timestamp for next packet (in case this is not the last)
|
// Update timestamp for next packet (in case this is not the last)
|
||||||
timestamp += max_payload_length/(CHDR_W/8) * ticks_per_word;
|
timestamp += max_payload_length/BYTES_PER_CHDR_W * ticks_per_word;
|
||||||
end
|
end
|
||||||
endtask : send_packets
|
endtask : send_packets
|
||||||
|
|
||||||
|
|
||||||
|
// Send one or more CHDR data packets, filling the payload with items. The
|
||||||
|
// input data and metadata is automatically broken into
|
||||||
|
// max_payload_length'd packets. The timestamp, if present, is set for the
|
||||||
|
// first packet and updated for subsequent packets. The EOB and EOV are
|
||||||
|
// only applied to the last packet.
|
||||||
|
//
|
||||||
|
// items: Data items to insert into the payload of the CHDR packets.
|
||||||
|
// metadata: Metadata words to insert into the CHDR packet. Omit this
|
||||||
|
// argument (or set to an empty array) to not include metadata.
|
||||||
|
// pkt_info: Data structure containing packet header information.
|
||||||
|
//
|
||||||
|
task send_packets_items (
|
||||||
|
input item_t items[$],
|
||||||
|
input chdr_word_t metadata[$] = {},
|
||||||
|
input packet_info_t pkt_info = 0
|
||||||
|
);
|
||||||
|
chdr_word_t data[$];
|
||||||
|
data = ChdrData#(CHDR_W, ITEM_W)::item_to_chdr(items);
|
||||||
|
send_packets(data, items.size()*BYTES_PER_ITEM_W, metadata, pkt_info);
|
||||||
|
endtask : send_packets_items
|
||||||
|
|
||||||
|
|
||||||
// Receive a CHDR data packet and extract its contents.
|
// Receive a CHDR data packet and extract its contents.
|
||||||
//
|
//
|
||||||
// data: Data words from the received CHDR packet.
|
// data: Data words from the received CHDR packet.
|
||||||
@@ -251,6 +304,31 @@ package PkgChdrIfaceBfm;
|
|||||||
endtask : recv_adv
|
endtask : recv_adv
|
||||||
|
|
||||||
|
|
||||||
|
// Receive a CHDR data packet and extract its contents, putting the payload
|
||||||
|
// into a queue of items.
|
||||||
|
//
|
||||||
|
// items: Items extracted from the payload of the received packet.
|
||||||
|
// metadata: Metadata words from the received CHDR packet. This will be
|
||||||
|
// an empty array if there was no metadata.
|
||||||
|
// pkt_info: Data structure to receive packet header information.
|
||||||
|
//
|
||||||
|
task recv_items_adv (
|
||||||
|
output item_t items[$],
|
||||||
|
output chdr_word_t metadata[$],
|
||||||
|
output packet_info_t pkt_info
|
||||||
|
);
|
||||||
|
chdr_word_t data[$];
|
||||||
|
int data_bytes;
|
||||||
|
|
||||||
|
recv_adv(data, data_bytes, metadata, pkt_info);
|
||||||
|
items = ChdrData#(CHDR_W, ITEM_W)::chdr_to_item(data, data_bytes);
|
||||||
|
assert (data_bytes % BYTES_PER_ITEM_W == 0) else begin
|
||||||
|
$error({"ChdrIfaceBfm::recv_items_adv: ",
|
||||||
|
"Received data was not a multiple of items"});
|
||||||
|
end
|
||||||
|
endtask : recv_items_adv
|
||||||
|
|
||||||
|
|
||||||
// Receive a CHDR data packet and extract the data. Any metadata or
|
// Receive a CHDR data packet and extract the data. Any metadata or
|
||||||
// timestamp, if present, are discarded.
|
// timestamp, if present, are discarded.
|
||||||
//
|
//
|
||||||
@@ -266,6 +344,123 @@ package PkgChdrIfaceBfm;
|
|||||||
data_bytes = chdr_packet.data_bytes();
|
data_bytes = chdr_packet.data_bytes();
|
||||||
endtask : recv
|
endtask : recv
|
||||||
|
|
||||||
|
|
||||||
|
// Receive a CHDR data packet and extract its payload into a queue of
|
||||||
|
// items. Any metadata or timestamp, if present, are discarded.
|
||||||
|
//
|
||||||
|
// items: Data items extracted from payload of the received CHDR packet.
|
||||||
|
//
|
||||||
|
task recv_items(
|
||||||
|
output item_t items[$]
|
||||||
|
);
|
||||||
|
chdr_word_t data[$];
|
||||||
|
int data_bytes;
|
||||||
|
|
||||||
|
recv(data, data_bytes);
|
||||||
|
items = ChdrData#(CHDR_W, ITEM_W)::chdr_to_item(data, data_bytes);
|
||||||
|
assert (data_bytes % BYTES_PER_ITEM_W == 0) else begin
|
||||||
|
$error({"ChdrIfaceBfm::recv_items: ",
|
||||||
|
"Received data was not a multiple of items"});
|
||||||
|
end
|
||||||
|
endtask : recv_items
|
||||||
|
|
||||||
|
|
||||||
|
// Receive one ore more CHDR data packets and extract their contents,
|
||||||
|
// putting the payload into a queue of items. Any metadata or timestamp, if
|
||||||
|
// present, are discarded.
|
||||||
|
//
|
||||||
|
// items: Items extracted from the payload of the received packets.
|
||||||
|
// num_items: (Optional) Minimum number of items to receive. This must be
|
||||||
|
// provided, unless eob or eov are set. Defaults to -1, which
|
||||||
|
// means that the number of items is not limited.
|
||||||
|
// eob: (Optional) Receive up until the next End of Burst (EOB).
|
||||||
|
// Default value is 1, so an entire burst is received.
|
||||||
|
// eov: (Optional) Receive up until the next End of Vector (EOV).
|
||||||
|
//
|
||||||
|
task recv_packets_items(
|
||||||
|
output item_t items[$],
|
||||||
|
input int num_items = -1, // Receive a full burst by default
|
||||||
|
input bit eob = 1,
|
||||||
|
input bit eov = 0
|
||||||
|
);
|
||||||
|
chdr_word_t metadata[$];
|
||||||
|
packet_info_t pkt_info;
|
||||||
|
recv_packets_items_adv(items, metadata, pkt_info, num_items, eob, eov);
|
||||||
|
endtask : recv_packets_items
|
||||||
|
|
||||||
|
|
||||||
|
// Receive one or more CHDR data packets and extract their contents,
|
||||||
|
// putting the payload into a queue of items and the metadata into a queue
|
||||||
|
// of CHDR words.
|
||||||
|
//
|
||||||
|
// items: Items extracted from the payload of the received packets.
|
||||||
|
// metadata: Metadata words from the received CHDR packets. This will be
|
||||||
|
// an empty array if there was no metadata.
|
||||||
|
// pkt_info: Data structure to receive packet information. The
|
||||||
|
// timestamp, if present, will correspond to the time of the
|
||||||
|
// first sample, whereas vc/eob/eov will correspond to the
|
||||||
|
// state of the last packet.
|
||||||
|
// num_items: (Optional) Minimum number of items to receive. This must be
|
||||||
|
// provided, unless eob or eov are set. Defaults to -1, which
|
||||||
|
// means that the number of items is not limited.
|
||||||
|
// eob: (Optional) Receive up until the next End of Burst (EOB).
|
||||||
|
// Default value is 1, so an entire burst is received.
|
||||||
|
// eov: (Optional) Receive up until the next End of Vector (EOV).
|
||||||
|
//
|
||||||
|
task recv_packets_items_adv(
|
||||||
|
output item_t items[$],
|
||||||
|
output chdr_word_t metadata[$],
|
||||||
|
output packet_info_t pkt_info,
|
||||||
|
input int num_items = -1, // Receive a full burst by default
|
||||||
|
input bit eob = 1,
|
||||||
|
input bit eov = 0
|
||||||
|
);
|
||||||
|
chdr_word_t pkt_data[$];
|
||||||
|
chdr_word_t pkt_metadata[$];
|
||||||
|
int pkt_data_bytes;
|
||||||
|
int item_count;
|
||||||
|
packet_info_t time_info;
|
||||||
|
item_t new_items[$];
|
||||||
|
|
||||||
|
if (num_items < 0) begin
|
||||||
|
assert (eob || eov) else begin
|
||||||
|
$fatal(1, {"ChdrIfaceBfm::recv_packets_items_adv: ",
|
||||||
|
"eob or eov must be set when num_items is not limited"});
|
||||||
|
end
|
||||||
|
num_items = 32'h7FFF_FFFF;
|
||||||
|
end
|
||||||
|
|
||||||
|
time_info = 0;
|
||||||
|
items = {};
|
||||||
|
metadata = {};
|
||||||
|
while (items.size() < num_items) begin
|
||||||
|
// Receive the next packet
|
||||||
|
recv_adv(pkt_data, pkt_data_bytes, pkt_metadata, pkt_info);
|
||||||
|
|
||||||
|
if (items.size() == 0) begin
|
||||||
|
// First packet, so grab the timestamp if it exists
|
||||||
|
if (pkt_info.has_time) time_info = pkt_info;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Enqueue the data
|
||||||
|
new_items = ChdrData#(CHDR_W, ITEM_W)::chdr_to_item(pkt_data, pkt_data_bytes);
|
||||||
|
items = {items, new_items};
|
||||||
|
assert (pkt_data_bytes % BYTES_PER_ITEM_W == 0) else begin
|
||||||
|
$error({"ChdrIfaceBfm::recv_packets_items_adv: ",
|
||||||
|
"Received data was not a multiple of items"});
|
||||||
|
end
|
||||||
|
|
||||||
|
// Enqueue the metadata
|
||||||
|
metadata = {metadata, pkt_metadata};
|
||||||
|
|
||||||
|
if ((eob && pkt_info.eob) || (eov && pkt_info.eov)) break;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Restore timestamp from the first packet
|
||||||
|
pkt_info.has_time = time_info.has_time;
|
||||||
|
pkt_info.timestamp = time_info.timestamp;
|
||||||
|
endtask : recv_packets_items_adv
|
||||||
|
|
||||||
endclass : ChdrIfaceBfm
|
endclass : ChdrIfaceBfm
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -93,17 +93,18 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
//
|
//
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
|
|
||||||
class RfnocBlockCtrlBfm #(CHDR_W = 64);
|
class RfnocBlockCtrlBfm #(CHDR_W = 64, ITEM_W = 32);
|
||||||
|
|
||||||
local virtual RfnocBackendIf.master backend;
|
local virtual RfnocBackendIf.master backend;
|
||||||
local CtrlIfaceBfm ctrl;
|
local CtrlIfaceBfm ctrl;
|
||||||
local ChdrIfaceBfm #(CHDR_W) m_data[$];
|
local ChdrIfaceBfm #(CHDR_W, ITEM_W) m_data[$];
|
||||||
local ChdrIfaceBfm #(CHDR_W) s_data[$];
|
local ChdrIfaceBfm #(CHDR_W, ITEM_W) s_data[$];
|
||||||
local bit running;
|
local bit running;
|
||||||
|
|
||||||
localparam CMD_PROP_CYC = 5;
|
localparam CMD_PROP_CYC = 5;
|
||||||
|
|
||||||
typedef ChdrData #(CHDR_W)::chdr_word_t chdr_word_t;
|
typedef ChdrData #(CHDR_W, ITEM_W)::chdr_word_t chdr_word_t;
|
||||||
|
typedef ChdrData #(CHDR_W, ITEM_W)::item_t item_t;
|
||||||
|
|
||||||
// Class constructor to create a new BFM instance.
|
// Class constructor to create a new BFM instance.
|
||||||
//
|
//
|
||||||
@@ -128,17 +129,18 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
// Add a master data port. This should connect to a DUT slave input.
|
// Add a master data port. This should connect to a DUT slave input.
|
||||||
//
|
//
|
||||||
// m_chdr: Virtual master interface to connect new port to.
|
// m_chdr: Virtual master interface to connect new port to.
|
||||||
// max_payload_length: Maximum payload length to create when building
|
// max_payload_length: Maximum payload length (in bytes) to create when
|
||||||
// packets from data.
|
// building packets from data.
|
||||||
// ticks_per_word: Number of timebase clock ticks to increment per
|
// ticks_per_word: Number of timebase clock ticks to increment per
|
||||||
// CHDR word.
|
// CHDR word.
|
||||||
//
|
//
|
||||||
function int add_master_data_port(
|
function int add_master_data_port(
|
||||||
virtual AxiStreamIf #(CHDR_W).master m_chdr,
|
virtual AxiStreamIf #(CHDR_W).master m_chdr,
|
||||||
int max_payload_length = 2**$bits(chdr_length_t),
|
int max_payload_length = 2**$bits(chdr_length_t),
|
||||||
int ticks_per_word = CHDR_W/32
|
int ticks_per_word = CHDR_W/ITEM_W
|
||||||
);
|
);
|
||||||
ChdrIfaceBfm #(CHDR_W) bfm = new(m_chdr, null, max_payload_length, ticks_per_word);
|
ChdrIfaceBfm #(CHDR_W, ITEM_W) bfm =
|
||||||
|
new(m_chdr, null, max_payload_length, ticks_per_word);
|
||||||
m_data.push_back(bfm);
|
m_data.push_back(bfm);
|
||||||
return m_data.size() - 1;
|
return m_data.size() - 1;
|
||||||
endfunction : add_master_data_port
|
endfunction : add_master_data_port
|
||||||
@@ -150,7 +152,7 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
function int add_slave_data_port(
|
function int add_slave_data_port(
|
||||||
virtual AxiStreamIf #(CHDR_W).slave s_chdr
|
virtual AxiStreamIf #(CHDR_W).slave s_chdr
|
||||||
);
|
);
|
||||||
ChdrIfaceBfm #(CHDR_W) bfm = new(null, s_chdr);
|
ChdrIfaceBfm #(CHDR_W, ITEM_W) bfm = new(null, s_chdr);
|
||||||
s_data.push_back(bfm);
|
s_data.push_back(bfm);
|
||||||
return s_data.size() - 1;
|
return s_data.size() - 1;
|
||||||
endfunction : add_slave_data_port
|
endfunction : add_slave_data_port
|
||||||
@@ -161,8 +163,8 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
//
|
//
|
||||||
// port_num: The port number to which m_chdr should be connected
|
// port_num: The port number to which m_chdr should be connected
|
||||||
// m_chdr: Master CHDR interface to connect to the port
|
// m_chdr: Master CHDR interface to connect to the port
|
||||||
// max_payload_length: Maximum payload length to create when building
|
// max_payload_length: Maximum payload length (in bytes) to create when
|
||||||
// packets from data.
|
// building packets from data.
|
||||||
// ticks_per_word: Number of timebase clock ticks to increment per
|
// ticks_per_word: Number of timebase clock ticks to increment per
|
||||||
// CHDR word.
|
// CHDR word.
|
||||||
//
|
//
|
||||||
@@ -170,9 +172,10 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
int port_num,
|
int port_num,
|
||||||
virtual AxiStreamIf #(CHDR_W).master m_chdr,
|
virtual AxiStreamIf #(CHDR_W).master m_chdr,
|
||||||
int max_payload_length = 2**$bits(chdr_length_t),
|
int max_payload_length = 2**$bits(chdr_length_t),
|
||||||
int ticks_per_word = CHDR_W/32
|
int ticks_per_word = CHDR_W/ITEM_W
|
||||||
);
|
);
|
||||||
ChdrIfaceBfm #(CHDR_W) bfm = new(m_chdr, null, max_payload_length, ticks_per_word);
|
ChdrIfaceBfm #(CHDR_W, ITEM_W) bfm =
|
||||||
|
new(m_chdr, null, max_payload_length, ticks_per_word);
|
||||||
wait (m_data.size() == port_num);
|
wait (m_data.size() == port_num);
|
||||||
m_data.push_back(bfm);
|
m_data.push_back(bfm);
|
||||||
endtask : connect_master_data_port
|
endtask : connect_master_data_port
|
||||||
@@ -188,7 +191,7 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
int port_num,
|
int port_num,
|
||||||
virtual AxiStreamIf #(CHDR_W).slave s_chdr
|
virtual AxiStreamIf #(CHDR_W).slave s_chdr
|
||||||
);
|
);
|
||||||
ChdrIfaceBfm #(CHDR_W) bfm = new(null, s_chdr);
|
ChdrIfaceBfm #(CHDR_W, ITEM_W) bfm = new(null, s_chdr);
|
||||||
wait (s_data.size() == port_num);
|
wait (s_data.size() == port_num);
|
||||||
s_data.push_back(bfm);
|
s_data.push_back(bfm);
|
||||||
endtask : connect_slave_data_port
|
endtask : connect_slave_data_port
|
||||||
@@ -214,7 +217,7 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
endfunction : get_ctrl_bfm
|
endfunction : get_ctrl_bfm
|
||||||
|
|
||||||
// Return a handle to the indicated master port BFM
|
// Return a handle to the indicated master port BFM
|
||||||
function ChdrIfaceBfm #(CHDR_W) get_master_data_bfm(int port);
|
function ChdrIfaceBfm #(CHDR_W, ITEM_W) get_master_data_bfm(int port);
|
||||||
assert (port >= 0 && port < m_data.size()) else begin
|
assert (port >= 0 && port < m_data.size()) else begin
|
||||||
$fatal(1, "Invalid master port number");
|
$fatal(1, "Invalid master port number");
|
||||||
end
|
end
|
||||||
@@ -222,7 +225,7 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
endfunction : get_master_data_bfm
|
endfunction : get_master_data_bfm
|
||||||
|
|
||||||
// Return a handle to the indicated slave port BFM
|
// Return a handle to the indicated slave port BFM
|
||||||
function ChdrIfaceBfm #(CHDR_W) get_slave_data_bfm(int port);
|
function ChdrIfaceBfm #(CHDR_W, ITEM_W) get_slave_data_bfm(int port);
|
||||||
assert (port >= 0 && port < m_data.size()) else begin
|
assert (port >= 0 && port < m_data.size()) else begin
|
||||||
$fatal(1, "Invalid slave port number");
|
$fatal(1, "Invalid slave port number");
|
||||||
end
|
end
|
||||||
@@ -429,10 +432,39 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
endtask : send
|
endtask : send
|
||||||
|
|
||||||
|
|
||||||
// Send data as one or more CHDR data packets out the CHDR data interface.
|
// Send a CHDR data packet, filling the payload with items.
|
||||||
|
//
|
||||||
|
// port: Port to send the CHDR packet on.
|
||||||
|
// items: Data items to insert into the CHDR packet's payload.
|
||||||
|
// metadata: Metadata words to insert into the CHDR packet. Omit this
|
||||||
|
// argument (or set to an empty array) to not include
|
||||||
|
// metadata.
|
||||||
|
// pkt_info: Data structure containing packet header information.
|
||||||
|
//
|
||||||
|
task send_items(
|
||||||
|
input int port,
|
||||||
|
input item_t items[$],
|
||||||
|
input chdr_word_t metadata[$] = {},
|
||||||
|
input packet_info_t pkt_info = 0
|
||||||
|
);
|
||||||
|
assert (running) else begin
|
||||||
|
$fatal(1, "Cannot call send_items until RfnocBlockCtrlBfm is running");
|
||||||
|
end
|
||||||
|
assert (port >= 0 && port < m_data.size()) else begin
|
||||||
|
$fatal(1, "Invalid master port number");
|
||||||
|
end
|
||||||
|
|
||||||
|
m_data[port].send_items(items, metadata, pkt_info);
|
||||||
|
endtask : send_items
|
||||||
|
|
||||||
|
|
||||||
|
// Send data as one or more CHDR data packets. The input data and metadata
|
||||||
|
// is automatically broken into max_payload_length'd packets. The
|
||||||
|
// timestamp, if present, is set for the first packet and updated for
|
||||||
|
// subsequent packets. The EOB and EOV are only applied to the last packet.
|
||||||
//
|
//
|
||||||
// port: Port to send the CHDR packet(s) on.
|
// port: Port to send the CHDR packet(s) on.
|
||||||
// data: Data words to insert into the CHDR packet.
|
// data: Data words to insert into the CHDR packets.
|
||||||
// data_bytes: Size of data in bytes. If omitted or -1, data_bytes will
|
// data_bytes: Size of data in bytes. If omitted or -1, data_bytes will
|
||||||
// be calculated based on the number of words in data.
|
// be calculated based on the number of words in data.
|
||||||
// metadata: Metadata words to insert into the CHDR packet(s). Omit
|
// metadata: Metadata words to insert into the CHDR packet(s). Omit
|
||||||
@@ -458,6 +490,36 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
endtask : send_packets
|
endtask : send_packets
|
||||||
|
|
||||||
|
|
||||||
|
// Send one or more CHDR data packets, filling the payload with items. The
|
||||||
|
// input data and metadata is automatically broken into
|
||||||
|
// max_payload_length'd packets. The timestamp, if present, is set for the
|
||||||
|
// first packet and updated for subsequent packets. The EOB and EOV are
|
||||||
|
// only applied to the last packet.
|
||||||
|
//
|
||||||
|
// port: Port to send the CHDR packet(s) on.
|
||||||
|
// items: Data items to insert into the payload of the CHDR packets.
|
||||||
|
// metadata: Metadata words to insert into the CHDR packet(s). Omit
|
||||||
|
// this argument (or set to an empty array) to not include
|
||||||
|
// metadata.
|
||||||
|
// pkt_info: Data structure containing packet header information.
|
||||||
|
//
|
||||||
|
task send_packets_items(
|
||||||
|
input int port,
|
||||||
|
input item_t items[$],
|
||||||
|
input chdr_word_t metadata[$] = {},
|
||||||
|
input packet_info_t pkt_info = 0
|
||||||
|
);
|
||||||
|
assert (running) else begin
|
||||||
|
$fatal(1, "Cannot call send_packets_items until RfnocBlockCtrlBfm is running");
|
||||||
|
end
|
||||||
|
assert (port >= 0 && port < m_data.size()) else begin
|
||||||
|
$fatal(1, "Invalid master port number");
|
||||||
|
end
|
||||||
|
|
||||||
|
m_data[port].send_packets_items(items, metadata, pkt_info);
|
||||||
|
endtask : send_packets_items
|
||||||
|
|
||||||
|
|
||||||
// Receive a CHDR data packet on the CHDR data interface and extract its
|
// Receive a CHDR data packet on the CHDR data interface and extract its
|
||||||
// contents.
|
// contents.
|
||||||
//
|
//
|
||||||
@@ -488,6 +550,32 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
endtask : recv_adv
|
endtask : recv_adv
|
||||||
|
|
||||||
|
|
||||||
|
// Receive a CHDR data packet on the CHDR data interface and extract its
|
||||||
|
// contents, putting the payload into a queue of items.
|
||||||
|
//
|
||||||
|
// port: Port to receive the CHDR packet from.
|
||||||
|
// items: Items extracted from the payload of the received packet.
|
||||||
|
// metadata: Metadata words from the received CHDR packet. This
|
||||||
|
// will be an empty array if there was no metadata.
|
||||||
|
// pkt_info: Data structure to receive packet header information.
|
||||||
|
//
|
||||||
|
task recv_items_adv(
|
||||||
|
input int port,
|
||||||
|
output item_t items[$],
|
||||||
|
output chdr_word_t metadata[$],
|
||||||
|
output packet_info_t pkt_info
|
||||||
|
);
|
||||||
|
assert (running) else begin
|
||||||
|
$fatal(1, "Cannot call recv_items_adv until RfnocBlockCtrlBfm is running");
|
||||||
|
end
|
||||||
|
assert (port >= 0 && port < s_data.size()) else begin
|
||||||
|
$fatal(1, "Invalid slave port number");
|
||||||
|
end
|
||||||
|
|
||||||
|
s_data[port].recv_items_adv(items, metadata, pkt_info);
|
||||||
|
endtask : recv_items_adv
|
||||||
|
|
||||||
|
|
||||||
// Receive a CHDR data packet on the CHDR data interface and extract the
|
// Receive a CHDR data packet on the CHDR data interface and extract the
|
||||||
// data. Any metadata or timestamp, if present, are discarded.
|
// data. Any metadata or timestamp, if present, are discarded.
|
||||||
//
|
//
|
||||||
@@ -513,6 +601,103 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
endtask : recv
|
endtask : recv
|
||||||
|
|
||||||
|
|
||||||
|
// Receive a CHDR data packet on the CHDR data interface and extract its
|
||||||
|
// payload into a queue of items. Any metadata or timestamp, if present,
|
||||||
|
// are discarded.
|
||||||
|
//
|
||||||
|
// port: Port number for the block to receive from.
|
||||||
|
// items: Data items extracted from payload of the received packet.
|
||||||
|
// data_bytes: The number of data bytes in the CHDR packet. This
|
||||||
|
// is useful if the data is not a multiple of the
|
||||||
|
// chdr_word_t size.
|
||||||
|
//
|
||||||
|
task recv_items(
|
||||||
|
input int port,
|
||||||
|
output item_t items[$]
|
||||||
|
);
|
||||||
|
assert (running) else begin
|
||||||
|
$fatal(1, "Cannot call recv_items until RfnocBlockCtrlBfm is running");
|
||||||
|
end
|
||||||
|
assert (port >= 0 && port < s_data.size()) else begin
|
||||||
|
$fatal(1, "Invalid slave port number");
|
||||||
|
end
|
||||||
|
|
||||||
|
s_data[port].recv_items(items);
|
||||||
|
endtask : recv_items
|
||||||
|
|
||||||
|
|
||||||
|
// Receive one ore more CHDR data packets and extract their contents,
|
||||||
|
// putting the payload into a queue of items. Any metadata or timestamp, if
|
||||||
|
// present, are discarded.
|
||||||
|
//
|
||||||
|
// port: Port number for the block to receive from.
|
||||||
|
// items: Items extracted from the payload of the received packets.
|
||||||
|
// num_items: (Optional) Minimum number of items to receive. This must be
|
||||||
|
// provided, unless eob or eov are set. Defaults to -1, which
|
||||||
|
// means that the number of items is not limited.
|
||||||
|
// eob: (Optional) Receive up until the next End of Burst (EOB).
|
||||||
|
// Default value is 1, so an entire burst is received.
|
||||||
|
// eov: (Optional) Receive up until the next End of Vector (EOV).
|
||||||
|
//
|
||||||
|
task recv_packets_items(
|
||||||
|
input int port,
|
||||||
|
output item_t items[$],
|
||||||
|
input int num_items = -1, // Receive a full burst by default
|
||||||
|
input bit eob = 1,
|
||||||
|
input bit eov = 0
|
||||||
|
);
|
||||||
|
assert (running) else begin
|
||||||
|
$fatal(1, "Cannot call recv_items_adv until RfnocBlockCtrlBfm is running");
|
||||||
|
end
|
||||||
|
assert (port >= 0 && port < s_data.size()) else begin
|
||||||
|
$fatal(1, "Invalid slave port number");
|
||||||
|
end
|
||||||
|
|
||||||
|
s_data[port].recv_packets_items(
|
||||||
|
items, num_items, eob, eov);
|
||||||
|
endtask : recv_packets_items
|
||||||
|
|
||||||
|
|
||||||
|
// Receive one or more CHDR data packets and extract their contents,
|
||||||
|
// putting the payload into a queue of items and the metadata into a queue
|
||||||
|
// of CHDR words.
|
||||||
|
//
|
||||||
|
// port: Port number for the block to receive from.
|
||||||
|
// items: Items extracted from the payload of the received packets.
|
||||||
|
// metadata: Metadata words from the received CHDR packets. This will be
|
||||||
|
// an empty array if there was no metadata.
|
||||||
|
// pkt_info: Data structure to receive packet information. The
|
||||||
|
// timestamp, if present, will correspond to the time of the
|
||||||
|
// first sample, whereas vc/eob/eov will correspond to the
|
||||||
|
// state of the last packet.
|
||||||
|
// num_items: (Optional) Minimum number of items to receive. This must be
|
||||||
|
// provided, unless eob or eov are set. Defaults to -1, which
|
||||||
|
// means that the number of items is not limited.
|
||||||
|
// eob: (Optional) Receive up until the next End of Burst (EOB).
|
||||||
|
// Default value is 1, so an entire burst is received.
|
||||||
|
// eov: (Optional) Receive up until the next End of Vector (EOV).
|
||||||
|
//
|
||||||
|
task recv_packets_items_adv(
|
||||||
|
input int port,
|
||||||
|
output item_t items[$],
|
||||||
|
output chdr_word_t metadata[$],
|
||||||
|
output packet_info_t pkt_info,
|
||||||
|
input int num_items = -1, // Receive a full burst by default
|
||||||
|
input bit eob = 1,
|
||||||
|
input bit eov = 0
|
||||||
|
);
|
||||||
|
assert (running) else begin
|
||||||
|
$fatal(1, "Cannot call recv_items_adv until RfnocBlockCtrlBfm is running");
|
||||||
|
end
|
||||||
|
assert (port >= 0 && port < s_data.size()) else begin
|
||||||
|
$fatal(1, "Invalid slave port number");
|
||||||
|
end
|
||||||
|
|
||||||
|
s_data[port].recv_packets_items_adv(
|
||||||
|
items, metadata, pkt_info, num_items, eob, eov);
|
||||||
|
endtask : recv_packets_items_adv
|
||||||
|
|
||||||
|
|
||||||
// Transmit a raw CHDR packet.
|
// Transmit a raw CHDR packet.
|
||||||
//
|
//
|
||||||
// port: Port number on which to transmit the packet
|
// port: Port number on which to transmit the packet
|
||||||
|
|||||||
Reference in New Issue
Block a user