aboutsummaryrefslogtreecommitdiffstats
path: root/fpga/usrp3/sim/rfnoc
diff options
context:
space:
mode:
Diffstat (limited to 'fpga/usrp3/sim/rfnoc')
-rw-r--r--fpga/usrp3/sim/rfnoc/Makefile.srcs2
-rw-r--r--fpga/usrp3/sim/rfnoc/PkgAxiStreamBfm.sv239
-rw-r--r--fpga/usrp3/sim/rfnoc/PkgAxisCtrlBfm.sv18
-rw-r--r--fpga/usrp3/sim/rfnoc/PkgEthernet.sv809
4 files changed, 990 insertions, 78 deletions
diff --git a/fpga/usrp3/sim/rfnoc/Makefile.srcs b/fpga/usrp3/sim/rfnoc/Makefile.srcs
index d1b59c8e2..31196678f 100644
--- a/fpga/usrp3/sim/rfnoc/Makefile.srcs
+++ b/fpga/usrp3/sim/rfnoc/Makefile.srcs
@@ -11,6 +11,7 @@
SIM_RFNOC_SRCS = $(abspath $(addprefix $(BASE_DIR)/../lib/, \
axi4lite_sv/PkgAxiLite.sv \
axi4lite_sv/AxiLiteIf.sv \
+axi4s_sv/AxiStreamIf.sv \
))
##################################################
@@ -23,6 +24,7 @@ test_exec.svh \
sim_clock_gen.sv \
PkgAxiStreamBfm.sv \
PkgAxiLiteBfm.sv \
+PkgEthernet.sv \
PkgChdrData.sv \
PkgChdrUtils.sv \
PkgChdrBfm.sv \
diff --git a/fpga/usrp3/sim/rfnoc/PkgAxiStreamBfm.sv b/fpga/usrp3/sim/rfnoc/PkgAxiStreamBfm.sv
index 9e2530350..fd23e501c 100644
--- a/fpga/usrp3/sim/rfnoc/PkgAxiStreamBfm.sv
+++ b/fpga/usrp3/sim/rfnoc/PkgAxiStreamBfm.sv
@@ -1,61 +1,79 @@
//
-// Copyright 2019 Ettus Research, A National Instruments Company
+// Copyright 2020 Ettus Research, A National Instruments Brand
//
// SPDX-License-Identifier: LGPL-3.0-or-later
//
-// Module: PkgAxiStream
+// Module: PkgAxiStreamBfm
//
-// Description: Package for a bi-directional AXI Stream bus functional model
-// (BFM). This consists of the AxiStreamIf interface, and the
-// AxiStreamPacket and AxiStreamBfm classes.
+// Description: Package for a bi-directional AXI Stream bus functional model
+// (BFM). This consists of the AxiStreamPacket and AxiStreamBfm classes.
+// It's based on the AxiStreamIf in lib/axi4s_svPkgAXI4S.sv
//
-
//-----------------------------------------------------------------------------
-// Unidirectional AXI4-Stream interface
+// AXI-Stream BFM Package
//-----------------------------------------------------------------------------
-interface AxiStreamIf #(
- parameter int DATA_WIDTH = 64,
- parameter int USER_WIDTH = 1
-) (
- input logic clk,
- input logic rst = 1'b0
-);
-
- // Signals that make up a unidirectional AXI-Stream interface
- logic tready;
- logic tvalid;
- logic [DATA_WIDTH-1:0] tdata;
- logic [USER_WIDTH-1:0] tuser;
- logic [DATA_WIDTH/8-1:0] tkeep;
- logic tlast;
-
- // View from the master side
- modport master (
- input clk, rst,
- output tvalid, tdata, tuser, tkeep, tlast,
- input tready
- );
-
- // View from the slave side
- modport slave (
- input clk, rst,
- input tvalid, tdata, tuser, tkeep, tlast,
- output tready
- );
-
-endinterface : AxiStreamIf
+package PkgAxiStreamBfm;
+ //---------------------------------------------------------------------------
+ // Raw packets - packet of just bytes
+ //---------------------------------------------------------------------------
+ // Ethernet in particular defies normal word boundaries, so underneath treating
+ // it as the most fundamental quantity - Bytes.
+ typedef byte raw_pkt_t[$]; // Byte Queue
+
+ // Push a packet with random data onto to the AXI Stream bus
+ // Args:
+ // - num_bytes: number of random bytes to add to the raw packet.
+ // - pkt: packet with rand data
+ task automatic get_rand_raw_pkt (
+ input int num_bytes,
+ output raw_pkt_t pkt);
+ begin
+ repeat(num_bytes) begin
+ pkt.push_back($urandom);
+ end
+ end
+ endtask
+
+ // Push a packet with a ramp on to the AXI Stream bus
+ // Args:
+ // - num_samps: Packet size in bytes *8
+ // - ramp_start: Start value for the ramp
+ // - ramp_inc: Increment per clock cycle
+ task automatic get_ramp_raw_pkt (
+ input int num_samps,
+ input logic [63:0] ramp_start,
+ input logic [63:0] ramp_inc,
+ input int SWIDTH=64,
+ output raw_pkt_t pkt);
+ begin
+ logic[63:0] word;
+ automatic integer counter = 0;
+ repeat(num_samps) begin
+ word = ramp_start+(counter*ramp_inc);
+ for (int i=0; i < SWIDTH ; i+=8) begin
+ pkt.push_back(word[i +: 8]);
+ end
+ counter = counter + 1;
+ end
+ end
+ endtask
-//-----------------------------------------------------------------------------
-// AXI-Stream BFM Package
-//-----------------------------------------------------------------------------
+ // Comparison Functions
+ function automatic bit raw_pkt_compare(input raw_pkt_t a, input raw_pkt_t b);
-package PkgAxiStreamBfm;
+ bit queue_match;
+ queue_match = 1;
+ // check each element of the queue and clear queue_match if they don't match.
+ // workaround for vivado bug - could be a==b
+ foreach(a[i]) queue_match = queue_match && a[i] == b[i];
+ return ((a.size() == b.size()) && queue_match);
+ endfunction
//---------------------------------------------------------------------------
// AXI Stream Packet Class
@@ -73,7 +91,7 @@ package PkgAxiStreamBfm;
typedef AxiStreamPacket #(DATA_WIDTH, USER_WIDTH) AxisPacket_t;
-
+ bit verbose=0;
//------------
// Properties
//------------
@@ -105,6 +123,12 @@ package PkgAxiStreamBfm;
keep = {};
endfunction;
+ // Delete a word from the current packet
+ function void delete(int i);
+ data.delete(i);
+ user.delete(i);
+ keep.delete(i);
+ endfunction;
// Return true if this packet equals that of the argument
virtual function bit equal(AxisPacket_t packet);
@@ -117,21 +141,30 @@ package PkgAxiStreamBfm;
foreach (data[i]) begin
data_a = data[i];
data_b = packet.data[i];
- if (data_a !== data_b) return 0;
+ if (data_a !== data_b) begin
+ if (verbose) $display("AxisPacket data mismatch a[%2d]=%X b[%2d]=%X",i,data_a,i,data_b);
+ return 0;
+ end
end
if (user.size() != packet.user.size()) return 0;
foreach (data[i]) begin
user_a = user[i];
user_b = packet.user[i];
- if (user_a !== user_b) return 0;
+ if (user_a !== user_b) begin
+ if (verbose) $display("AxisPacket user mismatch a[%2d]=%X b[%2d]=%X",i,user_a,i,user_b);
+ return 0;
+ end
end
if (keep.size() != packet.keep.size()) return 0;
foreach (keep[i]) begin
keep_a = keep[i];
keep_b = packet.keep[i];
- if (keep_a !== keep_b) return 0;
+ if (keep_a !== keep_b) begin
+ if (verbose) $display("AxisPacket keep mismatch a[%2d]=%X b[%2d]=%X",i,user_a,i,user_b);
+ return 0;
+ end
end
return 1;
@@ -141,10 +174,22 @@ package PkgAxiStreamBfm;
// Format the contents of the packet into a string
function string sprint();
string str = "";
+ string data_str = "";
if (data.size() == user.size() && data.size() == keep.size()) begin
str = { str, "data, user, keep:\n" };
foreach (data[i]) begin
- str = { str, $sformatf("%5d> %X %X %b\n", i, data[i], user[i], keep[i]) };
+ data_str = "";
+ if (DATA_WIDTH > 64) begin
+ for (int b=0; b < DATA_WIDTH; b +=64) begin
+ data_str = { data_str, $sformatf("%3d: %X",b,data[i][b+:64])};
+ if (b+64 < DATA_WIDTH) begin
+ data_str = { data_str, $sformatf("\n ")};
+ end
+ end
+ end else begin
+ data_str = { data_str, $sformatf("%X",data[i]) };
+ end
+ str = { str, $sformatf("%5d> %s %X %X \n", i, data_str, user[i], keep[i]) };
end
end else begin
str = { str, "data:\n" };
@@ -169,6 +214,45 @@ package PkgAxiStreamBfm;
$display(sprint());
endfunction : print
+ // Add an array of bytes (little endian)
+ function void push_bytes(raw_pkt_t raw, input user_t user = '0);
+ data_t word;
+ keep_t my_keep;
+ while (raw.size() > 0) begin
+ // fill tkeep
+ // SIZE = TKEEP / 0 = 0000, 1 = 0001, 2 = 0011, etc
+ my_keep = '1;
+ if (raw.size <= DATA_WIDTH/8) begin
+ foreach (my_keep[i]) my_keep[i] = i < (raw.size);
+ end
+ // fill the word with raw data from bottom up
+ word = '0;
+ for (int i = 0; i < DATA_WIDTH/8 ; i++) begin
+ if (my_keep[i]) word[i*8 +: 8] = raw.pop_front();
+ end
+ this.data.push_back(word);
+ this.keep.push_back(my_keep);
+ this.user.push_back(user);
+ end
+ endfunction
+
+ // Dump data contents as an array of bytes. (little endian)
+ function raw_pkt_t dump_bytes();
+ data_t word;
+ keep_t my_keep;
+ raw_pkt_t raw;
+ assert (data.size == keep.size) else
+ $fatal("data and keep have different sizes!");
+ foreach (data[i]) begin
+ my_keep = this.keep[i];
+ word = this.data[i];
+ for (int j = 0; j < DATA_WIDTH/8 ; j++) begin
+ if (my_keep[j]) raw.push_back(word[j*8 +: 8]);
+ end;
+ end
+ return raw;
+ endfunction
+
endclass : AxiStreamPacket;
@@ -178,8 +262,13 @@ package PkgAxiStreamBfm;
//---------------------------------------------------------------------------
class AxiStreamBfm #(
- parameter int DATA_WIDTH = 64,
- parameter int USER_WIDTH = 1
+ int DATA_WIDTH = 64,
+ int USER_WIDTH = 1,
+ int MAX_PACKET_BYTES = 0,
+ bit TDATA = 1,
+ bit TUSER = 1,
+ bit TKEEP = 1,
+ bit TLAST = 1
);
//------------------
@@ -205,13 +294,16 @@ package PkgAxiStreamBfm;
local const AxisPacket_t::keep_t IDLE_KEEP = {(DATA_WIDTH/8){1'bX}};
// Virtual interfaces for master and slave connections to DUT
- local virtual AxiStreamIf #(DATA_WIDTH, USER_WIDTH).master master;
- local virtual AxiStreamIf #(DATA_WIDTH, USER_WIDTH).slave slave;
+ local virtual AxiStreamIf #(DATA_WIDTH,USER_WIDTH,MAX_PACKET_BYTES,
+ TDATA,TUSER,TKEEP,TLAST).master master;
+ local virtual AxiStreamIf #(DATA_WIDTH,USER_WIDTH,MAX_PACKET_BYTES,
+ TDATA,TUSER,TKEEP,TLAST).slave slave;
// NOTE: We should not need these flags if Vivado would be OK with null check
// without throwing unnecessary null-ptr deref exceptions.
local bit master_en;
local bit slave_en;
+ bit slave_tready_init = 0;
// Queues to store the bus transactions
mailbox #(AxisPacket_t) tx_packets;
mailbox #(AxisPacket_t) rx_packets;
@@ -220,6 +312,8 @@ package PkgAxiStreamBfm;
protected int master_stall_prob = DEF_STALL_PROB;
protected int slave_stall_prob = DEF_STALL_PROB;
+ // Number of clocks betwen packets
+ int inter_packet_gap = 0;
//---------
// Methods
@@ -231,11 +325,13 @@ package PkgAxiStreamBfm;
endfunction : packets_equal
- // Class constructor. This must be given an interface for the master
+ // Class constructor. This must be given an interface for the master
// connection and an interface for the slave connection.
function new(
- virtual AxiStreamIf #(DATA_WIDTH, USER_WIDTH).master master,
- virtual AxiStreamIf #(DATA_WIDTH, USER_WIDTH).slave slave
+ virtual AxiStreamIf #(DATA_WIDTH,USER_WIDTH,MAX_PACKET_BYTES,
+ TDATA,TUSER,TKEEP,TLAST).master master,
+ virtual AxiStreamIf #(DATA_WIDTH,USER_WIDTH,MAX_PACKET_BYTES,
+ TDATA,TUSER,TKEEP,TLAST).slave slave
);
this.master_en = (master != null);
this.slave_en = (slave != null);
@@ -253,7 +349,7 @@ package PkgAxiStreamBfm;
endtask : put
- // Attempt to queue the provided packet for transmission. Return 1 if
+ // Attempt to queue the provided packet for transmission. Return 1 if
// successful, return 0 if the queue is full.
function bit try_put(AxisPacket_t packet);
assert (master_en) else $fatal(1, "Cannot use TX operations for a null master");
@@ -268,7 +364,7 @@ package PkgAxiStreamBfm;
endtask : get
- // Get the next packet if there's one available and return 1. Return 0 if
+ // Get the next packet if there's one available and return 1. Return 0 if
// there's no packet available.
function bit try_get(output AxisPacket_t packet);
assert (slave_en) else $fatal(1, "Cannot use RX operations for a null slave");
@@ -276,7 +372,7 @@ package PkgAxiStreamBfm;
endfunction : try_get
- // Get the next packet when it becomes available (wait if necessary), but
+ // Get the next packet when it becomes available (wait if necessary), but
// don't remove it from the receive queue.
task peek(output AxisPacket_t packet);
assert (slave_en) else $fatal(1, "Cannot use RX operations for a null slave");
@@ -284,8 +380,8 @@ package PkgAxiStreamBfm;
endtask : peek
- // Get the next packet if there's one available and return 1, but don't
- // remove it from the receive queue. Return 0 if there's no packet
+ // Get the next packet if there's one available and return 1, but don't
+ // remove it from the receive queue. Return 0 if there's no packet
// available.
function bit try_peek(output AxisPacket_t packet);
assert (slave_en) else $fatal(1, "Cannot use RX operations for a null slave");
@@ -300,8 +396,8 @@ package PkgAxiStreamBfm;
endfunction
- // Wait until num packets have started transmission (i.e., until num
- // packets have been dequeued). Set num = -1 to wait until all currently
+ // Wait until num packets have started transmission (i.e., until num
+ // packets have been dequeued). Set num = -1 to wait until all currently
// queued packets have started transmission.
task wait_send(int num = -1);
int end_num;
@@ -318,7 +414,7 @@ package PkgAxiStreamBfm;
endtask : wait_send
- // Wait until num packets have completed transmission. Set num = -1 to wait
+ // Wait until num packets have completed transmission. Set num = -1 to wait
// for all currently queued packets to complete transmission.
task wait_complete(int num = -1);
int end_num;
@@ -340,7 +436,7 @@ package PkgAxiStreamBfm;
endtask : wait_complete
- // Set the probability (as a percentage, 0 to 100) of the master interface
+ // Set the probability (as a percentage, 0 to 100) of the master interface
// stalling due to lack of data to send.
function void set_master_stall_prob(int stall_probability = DEF_STALL_PROB);
assert(stall_probability >= 0 && stall_probability <= 100) else begin
@@ -350,7 +446,7 @@ package PkgAxiStreamBfm;
endfunction
- // Set the probability (as a percentage, 0 to 100) of the slave interface
+ // Set the probability (as a percentage, 0 to 100) of the slave interface
// stalling due to lack of buffer space.
function void set_slave_stall_prob(int stall_probability = DEF_STALL_PROB);
assert(stall_probability >= 0 && stall_probability <= 100) else begin
@@ -360,14 +456,14 @@ package PkgAxiStreamBfm;
endfunction
- // Get the probability (as a percentage, 0 to 100) of the master interface
+ // Get the probability (as a percentage, 0 to 100) of the master interface
// stalling due to lack of data to send.
function int get_master_stall_prob(int stall_probability = DEF_STALL_PROB);
return master_stall_prob;
endfunction
- // Get the probability (as a percentage, 0 to 100) of the slave interface
+ // Get the probability (as a percentage, 0 to 100) of the slave interface
// stalling due to lack of buffer space.
function int get_slave_stall_prob(int stall_probability = DEF_STALL_PROB);
return slave_stall_prob;
@@ -397,8 +493,10 @@ package PkgAxiStreamBfm;
master.tlast <= 0;
forever begin
- @(posedge master.clk);
- if (master.rst) continue;
+ repeat(inter_packet_gap) begin
+ @(posedge master.clk);
+ if (master.rst) continue;
+ end
if (tx_packets.try_get(packet)) begin
foreach (packet.data[i]) begin
@@ -433,6 +531,9 @@ package PkgAxiStreamBfm;
master.tuser <= IDLE_USER;
master.tkeep <= IDLE_KEEP;
master.tlast <= 0;
+ end else begin
+ @(posedge master.clk);
+ if (master.rst) continue;
end
end
endtask : master_body
@@ -445,7 +546,7 @@ package PkgAxiStreamBfm;
local task slave_body();
AxisPacket_t packet = new();
- slave.tready <= 0;
+ slave.tready <= slave_tready_init;
forever begin
@(posedge slave.clk);
diff --git a/fpga/usrp3/sim/rfnoc/PkgAxisCtrlBfm.sv b/fpga/usrp3/sim/rfnoc/PkgAxisCtrlBfm.sv
index 148ef5488..cc4c9ac18 100644
--- a/fpga/usrp3/sim/rfnoc/PkgAxisCtrlBfm.sv
+++ b/fpga/usrp3/sim/rfnoc/PkgAxisCtrlBfm.sv
@@ -5,7 +5,7 @@
//
// Module: PkgAxisCtrlBfm
//
-// Description: Package for an AXIS-Ctrl bus functional model (BFM), which
+// Description: Package for an AXIS-Ctrl bus functional model (BFM), which
// consists primarily of the AxisCtrlPacket and AxisCtrlBfm classes.
//
@@ -129,7 +129,7 @@ package PkgAxisCtrlBfm;
// AXIS-Ctrl BFM Methods
//---------------------------------------------------------------------------
- // Class constructor. This must be given an interface for the master
+ // Class constructor. This must be given an interface for the master
// connection and an interface for the slave connection.
function AxisCtrlBfm::new (
virtual AxiStreamIf #(32).master master,
@@ -147,7 +147,7 @@ package PkgAxisCtrlBfm;
endtask : put_ctrl
- // Attempt to queue the provided packet for transmission. Return 1 if
+ // Attempt to queue the provided packet for transmission. Return 1 if
// successful, return 0 if the queue is full.
task AxisCtrlBfm::try_put_ctrl(AxisCtrlPacket ctrl_packet);
AxisPacket_t axis_packet;
@@ -163,7 +163,7 @@ package PkgAxisCtrlBfm;
endtask : get_ctrl
- // Get the next packet if there's one available and return 1. Return 0 if
+ // Get the next packet if there's one available and return 1. Return 0 if
// there's no packet available.
function bit AxisCtrlBfm::try_get_ctrl(output AxisCtrlPacket ctrl_packet);
AxisPacket_t axis_packet;
@@ -173,7 +173,7 @@ package PkgAxisCtrlBfm;
endfunction : try_get_ctrl
- // Convert an AXIS-Ctrl packet data structure to an AXI-Stream packet data
+ // Convert an AXIS-Ctrl packet data structure to an AXI-Stream packet data
// structure.
function AxisCtrlBfm::AxisPacket_t AxisCtrlBfm::axis_ctrl_to_axis(AxisCtrlPacket ctrl_packet);
AxisPacket_t axis_packet = new();
@@ -184,7 +184,7 @@ package PkgAxisCtrlBfm;
axis_packet.data.push_back(ctrl_packet.header[63:32]);
// Insert timestamp if has_time is set (words 2 and 3)
- if (ctrl_packet.header.has_time) begin
+ if (ctrl_packet.header.has_time) begin
axis_packet.data.push_back(ctrl_packet.timestamp[31: 0]);
axis_packet.data.push_back(ctrl_packet.timestamp[63:32]);
end
@@ -201,12 +201,12 @@ package PkgAxisCtrlBfm;
endfunction : axis_ctrl_to_axis
- // Convert an AXI-Stream packet data structure to an AXIS-Ctrl packet data
+ // Convert an AXI-Stream packet data structure to an AXIS-Ctrl packet data
// structure.
function AxisCtrlPacket AxisCtrlBfm::axis_to_axis_ctrl(AxisPacket_t axis_packet);
AxisCtrlPacket ctrl_packet = new();
int i; // Use an index instead of pop_front() to workaround a ModelSim bug
-
+
// Grab words 0 and 1 (header)
ctrl_packet.header[31: 0] = axis_packet.data[0];
ctrl_packet.header[63:32] = axis_packet.data[1];
@@ -224,7 +224,7 @@ package PkgAxisCtrlBfm;
// Grab data
ctrl_packet.data = axis_packet.data[(i+1):$];
-
+
return ctrl_packet;
endfunction : axis_to_axis_ctrl
diff --git a/fpga/usrp3/sim/rfnoc/PkgEthernet.sv b/fpga/usrp3/sim/rfnoc/PkgEthernet.sv
new file mode 100644
index 000000000..a971b5c7e
--- /dev/null
+++ b/fpga/usrp3/sim/rfnoc/PkgEthernet.sv
@@ -0,0 +1,809 @@
+//
+// Copyright 2020 Ettus Research, A National Instruments Brand
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+//
+// Module: PkgEthernet
+//
+// Description: This package defines the data types used to represent ETHER,
+// IPV4, and UDP. It's based on a queue of bytes representation named
+// raw_pkt_t.
+//
+
+package PkgEthernet;
+
+ import PkgAxiStreamBfm::*;
+ export PkgAxiStreamBfm::*;
+
+ //************************************************************//
+ //////////////////// ETHER PACKETS /////////////////////////////
+ //************************************************************//
+
+ // Ether type - subset of possibilities. Add more as needed.
+ typedef enum logic [15:0] {
+ // Byte order 1 0
+ IPV4=16'h0800, IPV6=16'h86_DD, VLAN_TAGGED=16'h8100,
+ DOUBLE_VLAN_TAGGED=16'h9100, ROCE=16'h8915
+ } ether_type_t;
+
+ // Some MAC addresses
+ // Byte order 5 4 3 2 1 0
+ localparam logic [47:0] DEF_DEST_MAC_ADDR = 48'h5E_35_EB_71_46_F7;
+ localparam logic [47:0] DEF_SRC_MAC_ADDR = 48'h98_03_9B_8E_09_B9;
+ localparam logic [47:0] DEF_BRIDGE_MAC_ADDR = 48'hBB_BB_BB_BB_BB_BB;
+
+ // Ether Header
+ typedef struct {
+ logic [47:0] dest_mac = DEF_DEST_MAC_ADDR;
+ logic [47:0] src_mac = DEF_SRC_MAC_ADDR;
+ ether_type_t ether_type = IPV4;
+ } eth_hdr_t;
+
+ // Ethernet Packet, Header + Payload
+ typedef struct {
+ eth_hdr_t hdr;
+ raw_pkt_t payload;
+ logic [31:0] fcs;
+ int ipg = 12; // interpacket gap
+ } eth_pkt_t;
+
+ // Break an eth_pkt into a queue of bytes
+ function automatic raw_pkt_t flatten_eth_pkt(input eth_pkt_t pkt);
+ raw_pkt_t pay;
+
+ pay.push_back(pkt.hdr.dest_mac[47:40]);
+ pay.push_back(pkt.hdr.dest_mac[39:32]);
+ pay.push_back(pkt.hdr.dest_mac[31:24]);
+ pay.push_back(pkt.hdr.dest_mac[23:16]);
+ pay.push_back(pkt.hdr.dest_mac[15:8]);
+ pay.push_back(pkt.hdr.dest_mac[7:0]);
+ pay.push_back(pkt.hdr.src_mac[47:40]);
+ pay.push_back(pkt.hdr.src_mac[39:32]);
+ pay.push_back(pkt.hdr.src_mac[31:24]);
+ pay.push_back(pkt.hdr.src_mac[23:16]);
+ pay.push_back(pkt.hdr.src_mac[15:8]);
+ pay.push_back(pkt.hdr.src_mac[7:0]);
+ pay.push_back(pkt.hdr.ether_type[15:8]);
+ pay.push_back(pkt.hdr.ether_type[7:0]);
+ pay = {pay,pkt.payload};
+
+ return pay;
+
+ endfunction
+
+ // Break a queue of bytes into a eth_pkt
+ function automatic eth_pkt_t unflatten_eth_pkt(input raw_pkt_t pay);
+ eth_pkt_t pkt;
+
+ pkt.hdr.dest_mac[47:40] = pay.pop_front();
+ pkt.hdr.dest_mac[39:32] = pay.pop_front();
+ pkt.hdr.dest_mac[31:24] = pay.pop_front();
+ pkt.hdr.dest_mac[23:16] = pay.pop_front();
+ pkt.hdr.dest_mac[15:8] = pay.pop_front();
+ pkt.hdr.dest_mac[7:0] = pay.pop_front();
+ pkt.hdr.src_mac[47:40] = pay.pop_front();
+ pkt.hdr.src_mac[39:32] = pay.pop_front();
+ pkt.hdr.src_mac[31:24] = pay.pop_front();
+ pkt.hdr.src_mac[23:16] = pay.pop_front();
+ pkt.hdr.src_mac[15:8] = pay.pop_front();
+ pkt.hdr.src_mac[7:0] = pay.pop_front();
+ pkt.hdr.ether_type[15:8] = pay.pop_front();
+ pkt.hdr.ether_type[7:0] = pay.pop_front();
+ pkt.payload = pay;
+
+ return pkt;
+
+ endfunction
+
+ function automatic logic eth_pkt_compare(input eth_pkt_t a, input eth_pkt_t b);
+
+ return ((a.hdr.dest_mac == b.hdr.dest_mac) &&
+ (a.hdr.src_mac == b.hdr.src_mac) &&
+ (a.hdr.ether_type == b.hdr.ether_type) &&
+ raw_pkt_compare(a.payload,b.payload));
+
+ endfunction
+
+ //************************************************************//
+ //////////////////// IPV4 PACKETS //////////////////////////////
+ //************************************************************//
+
+ // IP Protocol - subset of possibilities. add more as needed
+ typedef enum logic [7:0] {
+ UDP=8'd17, TCP=8'd6, ICMP=8'd1, IGMP=8'd2, ENCAP=8'd41
+ } ip_protocol_t;
+
+ // follow normal convention of an IP address
+ function automatic logic [31:0] ip(logic [7:0] a,b,c,d);
+ return {a,b,c,d};
+ endfunction
+
+
+ localparam logic [31:0] DEF_DEST_IP_ADDR = ip(192,168,10,2);
+ localparam logic [31:0] DEF_SRC_IP_ADDR = ip(192,168,10,1);
+ localparam logic [31:0] DEF_BRIDGE_IP_ADDR = 32'h33_33_33_33;
+
+ // IPv4 Header
+ typedef struct {
+ logic [3:0] header_length = 4'd5;
+ logic [3:0] version = 4'd4;
+ logic [5:0] dscp = 6'b0000_00;
+ logic [1:0] ecn = 2'b00;
+ logic [15:0] length = 16'hXXXX; //flag for (fill it in please)
+ logic [15:0] identification = 16'h462E;
+ logic rsv_zero = 1'b0;
+ logic dont_frag = 1'b1;
+ logic more_frag = 1'b0;
+ logic [12:0] frag_offset = 16'd0;
+ logic [7:0] time_to_live = 16'd64;
+ ip_protocol_t protocol = UDP;
+ logic [15:0] checksum = 16'hXXXX; //flag for (fill it in please)
+ logic [31:0] src_ip = DEF_SRC_IP_ADDR;
+ logic [31:0] dest_ip = DEF_DEST_IP_ADDR;
+ } ipv4_hdr_t;
+
+ // IP Packet, Header + Payload
+ typedef struct {
+ ipv4_hdr_t hdr;
+ raw_pkt_t payload;
+ } ipv4_pkt_t;
+
+ // The checksum for an IP header is the sum of all the 16 bit words that
+ // make up the header with the checksum set to zero. Add back the carry over
+ // from bits [31:16] then invert.
+ // See https://en.wikipedia.org/wiki/IPv4_header_checksum
+ function automatic logic [15:0] calc_ipv4_checksum(input raw_pkt_t pkt);
+
+ // This is a bit oversized, but it's not costing anything.
+ // 10 max sized words can at most add logbase2 of 10 bits.
+ logic [31:0] checksum;
+
+ checksum = 0;
+ // Iterate over 16 bit chunks reading from a byte addressed memory.
+ // There are 20 bytes in an ipv4 header.
+ for (int i = 0 ; i < 20 ; i+=2 ) begin
+ // BIG endian network ordering... so weird
+ checksum += {pkt[i],pkt[i+1]};
+ end
+ checksum += checksum[31:16];
+ checksum = ~checksum;
+
+ return checksum[15:0];
+
+ endfunction
+
+ // Break an eth_pkt into a queue of bytes
+ function automatic raw_pkt_t flatten_ipv4_pkt(input ipv4_pkt_t pkt);
+ raw_pkt_t pay;
+
+ logic [15:0] length;
+ logic [15:0] checksum;
+ logic [2:0] flags;
+
+ // If header or length is not set to default value then use the value in
+ // the packet.
+ if ($isunknown(pkt.hdr.length))
+ length = pkt.payload.size()+20; // 20 because length includes IP header length.
+ else
+ length = pkt.hdr.length;
+
+ flags = {pkt.hdr.more_frag,pkt.hdr.dont_frag,pkt.hdr.rsv_zero};
+ // Start off with checksum as 0
+ checksum = 0;
+
+ // 20 byte IP header
+ pay.push_back({pkt.hdr.version,pkt.hdr.header_length}); // byte 0
+ pay.push_back({pkt.hdr.dscp,pkt.hdr.ecn}); // byte 1
+ pay.push_back(length[15:8]); // byte 2
+ pay.push_back(length[7:0]); // byte 3
+ pay.push_back(pkt.hdr.identification[15:8]); // byte 4
+ pay.push_back(pkt.hdr.identification[7:0]); // byte 5
+ pay.push_back({flags,pkt.hdr.frag_offset[12:8]}); // byte 6
+ pay.push_back(pkt.hdr.frag_offset[7:0]); // byte 7
+ pay.push_back(pkt.hdr.time_to_live); // byte 8
+ pay.push_back(pkt.hdr.protocol); // byte 9
+ pay.push_back(checksum[15:8]); // byte 10
+ pay.push_back(checksum[7:0]); // byte 11
+ pay.push_back(pkt.hdr.src_ip[31:24]); // byte 12
+ pay.push_back(pkt.hdr.src_ip[23:16]); // byte 13
+ pay.push_back(pkt.hdr.src_ip[15:8]); // byte 14
+ pay.push_back(pkt.hdr.src_ip[7:0]); // byte 15
+ pay.push_back(pkt.hdr.dest_ip[31:24]); // byte 16
+ pay.push_back(pkt.hdr.dest_ip[23:16]); // byte 17
+ pay.push_back(pkt.hdr.dest_ip[15:8]); // byte 18
+ pay.push_back(pkt.hdr.dest_ip[7:0]); // byte 19
+ pay = {pay,pkt.payload};
+
+ if ($isunknown(pkt.hdr.checksum))
+ checksum = calc_ipv4_checksum(pay);
+ else
+ checksum = pkt.hdr.checksum;
+ // replace the checksum (bytes 11:10
+ pay[10] = checksum[15:8];
+ pay[11] = checksum[7:0];
+
+ return pay;
+ endfunction
+
+ // Break a queue of bytes into a ip_pkt
+ function automatic ipv4_pkt_t unflatten_ipv4_pkt(input raw_pkt_t pay);
+ ipv4_pkt_t pkt;
+
+ // 20 byte IP header
+ {pkt.hdr.version,
+ pkt.hdr.header_length} = pay.pop_front(); // byte 0
+ {pkt.hdr.dscp,pkt.hdr.ecn} = pay.pop_front(); // byte 1
+ pkt.hdr.length[15:8] = pay.pop_front(); // byte 2
+ pkt.hdr.length[7:0] = pay.pop_front(); // byte 3
+ pkt.hdr.identification[15:8] = pay.pop_front(); // byte 4
+ pkt.hdr.identification[7:0] = pay.pop_front(); // byte 5
+ {pkt.hdr.more_frag,
+ pkt.hdr.dont_frag,
+ pkt.hdr.rsv_zero,
+ pkt.hdr.frag_offset[12:8]} = pay.pop_front(); // byte 6
+ pkt.hdr.frag_offset[7:0] = pay.pop_front(); // byte 7
+ pkt.hdr.time_to_live = pay.pop_front(); // byte 8
+ pkt.hdr.protocol = ip_protocol_t'(pay.pop_front()); // byte 9
+ pkt.hdr.checksum[15:8] = pay.pop_front(); // byte 10
+ pkt.hdr.checksum[7:0] = pay.pop_front(); // byte 11
+ pkt.hdr.src_ip[31:24] = pay.pop_front(); // byte 12
+ pkt.hdr.src_ip[23:16] = pay.pop_front(); // byte 13
+ pkt.hdr.src_ip[15:8] = pay.pop_front(); // byte 14
+ pkt.hdr.src_ip[7:0] = pay.pop_front(); // byte 15
+ pkt.hdr.dest_ip[31:24] = pay.pop_front(); // byte 16
+ pkt.hdr.dest_ip[23:16] = pay.pop_front(); // byte 17
+ pkt.hdr.dest_ip[15:8] = pay.pop_front(); // byte 18
+ pkt.hdr.dest_ip[7:0] = pay.pop_front(); // byte 19
+ pkt.payload = pay;
+
+ return pkt;
+
+ endfunction
+
+ function automatic logic ipv4_pkt_compare(input ipv4_pkt_t a, input ipv4_pkt_t b);
+
+ return ((a.hdr.header_length == b.hdr.header_length) &&
+ (a.hdr.version == b.hdr.version) &&
+ (a.hdr.dscp == b.hdr.dscp) &&
+ (a.hdr.ecn == b.hdr.ecn) &&
+ (a.hdr.length == b.hdr.length) &&
+ (a.hdr.identification == b.hdr.identification) &&
+ (a.hdr.rsv_zero == b.hdr.rsv_zero) &&
+ (a.hdr.dont_frag == b.hdr.dont_frag) &&
+ (a.hdr.more_frag == b.hdr.more_frag) &&
+ (a.hdr.frag_offset == b.hdr.frag_offset) &&
+ (a.hdr.time_to_live == b.hdr.time_to_live) &&
+ (a.hdr.protocol == b.hdr.protocol) &&
+ (a.hdr.checksum == b.hdr.checksum) &&
+ (a.hdr.src_ip == b.hdr.src_ip) &&
+ (a.hdr.dest_ip == b.hdr.dest_ip) &&
+ raw_pkt_compare(a.payload,b.payload));
+
+ endfunction
+
+ //************************************************************//
+ //////////////////// UDP PACKETS ///////////////////////////////
+ //************************************************************//
+
+ localparam logic [15:0] DEF_SRC_UDP_PORT = 16'd49748;
+ localparam logic [15:0] DEF_DEST_UDP_PORT = 16'd49153;
+ localparam logic [15:0] DEF_BRIDGE_UDP_PORT = 16'h66_55;
+
+ // UDP Header
+ typedef struct {
+ logic [15:0] src_port = DEF_SRC_UDP_PORT;
+ logic [15:0] dest_port = DEF_DEST_UDP_PORT;
+ logic [15:0] length = 16'hXXXX; //flag for (fill it in please)
+ logic [15:0] checksum = 16'hXXXX; //flag for (fill it in please)
+ } udp_hdr_t;
+
+ // UDP Packet, Header + Payload
+ typedef struct {
+ udp_hdr_t hdr;
+ raw_pkt_t payload;
+ } udp_pkt_t;
+
+ function automatic logic [15:0] calc_udp_checksum(
+ input logic [31:0] src_ip,
+ input logic [31:0] dest_ip,
+ input raw_pkt_t pkt);
+
+ logic [31:0] checksum;
+ raw_pkt_t virtual_header;
+
+ // UDP checksum is calculated over a virtual header that is added to
+ // the front of the packet.
+ virtual_header.push_back(src_ip[31:24]); // byte 0
+ virtual_header.push_back(src_ip[23:16]); // byte 1
+ virtual_header.push_back(src_ip[15:8]); // byte 2
+ virtual_header.push_back(src_ip[7:0]); // byte 3
+ virtual_header.push_back(dest_ip[31:24]); // byte 4
+ virtual_header.push_back(dest_ip[23:16]); // byte 5
+ virtual_header.push_back(dest_ip[15:8]); // byte 6
+ virtual_header.push_back(dest_ip[7:0]); // byte 7
+ virtual_header.push_back(0); // byte 8
+ virtual_header.push_back(UDP); // byte 9 UDP (Protocol enum) x11
+ virtual_header.push_back(0); // byte 10
+ virtual_header.push_back(pkt[6]); // byte 11 Length
+ virtual_header.push_back(pkt[7]); // byte 12 Length
+
+ pkt = {virtual_header,pkt}; // add virtual header in front
+
+ checksum = 0;
+ // Iterate over 16 bit chunks reading from an array of bytes
+ // need to traverse the virtual header / udp header / udp data
+ for (int i = 0 ; i < pkt.size ; i+=2 ) begin
+ // BIG endian network ordering... so weird
+ checksum += {pkt[i],pkt[i+1]};
+ end
+ checksum += checksum[31:16];
+ checksum = ~checksum;
+
+ return checksum[15:0];
+
+ endfunction
+
+ // Break a udp_pkt into a queue of bytes
+ function automatic raw_pkt_t flatten_udp_pkt(
+ input logic [31:0] src_ip,
+ input logic [31:0] dest_ip,
+ input udp_pkt_t pkt);
+ raw_pkt_t pay;
+
+ logic [15:0] length;
+ logic [15:0] checksum;
+
+ // If header or length is not set to default value then use the value in
+ // the packet.
+ if ($isunknown(pkt.hdr.length))
+ length = pkt.payload.size()+8; // 8 because length includes UDP header length.
+ else
+ length = pkt.hdr.length;
+
+ //temporary checksum
+ checksum = 0;
+
+ pay.push_back(pkt.hdr.src_port[15:8]); // byte 0
+ pay.push_back(pkt.hdr.src_port[7:0]); // byte 1
+ pay.push_back(pkt.hdr.dest_port[15:8]); // byte 2
+ pay.push_back(pkt.hdr.dest_port[7:0]); // byte 3
+ pay.push_back(length[15:8]); // byte 4
+ pay.push_back(length[7:0]); // byte 5
+ pay.push_back(checksum[15:8]); // byte 6
+ pay.push_back(checksum[7:0]); // byte 7
+ pay = {pay,pkt.payload};
+
+ if ($isunknown(pkt.hdr.checksum))
+ checksum = calc_udp_checksum(src_ip,dest_ip,pay);
+ else
+ checksum = pkt.hdr.checksum;
+
+ pay[6] = checksum[15:8];
+ pay[7] = checksum[7:0];
+
+ return pay;
+
+ endfunction
+
+ // Break a queue of bytes into a udp_pkt
+ function automatic udp_pkt_t unflatten_udp_pkt(input raw_pkt_t pay);
+ udp_pkt_t pkt;
+
+ pkt.hdr.src_port[15:8] = pay.pop_front();
+ pkt.hdr.src_port[7:0] = pay.pop_front();
+ pkt.hdr.dest_port[15:8] = pay.pop_front();
+ pkt.hdr.dest_port[7:0] = pay.pop_front();
+ pkt.hdr.length[15:8] = pay.pop_front();
+ pkt.hdr.length[7:0] = pay.pop_front();
+ pkt.hdr.checksum[15:8] = pay.pop_front();
+ pkt.hdr.checksum[7:0] = pay.pop_front();
+ pkt.payload = pay;
+
+ return pkt;
+
+ endfunction
+
+ function automatic logic udp_pkt_compare(input udp_pkt_t a, input udp_pkt_t b);
+
+ return ((a.hdr.src_port == b.hdr.src_port) &&
+ (a.hdr.dest_port == b.hdr.dest_port) &&
+ (a.hdr.length == b.hdr.length) &&
+ raw_pkt_compare(a.payload,b.payload));
+
+ endfunction
+
+ typedef enum int {
+ NO_PREAMBLE=0, NORMAL_PREAMBLE=1, ZERO_PREAMBLE=2
+ } preamble_t;
+
+ // Build up a raw UDP packet.
+ // Args:
+ // - pkt: Packet data (queue)
+ // - stream: Stream to use (Optional)
+ function automatic raw_pkt_t build_udp_pkt (
+ input eth_hdr_t eth_hdr,
+ input ipv4_hdr_t ipv4_hdr,
+ input udp_hdr_t udp_hdr,
+ input raw_pkt_t pay,
+ input int preamble = NO_PREAMBLE);
+
+ automatic udp_pkt_t udp_pkt;
+ automatic ipv4_pkt_t ipv4_pkt;
+ automatic eth_pkt_t eth_pkt;
+ automatic raw_pkt_t raw_pkt;
+
+ udp_pkt.hdr = udp_hdr;
+ udp_pkt.payload = pay;
+ ipv4_pkt.hdr = ipv4_hdr;
+ ipv4_pkt.payload = flatten_udp_pkt(ipv4_hdr.src_ip,ipv4_hdr.dest_ip,udp_pkt);
+ eth_pkt.hdr = eth_hdr;
+ eth_pkt.payload = flatten_ipv4_pkt(ipv4_pkt);
+ raw_pkt = flatten_eth_pkt(eth_pkt);
+ if (preamble==NORMAL_PREAMBLE) begin
+ raw_pkt.push_front(8'hAB);
+ raw_pkt.push_front(8'hAA);
+ raw_pkt.push_front(8'hAA);
+ raw_pkt.push_front(8'hAA);
+ raw_pkt.push_front(8'hAA);
+ raw_pkt.push_front(8'hAA);
+ end else if (preamble==ZERO_PREAMBLE) begin
+ raw_pkt.push_front(8'h00);
+ raw_pkt.push_front(8'h00);
+ raw_pkt.push_front(8'h00);
+ raw_pkt.push_front(8'h00);
+ raw_pkt.push_front(8'h00);
+ raw_pkt.push_front(8'h00);
+ end
+ return raw_pkt;
+
+ endfunction
+
+ // Wait for a packet to finish on the bus
+ // and decode it
+ task automatic decode_udp_pkt (
+ input raw_pkt_t raw_pkt,
+ output eth_hdr_t eth_hdr,
+ output ipv4_hdr_t ipv4_hdr,
+ output udp_hdr_t udp_hdr,
+ output raw_pkt_t payload);
+
+ eth_pkt_t eth_pkt;
+ ipv4_pkt_t ipv4_pkt;
+ udp_pkt_t udp_pkt;
+
+ eth_pkt = unflatten_eth_pkt(raw_pkt);
+ ipv4_pkt = unflatten_ipv4_pkt(eth_pkt.payload);
+ udp_pkt = unflatten_udp_pkt(ipv4_pkt.payload);
+
+ eth_hdr = eth_pkt.hdr;
+ ipv4_hdr = ipv4_pkt.hdr;
+ udp_hdr = udp_pkt.hdr;
+ payload = udp_pkt.payload;
+
+ endtask
+
+ //---------------------------------------------------------------------------
+ // XPORT Stream Packet Class
+ //---------------------------------------------------------------------------
+ // Extensions to the AxiStreamPacket used in the XPORT code
+ class XportStreamPacket #(
+ int DATA_WIDTH = 64
+ ) extends AxiStreamPacket #(DATA_WIDTH, $clog2((DATA_WIDTH/8)+1));
+
+ typedef XportStreamPacket #(DATA_WIDTH) XportPacket_t;
+ localparam UWIDTH = $clog2((DATA_WIDTH/8)+1);
+ // Class constructor.
+ function new ();
+ super.new();
+ endfunction : new
+
+ // Return a handle to a copy of this transaction
+ function XportPacket_t copy();
+ XportPacket_t temp;
+ temp = new();
+ temp.data = this.data;
+ temp.user = this.user;
+ temp.keep = this.keep;
+ return temp;
+ endfunction
+
+ // bring in data from an AxisPacket
+ function void import_axis(AxisPacket_t axi_pkt);
+ this.data = axi_pkt.data;
+ this.user = axi_pkt.user;
+ this.keep = axi_pkt.keep;
+ endfunction : import_axis
+
+ // take the tuser signal's and use them to set
+ // tkeep signals
+ task automatic tuser_to_tkeep(logic PRESERVE_TUSER=1);
+ keep_t last_tkeep;
+ user_t last_tuser;
+ logic [$clog2(DATA_WIDTH/8)-1:0] last_bytes;
+ last_tuser = this.user[$];
+ last_bytes = last_tuser[$clog2(DATA_WIDTH/8)-1:0];
+ // set all the tuser values to zero
+ // set all the tuser values to zero
+ foreach (this.user[i]) begin
+ this.keep[i] = '1;
+ if(!PRESERVE_TUSER)
+ this.user[i] = 0;
+ end
+ // figure out the value of tkeep for the last word
+ if (last_bytes == 0) last_tkeep = '1;
+ // check if there is an X
+ else if ($isunknown(last_tuser)) last_tkeep = '1;
+ else begin
+ last_tkeep = 0;
+ foreach (last_tkeep[i]) begin
+ last_tkeep[i] = i < last_bytes;
+ end
+ end
+ this.keep[$] = last_tkeep;
+ // set data bytes where the value isn't used to zero
+ foreach (last_tkeep[i]) begin
+ if (last_tkeep[i] == 0) this.data[$][i*8 +: 8] = 0;
+ end
+ endtask : tuser_to_tkeep
+
+ // take the tkeep signal's and use them to set
+ // width in the user signals
+ task automatic tkeep_to_tuser(int ERROR_PROB=0);
+ keep_t last_tkeep;
+ user_t last_tuser;
+
+ last_tkeep = this.keep[$];
+ // set all the tuser values to zero
+ foreach (this.user[i]) begin
+ this.user[i] = 0;
+ this.user[i][UWIDTH-1] = $urandom_range(99) < ERROR_PROB;
+ end
+ // check if there is an X
+ if ($isunknown(last_tkeep)) last_tuser = '0;
+ else begin
+ last_tuser = 0;
+ foreach (last_tkeep[i]) begin
+ if (last_tkeep[i]==1'b1) begin
+ last_tuser = last_tuser+1;
+ end
+ end
+ end
+ // full word is 0. MSB set is error
+ if (last_tuser == DATA_WIDTH/8)last_tuser = 0;
+ this.user[$] = last_tuser;
+ this.user[$][UWIDTH-1] = $urandom_range(99) < ERROR_PROB;
+
+ // set data bytes where the value isn't used to zero
+ foreach (last_tkeep[i]) begin
+ if (last_tkeep[i] == 0) this.data[$][i*8 +: 8] = 0;
+ end
+
+ endtask : tkeep_to_tuser
+
+ function automatic logic has_error();
+ logic error;
+
+ error = 0;
+ foreach (this.user[i]) begin
+ //catch if error was set
+ error = error || this.user[i][UWIDTH-1];
+ end
+ return error;
+ endfunction : has_error
+
+ function automatic int byte_length();
+ int bytes;
+ int last_bytes;
+ bytes = (this.data.size()-1)*DATA_WIDTH/8;
+ last_bytes = this.user[$][UWIDTH-2:0];
+ if (last_bytes == 0)
+ bytes+=DATA_WIDTH/8;
+ else
+ bytes+=last_bytes;
+
+ return bytes;
+
+ endfunction : byte_length
+
+ function automatic void clear_error();
+
+ foreach (this.user[i]) begin
+ this.user[i][UWIDTH-1] = 0;
+ end
+
+ endfunction : clear_error
+
+ function automatic void clear_keep();
+
+ foreach (this.keep[i]) begin
+ this.keep[i] = 0;
+ end
+
+ endfunction : clear_keep
+
+ function automatic void clear_user();
+
+ foreach (this.user[i]) begin
+ this.user[i] = 0;
+ end
+
+ endfunction : clear_user
+
+ task automatic set_error();
+ foreach (this.user[i]) begin
+ this.user[i][UWIDTH-1] = 1;
+ end
+ endtask : set_error
+
+ ///// compare_w_error
+ // Check that this packet has expected error bit in tuser
+ // Keep is not compared
+ // If COMPARE_ERROR_PACKETS is 0
+ // Don't check packet contents
+ // If COMPARE_ERROR_PACKETS is 1
+ // Check that this packet matches the expected packet
+ function automatic logic compare_w_error(
+ XportPacket_t expected,
+ int COMPARE_ERROR_PACKETS=1
+ );
+
+ automatic XportPacket_t actual_copy = this.copy();
+ automatic XportPacket_t expected_copy = expected.copy();
+
+ logic exp_error=0;
+ logic act_error=0;
+ logic error_condition;
+
+ exp_error = expected.has_error();
+ act_error = this.has_error();
+
+ actual_copy.clear_error();
+ expected_copy.clear_error();
+ actual_copy.clear_keep();
+ expected_copy.clear_keep();
+
+ error_condition = (!expected_copy.equal(actual_copy) &&
+ (!exp_error || COMPARE_ERROR_PACKETS)) ||
+ act_error != exp_error;
+ if (error_condition) begin
+ $display("Expected");
+ expected.print();
+ $display("Actual");
+ this.print();
+ if (!expected_copy.equal(actual_copy))
+ $display("ERROR :: packet mismatch");
+ if (act_error != exp_error)
+ $display("ERROR :: error mismatch");
+ end
+
+ return error_condition;
+
+ endfunction : compare_w_error
+
+ ///// compare_w_sof
+ // Check that this packet has expected sof bit in tuser
+ // Keep is not compared
+ // Check that this packet matches the expected packet
+ function automatic logic compare_w_sof(XportPacket_t expected);
+
+ automatic XportPacket_t actual_copy = this.copy();
+ automatic XportPacket_t expected_copy = expected.copy();
+
+ logic sof_error=0;
+ foreach (this.user[i]) begin
+ if (i==0) begin
+ // set if top bit of user isn't set on the first word.
+ sof_error = !this.user[i][UWIDTH-1];
+ end else begin
+ // set if top bit of user is set on any other word.
+ sof_error = this.user[i][UWIDTH-1] || sof_error;
+ end
+ end
+
+ // error bit doubles for SOF
+ actual_copy.clear_error();
+ expected_copy.clear_error();
+ actual_copy.clear_keep();
+ expected_copy.clear_keep();
+
+ // set SOF in expected
+ expected_copy.user[0][UWIDTH-1] = 0;
+
+ if (!expected_copy.equal(actual_copy) ||
+ sof_error) begin
+ $display("Expected");
+ expected_copy.print();
+ $display("Actual");
+ this.print();
+ if (!expected_copy.equal(actual_copy))
+ $display("ERROR :: packet mismatch");
+ if (sof_error)
+ $display("ERROR :: sof mismatch");
+ end
+
+ return !expected_copy.equal(actual_copy) ||
+ sof_error;
+
+ endfunction : compare_w_sof
+
+ ///// compare_w_pad
+ // Check that this packet has expected sof bit in tuser
+ // Keep is not compared
+ // Check that this packet matches the expected packet
+ // if DUMB_ORIGINAL_WAY
+ // User is not compared
+ // else
+ // Check that this packets tuser matches the expected packet
+ function automatic logic compare_w_pad(
+ XportPacket_t expected,
+ logic DUMB_ORIGINAL_WAY=0
+ );
+
+ automatic XportPacket_t actual_copy = this.copy();
+ automatic XportPacket_t expected_copy = expected.copy();
+
+ // not using MSB as error here.
+ actual_copy.clear_error();
+ expected_copy.clear_error();
+ actual_copy.clear_keep();
+ expected_copy.clear_keep();
+ // Add pad bytes to user
+ if (DUMB_ORIGINAL_WAY) begin
+ // I can't figure out how to calculate the expected on the
+ // original so I'm just copying the actual
+ foreach (expected_copy.user[i]) begin
+ expected_copy.user[i] = actual_copy.user[i];
+ end
+ end
+
+ if (!expected_copy.equal(actual_copy)) begin
+ $display("Expected");
+ expected_copy.print();
+ $display("Actual");
+ this.print();
+ if (!expected_copy.equal(actual_copy))
+ $display("ERROR :: packet mismatch");
+ end
+
+ return !expected_copy.equal(actual_copy);
+
+ endfunction : compare_w_pad
+
+ ///// compare_no_user
+ // Check that this packet has expected sof bit in tuser
+ // Keep is not compared
+ // Check that this packet matches the expected packet
+ // User is not compared
+ function automatic logic compare_no_user(XportPacket_t expected);
+
+ automatic XportPacket_t actual_copy = this.copy();
+ automatic XportPacket_t expected_copy = expected.copy();
+
+ // not using MSB as error here.
+ actual_copy.clear_error();
+ expected_copy.clear_error();
+ actual_copy.clear_keep();
+ expected_copy.clear_keep();
+
+ // Add pad bytes to user
+ foreach (expected_copy.user[i]) begin
+ expected_copy.user[i] = 0;
+ actual_copy.user[i] = 0;
+ end
+
+ if (!expected_copy.equal(actual_copy)) begin
+ $display("Expected");
+ expected.print();
+ $display("Actual");
+ this.print();
+ if (!expected_copy.equal(actual_copy))
+ $display("ERROR :: packet mismatch");
+ end
+
+ return !expected_copy.equal(actual_copy);
+
+ endfunction : compare_no_user
+
+ endclass : XportStreamPacket;
+
+endpackage : PkgEthernet