aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv')
-rw-r--r--fpga/usrp3/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv1074
1 files changed, 1074 insertions, 0 deletions
diff --git a/fpga/usrp3/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv b/fpga/usrp3/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv
new file mode 100644
index 000000000..8712884c0
--- /dev/null
+++ b/fpga/usrp3/sim/rfnoc/PkgRfnocBlockCtrlBfm.sv
@@ -0,0 +1,1074 @@
+//
+// Copyright 2019 Ettus Research, A National Instruments Company
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: PkgRfnocBlockCtrlBfm
+//
+// Description: This package includes high-level bus functional models (BFMs)
+// for communicating with RFNoC. This includes the following:
+//
+// - ChdrDataStreamBfm: Model for the AXIS CHDR interface of a Transport
+// Adapter or Stream Endpoint.
+//
+// - RegisterIfaceBfm: Model for the AXIS CTRL interface of a Stream Endpoint.
+//
+// - RfnocBlockCtrlBfm: Model for a software block controller, which includes
+// both a ChdrDataStreamBfm and a RegisterIfaceBfm.
+//
+
+
+//-----------------------------------------------------------------------------
+// SV Interface for the RFNoC Backend Iface
+//-----------------------------------------------------------------------------
+
+typedef struct packed {
+ bit [476:0] reserved0;
+ bit soft_chdr_rst;
+ bit soft_ctrl_rst;
+ bit flush_en;
+ bit [31:0] flush_timeout;
+} backend_config_v1_t;
+
+typedef struct packed {
+ bit [439:0] reserved0;
+ bit [5:0] mtu;
+ bit flush_done;
+ bit flush_active;
+ bit [31:0] noc_id;
+ bit [7:0] ctrl_max_async_msgs;
+ bit [5:0] ctrl_fifosize;
+ bit [5:0] num_data_o;
+ bit [5:0] num_data_i;
+ bit [5:0] proto_ver;
+} backend_status_v1_t;
+
+typedef union packed {
+ backend_config_v1_t v1;
+} backend_config_t;
+
+typedef union packed {
+ backend_status_v1_t v1;
+} backend_status_t;
+
+
+interface RfnocBackendIf(
+ input logic chdr_clk,
+ input logic ctrl_clk
+);
+ backend_config_t cfg;
+ backend_status_t sts;
+
+ modport master (
+ input chdr_clk,
+ input ctrl_clk,
+ output cfg,
+ input sts
+ );
+ modport slave (
+ input chdr_clk,
+ input ctrl_clk,
+ input cfg,
+ output sts
+ );
+endinterface : RfnocBackendIf
+
+
+//-----------------------------------------------------------------------------
+// RFNoC Block Controller Bus Functional Model
+//-----------------------------------------------------------------------------
+
+package PkgRfnocBlockCtrlBfm;
+
+ import PkgChdrUtils::*;
+ import PkgChdrBfm::*;
+ import PkgAxisCtrlBfm::*;
+ import PkgRfnocItemUtils::*;
+
+
+ typedef struct packed {
+ chdr_vc_t vc;
+ logic eob;
+ logic eov;
+ logic has_time;
+ chdr_word_t timestamp;
+ } packet_info_t;
+
+
+ //---------------------------------------------------------------------------
+ // CHDR Stream BFM
+ //---------------------------------------------------------------------------
+ //
+ // This class models an AXIS CHDR interface, such as that on a Transport
+ // Adapter or in a Stream Endpoint.
+ //
+ //---------------------------------------------------------------------------
+
+ class ChdrDataStreamBfm #(CHDR_W = 64) extends ChdrBfm #(CHDR_W);
+ chdr_seq_num_t seq_num; // Sequence number
+
+ protected int max_payload_length; // Maximum number of payload bytes per packet
+ protected int ticks_per_word; // Timestamp increment per CHDR_W sized word
+
+
+ // Class constructor to create a new BFM instance.
+ //
+ // m_chdr: Interface for the master connection (BFM's CHDR output)
+ // s_chdr: Interface for the slave connection (BFM's CHDR input)
+ //
+ function new(
+ virtual AxiStreamIf #(CHDR_W).master m_chdr,
+ virtual AxiStreamIf #(CHDR_W).slave s_chdr,
+ input int max_payload_length = 2**$bits(chdr_length_t),
+ input int ticks_per_word = CHDR_W/32
+ );
+ super.new(m_chdr, s_chdr);
+ this.seq_num = 0;
+ set_max_payload_length(max_payload_length);
+ set_ticks_per_word(ticks_per_word);
+ endfunction : new
+
+
+ // Set the maximum payload size for packets. This value is used to split
+ // large send requests across multiple packets.
+ //
+ // max_length: Maximum payload length in bytes for each packet
+ //
+ function void set_max_payload_length(int max_payload_length);
+ assert (max_payload_length % (CHDR_W/8) == 0) else begin
+ $fatal(1, "ChdrDataStreamBfm::set_max_payload_length: max_payload_length must be a multiple of CHDR_W in bytes");
+ end
+ this.max_payload_length = max_payload_length;
+ endfunction
+
+
+ // Return the maximum payload size for packets. This value is used to split
+ // large send requests across multiple packets.
+ function int get_max_payload_length();
+ return max_payload_length;
+ endfunction
+
+
+ // Set the timestamp ticks per CHDR_W sized word.
+ //
+ // ticks_per_word: Amount to increment the timestamp per CHDR_W sized word
+ //
+ function void set_ticks_per_word(int ticks_per_word);
+ this.ticks_per_word = ticks_per_word;
+ endfunction
+
+
+ // Return the timestamp ticks per CHDR_W sized word.
+ function int get_ticks_per_word();
+ return ticks_per_word;
+ endfunction
+
+
+ // Send a CHDR data packet.
+ //
+ // data: Data words to insert into the CHDR packet.
+ // data_bytes: The number of data bytes in the CHDR packet. This
+ // is useful if the data is not a multiple of the
+ // chdr_word_t size.
+ // metadata: Metadata words to insert into the CHDR packet. Omit this
+ // argument (or set to an empty array) to not include
+ // metadata.
+ // pkt_info: Data structure containing packet header information.
+ //
+ task send (
+ input chdr_word_t data[$],
+ input int data_bytes = -1,
+ input chdr_word_t metadata[$] = {},
+ input packet_info_t pkt_info = 0
+ );
+ ChdrPacket chdr_packet;
+ chdr_header_t chdr_header;
+
+ // Build packet
+ chdr_packet = new();
+ chdr_header = '{
+ vc : pkt_info.vc,
+ eob : pkt_info.eob,
+ eov : pkt_info.eov,
+ seq_num : seq_num++,
+ pkt_type : pkt_info.has_time ? CHDR_DATA_WITH_TS : CHDR_DATA_NO_TS,
+ dst_epid : dst_epid,
+ default : 0
+ };
+ chdr_packet.write_raw(chdr_header, data, metadata, pkt_info.timestamp, data_bytes);
+
+ // Send the packet
+ put_chdr(chdr_packet);
+ endtask : send
+
+
+ // Send data as one or more CHDR data packets. The input data and metadata
+ // is automatically broken into max_payload_length'd packets. If multiple
+ // packets are needed, EOB and EOV are only applied to the last packet.
+ //
+ // data: Data words to insert into the CHDR packet.
+ // data_bytes: The number of data bytes in the CHDR packet. This
+ // is useful if the data is not a multiple of the
+ // chdr_word_t size.
+ // metadata: Metadata words to insert into the CHDR packet. Omit this
+ // argument (or set to an empty array) to not include
+ // metadata.
+ // pkt_info: Data structure containing packet header information.
+ //
+ task send_packets (
+ input chdr_word_t data[$],
+ input int data_bytes = -1,
+ input chdr_word_t metadata[$] = {},
+ input packet_info_t pkt_info = 0
+ );
+ ChdrPacket chdr_packet;
+ chdr_header_t chdr_header;
+ chdr_pkt_type_t pkt_type;
+ chdr_word_t timestamp;
+ int num_pkts;
+ int payload_length;
+ int first_dword, last_dword;
+ int first_mword, last_mword;
+ bit eob, eov;
+ chdr_word_t temp_data[$];
+ chdr_word_t temp_mdata[$];
+
+ num_pkts = $ceil(real'(data.size()*($bits(chdr_word_t)/8)) / max_payload_length);
+ pkt_type = pkt_info.has_time ? CHDR_DATA_WITH_TS : CHDR_DATA_NO_TS;
+ timestamp = pkt_info.timestamp;
+
+ // Make sure there's not too much metadata for this number of packets
+ assert(metadata.size()*$bits(chdr_word_t) < num_pkts * 2**$bits(chdr_num_mdata_t) * CHDR_W) else
+ $fatal(1, "ChdrDataStreamBfm::send: Too much metadata for this send request");
+
+ // Send the data, one packet at a time.
+ for (int i = 0; i < num_pkts; i++) begin
+ chdr_packet = new();
+
+ // Figure out which data chunk to send next
+ if (i == num_pkts-1) begin
+ // The last packet, which may or may not be full-sized
+ eob = pkt_info.eob;
+ eov = pkt_info.eov;
+ payload_length = (data_bytes < 0) ? data_bytes : data_bytes % max_payload_length;
+ first_dword = i*max_payload_length/($bits(chdr_word_t)/8);
+ last_dword = data.size()-1;
+ first_mword = i*(2**$bits(chdr_num_mdata_t) * CHDR_W / $bits(chdr_word_t));
+ last_mword = metadata.size()-1;
+ end else begin
+ // A full-sized packet, not the last
+ eob = 1'b0;
+ eov = 1'b0;
+ payload_length = max_payload_length;
+ first_dword = (i+0)*max_payload_length / ($bits(chdr_word_t)/8);
+ last_dword = (i+1)*max_payload_length / ($bits(chdr_word_t)/8) - 1;
+ first_mword = (i+0)*(2**$bits(chdr_num_mdata_t) * CHDR_W / $bits(chdr_word_t));
+ last_mword = (i+1)*(2**$bits(chdr_num_mdata_t) * CHDR_W / $bits(chdr_word_t)) - 1;
+ last_mword = last_mword > metadata.size() ? metadata.size() : last_mword;
+ end
+
+ // Build the packet
+ chdr_header = '{
+ vc : pkt_info.vc,
+ eob : eob,
+ eov : eov,
+ seq_num : seq_num++,
+ pkt_type : pkt_type,
+ dst_epid : dst_epid,
+ default : 0
+ };
+
+ // Copy region of data and metadata to be sent in next packet
+ temp_data = data[first_dword : last_dword];
+ if (first_mword < metadata.size()) temp_mdata = metadata[first_mword : last_mword];
+ else temp_mdata = {};
+
+ // Build the packet
+ chdr_packet.write_raw(
+ chdr_header,
+ temp_data,
+ temp_mdata,
+ timestamp,
+ payload_length
+ );
+
+ // Send the packet
+ put_chdr(chdr_packet);
+
+ // Update timestamp for next packet (in case this is not the last)
+ timestamp += max_payload_length/(CHDR_W/8) * ticks_per_word;
+ end
+ endtask : send_packets
+
+
+ // Receive a CHDR data packet and extract its contents.
+ //
+ // data: Data words from the received CHDR packet.
+ // data_bytes: The number of data bytes in the CHDR packet. This
+ // is useful if the data is not a multiple of the
+ // chdr_word_t size.
+ // metadata: Metadata words from the received CHDR packet. This
+ // will be an empty array if there was no metadata.
+ // pkt_info: Data structure to receive packet header information.
+ //
+ task recv_adv (
+ output chdr_word_t data[$],
+ output int data_bytes,
+ output chdr_word_t metadata[$],
+ output packet_info_t pkt_info
+ );
+ ChdrPacket chdr_packet;
+ get_chdr(chdr_packet);
+
+ data = chdr_packet.data;
+ data_bytes = chdr_packet.data_bytes();
+ metadata = chdr_packet.metadata;
+ pkt_info.timestamp = chdr_packet.timestamp;
+ pkt_info.vc = chdr_packet.header.vc;
+ pkt_info.eob = chdr_packet.header.eob;
+ pkt_info.eov = chdr_packet.header.eov;
+ pkt_info.has_time = chdr_packet.header.pkt_type == CHDR_DATA_WITH_TS ? 1 : 0;
+ endtask : recv_adv
+
+
+ // Receive a CHDR data packet and extract the data. Any metadata or
+ // timestamp, if present, are discarded.
+ //
+ // data: Data words from the received CHDR packet.
+ // data_bytes: The number of data bytes in the CHDR packet. This
+ // is useful if the data is not a multiple of the
+ // chdr_word_t size.
+ //
+ task recv(output chdr_word_t data[$], output int data_bytes);
+ ChdrPacket chdr_packet;
+ get_chdr(chdr_packet);
+ data = chdr_packet.data;
+ data_bytes = chdr_packet.data_bytes();
+ endtask : recv
+
+ endclass : ChdrDataStreamBfm
+
+
+
+ //---------------------------------------------------------------------------
+ // CTRL Stream BFM
+ //---------------------------------------------------------------------------
+ //
+ // This class models an AXIS CTRL interface, such as that in a Stream
+ // Endpoint.
+ //
+ //---------------------------------------------------------------------------
+
+ class RegisterIfaceBfm extends AxisCtrlBfm;
+ ctrl_port_t dst_port;
+ ctrl_port_t src_port;
+ ctrl_seq_num_t seq_num;
+
+ // Class constructor to create a new BFM instance.
+ //
+ // m_chdr: Interface for the master connection (BFM's AXIS output)
+ // s_chdr: Interface for the slave connection (BFM's AXIS input)
+ // src_port: Source port to use in generated control packets
+ //
+ function new(
+ virtual AxiStreamIf #(32).master m_chdr,
+ virtual AxiStreamIf #(32).slave s_chdr,
+ ctrl_port_t dst_port,
+ ctrl_port_t src_port
+ );
+ super.new(m_chdr, s_chdr);
+ this.dst_port = dst_port;
+ this.src_port = src_port;
+ this.seq_num = '0;
+ endfunction : new
+
+
+ // Send an AXIS-Ctrl read request packet and get the response.
+ //
+ // addr: Address for the read request
+ // word: Data word that was returned in response to the read
+ //
+ task reg_read (
+ input ctrl_address_t addr,
+ output ctrl_word_t word
+ );
+ AxisCtrlPacket ctrl_packet;
+
+ // Create the AXIS-Ctrl packet
+ ctrl_packet = new();
+ ctrl_packet.header = '{
+ seq_num : seq_num++,
+ num_data : 1,
+ src_port : src_port,
+ dst_port : dst_port,
+ default : 0
+ };
+ ctrl_packet.op_word = '{
+ op_code : CTRL_OP_READ,
+ byte_enable : ~0,
+ address : addr,
+ default : 0
+ };
+ ctrl_packet.data = { 0 };
+
+ // Send the control packet and get the response
+ put_ctrl(ctrl_packet);
+ get_ctrl(ctrl_packet);
+ word = ctrl_packet.data[0];
+
+ assert(ctrl_packet.header.is_ack == 1 &&
+ ctrl_packet.op_word.status == CTRL_STS_OKAY) else begin
+ $fatal(1, "RegisterIfaceBfm::reg_read: Did not receive CTRL_STS_OKAY status");
+ end
+ endtask : reg_read
+
+
+ // Send an AXIS-Ctrl write request packet and get the response.
+ //
+ // addr: Address for the write request
+ // word: Data word to write
+ //
+ task reg_write (
+ ctrl_address_t addr,
+ ctrl_word_t word
+ );
+ AxisCtrlPacket ctrl_packet;
+
+ // Create the AXIS-Ctrl packet
+ ctrl_packet = new();
+ ctrl_packet.header = '{
+ seq_num : seq_num++,
+ num_data : 1,
+ src_port : src_port,
+ dst_port : dst_port,
+ default : 0
+ };
+ ctrl_packet.op_word = '{
+ op_code : CTRL_OP_WRITE,
+ byte_enable : ~0,
+ address : addr,
+ default : 0
+ };
+
+ // Send the packet and get the response
+ ctrl_packet.data = { word };
+ put_ctrl(ctrl_packet);
+ get_ctrl(ctrl_packet);
+ word = ctrl_packet.data[0];
+
+ assert(ctrl_packet.header.is_ack == 1 &&
+ ctrl_packet.op_word.status == CTRL_STS_OKAY) else begin
+ $fatal(1, "RegisterIfaceBfm::reg_write: Did not receive CTRL_STS_OKAY status");
+ end
+ endtask : reg_write
+
+ endclass : RegisterIfaceBfm
+
+
+ //---------------------------------------------------------------------------
+ // Block Controller BFM
+ //---------------------------------------------------------------------------
+ //
+ // This class models a block controller in software
+ //
+ //---------------------------------------------------------------------------
+
+ class RfnocBlockCtrlBfm #(CHDR_W = 64);
+
+ local virtual RfnocBackendIf.master backend;
+ local RegisterIfaceBfm ctrl;
+ local ChdrDataStreamBfm #(CHDR_W) m_data[$];
+ local ChdrDataStreamBfm #(CHDR_W) s_data[$];
+ local bit running;
+
+ localparam CMD_PROP_CYC = 5;
+
+ // Class constructor to create a new BFM instance.
+ //
+ // backend: Interface for the backend signals of a block
+ // m_ctrl: Interface for the CTRL master connection (EP's AXIS-Ctrl output)
+ // s_ctrl: Interface for the CTRL slave connection (EP's AXIS-Ctrl input)
+ // dst_port: Destination port to use in generated control packets
+ // src_port: Source port to use in generated control packets
+ //
+ function new(
+ virtual RfnocBackendIf.master backend,
+ virtual AxiStreamIf #(32).master m_ctrl,
+ virtual AxiStreamIf #(32).slave s_ctrl,
+ input ctrl_port_t dst_port = 10'd2,
+ input ctrl_port_t src_port = 10'd1
+ );
+ this.backend = backend;
+ this.ctrl = new(m_ctrl, s_ctrl, dst_port, src_port);
+ this.running = 0;
+ endfunction : new
+
+ // Add a master data port. This should connect to a DUT slave input.
+ //
+ // m_chdr: Virtual master interface to connect new port to.
+ // max_payload_length: Maximum payload length to create when building
+ // packets from data.
+ // ticks_per_word: Number of timebase clock ticks to increment per
+ // CHDR word.
+ //
+ function int add_master_data_port(
+ virtual AxiStreamIf #(CHDR_W).master m_chdr,
+ int max_payload_length = 2**$bits(chdr_length_t),
+ int ticks_per_word = CHDR_W/32
+ );
+ ChdrDataStreamBfm #(CHDR_W) bfm = new(m_chdr, null, max_payload_length, ticks_per_word);
+ m_data.push_back(bfm);
+ return m_data.size() - 1;
+ endfunction : add_master_data_port
+
+ // Add a slave data port. This should connect to a DUT master output.
+ //
+ // s_chdr: Virtual slave interface to connect new port to
+ //
+ function int add_slave_data_port(
+ virtual AxiStreamIf #(CHDR_W).slave s_chdr
+ );
+ ChdrDataStreamBfm #(CHDR_W) bfm = new(null, s_chdr);
+ s_data.push_back(bfm);
+ return s_data.size() - 1;
+ endfunction : add_slave_data_port
+
+ // Add a master data port. This is equivalent to add_master_data_port()
+ // except it accepts a port number and it waits until the preceding ports
+ // are connected to ensure that ports are connected in the correct order.
+ //
+ // port_num: The port number to which m_chdr should be connected
+ // m_chdr: Master CHDR interface to connect to the port
+ // max_payload_length: Maximum payload length to create when building
+ // packets from data.
+ // ticks_per_word: Number of timebase clock ticks to increment per
+ // CHDR word.
+ //
+ task connect_master_data_port(
+ int port_num,
+ virtual AxiStreamIf #(CHDR_W).master m_chdr,
+ int max_payload_length = 2**$bits(chdr_length_t),
+ int ticks_per_word = CHDR_W/32
+ );
+ ChdrDataStreamBfm #(CHDR_W) bfm = new(m_chdr, null, max_payload_length, ticks_per_word);
+ wait (m_data.size() == port_num);
+ m_data.push_back(bfm);
+ endtask : connect_master_data_port
+
+ // Add a slave data port. This is equivalent to add_slave_data_port()
+ // except it accepts a port number and it waits until the preceding ports
+ // are connected to ensure that ports are connected in the correct order.
+ //
+ // port_num: The port number to which m_chdr should be connected
+ // s_chdr: Master CHDR interface to connect to the port
+ //
+ task connect_slave_data_port(
+ int port_num,
+ virtual AxiStreamIf #(CHDR_W).slave s_chdr
+ );
+ ChdrDataStreamBfm #(CHDR_W) bfm = new(null, s_chdr);
+ wait (s_data.size() == port_num);
+ s_data.push_back(bfm);
+ endtask : connect_slave_data_port
+
+ // Start the data and control BFM's processes running.
+ task run();
+ assert (backend.sts.v1.proto_ver == 1) else begin
+ $fatal(1, "The connected block has an incompatible backend interface");
+ end
+ if (!running) begin
+ ctrl.run();
+ foreach (m_data[i])
+ m_data[i].run();
+ foreach (s_data[i])
+ s_data[i].run();
+ running = 1;
+ end
+ endtask : run
+
+ // Return a handle to the control BFM
+ function RegisterIfaceBfm get_ctrl_bfm();
+ return ctrl;
+ endfunction : get_ctrl_bfm
+
+ // Return a handle to the indicated master port BFM
+ function ChdrDataStreamBfm #(CHDR_W) get_master_data_bfm(int port);
+ assert (port >= 0 && port < m_data.size()) else begin
+ $fatal(1, "Invalid master port number");
+ end
+ return m_data[port];
+ endfunction : get_master_data_bfm
+
+ // Return a handle to the indicated slave port BFM
+ function ChdrDataStreamBfm #(CHDR_W) get_slave_data_bfm(int port);
+ assert (port >= 0 && port < m_data.size()) else begin
+ $fatal(1, "Invalid slave port number");
+ end
+ return s_data[port];
+ endfunction : get_slave_data_bfm
+
+ // Set the maximum payload size for packets. This value is used to split
+ // large send requests across multiple packets.
+ //
+ // port: Master port whose maximum length you want to set
+ // max_length: Maximum payload length in bytes for each packet
+ //
+ function void set_max_payload_length(int port, int max_length);
+ assert (port >= 0 && port < m_data.size()) else begin
+ $fatal(1, "Invalid master port number");
+ end
+ m_data[port].set_max_payload_length(max_length);
+ endfunction
+
+ // Return the maximum payload size for packets. This value is used to split
+ // large send requests across multiple packets.
+ //
+ // port: Master port whose maximum length you want to get
+ //
+ function int get_max_payload_length(int port);
+ assert (port >= 0 && port < m_data.size()) else begin
+ $fatal(1, "Invalid master port number");
+ end
+ return m_data[port].get_max_payload_length();
+ endfunction
+
+ // Set the timestamp ticks per CHDR_W sized word.
+ //
+ // port: Master port whose timestamp increment you want to set
+ // ticks_per_word: Amount to increment the timestamp per CHDR_W sized word
+ //
+ function void set_ticks_per_word(int port, int ticks_per_word);
+ assert (port >= 0 && port < m_data.size()) else begin
+ $fatal(1, "Invalid master port number");
+ end
+ m_data[port].set_ticks_per_word(ticks_per_word);
+ endfunction
+
+ // Return the timestamp ticks per CHDR_W sized word.
+ //
+ // port: Master port whose timestamp increment you want to get
+ //
+ function int get_ticks_per_word(int port);
+ assert (port >= 0 && port < m_data.size()) else begin
+ $fatal(1, "Invalid master port number");
+ end
+ return m_data[port].get_ticks_per_word();
+ endfunction
+
+ // Get static info about the block
+ function logic [7:0] get_proto_ver();
+ return backend.sts.v1.proto_ver;
+ endfunction : get_proto_ver
+
+ function logic [31:0] get_noc_id();
+ return backend.sts.v1.noc_id;
+ endfunction : get_noc_id
+
+ function logic [5:0] get_num_data_i();
+ return backend.sts.v1.num_data_i;
+ endfunction : get_num_data_i
+
+ function logic [5:0] get_num_data_o();
+ return backend.sts.v1.num_data_o;
+ endfunction : get_num_data_o
+
+ function logic [5:0] get_ctrl_fifosize();
+ return backend.sts.v1.ctrl_fifosize;
+ endfunction : get_ctrl_fifosize
+
+ function logic [5:0] get_mtu();
+ return backend.sts.v1.mtu;
+ endfunction : get_mtu
+
+ // Soft-Reset the CHDR path
+ //
+ // rst_cyc: Number of cycles to wait for reset completion
+ //
+ task reset_chdr(input int rst_cyc = 100);
+ assert (running) else begin
+ $fatal(1, "Cannot call flush_and_reset until RfnocBlockCtrlBfm is running");
+ end
+
+ // Assert soft_chdr_rst then wait
+ // Note: soft_chdr_rst must be driven in the ctrl_clk domain
+ @(posedge backend.ctrl_clk);
+ backend.cfg.v1.soft_chdr_rst = 1;
+ repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk);
+ backend.cfg.v1.soft_chdr_rst = 0;
+ @(posedge backend.ctrl_clk);
+ repeat (rst_cyc) @(posedge backend.ctrl_clk);
+ endtask : reset_chdr
+
+ // Soft-Reset the Control path
+ //
+ // rst_cyc: Number of cycles to wait for reset completion
+ //
+ task reset_ctrl(input int rst_cyc = 100);
+ assert (running) else begin
+ $fatal(1, "Cannot call flush_and_reset until RfnocBlockCtrlBfm is running");
+ end
+
+ // Assert soft_ctrl_rst then wait
+ @(posedge backend.ctrl_clk);
+ backend.cfg.v1.soft_ctrl_rst = 1;
+ repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk);
+ backend.cfg.v1.soft_ctrl_rst = 0;
+ repeat (rst_cyc) @(posedge backend.ctrl_clk);
+ endtask : reset_ctrl
+
+ // Flush the data ports of the block
+ //
+ // idle_cyc: Number of idle cycles before done is asserted
+ //
+ task flush(input logic [31:0] idle_cyc = 100);
+ assert (running) else begin
+ $fatal(1, "Cannot call flush until RfnocBlockCtrlBfm is running");
+ end
+
+ // Set flush timeout then wait
+ backend.cfg.v1.flush_timeout = idle_cyc;
+ repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk);
+ repeat (CMD_PROP_CYC) @(posedge backend.chdr_clk);
+ // Start flush then wait for done
+ @(posedge backend.ctrl_clk);
+ backend.cfg.v1.flush_en = 1;
+ @(posedge backend.ctrl_clk);
+ while (~backend.sts.v1.flush_done) @(posedge backend.ctrl_clk);
+ // Deassert flush then wait
+ backend.cfg.v1.flush_en = 0;
+ while (backend.sts.v1.flush_active) @(posedge backend.ctrl_clk);
+ repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk);
+ repeat (CMD_PROP_CYC) @(posedge backend.chdr_clk);
+ endtask : flush
+
+ // Flush the data ports of the block then reset the CHDR
+ // path, wait then reset the ctrl path
+ //
+ // idle_cyc: Number of idle cycles before done is asserted
+ // chdr_rst_cyc: Number of cycles to wait for chdr_rst completion
+ // ctrl_rst_cyc: Number of cycles to wait for ctrl_rst completion
+ //
+ task flush_and_reset(
+ input logic [31:0] idle_cyc = 100,
+ input int chdr_rst_cyc = 100,
+ input int ctrl_rst_cyc = 100
+ );
+ assert (running) else begin
+ $fatal(1, "Cannot call flush_and_reset until RfnocBlockCtrlBfm is running");
+ end
+
+ // Set flush timeout then wait
+ backend.cfg.v1.flush_timeout = idle_cyc;
+ repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk);
+ repeat (CMD_PROP_CYC) @(posedge backend.chdr_clk);
+ // Start flush then wait for done
+ @(posedge backend.ctrl_clk);
+ backend.cfg.v1.flush_en = 1;
+ @(posedge backend.ctrl_clk);
+ while (~backend.sts.v1.flush_done) @(posedge backend.ctrl_clk);
+ // Assert chdr_rst then wait
+ reset_chdr(chdr_rst_cyc);
+ // Assert ctrl_rst then wait
+ reset_ctrl(ctrl_rst_cyc);
+ // Deassert flush then wait
+ backend.cfg.v1.flush_en = 0;
+ while (backend.sts.v1.flush_active) @(posedge backend.ctrl_clk);
+ repeat (CMD_PROP_CYC) @(posedge backend.ctrl_clk);
+ repeat (CMD_PROP_CYC) @(posedge backend.chdr_clk);
+ endtask : flush_and_reset
+
+
+ // Send a CHDR data packet out the CHDR data interface.
+ //
+ // port: Port to send the CHDR packet on.
+ // data: Data words to insert into the CHDR packet.
+ // data_bytes: Size of data in bytes. If omitted or -1, data_bytes will
+ // be calculated based on the number of words in data.
+ // metadata: Metadata words to insert into the CHDR packet. Omit this
+ // argument (or set to an empty array) to not include
+ // metadata.
+ // pkt_info: Data structure containing packet header information.
+ //
+ task send(
+ input int port,
+ input chdr_word_t data[$],
+ input int data_bytes = -1,
+ input chdr_word_t metadata[$] = {},
+ input packet_info_t pkt_info = 0
+ );
+ assert (running) else begin
+ $fatal(1, "Cannot call send until RfnocBlockCtrlBfm is running");
+ end
+ assert (port >= 0 && port < m_data.size()) else begin
+ $fatal(1, "Invalid master port number");
+ end
+
+ m_data[port].send(data, data_bytes, metadata, pkt_info);
+ endtask : send
+
+
+ // Send data as one or more CHDR data packets out the CHDR data interface.
+ //
+ // port: Port to send the CHDR packet(s) on.
+ // data: Data words to insert into the CHDR packet.
+ // data_bytes: Size of data in bytes. If omitted or -1, data_bytes will
+ // be calculated based on the number of words in data.
+ // metadata: Metadata words to insert into the CHDR packet(s). Omit
+ // this argument (or set to an empty array) to not include
+ // metadata.
+ // pkt_info: Data structure containing packet header information.
+ //
+ task send_packets(
+ input int port,
+ input chdr_word_t data[$],
+ input int data_bytes = -1,
+ input chdr_word_t metadata[$] = {},
+ input packet_info_t pkt_info = 0
+ );
+ assert (running) else begin
+ $fatal(1, "Cannot call send_packets until RfnocBlockCtrlBfm is running");
+ end
+ assert (port >= 0 && port < m_data.size()) else begin
+ $fatal(1, "Invalid master port number");
+ end
+
+ m_data[port].send_packets(data, data_bytes, metadata, pkt_info);
+ endtask : send_packets
+
+
+ // Receive a CHDR data packet on the CHDR data interface and extract its
+ // contents.
+ //
+ // port: Port to receive the CHDR packet from.
+ // data: Data words from the received CHDR packet.
+ // data_bytes: The number of data bytes in the CHDR packet. This
+ // is useful if the data is not a multiple of the
+ // chdr_word_t size.
+ // metadata: Metadata words from the received CHDR packet. This
+ // will be an empty array if there was no metadata.
+ // pkt_info: Data structure to receive packet header information.
+ //
+ task recv_adv(
+ input int port,
+ output chdr_word_t data[$],
+ output int data_bytes,
+ output chdr_word_t metadata[$],
+ output packet_info_t pkt_info
+ );
+ assert (running) else begin
+ $fatal(1, "Cannot call recv_adv until RfnocBlockCtrlBfm is running");
+ end
+ assert (port >= 0 && port < s_data.size()) else begin
+ $fatal(1, "Invalid slave port number");
+ end
+
+ s_data[port].recv_adv(data, data_bytes, metadata, pkt_info);
+ endtask : recv_adv
+
+
+ // Receive a CHDR data packet on the CHDR data interface and extract the
+ // data. Any metadata or timestamp, if present, are discarded.
+ //
+ // port: Port number for the block to receive from
+ // data: Data words from the received CHDR packet
+ // data_bytes: The number of data bytes in the CHDR packet. This
+ // is useful if the data is not a multiple of the
+ // chdr_word_t size.
+ //
+ task recv(
+ input int port,
+ output chdr_word_t data[$],
+ output int data_bytes
+ );
+ assert (running) else begin
+ $fatal(1, "Cannot call recv until RfnocBlockCtrlBfm is running");
+ end
+ assert (port >= 0 && port < s_data.size()) else begin
+ $fatal(1, "Invalid slave port number");
+ end
+
+ s_data[port].recv(data, data_bytes);
+ endtask : recv
+
+
+ // Transmit a raw CHDR packet.
+ //
+ // port: Port number on which to transmit the packet
+ // packet: Packet to transmit
+ //
+ task put_chdr(
+ input int port,
+ input ChdrPacket #(CHDR_W) packet
+ );
+ assert (running) else begin
+ $fatal(1, "Cannot call put_chdr until RfnocBlockCtrlBfm is running");
+ end
+ assert (port >= 0 && port < m_data.size()) else begin
+ $fatal(1, "Invalid master port number");
+ end
+
+ m_data[port].put_chdr(packet);
+ endtask : put_chdr
+
+
+ // Receive a raw CHDR packet.
+ //
+ // port: Port number on which to receive the packet
+ // packet: Data structure to store received packet
+ //
+ task get_chdr(
+ input int port,
+ output ChdrPacket #(CHDR_W) packet
+ );
+ assert (running) else begin
+ $fatal(1, "Cannot call get_chdr until RfnocBlockCtrlBfm is running");
+ end
+ assert (port >= 0 && port < s_data.size()) else begin
+ $fatal(1, "Invalid slave port number");
+ end
+
+ s_data[port].get_chdr(packet);
+ endtask : get_chdr
+
+
+ // Receive a raw CHDR packet, but don't remove it from the receive queue.
+ //
+ // port: Port number on which to peek
+ // packet: Data structure to store received packet
+ //
+ task peek_chdr(
+ input int port,
+ output ChdrPacket #(CHDR_W) packet
+ );
+ assert (running) else begin
+ $fatal(1, "Cannot call peek_chdr until RfnocBlockCtrlBfm is running");
+ end
+ assert (port >= 0 && port < s_data.size()) else begin
+ $fatal(1, "Invalid slave port number");
+ end
+
+ s_data[port].peek_chdr(packet);
+ endtask : peek_chdr
+
+
+ // Return the number of packets available in the receive queue for the
+ // given port.
+ //
+ // port: Port for which to get the number of received packets
+ //
+ function int num_received(int port);
+ assert (port >= 0 && port < s_data.size()) else begin
+ $fatal(1, "Invalid slave port number");
+ end
+
+ return s_data[port].num_received();
+ endfunction
+
+
+ // Wait until packets have completed transmission.
+ //
+ // port: Port for which to wait
+ // num: Number of packets to wait for. Set to -1 or omit the argument
+ // to wait for all currently queued packets to complete
+ // transmission.
+ //
+ task wait_complete(int port, int num = -1);
+ assert (running) else begin
+ $fatal(1, "Cannot call wait_complete until RfnocBlockCtrlBfm is running");
+ end
+ assert (port >= 0 && port < m_data.size()) else begin
+ $fatal(1, "Invalid master port number");
+ end
+
+ m_data[port].wait_complete(num);
+ endtask
+
+
+ // Set the stall probability for the indicated slave port.
+ //
+ // port: Port for which to set the probability
+ // stall_prob: Probability as a percentage (0-100)
+ //
+ function void set_slave_stall_prob(int port, int stall_prob);
+ assert (port >= 0 && port < s_data.size()) else begin
+ $fatal(1, "Invalid slave port number");
+ end
+
+ s_data[port].set_slave_stall_prob(stall_prob);
+ endfunction
+
+
+ // Set the stall probability for the indicated master port.
+ //
+ // port: Port for which to set the probability
+ // stall_prob: Probability as a percentage (0-100)
+ //
+ function void set_master_stall_prob(int port, int stall_prob);
+ assert (port >= 0 && port < m_data.size()) else begin
+ $fatal(1, "Invalid master port number");
+ end
+
+ m_data[port].set_master_stall_prob(stall_prob);
+ endfunction
+
+
+ // Send a read request packet on the AXIS-Ctrl interface and get the
+ // response.
+ //
+ // addr: Address for the read request
+ // word: Data word that was returned in response to the read
+ //
+ task reg_read(
+ input ctrl_address_t addr,
+ output ctrl_word_t word
+ );
+ assert (running) else begin
+ $fatal(1, "Cannot call reg_read until RfnocBlockCtrlBfm is running");
+ end
+
+ ctrl.reg_read(addr, word);
+ endtask : reg_read
+
+
+ // Send a a write request packet on the AXIS-Ctrl interface and get the
+ // response.
+ //
+ // addr: Address for the write request
+ // word: Data word to write
+ //
+ task reg_write(
+ ctrl_address_t addr,
+ ctrl_word_t word
+ );
+ assert (running) else begin
+ $fatal(1, "Cannot call reg_write until RfnocBlockCtrlBfm is running");
+ end
+
+ ctrl.reg_write(addr, word);
+ endtask : reg_write
+
+ // Compare data vectors
+ static function bit compare_data(
+ input chdr_word_t lhs[$],
+ input chdr_word_t rhs[$],
+ input int bytes = -1
+ );
+ int bytes_left;
+ if (lhs.size() != rhs.size()) return 0;
+ bytes_left = (bytes > 0) ? bytes : ((lhs.size()*$size(chdr_word_t))/8);
+ for (int i = 0; i < lhs.size(); i++) begin
+ chdr_word_t mask = {$size(chdr_word_t){1'b1}};
+ if (bytes_left < $size(chdr_word_t)/8) begin
+ mask = (1 << (bytes_left * 8)) - 1;
+ end else if (bytes_left < 0) begin
+ return 1;
+ end
+ if ((lhs[i] & mask) != (rhs[i] & mask)) return 0;
+ end
+ return 1;
+ endfunction : compare_data
+
+ endclass : RfnocBlockCtrlBfm
+
+
+endpackage : PkgRfnocBlockCtrlBfm