Files
b210-k7-fpga/sim/rfnoc/PkgChdrBfm.sv
T
Wade Fife 14dc3950cf 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
2020-03-09 13:43:05 -05:00

808 lines
28 KiB
Systemverilog

//
// Copyright 2019 Ettus Research, A National Instruments Company
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
// Module: PkgChdrBfm
//
// Description: Package for a bi-directional CHDR bus functional model (BFM),
// which consists primarily of the ChdrPacket and ChdrBfm classes.
//
package PkgChdrBfm;
import PkgChdrUtils::*;
import PkgAxiStreamBfm::*;
//---------------------------------------------------------------------------
// CHDR Packet Class
//---------------------------------------------------------------------------
class ChdrPacket #(int CHDR_W = 64);
typedef ChdrPacket #(CHDR_W) ChdrPacket;
typedef ChdrData #(CHDR_W)::chdr_word_t chdr_word_t;
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);
extern function string sprint(bit pretty = 1);
extern function void print(bit pretty = 1);
// Accessors
extern function void write_raw (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);
extern function void read_raw (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);
extern function void write_stream_status(ref chdr_header_t header,
ref chdr_str_status_t status);
extern function void read_stream_status (output chdr_header_t header,
output chdr_str_status_t status);
extern function void write_stream_cmd (ref chdr_header_t header,
ref chdr_str_command_t command);
extern function void read_stream_cmd (output chdr_header_t header,
output chdr_str_command_t command);
extern function void write_mgmt (ref chdr_header_t header,
ref chdr_mgmt_t mgmt);
extern function void read_mgmt (output chdr_header_t header,
output chdr_mgmt_t mgmt);
extern function void write_ctrl (ref chdr_header_t header,
ref chdr_ctrl_header_t ctrl_header,
ref ctrl_op_word_t ctrl_op_word,
ref ctrl_word_t ctrl_data[$],
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_timestamp_t ctrl_timestamp);
// Helper methods
extern function int header_bytes();
extern function int mdata_bytes();
extern function int data_bytes();
extern function void update_lengths();
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;
//---------------------------------------------------------------------------
// CHDR BFM Class
//---------------------------------------------------------------------------
class ChdrBfm #(
parameter int CHDR_W = 64,
parameter int USER_WIDTH = 1
) extends AxiStreamBfm #(CHDR_W, USER_WIDTH);
typedef ChdrPacket #(CHDR_W) ChdrPacket;
typedef ChdrData #(CHDR_W)::chdr_word_t 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;
chdr_seq_num_t seq_num;
extern function new (
virtual AxiStreamIf #(CHDR_W, USER_WIDTH).master master,
virtual AxiStreamIf #(CHDR_W, USER_WIDTH).slave slave
);
// Send Transactions
extern task put_chdr(ChdrPacket chdr_packet);
extern function bit try_put_chdr(ChdrPacket chdr_packet);
// Receive Transactions
extern task get_chdr(output ChdrPacket chdr_packet);
extern function bit try_get_chdr(output ChdrPacket chdr_packet);
extern task peek_chdr(output ChdrPacket chdr_packet);
extern function bit try_peek_chdr(output ChdrPacket chdr_packet);
// AXI-Stream/CHDR Conversion Functions
extern function ChdrPacket axis_to_chdr (AxisPacket axis_packet);
extern function AxisPacket chdr_to_axis (ChdrPacket chdr_packet);
endclass : ChdrBfm
//---------------------------------------------------------------------------
// CHDR Packet Class Methods
//---------------------------------------------------------------------------
// Create a copy of this packet and return a handle to the copy
function ChdrPacket::ChdrPacket ChdrPacket::copy();
ChdrPacket temp;
temp = new();
temp.header = this.header;
temp.timestamp = this.timestamp;
temp.metadata = this.metadata;
temp.data = this.data;
return temp;
endfunction
// Return true if this packet equals that of the argument
function bit ChdrPacket::equal(ChdrPacket packet);
if (header != packet.header) return 0;
if (!chdr_word_queues_equal(data, packet.data)) return 0;
if (!chdr_word_queues_equal(metadata, packet.metadata)) return 0;
if (header.pkt_type == CHDR_DATA_WITH_TS && timestamp !== packet.timestamp) return 0;
return 1;
endfunction : equal
// Format the contents of the packet into a string (don't dissect contents)
function string ChdrPacket::sprint_raw();
string str;
str = {str, $sformatf("ChdrPacket:\n")};
str = {str, $sformatf("- header: %p\n", header) };
str = {str, $sformatf("- timestamp: %X\n", timestamp) };
str = {str, $sformatf("- metadata:\n") };
foreach (metadata[i]) begin
str = {str, $sformatf("%5d> %X\n", i, metadata[i]) };
end
str = {str, $sformatf("- data:\n") };
foreach (data[i]) begin
str = {str, $sformatf("%5d> %X\n", i, data[i]) };
end
return str;
endfunction : sprint_raw
// Format the contents of the packet into a string (dissect contents)
function string ChdrPacket::sprint_pretty();
string str;
str = {str, $sformatf("ChdrPacket:\n")};
str = {str, $sformatf("- header: %p\n", header) };
if (header.pkt_type == CHDR_DATA_WITH_TS) begin
str = {str, $sformatf("- timestamp: %0d\n", timestamp) };
end
if (header.num_mdata != '0) begin
str = {str, $sformatf("- metadata:\n") };
foreach (metadata[i]) begin
str = {str, $sformatf("%5d> %X\n", i, metadata[i]) };
end
end
str = {str, $sformatf("- data (%s):\n", header.pkt_type.name) };
if (header.pkt_type == CHDR_MANAGEMENT) begin
chdr_header_t tmp_hdr;
chdr_mgmt_t tmp_mgmt;
read_mgmt(tmp_hdr, tmp_mgmt);
str = {str, $sformatf(" > chdr_mgmt_header_t : %p\n", tmp_mgmt.header)};
foreach (tmp_mgmt.ops[i]) begin
str = {str, $sformatf(" > %3d: chdr_mgmt_op_t: '{op_payload:0x%12x,op_code:%s,ops_pending:%0d}\n",
i, tmp_mgmt.ops[i].op_payload, tmp_mgmt.ops[i].op_code.name, tmp_mgmt.ops[i].ops_pending)};
end
end else if (header.pkt_type == CHDR_STRM_STATUS) begin
chdr_header_t tmp_hdr;
chdr_str_status_t tmp_sts;
read_stream_status(tmp_hdr, tmp_sts);
str = {str, $sformatf(" > chdr_str_status_t0 : '{status_info:0x%012x,buff_info:0x%04x,xfer_count_bytes:%0d,xfer_count_pkts:%0d...\n",
tmp_sts.status_info,tmp_sts.buff_info,tmp_sts.xfer_count_bytes,tmp_sts.xfer_count_pkts)};
str = {str, $sformatf(" > chdr_str_status_t1 : capacity_pkts:%0d,capacity_bytes:%0d,status:%s,src_epid:%0d}\n",
tmp_sts.capacity_pkts,tmp_sts.capacity_bytes,tmp_sts.status.name,tmp_sts.src_epid)};
end else if (header.pkt_type == CHDR_STRM_CMD) begin
chdr_header_t tmp_hdr;
chdr_str_command_t tmp_cmd;
read_stream_cmd(tmp_hdr, tmp_cmd);
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_timestamp_t tmp_ts;
chdr_ctrl_header_t tmp_ctrl_hdr;
ctrl_op_word_t tmp_op_word;
ctrl_word_t tmp_ctrl_data[$];
read_ctrl(tmp_hdr, tmp_ctrl_hdr, tmp_op_word, tmp_ctrl_data, tmp_ts);
str = {str, $sformatf(" > chdr_ctrl_header_t : %p\n", tmp_ctrl_hdr)};
if (tmp_ctrl_hdr.has_time)
str = {str, $sformatf(" > timestamp : %0d\n", tmp_ts)};
str = {str, $sformatf(" > ctrl_op_word_t : '{status:%s,op_code:%s,byte_enable:0b%4b,address:0x%05x}\n",
tmp_op_word.status.name, tmp_op_word.op_code.name, tmp_op_word.byte_enable, tmp_op_word.address)};
foreach (tmp_ctrl_data[i]) begin
str = {str, $sformatf(" > data %2d : 0x%08x\n", i, tmp_ctrl_data[i])};
end
end else begin
foreach (data[i]) begin
str = {str, $sformatf("%5d> %X\n", i, data[i]) };
end
end
return str;
endfunction : sprint_pretty
function string ChdrPacket::sprint(bit pretty = 1);
if (pretty)
return sprint_pretty();
else
return sprint_raw();
endfunction: sprint
// Print the contents of the packet
function void ChdrPacket::print(bit pretty = 1);
$display(sprint(pretty));
endfunction : print
// Populate the packet with the provided info. The packet Length and NumMData
// fields are calculated and set in this method. Omitting the
// data_byte_length argument, or providing a negative value, causes this
// 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_timestamp_t timestamp = 0,
input int data_byte_length = -1
);
this.header = header;
this.timestamp = timestamp;
this.data = data;
this.metadata = metadata;
update_lengths();
// Adjust length field according to data_byte_length
if (data_byte_length >= 0) begin
int array_num_bytes;
// Make sure number of words for data_byte_length matches data length
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() * BYTES_PER_CHDR_W;
this.header.length -= (array_num_bytes - data_byte_length);
end
endfunction : write_raw
// 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_timestamp_t timestamp,
output int data_byte_length
);
header = this.header;
data = this.data;
metadata = this.metadata;
timestamp = this.timestamp;
data_byte_length = data_bytes();
endfunction : read_raw
// Populate this packet as a status packet
function void ChdrPacket::write_stream_status (
ref chdr_header_t header,
ref chdr_str_status_t status
);
header.pkt_type = CHDR_STRM_STATUS; // Update packet type in header
this.header = header;
data = {};
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
// Read this packet as a status packet
function void ChdrPacket::read_stream_status (
output chdr_header_t header,
output chdr_str_status_t status
);
// Make sure it's a stream status packet
assert(this.header.pkt_type == CHDR_STRM_STATUS) else begin
$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_stream_status: Not enough data for status payload");
end
header = this.header;
for (int i = 0; i < $bits(status)/CHDR_W; i++) begin
status[i*CHDR_W +: CHDR_W] = data[i];
end
endfunction : read_stream_status
// Populate this packet as a command packet
function void ChdrPacket::write_stream_cmd (
ref chdr_header_t header,
ref chdr_str_command_t command
);
header.pkt_type = CHDR_STRM_CMD; // Update packet type in header
this.header = header;
data = {};
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
// Read this packet as a command packet
function void ChdrPacket::read_stream_cmd (
output chdr_header_t header,
output chdr_str_command_t command
);
// Make sure it's a stream command packet
assert(this.header.pkt_type == CHDR_STRM_CMD) else begin
$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_stream_cmd: Not enough data for command payload");
end
header = this.header;
for (int i = 0; i < $bits(command)/CHDR_W; i++) begin
command[i*CHDR_W +: CHDR_W] = data[i];
end
endfunction : read_stream_cmd
// Populate this packet as a management packet
function void ChdrPacket::write_mgmt (
ref chdr_header_t header,
ref chdr_mgmt_t mgmt
);
header.pkt_type = CHDR_MANAGEMENT; // Update packet type in header
this.header = header;
data = {};
// Insert the header
data.push_back( mgmt.header );
// Insert the ops
foreach (mgmt.ops[i]) begin
data.push_back( mgmt.ops[i] );
end
update_lengths();
endfunction : write_mgmt
// Read this packet as a management packet
function void ChdrPacket::read_mgmt (
output chdr_header_t header,
output chdr_mgmt_t mgmt
);
int num_ops;
// Make sure it's a management packet
assert(header.pkt_type == CHDR_MANAGEMENT) else begin
$error("ChdrPacket::read_mgmt: Packet type is not CHDR_MANAGEMENT");
end
header = this.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
$error("ChdrPacket::read_mgmt: Not enough data for management payload");
end
// Read the management header
mgmt.header = data[0];
// Read the management operations
for (int i = 0; i < num_ops; i++) begin
mgmt.ops.push_back(data[i+1]);
end
endfunction : read_mgmt
// Populate this packet as a control packet
function void ChdrPacket::write_ctrl (
ref chdr_header_t header,
ref chdr_ctrl_header_t ctrl_header,
ref ctrl_op_word_t ctrl_op_word,
ref ctrl_word_t ctrl_data[$],
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;
data64 = {};
// Insert word 0 of control payload
data64.push_back(ctrl_header);
// Insert word 1 of control payload, if timestamp is used
if (ctrl_header.has_time) begin
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;
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
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
// Read this packet as a control packet
function void ChdrPacket::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_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_ctrl: Packet type is not CHDR_CONTROL");
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;
// Ctrl header
ctrl_header = { data32[dptr+1], data32[dptr] };
dptr += 2;
// 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
// Timestamp (optional)
if(ctrl_header.has_time) begin
ctrl_timestamp = { data32[dptr+1], data32[dptr] };
dptr += 2;
end
// 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 (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 = 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 * BYTES_PER_CHDR_W;
endfunction : mdata_bytes
// Calculate the data payload size, in bytes, from the header information
function int ChdrPacket::data_bytes();
data_bytes = header.length - header_bytes() - mdata_bytes();
endfunction : data_bytes;
// Update the length and num_mdata header fields of the packet based on the
// size of the metadata queue and the data queue.
function void ChdrPacket::update_lengths();
int num_bytes;
int num_mdata;
// Calculate NumMData based on the size of metadata queue
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() * 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");
// Update header
header.num_mdata = num_mdata;
header.length = num_bytes;
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
//---------------------------------------------------------------------------
// CHDR BFM Class Methods
//---------------------------------------------------------------------------
// Class constructor. This must be given an interface for the master
// connection and an interface for the slave connection.
function ChdrBfm::new (
virtual AxiStreamIf #(CHDR_W, USER_WIDTH).master master,
virtual AxiStreamIf #(CHDR_W, USER_WIDTH).slave slave
);
super.new(master, slave);
assert(CHDR_W % 64 == 0) else begin
$fatal(1, "ChdrBfm::new: CHDR bus width must be a multiple of 64 bits");
end
endfunction : new
// Queue the provided packet for transmission
task ChdrBfm::put_chdr (ChdrPacket chdr_packet);
AxisPacket axis_packet;
axis_packet = chdr_to_axis(chdr_packet);
super.put(axis_packet);
endtask : put_chdr
// Attempt to queue the provided packet for transmission. Return 1 if
// successful, return 0 if the queue is full.
function bit ChdrBfm::try_put_chdr (ChdrPacket chdr_packet);
AxisPacket axis_packet;
bit status;
axis_packet = chdr_to_axis(chdr_packet);
return super.try_put(axis_packet);
endfunction : try_put_chdr
// Get the next packet when it becomes available (wait if necessary)
task ChdrBfm::get_chdr (output ChdrPacket chdr_packet);
AxisPacket axis_packet;
super.get(axis_packet);
chdr_packet = axis_to_chdr(axis_packet);
endtask : get_chdr
// Get the next packet if there's one available and return 1. Return 0 if
// there's no packet available.
function bit ChdrBfm::try_get_chdr (output ChdrPacket chdr_packet);
AxisPacket axis_packet;
if (!super.try_get(axis_packet)) return 0;
chdr_packet = axis_to_chdr(axis_packet);
return 1;
endfunction : try_get_chdr
// Get the next packet when it becomes available (wait if necessary), but
// don't remove it from the receive queue.
task ChdrBfm::peek_chdr (output ChdrPacket chdr_packet);
AxisPacket axis_packet;
super.peek(axis_packet);
chdr_packet = axis_to_chdr(axis_packet);
endtask : peek_chdr
// Get the next packet if there's one available and return 1, but don't
// remove it from the receive queue. Return 0 if there's no packet available.
function bit ChdrBfm::try_peek_chdr (output ChdrPacket chdr_packet);
AxisPacket axis_packet;
if (!super.try_get(axis_packet)) return 0;
chdr_packet = axis_to_chdr(axis_packet);
return 1;
endfunction : try_peek_chdr
// Convert the data payload of an AXI Stream packet data structure to a CHDR
// packet data structure.
function ChdrBfm::ChdrPacket ChdrBfm::axis_to_chdr (AxisPacket axis_packet);
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;
for(int i = 0; i < axis_packet.data.size(); i++) begin
word = axis_packet.data[i];
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 (CHDR_W >= 128) begin
chdr_packet.timestamp = word[127:64];
rx_state = ST_METADATA;
end else begin
rx_state = ST_TIMESTAMP;
end
end else begin
rx_state = ST_METADATA;
end
// Check if there's no metadata, in which case we can skip it
if (rx_state == ST_METADATA && chdr_packet.header.num_mdata == 0) begin
rx_state = ST_PAYLOAD;
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
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
chdr_packet.data.push_back(word);
num_rx_bytes += BYTES_PER_CHDR_W;
end
endcase
end
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
// 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_words, expected_words;
data_t bus_word = 0;
AxisPacket axis_packet = new();
// Check that we have the right number of metadata words
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_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_words += 2;
end else begin
// Add one word only for header (which may or may not include a timestamp)
num_words += 1;
end
// Calculate the number of words represented by the Length field
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_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 (CHDR_W == 64) begin
axis_packet.data.push_back(bus_word);
if (chdr_packet.header.pkt_type == CHDR_DATA_WITH_TS) begin
// Insert timestamp
axis_packet.data.push_back(chdr_packet.timestamp);
end
end else begin
// Copy the timestamp word from the header, regardless of whether or not
// this packet uses the timestamp field.
bus_word[127:64] = chdr_packet.timestamp;
axis_packet.data.push_back(bus_word);
end
// Insert metadata
while (chdr_packet.metadata.size() > 0) begin
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 = chdr_packet.data.pop_front();
axis_packet.data.push_back(bus_word);
end
return axis_packet;
endfunction : chdr_to_axis
endpackage : PkgChdrBfm