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
844 lines
28 KiB
Systemverilog
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
|