aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv')
-rw-r--r--fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv780
1 files changed, 780 insertions, 0 deletions
diff --git a/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv b/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv
new file mode 100644
index 000000000..fd0bd0048
--- /dev/null
+++ b/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv
@@ -0,0 +1,780 @@
+//
+// 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