Equilibrius
Equilibrius

Reputation: 348

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.

Upvotes: 2

Views: 2175

Answers (2)

Justin N
Justin N

Reputation: 911

Your difficulties may be related more to bugs in Vivado than any issues with your code. Passing parameters via interfaces is one of the many buggy aspects of Vivado. Each version of Vivado has different bugs that occur randomly, ranging from nonsensical error messages, to incorrect elaboration, to crashes. On top of all that, synthesis and simulation both have different bugs and code that works in one will probably not work in the other.

If you're willing to deal with all that, here's an example that should work in recent versions of Vivado synthesis. I tested this with version 2019.1.

interface iface #(PARAM=1);
    logic [PARAM-1:0] t = 0;
endinterface

module test(iface i);
    wire [i.PARAM-1:0] var1;
    initial if ($bits(var1) != 42) $error("err1");

    localparam int PARAM2 = i.PARAM + 1;
    wire [PARAM2-1:0] var2;
    initial if ($bits(var2) != 43) $error("err2");
endmodule

module tb;
    iface #(42) my_iface ();
    test my_test (my_iface);
endmodule

Note that this will probably not work in the Vivado simulator. And watch out, because some bugs only appear once you've passed an interface down multiple levels of hierarchy.

Upvotes: 1

Serge
Serge

Reputation: 12354

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,

Upvotes: 1

Related Questions