aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv
diff options
context:
space:
mode:
authorMartin Braun <martin.braun@ettus.com>2020-01-23 16:10:22 -0800
committerMartin Braun <martin.braun@ettus.com>2020-01-28 09:35:36 -0800
commitbafa9d95453387814ef25e6b6256ba8db2df612f (patch)
tree39ba24b5b67072d354775272e687796bb511848d /fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv
parent3075b981503002df3115d5f1d0b97d2619ba30f2 (diff)
downloaduhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.gz
uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.tar.bz2
uhd-bafa9d95453387814ef25e6b6256ba8db2df612f.zip
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>
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