// // 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; // check for empty packet, in which case there's nothing to clear if (this.data.size() == 0) return; 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