diff options
Diffstat (limited to 'fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv')
| -rw-r--r-- | fpga/usrp3/sim/rfnoc/PkgChdrBfm.sv | 780 |
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 |
