sim: Split PkgRfnocBlockCtrlBfm into separate packages
Original-commit: 369594ef16d7b2d519940269d2af035cfe648f50
This commit is contained in:
@@ -16,6 +16,8 @@ PkgChdrUtils.sv \
|
|||||||
PkgChdrBfm.sv \
|
PkgChdrBfm.sv \
|
||||||
PkgAxisCtrlBfm.sv \
|
PkgAxisCtrlBfm.sv \
|
||||||
PkgRfnocItemUtils.sv \
|
PkgRfnocItemUtils.sv \
|
||||||
|
PkgCtrlIfaceBfm.sv \
|
||||||
|
PkgChdrIfaceBfm.sv \
|
||||||
PkgRfnocBlockCtrlBfm.sv \
|
PkgRfnocBlockCtrlBfm.sv \
|
||||||
))
|
))
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,272 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2020 Ettus Research, A National Instruments Company
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
//
|
||||||
|
// Module: PkgChdrIfaceBfm
|
||||||
|
//
|
||||||
|
// Description: This package includes a high-level bus functional model (BFM)
|
||||||
|
// for the AXIS-CHDR interface of a Transport Adapter or Stream Endpoint.
|
||||||
|
//
|
||||||
|
|
||||||
|
package PkgChdrIfaceBfm;
|
||||||
|
|
||||||
|
import PkgChdrUtils::*;
|
||||||
|
import PkgChdrBfm::*;
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct packed {
|
||||||
|
chdr_vc_t vc;
|
||||||
|
logic eob;
|
||||||
|
logic eov;
|
||||||
|
logic has_time;
|
||||||
|
chdr_word_t timestamp;
|
||||||
|
} packet_info_t;
|
||||||
|
|
||||||
|
|
||||||
|
class ChdrIfaceBfm #(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, "ChdrIfaceBfm::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, "ChdrIfaceBfm::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 : ChdrIfaceBfm
|
||||||
|
|
||||||
|
|
||||||
|
endpackage : PkgChdrIfaceBfm
|
||||||
@@ -150,7 +150,6 @@ package PkgChdrUtils;
|
|||||||
chdr_epid_t dst_epid;
|
chdr_epid_t dst_epid;
|
||||||
} chdr_header_t;
|
} chdr_header_t;
|
||||||
|
|
||||||
|
|
||||||
// AXIS-Ctrl packet header
|
// AXIS-Ctrl packet header
|
||||||
typedef struct packed {
|
typedef struct packed {
|
||||||
// Word 1
|
// Word 1
|
||||||
|
|||||||
@@ -0,0 +1,125 @@
|
|||||||
|
//
|
||||||
|
// Copyright 2020 Ettus Research, A National Instruments Company
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||||
|
//
|
||||||
|
// Module: PkgCtrlIfaceBfm
|
||||||
|
//
|
||||||
|
// Description: This package includes high-level bus functional models (BFM)
|
||||||
|
// for the AXIS-Ctrl interface of a Stream Endpoint.
|
||||||
|
//
|
||||||
|
|
||||||
|
|
||||||
|
package PkgCtrlIfaceBfm;
|
||||||
|
|
||||||
|
import PkgChdrUtils::*;
|
||||||
|
import PkgAxisCtrlBfm::*;
|
||||||
|
|
||||||
|
|
||||||
|
class CtrlIfaceBfm 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, "CtrlIfaceBfm::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, "CtrlIfaceBfm::reg_write: Did not receive CTRL_STS_OKAY status");
|
||||||
|
end
|
||||||
|
endtask : reg_write
|
||||||
|
|
||||||
|
endclass : CtrlIfaceBfm
|
||||||
|
|
||||||
|
|
||||||
|
endpackage : PkgCtrlIfaceBfm
|
||||||
@@ -5,16 +5,13 @@
|
|||||||
//
|
//
|
||||||
// Module: PkgRfnocBlockCtrlBfm
|
// Module: PkgRfnocBlockCtrlBfm
|
||||||
//
|
//
|
||||||
// Description: This package includes high-level bus functional models (BFMs)
|
// Description: This package includes a high-level bus functional model (BFM)
|
||||||
// for communicating with RFNoC. This includes the following:
|
// for a block controller. This models a software block controller and allows
|
||||||
|
// communication with a single RFNoC block. It includes the following:
|
||||||
//
|
//
|
||||||
// - ChdrDataStreamBfm: Model for the AXIS CHDR interface of a Transport
|
// - A CtrlIfaceBfm for an AXIS-Ctrl port connection of a block
|
||||||
// Adapter or Stream Endpoint.
|
// - One or more ChdrIfaceBfm for the AXIS-CHDR port connections
|
||||||
//
|
// - A connection for the backend interface of a block
|
||||||
// - 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.
|
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
@@ -82,387 +79,10 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
|
|
||||||
import PkgChdrUtils::*;
|
import PkgChdrUtils::*;
|
||||||
import PkgChdrBfm::*;
|
import PkgChdrBfm::*;
|
||||||
import PkgAxisCtrlBfm::*;
|
import PkgCtrlIfaceBfm::*;
|
||||||
import PkgRfnocItemUtils::*;
|
import PkgChdrIfaceBfm::*;
|
||||||
|
|
||||||
|
export PkgChdrIfaceBfm::packet_info_t;
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
//---------------------------------------------------------------------------
|
//---------------------------------------------------------------------------
|
||||||
@@ -476,9 +96,9 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
class RfnocBlockCtrlBfm #(CHDR_W = 64);
|
class RfnocBlockCtrlBfm #(CHDR_W = 64);
|
||||||
|
|
||||||
local virtual RfnocBackendIf.master backend;
|
local virtual RfnocBackendIf.master backend;
|
||||||
local RegisterIfaceBfm ctrl;
|
local CtrlIfaceBfm ctrl;
|
||||||
local ChdrDataStreamBfm #(CHDR_W) m_data[$];
|
local ChdrIfaceBfm #(CHDR_W) m_data[$];
|
||||||
local ChdrDataStreamBfm #(CHDR_W) s_data[$];
|
local ChdrIfaceBfm #(CHDR_W) s_data[$];
|
||||||
local bit running;
|
local bit running;
|
||||||
|
|
||||||
localparam CMD_PROP_CYC = 5;
|
localparam CMD_PROP_CYC = 5;
|
||||||
@@ -516,7 +136,7 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
int max_payload_length = 2**$bits(chdr_length_t),
|
int max_payload_length = 2**$bits(chdr_length_t),
|
||||||
int ticks_per_word = CHDR_W/32
|
int ticks_per_word = CHDR_W/32
|
||||||
);
|
);
|
||||||
ChdrDataStreamBfm #(CHDR_W) bfm = new(m_chdr, null, max_payload_length, ticks_per_word);
|
ChdrIfaceBfm #(CHDR_W) bfm = new(m_chdr, null, max_payload_length, ticks_per_word);
|
||||||
m_data.push_back(bfm);
|
m_data.push_back(bfm);
|
||||||
return m_data.size() - 1;
|
return m_data.size() - 1;
|
||||||
endfunction : add_master_data_port
|
endfunction : add_master_data_port
|
||||||
@@ -528,7 +148,7 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
function int add_slave_data_port(
|
function int add_slave_data_port(
|
||||||
virtual AxiStreamIf #(CHDR_W).slave s_chdr
|
virtual AxiStreamIf #(CHDR_W).slave s_chdr
|
||||||
);
|
);
|
||||||
ChdrDataStreamBfm #(CHDR_W) bfm = new(null, s_chdr);
|
ChdrIfaceBfm #(CHDR_W) bfm = new(null, s_chdr);
|
||||||
s_data.push_back(bfm);
|
s_data.push_back(bfm);
|
||||||
return s_data.size() - 1;
|
return s_data.size() - 1;
|
||||||
endfunction : add_slave_data_port
|
endfunction : add_slave_data_port
|
||||||
@@ -550,7 +170,7 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
int max_payload_length = 2**$bits(chdr_length_t),
|
int max_payload_length = 2**$bits(chdr_length_t),
|
||||||
int ticks_per_word = CHDR_W/32
|
int ticks_per_word = CHDR_W/32
|
||||||
);
|
);
|
||||||
ChdrDataStreamBfm #(CHDR_W) bfm = new(m_chdr, null, max_payload_length, ticks_per_word);
|
ChdrIfaceBfm #(CHDR_W) bfm = new(m_chdr, null, max_payload_length, ticks_per_word);
|
||||||
wait (m_data.size() == port_num);
|
wait (m_data.size() == port_num);
|
||||||
m_data.push_back(bfm);
|
m_data.push_back(bfm);
|
||||||
endtask : connect_master_data_port
|
endtask : connect_master_data_port
|
||||||
@@ -566,7 +186,7 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
int port_num,
|
int port_num,
|
||||||
virtual AxiStreamIf #(CHDR_W).slave s_chdr
|
virtual AxiStreamIf #(CHDR_W).slave s_chdr
|
||||||
);
|
);
|
||||||
ChdrDataStreamBfm #(CHDR_W) bfm = new(null, s_chdr);
|
ChdrIfaceBfm #(CHDR_W) bfm = new(null, s_chdr);
|
||||||
wait (s_data.size() == port_num);
|
wait (s_data.size() == port_num);
|
||||||
s_data.push_back(bfm);
|
s_data.push_back(bfm);
|
||||||
endtask : connect_slave_data_port
|
endtask : connect_slave_data_port
|
||||||
@@ -587,12 +207,12 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
endtask : run
|
endtask : run
|
||||||
|
|
||||||
// Return a handle to the control BFM
|
// Return a handle to the control BFM
|
||||||
function RegisterIfaceBfm get_ctrl_bfm();
|
function CtrlIfaceBfm get_ctrl_bfm();
|
||||||
return ctrl;
|
return ctrl;
|
||||||
endfunction : get_ctrl_bfm
|
endfunction : get_ctrl_bfm
|
||||||
|
|
||||||
// Return a handle to the indicated master port BFM
|
// Return a handle to the indicated master port BFM
|
||||||
function ChdrDataStreamBfm #(CHDR_W) get_master_data_bfm(int port);
|
function ChdrIfaceBfm #(CHDR_W) get_master_data_bfm(int port);
|
||||||
assert (port >= 0 && port < m_data.size()) else begin
|
assert (port >= 0 && port < m_data.size()) else begin
|
||||||
$fatal(1, "Invalid master port number");
|
$fatal(1, "Invalid master port number");
|
||||||
end
|
end
|
||||||
@@ -600,7 +220,7 @@ package PkgRfnocBlockCtrlBfm;
|
|||||||
endfunction : get_master_data_bfm
|
endfunction : get_master_data_bfm
|
||||||
|
|
||||||
// Return a handle to the indicated slave port BFM
|
// Return a handle to the indicated slave port BFM
|
||||||
function ChdrDataStreamBfm #(CHDR_W) get_slave_data_bfm(int port);
|
function ChdrIfaceBfm #(CHDR_W) get_slave_data_bfm(int port);
|
||||||
assert (port >= 0 && port < m_data.size()) else begin
|
assert (port >= 0 && port < m_data.size()) else begin
|
||||||
$fatal(1, "Invalid slave port number");
|
$fatal(1, "Invalid slave port number");
|
||||||
end
|
end
|
||||||
|
|||||||
Reference in New Issue
Block a user