Using a struct as a static config for a parameterized class

Since the Parameter lists of my classes grow and grow, I would like to replace this list by a struct or class Definition like this:


 // collection of all class parameters 
 class abc_config; 
     localparam int ADDRWIDTH = 10; 
     localparam int DATAWIDTH = 8; 
 endclass 

 // class depending on a collection of parameters 
 class abc#(type CONF = int) extends abc_base#(CONF::ADDRWIDTH); 
     bit [CONF::DATAWIDTH-1:0] d; 

     covergroup abc_cov; 
         Option.per_instance = 0; 
     endgroup 

 endclass 

 program 
     abc#(abc_config) a; 

It seems, that my Simulator has issues with such an Approach.

One issue is, that it does not like the usage of CONF::ADDRWIDTH in the Parameter list of the base class. I do not get that compiled.

The other issue is a bit more hidden. I wanted to use this construction in the context of coverage. And my Intention was to have one covergroup for all instantiations of abc (per_instance = 0). But my Simulator did not honor this per_instance Setting and always gave me a individual covergroup per instance. After removing the configuration class from the Parameter list, the per_instance Setting works fine.

My questions now are:

  • Is this a specific Problem of my Simulator or is it a Basic SystemVerilog Limitation which causes this Approach not to work as intended?

  • Is there another elegant implementation of this concept?

It is kind of strange that the compiler complains, since CONF::ADDRWIDTH should only be “dereferenced” once an actual specialization of your abc class was declared. At that point, CONF would contain a field called ADDRWIDTH.

Maybe you’d be better off using a struct parameter:


typedef struct {
  int unsigned addr_width;
  int unsigned data_width;
} config_t;

I’d guess you have a mixture of classes, some that only take on integral parameter and some that need more of them bundled up:


class some_base #(int width);
  bit [width-1:0] some_field;
endclass


class some_class #(config_t cfg) extends some_base #(cfg.addr_width);
  bit [cfg.data_width-1:0] some_other_field;
endclass

You could create different parameterizations of these classes by having two parameters of type config_t:


module top;
  parameter config_t cfg = '{ 32, 64 };
  parameter config_t other_cfg = '{ 16, 32 };
  
  some_class #(cfg) obj1 = new();
  some_class #(other_cfg) obj2 = new();
  
  initial begin
    $display("obj1.some_field = %x", obj1.some_field);
    $display("obj1.some_other_field = %x", obj1.some_other_field);
    
    $display("obj2.some_field = %x", obj2.some_field);
    $display("obj2.some_other_field = %x", obj2.some_other_field);
  end
endmodule

The $display(…) calls are there so that you can see the widths of each field in the log file (count the number of 0s). You can find the full example at http://www.edaplayground.com/x/KX3

The other thing you describe with per_instance not being honored is most likely a tool bug.

Thanks so far, this is exactly what was needed! I Extended your Version a bit to be more General, i.e. the base class has a different Parameter struct:

 
// Code your testbench here
// or browse Examples

typedef struct {
  int unsigned addr_width;
  int unsigned data_width;
} config_t;

typedef struct {
  int unsigned data_width;
} config_base_t;


class some_base #(config_base_t cfg);
  bit [cfg.data_width-1:0] some_field;
endclass


class some_class #(config_t cfg) extends some_base #('{cfg.data_width});
  bit [cfg.data_width-1:0] some_other_field;
endclass


module top;
  parameter config_t cfg = '{ 32, 64 };
  parameter config_t other_cfg = '{ 16, 32 };
  
  some_class #(cfg) obj1 = new();
  some_class #(other_cfg) obj2 = new();
  
  initial begin
    $display("obj1.some_field = %x", obj1.some_field);
    $display("obj1.some_other_field = %x", obj1.some_other_field);
    
    $display("obj2.some_field = %x", obj2.some_field);
    $display("obj2.some_other_field = %x", obj2.some_other_field);
  end
endmodule


I tried this on EDAplayground with VCS and it worked fine. Riviera gave an internal error. BTW: Do you know what happened to Questasim on EDAplayground?

One more Thing: as an Extension to this pattern is it possible to include type definitions as well somehow, so that I can use such definitions for type Parameters?

Kind of:


typedef struct#(type T = int) {
  int unsigned addr_width;
  int unsigned data_width;
} config_t;
...
class some_base #(type T = int);
  T some_field;
endclass
...
class some_class #(config_t cfg) extends some_base #(cfg::T);
  bit [cfg.data_width-1:0] some_other_field;
endclass

parameter config_t#(real) cfg = '{ 32, 64 };
...
some_class #(cfg) obj1 = new();

I know that this does not work like that, but it is supposed to give you an idea of what I mean.

In reply to jknaeblein:

I don’t think you can. The only thing you can parameterize like that is a class, but then I don’t think you can use the class as a parameter further down. You could try it out, but on EDAPlayground it doesn’t work (feature not implemented). Maybe you’re luckier with your simulator.