Parameterizing a Class With run-time variable?

Hello !

I am trying to form a pkt [whose size can be variable] which has 4 fields [whose size is also variable]. When i try to create a parameterized class with run time variable I face the below issue [which make sense/standard].


module pkt_stim;
   class req_pkt #(int f1_size = 16, int f2_size = 16, int f3_size = 16, int f4_size = 16);
      rand bit [f1_size-1:0] f1;
      rand bit [f2_size-1:0] f2;
      rand bit [f3_size-1:0] f3;
      rand bit [f4_size-1:0] f4;
   endclass: req_pkt


   class tran_pkt #(int pkt_size = 64);
      rand bit [pkt_size-1:0] pkt;
      rand bit [31:0] A,B,C,D;  

      constraint fld_size_c {
         A <= pkt_size;
         B <= pkt_size;
         C <= pkt_size;
         D <= pkt_size;
         A + B + C + D == pkt_size;
      }
       
      function void post_randomize;
        req_pkt #(.f1_size(A), .f2_size(B), .f3_size(C), .f4_size(D))  rp = new();
        assert (rp.randomize());           
        pkt = {rp.f4, rp.f3, rp.f2, rp.f1};
      endfunction: post_randomize

      function print_inf;
       $display("Size A = %0d, B = %0d, C = %0d, D = %0d\n", A, B, C, D);
       $display("f4 = %0h, f3 = %0h, f2 = %0h, f1 = %0h\n", rp.f4, rp.f3, rp.f2, rp.f1);
       $display("pkt Value = %0h\n", pkt);
      endfunction: print_inf
   endclass: tran_pkt


   initial begin
      tran_pkt tp = new();
      assert(tp.randomize());
      tp.print_inf();
   end

endmodule: pkt_stim



Error:
Error-[NCE] Non-constant expression
  The following expression should be a constant.
  Expression: this.A
  "pkt_stim.sv", 19
  Source info:         req_pkt #(.f1_size(A), .f2_size(B), .f3_size(C), 
  .f4_size(D))  rp = new();
  'this' is not an elaboration-time constant. To correct the error you may 
  convert this const variable to a parameter or a localparam.


Error-[NCE] Non-constant expression
  The following expression should be a constant.
  Expression: this.B
  "pkt_stim.sv", 19
  Source info:         req_pkt #(.f1_size(A), .f2_size(B), .f3_size(C), 
  .f4_size(D))  rp = new();
  'this' is not an elaboration-time constant. To correct the error you may 
  convert this const variable to a parameter or a localparam.


Error-[NCE] Non-constant expression
  The following expression should be a constant.
  Expression: this.C
  "pkt_stim.sv", 19
  Source info:         req_pkt #(.f1_size(A), .f2_size(B), .f3_size(C), 
  .f4_size(D))  rp = new();
  'this' is not an elaboration-time constant. To correct the error you may 
  convert this const variable to a parameter or a localparam.


Error-[NCE] Non-constant expression
  The following expression should be a constant.
  Expression: this.D
  "pkt_stim.sv", 19
  Source info:         req_pkt #(.f1_size(A), .f2_size(B), .f3_size(C), 
  .f4_size(D))  rp = new();
  'this' is not an elaboration-time constant. To correct the error you may 
  convert this const variable to a parameter or a localparam.

How can I overcome this scenario or any other better way of packetizing variable size pkt with variable size fields ?

In reply to desperadorocks:

Two approaches people take depending on what you plan to do with these fields

  1. Use dynamic unpacked arrays of bit or byte. Use the streaming operators to pack into your packet.
  2. Use fixed size fields with a maximum width. That maximum could be parameterized by the packet size. Then declare field width variables that can be randomized and then used to help mask your field values, and manual packing of fields.

In reply to dave_59:

Hello Dave !

Thanks for your inputs. Does the below code make sense as you suggested in your 1st way ? Is there any other better way ? bcoz if its random variable for the dynamic array then its fine but if we need to load up some specific values using constraints then its going to be cumbersome. Kindly provide your comments.


module pkt_stim;
   class tran_pkt #(int pkt_size = 64);
      rand bit [pkt_size-1:0] pkt;
      rand bit A [];
      rand bit B [];
      rand bit C [];
      rand bit D [];
      
      constraint fld_size_c {
         A.size <= pkt_size;
         B.size <= pkt_size;
         C.size <= pkt_size;
         D.size <= pkt_size;
         A.size + B.size + C.size + D.size == pkt_size;
      }
       
      function void post_randomize;
        $display("Size A = %0d, B = %0d, C = %0d, D = %0d\n", A.size, B.size, C.size, D.size);
        $display("pkt Value b4 streaming = %0h\n", pkt);
        pkt = {>>{D,C,B,A}};
        $display("pkt Value a8 streaming = %0h\n", pkt);
      endfunction: post_randomize

      function void print_inf;
       $display("pkt_size = %0d\n", pkt_size);
       $display("Size A = %0d, B = %0d, C = %0d, D = %0d\n", A.size, B.size, C.size, D.size);
       foreach (A[i]) $display("A[%0d] %d\n", i, A[i]);
       foreach (B[i]) $display("B[%0d] %d\n", i, B[i]);
       foreach (C[i]) $display("C[%0d] %d\n", i, C[i]);
       foreach (D[i]) $display("D[%0d] %d\n", i, D[i]);
       $display("pkt Value = %0h\n", pkt);
      endfunction: print_inf
   endclass: tran_pkt


   initial begin
      tran_pkt tp = new();
      assert(tp.randomize());
      tp.print_inf();
   end

endmodule: pkt_stim

In reply to desperadorocks:

Difficult to say without showing what you plan to do with these fields.

In reply to dave_59:

Hello Dave !

a. Main plan is to generate pkt of data [variable size] with variable size fields.
b. And feed into the DUT with a bus width of 16 bits - so 4 times for 64bit data.

So was checking way to get pkt of such configuration.

In reply to desperadorocks:

If all you are going to be doing is packing these fields in another variable, your solution should be fine. When you need to do arithmetic on these fields, that’s when using fixed sizes is easier.