1//
2// Copyright 2020 Ettus Research, A National Instruments Brand
3//
4// SPDX-License-Identifier: LGPL-3.0-or-later
5//
6// Module: PkgEthernet
7//
8// Description: This package defines the data types used to represent ETHER,
9// IPV4, and UDP.  It's based on a queue of bytes representation named
10// raw_pkt_t.
11//
12
13package PkgEthernet;
14
15  import PkgAxiStreamBfm::*;
16  export PkgAxiStreamBfm::*;
17
18  //************************************************************//
19  //////////////////// ETHER PACKETS /////////////////////////////
20  //************************************************************//
21
22  // Ether type - subset of possibilities. Add more as needed.
23  typedef enum logic [15:0] {
24    // Byte order            1  0
25    IPV4=16'h0800, IPV6=16'h86_DD, VLAN_TAGGED=16'h8100,
26    DOUBLE_VLAN_TAGGED=16'h9100, ROCE=16'h8915
27  } ether_type_t;
28
29  // Some MAC addresses
30  // Byte order                                      5  4  3  2  1  0
31  localparam logic [47:0] DEF_DEST_MAC_ADDR   = 48'h5E_35_EB_71_46_F7;
32  localparam logic [47:0] DEF_SRC_MAC_ADDR    = 48'h98_03_9B_8E_09_B9;
33  localparam logic [47:0] DEF_BRIDGE_MAC_ADDR = 48'hBB_BB_BB_BB_BB_BB;
34
35  // Ether Header
36  typedef struct {
37    logic [47:0]  dest_mac   = DEF_DEST_MAC_ADDR;
38    logic [47:0]  src_mac    = DEF_SRC_MAC_ADDR;
39    ether_type_t  ether_type = IPV4;
40  } eth_hdr_t;
41
42  // Ethernet Packet, Header + Payload
43  typedef struct {
44    eth_hdr_t     hdr;
45    raw_pkt_t     payload;
46    logic [31:0]  fcs;
47    int           ipg     = 12; // interpacket gap
48  } eth_pkt_t;
49
50  // Break an eth_pkt into a queue of bytes
51  function automatic raw_pkt_t flatten_eth_pkt(input eth_pkt_t pkt);
52    raw_pkt_t pay;
53
54    pay.push_back(pkt.hdr.dest_mac[47:40]);
55    pay.push_back(pkt.hdr.dest_mac[39:32]);
56    pay.push_back(pkt.hdr.dest_mac[31:24]);
57    pay.push_back(pkt.hdr.dest_mac[23:16]);
58    pay.push_back(pkt.hdr.dest_mac[15:8]);
59    pay.push_back(pkt.hdr.dest_mac[7:0]);
60    pay.push_back(pkt.hdr.src_mac[47:40]);
61    pay.push_back(pkt.hdr.src_mac[39:32]);
62    pay.push_back(pkt.hdr.src_mac[31:24]);
63    pay.push_back(pkt.hdr.src_mac[23:16]);
64    pay.push_back(pkt.hdr.src_mac[15:8]);
65    pay.push_back(pkt.hdr.src_mac[7:0]);
66    pay.push_back(pkt.hdr.ether_type[15:8]);
67    pay.push_back(pkt.hdr.ether_type[7:0]);
68    pay = {pay,pkt.payload};
69
70    return pay;
71
72  endfunction
73
74  // Break a queue of bytes into a eth_pkt
75  function automatic eth_pkt_t unflatten_eth_pkt(input raw_pkt_t pay);
76    eth_pkt_t pkt;
77
78    pkt.hdr.dest_mac[47:40]  = pay.pop_front();
79    pkt.hdr.dest_mac[39:32]  = pay.pop_front();
80    pkt.hdr.dest_mac[31:24]  = pay.pop_front();
81    pkt.hdr.dest_mac[23:16]  = pay.pop_front();
82    pkt.hdr.dest_mac[15:8]   = pay.pop_front();
83    pkt.hdr.dest_mac[7:0]    = pay.pop_front();
84    pkt.hdr.src_mac[47:40]   = pay.pop_front();
85    pkt.hdr.src_mac[39:32]   = pay.pop_front();
86    pkt.hdr.src_mac[31:24]   = pay.pop_front();
87    pkt.hdr.src_mac[23:16]   = pay.pop_front();
88    pkt.hdr.src_mac[15:8]    = pay.pop_front();
89    pkt.hdr.src_mac[7:0]     = pay.pop_front();
90    pkt.hdr.ether_type[15:8] = pay.pop_front();
91    pkt.hdr.ether_type[7:0]  = pay.pop_front();
92    pkt.payload = pay;
93
94    return pkt;
95
96  endfunction
97
98  function automatic logic eth_pkt_compare(input eth_pkt_t a, input eth_pkt_t b);
99
100    return ((a.hdr.dest_mac == b.hdr.dest_mac) &&
101            (a.hdr.src_mac == b.hdr.src_mac) &&
102            (a.hdr.ether_type == b.hdr.ether_type) &&
103            raw_pkt_compare(a.payload,b.payload));
104
105  endfunction
106
107  //************************************************************//
108  //////////////////// IPV4 PACKETS //////////////////////////////
109  //************************************************************//
110
111  // IP Protocol - subset of possibilities. add more as needed
112  typedef enum logic [7:0] {
113    UDP=8'd17, TCP=8'd6, ICMP=8'd1, IGMP=8'd2, ENCAP=8'd41
114  } ip_protocol_t;
115
116  // follow normal convention of an IP address
117  function automatic logic [31:0] ip(logic [7:0] a,b,c,d);
118    return {a,b,c,d};
119  endfunction
120
121
122  localparam logic [31:0] DEF_DEST_IP_ADDR   = ip(192,168,10,2);
123  localparam logic [31:0] DEF_SRC_IP_ADDR    = ip(192,168,10,1);
124  localparam logic [31:0] DEF_BRIDGE_IP_ADDR = 32'h33_33_33_33;
125
126  // IPv4 Header
127  typedef struct {
128    logic  [3:0]      header_length  = 4'd5;
129    logic  [3:0]      version        = 4'd4;
130    logic  [5:0]      dscp           = 6'b0000_00;
131    logic  [1:0]      ecn            = 2'b00;
132    logic [15:0]      length         = 16'hXXXX; //flag for (fill it in please)
133    logic [15:0]      identification = 16'h462E;
134    logic             rsv_zero       = 1'b0;
135    logic             dont_frag      = 1'b1;
136    logic             more_frag      = 1'b0;
137    logic [12:0]      frag_offset    = 16'd0;
138    logic  [7:0]      time_to_live   = 16'd64;
139    ip_protocol_t     protocol       = UDP;
140    logic [15:0]      checksum       = 16'hXXXX; //flag for (fill it in please)
141    logic [31:0]      src_ip         = DEF_SRC_IP_ADDR;
142    logic [31:0]      dest_ip        = DEF_DEST_IP_ADDR;
143  } ipv4_hdr_t;
144
145  // IP Packet, Header + Payload
146  typedef struct {
147    ipv4_hdr_t    hdr;
148    raw_pkt_t     payload;
149  } ipv4_pkt_t;
150
151  // The checksum for an IP header is the sum of all the 16 bit words that
152  // make up the header with the checksum set to zero. Add back the carry over
153  // from bits [31:16] then invert.
154  // See https://en.wikipedia.org/wiki/IPv4_header_checksum
155  function automatic logic [15:0] calc_ipv4_checksum(input raw_pkt_t pkt);
156
157    // This is a bit oversized, but it's not costing anything.
158    // 10 max sized words can at most add logbase2 of 10 bits.
159    logic [31:0] checksum;
160
161    checksum = 0;
162    // Iterate over 16 bit chunks reading from a byte addressed memory.
163    // There are 20 bytes in an ipv4 header.
164    for (int i = 0 ; i < 20 ; i+=2 ) begin
165      // BIG endian network ordering... so weird
166      checksum += {pkt[i],pkt[i+1]};
167    end
168    checksum += checksum[31:16];
169    checksum = ~checksum;
170
171    return checksum[15:0];
172
173  endfunction
174
175  // Break an eth_pkt into a queue of bytes
176  function automatic raw_pkt_t flatten_ipv4_pkt(input ipv4_pkt_t pkt);
177    raw_pkt_t pay;
178
179    logic [15:0] length;
180    logic [15:0] checksum;
181    logic [2:0]  flags;
182
183    // If header or length is not set to default value then use the value in
184    // the packet.
185    if ($isunknown(pkt.hdr.length))
186       length = pkt.payload.size()+20; // 20 because length includes IP header length.
187    else
188       length = pkt.hdr.length;
189
190    flags = {pkt.hdr.more_frag,pkt.hdr.dont_frag,pkt.hdr.rsv_zero};
191    // Start off with checksum as 0
192    checksum = 0;
193
194    // 20 byte IP header
195    pay.push_back({pkt.hdr.version,pkt.hdr.header_length}); // byte 0
196    pay.push_back({pkt.hdr.dscp,pkt.hdr.ecn});              // byte 1
197    pay.push_back(length[15:8]);                            // byte 2
198    pay.push_back(length[7:0]);                             // byte 3
199    pay.push_back(pkt.hdr.identification[15:8]);            // byte 4
200    pay.push_back(pkt.hdr.identification[7:0]);             // byte 5
201    pay.push_back({flags,pkt.hdr.frag_offset[12:8]});       // byte 6
202    pay.push_back(pkt.hdr.frag_offset[7:0]);                // byte 7
203    pay.push_back(pkt.hdr.time_to_live);                    // byte 8
204    pay.push_back(pkt.hdr.protocol);                        // byte 9
205    pay.push_back(checksum[15:8]);                          // byte 10
206    pay.push_back(checksum[7:0]);                           // byte 11
207    pay.push_back(pkt.hdr.src_ip[31:24]);                   // byte 12
208    pay.push_back(pkt.hdr.src_ip[23:16]);                   // byte 13
209    pay.push_back(pkt.hdr.src_ip[15:8]);                    // byte 14
210    pay.push_back(pkt.hdr.src_ip[7:0]);                     // byte 15
211    pay.push_back(pkt.hdr.dest_ip[31:24]);                  // byte 16
212    pay.push_back(pkt.hdr.dest_ip[23:16]);                  // byte 17
213    pay.push_back(pkt.hdr.dest_ip[15:8]);                   // byte 18
214    pay.push_back(pkt.hdr.dest_ip[7:0]);                    // byte 19
215    pay = {pay,pkt.payload};
216
217    if ($isunknown(pkt.hdr.checksum))
218       checksum = calc_ipv4_checksum(pay);
219    else
220    checksum = pkt.hdr.checksum;
221    // replace the checksum (bytes 11:10
222    pay[10] = checksum[15:8];
223    pay[11] = checksum[7:0];
224
225    return pay;
226  endfunction
227
228  // Break a queue of bytes into a ip_pkt
229  function automatic ipv4_pkt_t unflatten_ipv4_pkt(input raw_pkt_t pay);
230    ipv4_pkt_t pkt;
231
232    // 20 byte IP header
233    {pkt.hdr.version,
234     pkt.hdr.header_length}       = pay.pop_front(); // byte 0
235    {pkt.hdr.dscp,pkt.hdr.ecn}    = pay.pop_front(); // byte 1
236    pkt.hdr.length[15:8]          = pay.pop_front(); // byte 2
237    pkt.hdr.length[7:0]           = pay.pop_front(); // byte 3
238    pkt.hdr.identification[15:8]  = pay.pop_front(); // byte 4
239    pkt.hdr.identification[7:0]   = pay.pop_front(); // byte 5
240    {pkt.hdr.more_frag,
241     pkt.hdr.dont_frag,
242     pkt.hdr.rsv_zero,
243     pkt.hdr.frag_offset[12:8]}   = pay.pop_front(); // byte 6
244    pkt.hdr.frag_offset[7:0]      = pay.pop_front(); // byte 7
245    pkt.hdr.time_to_live          = pay.pop_front(); // byte 8
246    pkt.hdr.protocol              = ip_protocol_t'(pay.pop_front()); // byte 9
247    pkt.hdr.checksum[15:8]        = pay.pop_front(); // byte 10
248    pkt.hdr.checksum[7:0]         = pay.pop_front(); // byte 11
249    pkt.hdr.src_ip[31:24]         = pay.pop_front(); // byte 12
250    pkt.hdr.src_ip[23:16]         = pay.pop_front(); // byte 13
251    pkt.hdr.src_ip[15:8]          = pay.pop_front(); // byte 14
252    pkt.hdr.src_ip[7:0]           = pay.pop_front(); // byte 15
253    pkt.hdr.dest_ip[31:24]        = pay.pop_front(); // byte 16
254    pkt.hdr.dest_ip[23:16]        = pay.pop_front(); // byte 17
255    pkt.hdr.dest_ip[15:8]         = pay.pop_front(); // byte 18
256    pkt.hdr.dest_ip[7:0]          = pay.pop_front(); // byte 19
257    pkt.payload = pay;
258
259    return pkt;
260
261  endfunction
262
263  function automatic logic ipv4_pkt_compare(input ipv4_pkt_t a, input ipv4_pkt_t b);
264
265    return ((a.hdr.header_length == b.hdr.header_length) &&
266            (a.hdr.version == b.hdr.version) &&
267            (a.hdr.dscp == b.hdr.dscp) &&
268            (a.hdr.ecn == b.hdr.ecn) &&
269            (a.hdr.length == b.hdr.length) &&
270            (a.hdr.identification == b.hdr.identification) &&
271            (a.hdr.rsv_zero == b.hdr.rsv_zero) &&
272            (a.hdr.dont_frag == b.hdr.dont_frag) &&
273            (a.hdr.more_frag == b.hdr.more_frag) &&
274            (a.hdr.frag_offset == b.hdr.frag_offset) &&
275            (a.hdr.time_to_live == b.hdr.time_to_live) &&
276            (a.hdr.protocol == b.hdr.protocol) &&
277            (a.hdr.checksum == b.hdr.checksum) &&
278            (a.hdr.src_ip == b.hdr.src_ip) &&
279            (a.hdr.dest_ip == b.hdr.dest_ip) &&
280            raw_pkt_compare(a.payload,b.payload));
281
282  endfunction
283
284  //************************************************************//
285  //////////////////// UDP PACKETS ///////////////////////////////
286  //************************************************************//
287
288  localparam logic [15:0] DEF_SRC_UDP_PORT    = 16'd49748;
289  localparam logic [15:0] DEF_DEST_UDP_PORT   = 16'd49153;
290  localparam logic [15:0] DEF_BRIDGE_UDP_PORT = 16'h66_55;
291
292  // UDP Header
293  typedef struct {
294    logic [15:0]       src_port   = DEF_SRC_UDP_PORT;
295    logic [15:0]       dest_port  = DEF_DEST_UDP_PORT;
296    logic [15:0]       length     = 16'hXXXX; //flag for (fill it in please)
297    logic [15:0]       checksum   = 16'hXXXX; //flag for (fill it in please)
298  } udp_hdr_t;
299
300  // UDP Packet, Header + Payload
301  typedef struct {
302    udp_hdr_t     hdr;
303    raw_pkt_t     payload;
304  } udp_pkt_t;
305
306  function automatic logic [15:0] calc_udp_checksum(
307    input logic [31:0] src_ip,
308    input logic [31:0]  dest_ip,
309    input raw_pkt_t pkt);
310
311    logic [31:0] checksum;
312    raw_pkt_t    virtual_header;
313
314    // UDP checksum is calculated over a virtual header that is added to
315    // the front of the packet.
316    virtual_header.push_back(src_ip[31:24]);  // byte 0
317    virtual_header.push_back(src_ip[23:16]);  // byte 1
318    virtual_header.push_back(src_ip[15:8]);   // byte 2
319    virtual_header.push_back(src_ip[7:0]);    // byte 3
320    virtual_header.push_back(dest_ip[31:24]); // byte 4
321    virtual_header.push_back(dest_ip[23:16]); // byte 5
322    virtual_header.push_back(dest_ip[15:8]);  // byte 6
323    virtual_header.push_back(dest_ip[7:0]);   // byte 7
324    virtual_header.push_back(0);              // byte 8
325    virtual_header.push_back(UDP);            // byte 9  UDP (Protocol enum) x11
326    virtual_header.push_back(0);              // byte 10
327    virtual_header.push_back(pkt[6]);         // byte 11 Length
328    virtual_header.push_back(pkt[7]);         // byte 12 Length
329
330    pkt = {virtual_header,pkt}; // add virtual header in front
331
332    checksum = 0;
333    // Iterate over 16 bit chunks reading from an array of bytes
334    // need to traverse the virtual header / udp header / udp data
335    for (int i = 0 ; i < pkt.size ; i+=2 ) begin
336      // BIG endian network ordering... so weird
337      checksum += {pkt[i],pkt[i+1]};
338    end
339    checksum += checksum[31:16];
340    checksum = ~checksum;
341
342    return checksum[15:0];
343
344  endfunction
345
346  // Break a udp_pkt into a queue of bytes
347  function automatic raw_pkt_t flatten_udp_pkt(
348    input logic [31:0] src_ip,
349    input logic [31:0] dest_ip,
350    input udp_pkt_t pkt);
351    raw_pkt_t pay;
352
353    logic [15:0] length;
354    logic [15:0] checksum;
355
356    // If header or length is not set to default value then use the value in
357    // the packet.
358    if ($isunknown(pkt.hdr.length))
359      length = pkt.payload.size()+8; // 8 because length includes UDP header length.
360    else
361    length = pkt.hdr.length;
362
363    //temporary checksum
364    checksum = 0;
365
366    pay.push_back(pkt.hdr.src_port[15:8]);  // byte 0
367    pay.push_back(pkt.hdr.src_port[7:0]);   // byte 1
368    pay.push_back(pkt.hdr.dest_port[15:8]); // byte 2
369    pay.push_back(pkt.hdr.dest_port[7:0]);  // byte 3
370    pay.push_back(length[15:8]);            // byte 4
371    pay.push_back(length[7:0]);             // byte 5
372    pay.push_back(checksum[15:8]);          // byte 6
373    pay.push_back(checksum[7:0]);           // byte 7
374    pay = {pay,pkt.payload};
375
376    if ($isunknown(pkt.hdr.checksum))
377      checksum = calc_udp_checksum(src_ip,dest_ip,pay);
378    else
379      checksum = pkt.hdr.checksum;
380
381    pay[6] = checksum[15:8];
382    pay[7] = checksum[7:0];
383
384    return pay;
385
386  endfunction
387
388  // Break a queue of bytes into a udp_pkt
389  function automatic udp_pkt_t unflatten_udp_pkt(input raw_pkt_t pay);
390    udp_pkt_t pkt;
391
392    pkt.hdr.src_port[15:8]    = pay.pop_front();
393    pkt.hdr.src_port[7:0]     = pay.pop_front();
394    pkt.hdr.dest_port[15:8]   = pay.pop_front();
395    pkt.hdr.dest_port[7:0]    = pay.pop_front();
396    pkt.hdr.length[15:8]      = pay.pop_front();
397    pkt.hdr.length[7:0]       = pay.pop_front();
398    pkt.hdr.checksum[15:8]    = pay.pop_front();
399    pkt.hdr.checksum[7:0]     = pay.pop_front();
400    pkt.payload = pay;
401
402    return pkt;
403
404  endfunction
405
406  function automatic logic udp_pkt_compare(input udp_pkt_t a, input udp_pkt_t b);
407
408    return ((a.hdr.src_port == b.hdr.src_port) &&
409            (a.hdr.dest_port == b.hdr.dest_port) &&
410            (a.hdr.length == b.hdr.length) &&
411            raw_pkt_compare(a.payload,b.payload));
412
413  endfunction
414
415  typedef enum int {
416     NO_PREAMBLE=0, NORMAL_PREAMBLE=1, ZERO_PREAMBLE=2
417  } preamble_t;
418
419  // Build up a raw UDP packet.
420  // Args:
421  // - pkt: Packet data (queue)
422  // - stream: Stream to use (Optional)
423  function automatic raw_pkt_t build_udp_pkt (
424    input eth_hdr_t  eth_hdr,
425    input ipv4_hdr_t ipv4_hdr,
426    input udp_hdr_t  udp_hdr,
427    input raw_pkt_t  pay,
428    input int        preamble = NO_PREAMBLE);
429
430    automatic udp_pkt_t  udp_pkt;
431    automatic ipv4_pkt_t ipv4_pkt;
432    automatic eth_pkt_t  eth_pkt;
433    automatic raw_pkt_t  raw_pkt;
434
435    udp_pkt.hdr      = udp_hdr;
436    udp_pkt.payload  = pay;
437    ipv4_pkt.hdr     = ipv4_hdr;
438    ipv4_pkt.payload = flatten_udp_pkt(ipv4_hdr.src_ip,ipv4_hdr.dest_ip,udp_pkt);
439    eth_pkt.hdr      = eth_hdr;
440    eth_pkt.payload  = flatten_ipv4_pkt(ipv4_pkt);
441    raw_pkt          = flatten_eth_pkt(eth_pkt);
442    if (preamble==NORMAL_PREAMBLE) begin
443      raw_pkt.push_front(8'hAB);
444      raw_pkt.push_front(8'hAA);
445      raw_pkt.push_front(8'hAA);
446      raw_pkt.push_front(8'hAA);
447      raw_pkt.push_front(8'hAA);
448      raw_pkt.push_front(8'hAA);
449    end else if (preamble==ZERO_PREAMBLE) begin
450      raw_pkt.push_front(8'h00);
451      raw_pkt.push_front(8'h00);
452      raw_pkt.push_front(8'h00);
453      raw_pkt.push_front(8'h00);
454      raw_pkt.push_front(8'h00);
455      raw_pkt.push_front(8'h00);
456    end
457    return raw_pkt;
458
459  endfunction
460
461  // Wait for a packet to finish on the bus
462  // and decode it
463  task automatic decode_udp_pkt (
464    input  raw_pkt_t  raw_pkt,
465    output eth_hdr_t  eth_hdr,
466    output ipv4_hdr_t ipv4_hdr,
467    output udp_hdr_t  udp_hdr,
468    output raw_pkt_t  payload);
469
470    eth_pkt_t  eth_pkt;
471    ipv4_pkt_t ipv4_pkt;
472    udp_pkt_t  udp_pkt;
473
474    eth_pkt  = unflatten_eth_pkt(raw_pkt);
475    ipv4_pkt = unflatten_ipv4_pkt(eth_pkt.payload);
476    udp_pkt  = unflatten_udp_pkt(ipv4_pkt.payload);
477
478    eth_hdr  = eth_pkt.hdr;
479    ipv4_hdr = ipv4_pkt.hdr;
480    udp_hdr  = udp_pkt.hdr;
481    payload  = udp_pkt.payload;
482
483  endtask
484
485  //---------------------------------------------------------------------------
486  // XPORT Stream Packet Class
487  //---------------------------------------------------------------------------
488  // Extensions to the AxiStreamPacket used in the XPORT code
489  class XportStreamPacket #(
490    int DATA_WIDTH = 64
491  ) extends AxiStreamPacket #(DATA_WIDTH, $clog2((DATA_WIDTH/8)+1));
492
493    typedef XportStreamPacket #(DATA_WIDTH) XportPacket_t;
494    localparam UWIDTH = $clog2((DATA_WIDTH/8)+1);
495    // Class constructor.
496    function new ();
497      super.new();
498    endfunction : new
499
500    // Return a handle to a copy of this transaction
501    function XportPacket_t copy();
502      XportPacket_t temp;
503      temp = new();
504      temp.data = this.data;
505      temp.user = this.user;
506      temp.keep = this.keep;
507      return temp;
508    endfunction
509
510    // bring in data from an AxisPacket
511    function void import_axis(AxisPacket_t axi_pkt);
512      this.data = axi_pkt.data;
513      this.user = axi_pkt.user;
514      this.keep = axi_pkt.keep;
515    endfunction : import_axis
516
517
518    // figure out the value of tkeep based on tuser(trailing words)
519    function keep_t calc_last_tkeep(user_t tuser);
520      keep_t last_tkeep;
521      // check if there is an X
522      if (tuser[$clog2(DATA_WIDTH/8)-1:0] === 0) begin
523        last_tkeep = '1;
524      end else if ($isunknown(tuser[$clog2(DATA_WIDTH/8)-1:0])) begin
525        last_tkeep = '1;
526      end else begin
527        last_tkeep = 0;
528        foreach (calc_last_tkeep[i]) begin
529          // set the bit if it's less than value of tuser
530          last_tkeep[i] = i < tuser[$clog2(DATA_WIDTH/8)-1:0];
531        end
532      end
533      return last_tkeep;
534    endfunction : calc_last_tkeep;
535
536    // figure out the value of tuser(traling words) based on tkeep
537    function user_t calc_last_tuser(keep_t tkeep);
538      user_t last_tuser;
539      // check if there is an X
540      if ($isunknown(tkeep)) begin
541        last_tuser = '0;
542      end else begin
543        last_tuser = 0;
544        foreach (tkeep[i]) begin
545          if (tkeep[i]==1'b1) begin
546            last_tuser = last_tuser+1;
547          end
548        end
549      end
550      // full word is 0
551      if (last_tuser == DATA_WIDTH/8) begin
552        last_tuser = 0;
553      end
554      return last_tuser;
555    endfunction : calc_last_tuser;
556
557    // clear bytes that aren't marked as valid by keep
558    function void  clear_unused_bytes();
559      keep_t last_tkeep;
560      last_tkeep = this.keep[$];
561      // check that the last user is the same as the last keep
562      assert (this.keep[$] == calc_last_tkeep(this.user[$])) else
563        $error("clear_unused_bytes: final tkeep and tuser don't match");
564
565      // set data bytes where the value isn't used to zero
566      foreach (last_tkeep[i]) begin
567        if (last_tkeep[i] == 0) this.data[$][i*8 +: 8] = 0;
568      end
569    endfunction : clear_unused_bytes;
570
571    // take the tuser signal's and use them to set
572    // tkeep signals
573    task automatic tuser_to_tkeep(logic PRESERVE_TUSER=1);
574
575      // set all the tuser values to zero
576      // set all the tuser values to zero
577      foreach (this.user[i]) begin
578        this.keep[i] = '1;
579        if(!PRESERVE_TUSER)
580          this.user[i] = 0;
581      end
582
583      // figure out the value of tkeep for the last word
584      this.keep[$] = calc_last_tkeep(this.user[$]);
585
586      // set data bytes where the value isn't used to zero
587      clear_unused_bytes;
588
589    endtask : tuser_to_tkeep
590
591    // take the tkeep signal's and use them to set
592    // width in the user signals
593    task automatic tkeep_to_tuser(int ERROR_PROB=0);
594
595      // set all the tuser values to zero
596      foreach (this.user[i]) begin
597        this.user[i] = 0;
598        this.user[i][UWIDTH-1] = $urandom_range(99) < ERROR_PROB;
599      end
600
601      // figure out the value of user for the last word
602      this.user[$] = calc_last_tuser(this.keep[$]);
603      this.user[$][UWIDTH-1] = $urandom_range(99) < ERROR_PROB;
604
605      // set data bytes where the value isn't used to zero
606      clear_unused_bytes;
607
608    endtask : tkeep_to_tuser
609
610    function automatic logic has_error();
611      logic error;
612
613      error = 0;
614      foreach (this.user[i]) begin
615        //catch if error was set
616        error = error || this.user[i][UWIDTH-1];
617      end
618      return error;
619    endfunction : has_error
620
621    function automatic int byte_length();
622      int bytes;
623      int last_bytes;
624      bytes = (this.data.size()-1)*DATA_WIDTH/8;
625      last_bytes = this.user[$][UWIDTH-2:0];
626      if (last_bytes == 0)
627        bytes+=DATA_WIDTH/8;
628      else
629        bytes+=last_bytes;
630
631      return bytes;
632
633    endfunction : byte_length
634
635    function automatic void clear_error();
636
637      foreach (this.user[i]) begin
638        this.user[i][UWIDTH-1] = 0;
639      end
640
641    endfunction : clear_error
642
643    function automatic void clear_keep();
644
645      foreach (this.keep[i]) begin
646        this.keep[i] = 0;
647      end
648
649    endfunction : clear_keep
650
651    function automatic void clear_user();
652
653      foreach (this.user[i]) begin
654        this.user[i] = 0;
655      end
656
657    endfunction : clear_user
658
659    task automatic set_error();
660      foreach (this.user[i]) begin
661        this.user[i][UWIDTH-1] = 1;
662      end
663    endtask : set_error
664
665    ///// compare_w_error
666    // Check that this packet has expected error bit in tuser
667    // Keep is not compared
668    // If COMPARE_ERROR_PACKETS is 0
669    //   Don't check packet contents
670    // If COMPARE_ERROR_PACKETS is 1
671    //   Check that this packet matches the expected packet
672    function automatic logic compare_w_error(
673      XportPacket_t expected,
674      int COMPARE_ERROR_PACKETS=1
675    );
676
677      automatic XportPacket_t actual_copy = this.copy();
678      automatic XportPacket_t expected_copy = expected.copy();
679
680      logic exp_error=0;
681      logic act_error=0;
682      logic error_condition;
683
684      exp_error = expected.has_error();
685      act_error = this.has_error();
686
687      actual_copy.clear_error();
688      expected_copy.clear_error();
689      actual_copy.clear_unused_bytes();
690      expected_copy.clear_unused_bytes();
691
692      error_condition = (!expected_copy.equal(actual_copy) &&
693                         (!exp_error || COMPARE_ERROR_PACKETS)) ||
694                         act_error != exp_error;
695      if (error_condition) begin
696        $display("Expected");
697        expected.print();
698        $display("Actual");
699        this.print();
700        if (!expected_copy.equal(actual_copy))
701          $display("ERROR :: packet mismatch");
702        if (act_error != exp_error)
703          $display("ERROR :: error mismatch");
704      end
705
706      return error_condition;
707
708    endfunction : compare_w_error
709
710    ///// compare_w_sof
711    // Check that this packet has expected sof bit in tuser
712    // Keep is not compared
713    // Check that this packet matches the expected packet
714    function automatic logic compare_w_sof(XportPacket_t expected);
715
716      automatic XportPacket_t actual_copy = this.copy();
717      automatic XportPacket_t expected_copy = expected.copy();
718
719      logic sof_error=0;
720      foreach (this.user[i]) begin
721        if (i==0) begin
722          // set if top bit of user isn't set on the first word.
723          sof_error = !this.user[i][UWIDTH-1];
724        end else begin
725          // set if top bit of user is set on any other word.
726          sof_error = this.user[i][UWIDTH-1] || sof_error;
727        end
728      end
729
730      // error bit doubles for SOF
731      actual_copy.clear_error();
732      expected_copy.clear_error();
733      actual_copy.clear_unused_bytes();
734      expected_copy.clear_unused_bytes();
735
736      // set SOF in expected
737      expected_copy.user[0][UWIDTH-1] = 0;
738
739      if (!expected_copy.equal(actual_copy) ||
740          sof_error) begin
741        $display("Expected");
742        expected_copy.print();
743        $display("Actual");
744        this.print();
745        if (!expected_copy.equal(actual_copy))
746          $display("ERROR :: packet mismatch");
747        if (sof_error)
748          $display("ERROR :: sof mismatch");
749      end
750
751      return !expected_copy.equal(actual_copy) ||
752             sof_error;
753
754    endfunction : compare_w_sof
755
756    ///// compare_w_pad
757    // Check that this packet has expected sof bit in tuser
758    // Keep is not compared
759    // Check that this packet matches the expected packet
760    // if DUMB_ORIGINAL_WAY
761    //   User is not compared
762    // else
763    //   Check that this packets tuser matches the expected packet
764    function automatic logic compare_w_pad(
765      XportPacket_t expected,
766      logic DUMB_ORIGINAL_WAY=0
767    );
768
769      automatic XportPacket_t actual_copy = this.copy();
770      automatic XportPacket_t expected_copy = expected.copy();
771
772      // not using MSB as error here.
773      actual_copy.clear_error();
774      expected_copy.clear_error();
775      actual_copy.clear_unused_bytes();
776      expected_copy.clear_unused_bytes();
777
778      // Add pad bytes to user
779      if (DUMB_ORIGINAL_WAY) begin
780        actual_copy.clear_keep();
781        expected_copy.clear_keep();
782        // I can't figure out a goodway to calculate the
783        // expected tuser for non last word values.
784        // So I'm just copying the actual
785        foreach (expected_copy.user[i]) begin
786          if (i != expected_copy.user.size()-1) begin
787            expected_copy.user[i] = actual_copy.user[i];
788          end
789        end
790      end
791
792      if (!expected_copy.equal(actual_copy)) begin
793        $display("Expected");
794        expected_copy.print();
795        $display("Actual");
796        this.print();
797        if (!expected_copy.equal(actual_copy))
798          $display("ERROR :: packet mismatch");
799      end
800
801      return !expected_copy.equal(actual_copy);
802
803    endfunction : compare_w_pad
804
805    ///// compare_no_user
806    // Check that this packet has expected sof bit in tuser
807    // Keep is not compared
808    // Check that this packet matches the expected packet
809    // User is not compared
810    function automatic logic compare_no_user(XportPacket_t expected, int PRINT_LVL=1);
811
812      automatic XportPacket_t actual_copy = this.copy();
813      automatic XportPacket_t expected_copy = expected.copy();
814
815      // not using MSB as error here.
816      actual_copy.clear_error();
817      expected_copy.clear_error();
818      actual_copy.clear_unused_bytes();
819      expected_copy.clear_unused_bytes();
820
821      // Add pad bytes to user
822      foreach (expected_copy.user[i]) begin
823       expected_copy.user[i] = 0;
824       actual_copy.user[i] = 0;
825      end
826
827      if (PRINT_LVL==1) begin
828        if (!expected_copy.equal(actual_copy)) begin
829          $display("Expected");
830          expected.print();
831          $display("Actual");
832          this.print();
833          if (!expected_copy.equal(actual_copy))
834            $display("ERROR :: packet mismatch");
835        end
836      end
837      return !expected_copy.equal(actual_copy);
838
839    endfunction : compare_no_user
840
841  endclass : XportStreamPacket;
842
843endpackage : PkgEthernet
844