Search code examples
verilogsystem-veriloghdl

Parameterizing a module based on an interface (SystemVerilog)


I have a highly hierarchical design in SystemVerilog (synthesized using Xilinx Vivado). I use parametrized interfaces and modules. Some data types inside the interfaces are calculated using (interface internal) functions, based on their parameters. I want to be able to access information(specifically, bit width) of those types inside the modules that are using those interfaces. It seems that I can instantiate the type from the interfaces inside a module, but can not use the bit width as a constant.

That is, given the following:

interface iface #(PARAM=1);
    const int i = PARAM; //Gives a warning about simultaneous procedural and continuous assignment
    typedef logic [PARAM-1:0] t;
    t s;
endinterface

module test(iface i);
    i.t s; //Works
    const int size1 = $bits(i.s); //Works
    const int size2 = $bits(s); //Works
    localparam int p = $bits(i.s); //Fails: Heirarchial name access not allowed

    wire [size1-1:0] s1; //Fails: "size1" is not constant
    wire [size2-1:0] s2; //Fails: "size2" is not constant
    wire [i.i-1:0] s3; //Fails: "i" is not constant
    wire [p-1:0] s3; //Would have worked, is "p" was defined

    localparam int p2 = i.i; //Fails: "i" is not constant
    localparam int p3 = i.PARAM; //Fails: Heirarchial name access not allowed
    //Any of the above two lines would solve all my problems
endmodule

I tried several solutions, including using packages. But in that case there seems to be no way to initialize the package parameters from the top-level parameters.

I read the SystemVerilog LRM regarding both interfaces and packages and could not find any solution in there.

Any solution (short of calculating the derived parameters outside the interface and passing them down the heirarchy) or a pointer in the right direction would be highly appreciated.


Solution

  • There is a confusing part in System Verilog. const int and others are not constants in the verilog sense. They are just const variables. Const keyword is just a contract with the run-time system not to modify them at run time.

    "Real" constants are parameters and localparams. Those are compile time (elaboration time) constants. Only they could be used in width declarations. So a bunch of issues in your case are expected.

    The second point is that only instances of variables and functions are supposed to be accessible by cross-module references. The typedef is not a such thing, so you cannot reference it this way. Potentially you are supposed to be able to access parameters as well and define your typdef inside the module:

    module test(iface i);
     typedef logic [i.PARAM - 1: 0] t;
     t s;
    

    The above has only one problem: it does not work with all compilers. It works with vcs but does not with nc.

    If you want to be generic, I suggest that you paremeterize the module and the interface with the same value.

    module test#(PARAM = 1) (iface i);
      typedef logic [PARAM - 1: 0] t;
      t s;
    
      const int size1 = $bits(i.s); //Works
      const int size2 = $bits(s); //Works
      localparam int p = $bits(i.s); //Fails: Heirarchial name access not allowed
    
      wire [p-1:0] s4; //Would have worked, is "p" was defined
    
      localparam int p3 = PARAM; 
    endmodule
    
    module top;
      localparam P8 = 8; // could be from a package
      iface #(.PARAM(P8)) i8();
      test #(.PARAM(P8)) test(i8);
    endmodule 
    

    Now, this is alwo a cadence issue localparam int p = $bits(i.s); It works with synopsys. So, just do not use it. you can still say local int p = PARAM; instead,