diff options
Diffstat (limited to 'fpga/usrp3/sim/rfnoc/PkgEthernet.sv')
-rw-r--r-- | fpga/usrp3/sim/rfnoc/PkgEthernet.sv | 809 |
1 files changed, 809 insertions, 0 deletions
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 |