Files
b210-k7-fpga/sim/rfnoc/PkgEthernet.sv
T
Andrew Moch d5bbce58cb fpga: sim: Update PkgEthernet
Consolidated calcuation of last_tkeep and tkeep_last. Changed error
checking to support unwrinkling tkeep/trailing changes in 100G
etherent and support for testing packet dropping on backup.


Original-commit: 473b0f5abe882ca316a7fe1a59234676940ef26b
2020-07-31 13:22:29 -05:00

844 lines
28 KiB
Systemverilog

//
// 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
// figure out the value of tkeep based on tuser(trailing words)
function keep_t calc_last_tkeep(user_t tuser);
keep_t last_tkeep;
// check if there is an X
if (tuser[$clog2(DATA_WIDTH/8)-1:0] === 0) begin
last_tkeep = '1;
end else if ($isunknown(tuser[$clog2(DATA_WIDTH/8)-1:0])) begin
last_tkeep = '1;
end else begin
last_tkeep = 0;
foreach (calc_last_tkeep[i]) begin
// set the bit if it's less than value of tuser
last_tkeep[i] = i < tuser[$clog2(DATA_WIDTH/8)-1:0];
end
end
return last_tkeep;
endfunction : calc_last_tkeep;
// figure out the value of tuser(traling words) based on tkeep
function user_t calc_last_tuser(keep_t tkeep);
user_t last_tuser;
// check if there is an X
if ($isunknown(tkeep)) begin
last_tuser = '0;
end else begin
last_tuser = 0;
foreach (tkeep[i]) begin
if (tkeep[i]==1'b1) begin
last_tuser = last_tuser+1;
end
end
end
// full word is 0
if (last_tuser == DATA_WIDTH/8) begin
last_tuser = 0;
end
return last_tuser;
endfunction : calc_last_tuser;
// clear bytes that aren't marked as valid by keep
function void clear_unused_bytes();
keep_t last_tkeep;
last_tkeep = this.keep[$];
// check that the last user is the same as the last keep
assert (this.keep[$] == calc_last_tkeep(this.user[$])) else
$error("clear_unused_bytes: final tkeep and tuser don't match");
// 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
endfunction : clear_unused_bytes;
// take the tuser signal's and use them to set
// tkeep signals
task automatic tuser_to_tkeep(logic PRESERVE_TUSER=1);
// 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
this.keep[$] = calc_last_tkeep(this.user[$]);
// set data bytes where the value isn't used to zero
clear_unused_bytes;
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);
// 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
// figure out the value of user for the last word
this.user[$] = calc_last_tuser(this.keep[$]);
this.user[$][UWIDTH-1] = $urandom_range(99) < ERROR_PROB;
// set data bytes where the value isn't used to zero
clear_unused_bytes;
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_unused_bytes();
expected_copy.clear_unused_bytes();
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_unused_bytes();
expected_copy.clear_unused_bytes();
// 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_unused_bytes();
expected_copy.clear_unused_bytes();
// Add pad bytes to user
if (DUMB_ORIGINAL_WAY) begin
actual_copy.clear_keep();
expected_copy.clear_keep();
// I can't figure out a goodway to calculate the
// expected tuser for non last word values.
// So I'm just copying the actual
foreach (expected_copy.user[i]) begin
if (i != expected_copy.user.size()-1) begin
expected_copy.user[i] = actual_copy.user[i];
end
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, int PRINT_LVL=1);
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_unused_bytes();
expected_copy.clear_unused_bytes();
// Add pad bytes to user
foreach (expected_copy.user[i]) begin
expected_copy.user[i] = 0;
actual_copy.user[i] = 0;
end
if (PRINT_LVL==1) begin
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
end
return !expected_copy.equal(actual_copy);
endfunction : compare_no_user
endclass : XportStreamPacket;
endpackage : PkgEthernet