diff options
Diffstat (limited to 'fpga/usrp3/sim/rfnoc')
-rw-r--r-- | fpga/usrp3/sim/rfnoc/Makefile.srcs | 2 | ||||
-rw-r--r-- | fpga/usrp3/sim/rfnoc/PkgAxiStreamBfm.sv | 239 | ||||
-rw-r--r-- | fpga/usrp3/sim/rfnoc/PkgAxisCtrlBfm.sv | 18 | ||||
-rw-r--r-- | fpga/usrp3/sim/rfnoc/PkgEthernet.sv | 809 |
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 |