`ifndef __ETH_PACKET_SVH
`define __ETH_PACKET_SVH

`include "simdrv_defs.svh"

typedef byte mac_addr_t[6];
typedef bit[11:0] vid_t;
typedef bit[2:0] pcp_t;

typedef enum
  {
   NONE = 0,
   TX_FID,
   RX_TIMESTAMP
   } oob_type_t;

typedef struct {
   bit [27:0] ts_r;
   bit [3:0] ts_f;
   bit [5:0] port_id;
   bit [15:0] frame_id;
} wr_timestamp_t;

class EthPacket;

   static const mac_addr_t dummy_mac  = '{0,0,0,0,0,0}   ;

   static  int _zero             = 0;
   
   static const int CMP_OOB                  = 1;
   static const int CMP_STATUS               = 2;
   
   byte payload[];
   int size;

   mac_addr_t src, dst;
   oob_type_t oob_type;
   
   bit is_q;
   bit is_hp;  
   bit has_smac;
   bit has_crc;
   bit error;

   bit [15:0] ethertype;
   bit [7:0] pclass;
   
   vid_t vid;
   pcp_t pcp;
   
   oob_type_t oob;
   wr_timestamp_t ts;
   

   task set_size(int size);
      payload  = new[size](payload);
   endtask
   
   function new(int size = _zero);
//       size      = 0;
      src       = dummy_mac;
      dst       = dummy_mac;
      is_q      = 0;
      is_hp     = 0;
      has_crc   = 0;
      oob_type  = NONE;
      payload   = new[size](payload);
      
   endfunction // new

   task deserialize(byte data[]);
      int i, hsize, psize;

      if(data.size < 14)
        begin
           error         = 1;
           return;
           end
      
      for(i=0; i<6;i++)
        begin
           dst[i]     = data[i];
           src[i]     = data [i+6];
        end

      has_crc         = 0;
      if(data[12] == 'h81 && data[13] == 'h00)
        begin
           is_q       = 1;
           hsize      = 18;
           ethertype  = {data[16], data[17]};
           vid        = ((int'(data[14]) << 8) | data[15]) & 12'hfff;
           pcp        = data[14] >> 5;
        end else begin
           is_q       = 0;
           hsize      = 14;
           ethertype  = {data[12], data[13]};
        end

      psize           = data.size() - hsize;

      if(psize <= 0)
      begin
         error    = 1;
         return;
      end
      
      payload       = new[psize];

      for(i=0;i<data.size() - hsize;i++)
        payload[i]  = data[hsize + i];

			size = data.size;

//      error           = 0;
   endtask
  
   task automatic serialize(ref byte data[]); 
      int i, hsize;

      hsize              = is_q ? 18 : 14;
      data               = new[payload.size() + hsize](data);
      
      for(i=0; i<6;i++)
        begin
           data[i]       = dst[i];
           data[i + 6]   = src[i];
        end

      
      if(is_q)
        begin
           data [12]     = 8'h81;
           data [13]     = 8'h00;
           data [14]     = {pcp, 1'b0, vid[11:8]};
           data [15]     = vid[7:0];
           data [16]     = ethertype[15:8];
           data [17]     = ethertype[7:0];
        end else begin
           data[12]      = ethertype [15:8];
           data[13]      = ethertype [7:0];
        end
      
      for (i=0; i<payload.size(); i++)
        data[i + hsize]  = payload[i];
   endtask // serialize

   function bit equal(ref EthPacket b, input int flags = 0);
      
      if(src != b.src || dst != b.dst || ethertype != b.ethertype)
        begin
           $display("notequal: hdr");
           return 0;
        end
      
      if(is_q ^ b.is_q)
        begin
           $display("notequal: q");
           return 0;
        end

      if(is_q && (vid != b.vid || pcp != b.pcp))
        return 0;

      if(payload != b.payload)
        begin
           $display("notequal: payload");
           return 0;
        end
//        return 0;

      
      if(flags & CMP_STATUS)
        if(error ^ b.error)
          return 0;

      if(flags & CMP_OOB) begin
         if (b.oob_type != oob_type)
          return 0;

         if(oob_type == TX_FID && (b.ts.frame_id != ts.frame_id))
           return 0;

         if(oob_type == RX_TIMESTAMP && (b.ts.ts_r != ts.ts_r) && (b.ts.ts_f !=
           ts.ts_f))
           return 0;
      end 
      
      return 1;
   endfunction // equal
   

   task copy(ref EthPacket b);

      endtask // copy

   task hexdump(byte buffer []);
      string str;
      int size           ;
      int i;
      int offset         = 0;
      const int per_row  = 16;

      size               = buffer.size();

      
      while(size > 0)
        begin
           int n;
           n       = (size > per_row ? per_row : size);
           $sformat(str,"+%03x: ", offset);
           for(i=0;i<n;i++) $sformat(str,"%s%s%02x", str, (i==(per_row/2)?"-":" "), buffer[offset + i]);
           $display(str);

           offset  = offset + n;
           size    = size - n;
      end
      
      
   endtask // hexdump
   
   
   task dump(int full = _zero);
      string str, tmp;
      int t;
      
      if(is_q)
        $sformat(str, "802.1q [VID %5d/PCP %d] ", vid, pcp);
      else
        str  = "802.1                    ";

      $sformat(str, "%s DST [%02x:%02x:%02x:%02x:%02x:%02x] SRC: [%02x:%02x:%02x:%02x:%02x:%02x] Type = 0x%04x size = %d F:(%s%s)", str, dst[0],dst[1],dst[2],dst[3],dst[4],dst[5], src[0],src[1],src[2],src[3],src[4], src[5], ethertype, (is_q ? 18 : 14) + payload.size(), 
is_hp ? "H" : " ", has_crc ? "C" : " ");

      if(oob_type == TX_FID)
        begin
           $sformat(tmp, "TxOOB: %x", ts.frame_id);
           str  = {str, tmp};
        end
      if(oob_type == RX_TIMESTAMP) begin
        $sformat(tmp, "RxOOB: %x, %x", ts.ts_f, ts.ts_r);
        str  = {str, tmp};
      end

      $display(str);
      hexdump(payload);
   endtask // dump
   
      
endclass // EthPacket


class EthPacketGenerator;

   protected EthPacket template;
   protected int min_size, max_size;
   protected int seed;
   
   static const int SMAC              = (1<<0);
   static const int      DMAC         = (1<<1);
   static const int      ETHERTYPE    = (1<<2);
   static const int      VID          = (1<<3);
   static const int      PCP          = (1<<4); 
   static const int      PAYLOAD      = (1<<5);
   static const int      SEQ_PAYLOAD  = (1<<7);
   static const int      SEQ_ID  = (1<<10);
   static const int TX_OOB             = (1<<6);
   static const int EVEN_LENGTH        = (1<<8);
   static const int RX_OOB             = (1<<9);
   static const int      ALL          = SMAC | DMAC | VID | ETHERTYPE | PCP | PAYLOAD ;
   

   protected int r_flags;
   protected int m_current_frame_id;
   protected int current_ts;
   protected int cur_seq_id;

   function new();
      r_flags             =ALL;
      min_size            = 64;
      max_size            = 128;
      m_current_frame_id  = 0;
      current_ts          = 0;
      template            = new;
      cur_seq_id = 0;
      
   endfunction // new
   
   task set_randomization(int flags);
      r_flags  = flags;
   endtask // randomize


   typedef byte dyn_array[];
   
   
   protected function dyn_array random_bvec(int size);
      byte v[];
      int i;
//      $display("RandomBVEC %d", size);
      
      v       = new[size](v);
      for(i=0;i<size;i++)
        v[i]  = $dist_uniform(seed, 0, 256);
   
      return v;
      
   endfunction // random_bvec

   task set_seed(int seed_);
      seed = seed_;
   endtask // set_seed

   function int get_seed();
      return seed;
   endfunction // get_seed
   
      
      
   
   protected function dyn_array seq_payload(int size);
      byte v[];
      int i;
      
      v       = new[size](v);
      for(i=0;i<size;i++)
        v[i]  = i;
   
      return v;
      
   endfunction // random_bvec
   
   
	 function automatic EthPacket gen(int set_len = 0);
		 EthPacket pkt;
		 int len;

		 pkt = new;

		 if (r_flags & SMAC) pkt.src = random_bvec(6); else pkt.src = template.src;
		 if (r_flags & DMAC) pkt.dst = random_bvec(6); else pkt.dst =  template.dst;

		 pkt.ethertype = (r_flags & ETHERTYPE ? $dist_uniform(seed, 0, 1<<16) : template.ethertype);
		 pkt.is_q      = template.is_q;
		 pkt.vid       = template.vid;
		 pkt.pcp       = template.pcp;
		 pkt.has_smac  = template.has_smac;

		 if(set_len > 0) len = set_len;
		 else len = $dist_uniform(seed, min_size, max_size);

		 if((len & 1) && (r_flags & EVEN_LENGTH))
			 len++;

		 if(r_flags & PAYLOAD) pkt.payload           = random_bvec(len);
		 else if(r_flags & SEQ_PAYLOAD) pkt.payload  = seq_payload(len);
		 else pkt.payload                            = template.payload;

		 if(r_flags & SEQ_ID)
		 begin
		   pkt.payload[0] = cur_seq_id & 'hff;
		   pkt.payload[1] = (cur_seq_id>>8) & 'hff;
		   pkt.payload[2] = (cur_seq_id>>16) & 'hff;
		   pkt.payload[3] = (cur_seq_id>>24) & 'hff;
		   cur_seq_id++;
		 end

		 if(r_flags & TX_OOB)
		 begin
		   pkt.ts.frame_id                        = m_current_frame_id++;
		   pkt.oob_type                           = TX_FID;
		 end

     if(r_flags & RX_OOB) begin
       pkt.oob_type    = RX_TIMESTAMP;
       pkt.ts.port_id  = 0;
       pkt.ts.ts_r = ++current_ts;
       pkt.ts.ts_f = current_ts;
     end

		 pkt.size = len + 14; //payload + header
		 return pkt;

	 endfunction
   
   task set_template(EthPacket pkt);
      template  = pkt;
   endtask // set_template
                
   task set_size(int smin, int smax);
      min_size  = smin;
      max_size  = smax;
   endtask // set_size

   

endclass // EthPacketGenerator


virtual class EthPacketSink;

   static int _null  = 0;
   
   pure virtual function int poll();
   virtual function int permanent_stall_enable(); endfunction
   virtual function int permanent_stall_disable(); endfunction
   pure virtual task recv(ref EthPacket pkt, ref int result = _null);      
   
endclass // EthPacketSink

virtual class EthPacketSource;
   static int _null  = 0;

   pure virtual task send(ref EthPacket pkt, ref int result = _null);      
endclass // PacketSource


  

`endif
  
