Reputation: 348
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
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
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 parameter
s and localparam
s. 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