Files
b210-k7-fpga/sim/rfnoc/PkgChdrBfm.sv
T
+18 6b67702ad7 Merge FPGA repository back into UHD repository
The FPGA codebase was removed from the UHD repository in 2014 to reduce
the size of the repository. However, over the last half-decade, the
split between the repositories has proven more burdensome than it has
been helpful. By merging the FPGA code back, it will be possible to
create atomic commits that touch both FPGA and UHD codebases. Continuous
integration testing is also simplified by merging the repositories,
because it was previously difficult to automatically derive the correct
UHD branch when testing a feature branch on the FPGA repository.

This commit also updates the license files and paths therein.

We are therefore merging the repositories again. Future development for
FPGA code will happen in the same repository as the UHD host code and
MPM code.

== Original Codebase and Rebasing ==

The original FPGA repository will be hosted for the foreseeable future
at its original local location: https://github.com/EttusResearch/fpga/

It can be used for bisecting, reference, and a more detailed history.

The final commit from said repository to be merged here is
05003794e2da61cabf64dd278c45685a7abad7ec. This commit is tagged as
v4.0.0.0-pre-uhd-merge.

If you have changes in the FPGA repository that you want to rebase onto
the UHD repository, simply run the following commands:

- Create a directory to store patches (this should be an empty
  directory):

    mkdir ~/patches

- Now make sure that your FPGA codebase is based on the same state as
  the code that was merged:

    cd src/fpga # Or wherever your FPGA code is stored
    git rebase v4.0.0.0-pre-uhd-merge

  Note: The rebase command may look slightly different depending on what
  exactly you're trying to rebase.

- Create a patch set for your changes versus v4.0.0.0-pre-uhd-merge:

    git format-patch v4.0.0.0-pre-uhd-merge -o ~/patches

  Note: Make sure that only patches are stored in your output directory.
  It should otherwise be empty. Make sure that you picked the correct
  range of commits, and only commits you wanted to rebase were exported
  as patch files.

- Go to the UHD repository and apply the patches:

    cd src/uhd # Or wherever your UHD repository is stored
    git am --directory fpga ~/patches/*
    rm -rf ~/patches # This is for cleanup

== Contributors ==

The following people have contributed mainly to these files (this list
is not complete):

Co-authored-by: Alex Williams <alex.williams@ni.com>
Co-authored-by: Andrej Rode <andrej.rode@ettus.com>
Co-authored-by: Ashish Chaudhari <ashish@ettus.com>
Co-authored-by: Ben Hilburn <ben.hilburn@ettus.com>
Co-authored-by: Ciro Nishiguchi <ciro.nishiguchi@ni.com>
Co-authored-by: Daniel Jepson <daniel.jepson@ni.com>
Co-authored-by: Derek Kozel <derek.kozel@ettus.com>
Co-authored-by: EJ Kreinar <ej@he360.com>
Co-authored-by: Humberto Jimenez <humberto.jimenez@ni.com>
Co-authored-by: Ian Buckley <ian.buckley@gmail.com>
Co-authored-by: Jörg Hofrichter <joerg.hofrichter@ni.com>
Co-authored-by: Jon Kiser <jon.kiser@ni.com>
Co-authored-by: Josh Blum <josh@joshknows.com>
Co-authored-by: Jonathon Pendlum <jonathan.pendlum@ettus.com>
Co-authored-by: Martin Braun <martin.braun@ettus.com>
Co-authored-by: Matt Ettus <matt@ettus.com>
Co-authored-by: Michael West <michael.west@ettus.com>
Co-authored-by: Moritz Fischer <moritz.fischer@ettus.com>
Co-authored-by: Nick Foster <nick@ettus.com>
Co-authored-by: Nicolas Cuervo <nicolas.cuervo@ettus.com>
Co-authored-by: Paul Butler <paul.butler@ni.com>
Co-authored-by: Paul David <paul.david@ettus.com>
Co-authored-by: Ryan Marlow <ryan.marlow@ettus.com>
Co-authored-by: Sugandha Gupta <sugandha.gupta@ettus.com>
Co-authored-by: Sylvain Munaut <tnt@246tNt.com>
Co-authored-by: Trung Tran <trung.tran@ettus.com>
Co-authored-by: Vidush Vishwanath <vidush.vishwanath@ettus.com>
Co-authored-by: Wade Fife <wade.fife@ettus.com>


Original-commit: bafa9d95453387814ef25e6b6256ba8db2df612f
2020-01-28 09:35:36 -08:00

781 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 BUS_WIDTH = 64);
typedef ChdrPacket #(BUS_WIDTH) ChdrPacket;
chdr_header_t header;
chdr_word_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_word_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 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_word_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);
// 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();
endclass : ChdrPacket;
//---------------------------------------------------------------------------
// CHDR BFM Class
//---------------------------------------------------------------------------
class ChdrBfm #(
parameter int BUS_WIDTH = 64,
parameter int USER_WIDTH = 1
) extends AxiStreamBfm #(BUS_WIDTH, USER_WIDTH);
typedef ChdrPacket #(BUS_WIDTH) ChdrPacket;
// Number of 64-bit CHDR words per AXI word
const int CHDR_PER_BUS = BUS_WIDTH / $bits(chdr_word_t);
// Default fields used by high-level transaction methods
chdr_epid_t dst_epid;
chdr_seq_num_t seq_num;
extern function new (
virtual AxiStreamIf #(BUS_WIDTH, USER_WIDTH).master master,
virtual AxiStreamIf #(BUS_WIDTH, 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_word_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_word_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+7) / 8 == 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;
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_word_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 += $bits(chdr_word_t)) begin
data.push_back( status[i +: $bits(chdr_word_t)] );
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_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");
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];
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 += $bits(chdr_word_t)) begin
data.push_back( command[i +: $bits(chdr_word_t)] );
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_command: 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");
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];
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()/8 - 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_word_t ctrl_timestamp = 0
);
bit partial_word;
ctrl_word_t mandatory_data;
header.pkt_type = CHDR_CONTROL; // Update packet type in header
this.header = header;
data = {};
// Insert word 0 of control payload
data.push_back(ctrl_header[63:0]);
// Insert word 1 of control payload, if timestamp is used
if (ctrl_header.has_time) begin
data.push_back(ctrl_timestamp);
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]});
// 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]});
end
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_word_t ctrl_timestamp
);
chdr_word_t chdr_op_word;
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");
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
header = this.header;
// Word 0
ctrl_header[63:0] = data[dptr++];
// Word 1
if (ctrl_header.has_time) begin
ctrl_timestamp = data[dptr++];
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];
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]);
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)
end else begin
header_bytes = (BUS_WIDTH / 8); // 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;
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() / (BUS_WIDTH / $bits(chdr_word_t));
if (metadata.size() % (BUS_WIDTH / $bits(chdr_word_t)) != 0) begin
num_mdata++;
end
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 = 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
//---------------------------------------------------------------------------
// 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 #(BUS_WIDTH, USER_WIDTH).master master,
virtual AxiStreamIf #(BUS_WIDTH, USER_WIDTH).slave slave
);
super.new(master, slave);
assert(BUS_WIDTH % 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;
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
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
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
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
num_rx_mdata++;
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
end
endcase
end
assert(rx_state == ST_PAYLOAD) else begin
$error("ChdrBfm::axis_to_chdr: Malformed CHDR packet");
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_bus_words, num_chdr_words, expected_bus_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
$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
// Add two words, one for header and one for timestamp
num_bus_words += 2;
end else begin
// Add one word only for header (which may or may not include a timestamp)
num_bus_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++;
// Make sure length field matches actual packet length
assert (num_bus_words == expected_bus_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
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 = 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
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
axis_packet.data.push_back(bus_word);
end
return axis_packet;
endfunction : chdr_to_axis
endpackage : PkgChdrBfm