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