how to generate array of interfaces having different connections? - system-verilog

I have declared following interface:
interface data_x #(parameter g_DataWidth = 8)
(input ckrs_t ClkRs_ix);
logic [g_DataWidth-1:0] data;
bit enable;
ckrs_t ClkRs;
always_comb begin
ClkRs = ClkRs_ix;
end
endinterface
The interface has data bus and the data enable, and it is as well associated with the clock and reset signal, which is a typedef ckrs_t.
I have a module, which accepts as an argument array of those interfaces:
module fourmclinks
(...
data_x packet_ox[NUMBER_OF_GBT_LINKS-1:0],
data_x packet_ix[NUMBER_OF_GBT_LINKS-1:0],
...
);
The problem I have is, that I need to declare in top-level entity an array of those data_x interfaces, but each time use different ClkRs_ix input clock. (It is used in the gbts, where each receiver has its own clock and reset signal).
I tried many things, including this one:
ckrs_t txclock_x;
assign txclock_x.clk = GbtTxFrameClk40MHz_k;
assign txclock_x.reset = GbtReset_r;
data_x #(.g_DataWidth(g_FrameSize)) packet_ox[NUMBER_OF_GBT_LINKS-1:0](.ClkRs_ix(txclock_x));
data_x #(.g_DataWidth(g_FrameSize)) packet_ix[NUMBER_OF_GBT_LINKS-1:0]();
genvar linkiface;
generate
for(linkiface=1; linkiface < NUMBER_OF_GBT_LINKS+1; linkiface++) begin : linkgenerator
assign packet_ix[linkiface-1].ClkRs_ix.clk =
GbtRxFrameClk40Mhz_kb4[linkiface];
assign packet_ix[linkiface-1].ClkRs_ix.reset = GbtReset_r;
assign packet_ix[linkiface-1].enable = 0;
assign packet_ix[linkiface-1].data = RxDataAppSfpGbtUserData_4b80[linkiface];
end
endgenerate
Hence making empty/virtual/unassigned/... interface array declaration, and then in generate loop assign correct signals to it. This simulates, but quartus does not compile it claiming
value cannot be assigned to input "ClkRs_ix".
How to correctly generate array of interfaces, each having different input connection? Please help

I'm bit smarter now, so here is the solution to the problem. But first issues:
it is not possible just to remove 'input' direction from the port declaration in the data_x interface declaration above. If this is done, one has to then manually assign clock and reset lines for every instance of the data_x object. This is indeed possible, but one loses all the beauty of having the clock and reset automatically assigned during the instantiation of the interface
it is not possible either in this particular case to make a virtual interface, and connect the signals in the for loop. Root cause of this is the presence of always_comb, which takes in the input reset/clock and assigns it to the internal signals. So this assignment, together with manual assignment of reset and clock in the top-level entity results in driving those signals from two sources, which Quartus will not digest
So the only possible way, which I found is following:
Declare the data_x interface to generate the always_comb on demand:
interface data_x #(
parameter g_hasClock = 1,
parameter g_DataWidth = 8)
(
input ckrs_t ClkRs_ix
);
logic [g_DataWidth-1:0] data;
bit enable;
ckrs_t ClkRs;
generate
if(g_hasClock) begin
always_comb begin
ClkRs = ClkRs_ix;
end
end
endgenerate
endinterface // data_x
Instantiate the interface with unbound ClkRs_ix. Note the usage of g_hasClock, which instantiates the data_x interface without always_comb block, hence Quartus stops complaining about multiple drivers:
data_x #(.g_DataWidth(g_FrameSize),
.g_hasClock(0)) packet_ix[NUMBER_OF_GBT_LINKS-1:0]();
And then generate interface with different clocks:
genvar linkiface;
generate
for(linkiface=1; linkiface < NUMBER_OF_GBT_LINKS+1; linkiface++)
begin : linkgenerator
assign packet_ix[linkiface-1].ClkRs.clk = GbtRxFrameClk40Mhz_kb4[linkiface];
assign packet_ix[linkiface-1].ClkRs.reset = GbtReset_r;
assign packet_ix[linkiface-1].enable = 0;
assign packet_ix[linkiface-1].data = RxDataAppSfpGbtUserData_4b80[linkiface];
end
endgenerate
This works. It is not so nice because we have to do it manually. Just for sake of completeness: if the clocks for all interfaces is the same, all that code above boils down to this snippet:
ckrs_t txclock_x, rxclock_x;
assign txclock_x.clk = GbtTxFrameClk40MHz_k;
assign txclock_x.reset = GbtReset_r;
data_x #(.g_DataWidth(g_FrameSize)) packet_ox[NUMBER_OF_GBT_LINKS-1:0](.ClkRs_ix(txclock_x));
I'm sure this is not the best solution ever, but it is compilable and gives the result needed.

Related

System Verilog: randomization per instance at initial

I want to simulate a multiple latches with random starting conditions, but I want each instance to have its own initial condition
This is a simplified version of the code. I would like the value to be different in both of the instances, without changing the interface
module random_usage();
integer addr1;
real data;
initial begin
addr1 = $urandom();
data = $urandom();
$display("addr1=%0d, data=%0d",addr1,data);
end
endmodule
module tb();
integer seed = 1;
random_usage a();
random_usage b();
initial
begin
#5;
seed = $get_initial_random_seed();
$display("seed=%0d", seed);
end
endmodule
What I have seen so far:
Instance specific $urandom in system-verilog
solution doesn't work in initial condition, or even when you feed the same clock
https://www.systemverilog.io/randomization
I have modules, so i don't know how to apply the solutions, or even if it will work here
https://www.reddit.com/r/FPGA/comments/jd0dmu/system_verilog_force_randomization_different_per/
seems to be the same question, and there is no straight solution, but the last person gave a VCS flag. I am using VCS, but i have not been able to get the flag to work
The IEEE 1800-2017 SystemVerilog LRM section 18.14.1 Random stability properties says rather naively that each instances gets seeded with the same initialization seed.
Most tools now have a switch changing that behavior by using the hierarchical path name to seed each instance. Some tools have even made that the default. If you want tool independent behavior, you can use this package:
package seed_instance;
int initial_seed = $urandom;
function automatic void srandom(string path);
static int hash[int];
int hash_value = initial_seed;
process p = process::self();
for(int i=0;i<path.len();i++)
hash_value+=path[i]*(i*7);
if (!hash.exists(hash_value))
hash[hash_value] = hash_value;
else
hash[hash_value]+=$urandom; // next seed
p.srandom(hash[hash_value]);
endfunction
endpackage
module random_usage();
integer addr1;
real data;
initial begin
seed_instance::srandom($sformatf("%m"));
addr1 = $urandom();
data = $urandom();
$display("1: addr1=%0d, data=%0d",addr1,data);
end
initial begin
seed_instance::srandom($sformatf("%m"));
addr1 = $urandom();
data = $urandom();
$display("2: addr1=%0d, data=%0d",addr1,data);
end
endmodule
module tb();
integer seed = 1;
random_usage a();
random_usage b();
endmodule

Parameterizing the Bit Widths of fields in a packed struct so that modules can infer bit width if used in port map

Also discussed at:
https://verificationacademy.com/forums/systemverilog/parameterizing-bit-widths-fields-packed-struct-so-modules-can-infer-bit-width-if-used-port-map-virtual-interface-interface-compile-time-configured-struct-bit-width
https://forums.xilinx.com/t5/Synthesis/Parameterizing-the-Bit-Widths-of-fields-in-a-packed-struct-so/td-p/1191678
I am having trouble accomplishing my intent in SystemVerilog trying to use the latest language features to make my code more elegant and less verbose. For synthesis**
I would like to accomplish the following:
be able to parameterize the bit widths of fields in a packed struct that I want to define ... I have attempted to accomplish this using a parameterized interface construct
I would like for modules with that parameterized interface as an INPUT to the module to be able to infer the bit width of a field inside that packed struct defined inside the interface
I have been mostly successful in past experiments but I have run into an issue.
Please see the following simple interface definition:
interface MyInterface #(int DATA_W, ADDR_W) () ;
typedef struct packed
{ logic valid
; logic [ADDR_W-1:0] addr
; logic [DATA_W-1:0] data
; } SimpleStruct;
SimpleStruct bus;
logic ready;
modport SNK (input bus, output ready);
modport SRC (output bus, input ready);
endinterface
It is easy enough to instantiate an interface and use it at the input of a simple module in my Top module for this example:
module TopTest
( input wire Clock
, input wire Reset
, input wire [31:0] In
, output wire dummyOut
) ;
MyInterface # ( 32, 3 ) my_interface ();
assign my_interface.bus.data = In ;
assign my_interface.bus.addr = 3'h3 ;
InnerTest inst_mod_inner_test
( .Clock( Clock )
, .Reset( Reset )
, .Sink( my_interface )
) ;
assign dummyOut = my_interface.ready ;
endmodule
The problem that I am running into is that I do not want to parameterize the actual module with field bit widths, because I believe that at compile time the bit widths of the fields should be already established and accessible. This seems to not be the case, and I am wondering if there is anything I can do to accomplish inferring the bit width of the packed struct in the interface (remember that is the case because I want it parameterized, I know it is easy to get $bits of a field of a struct that is not defined in an interface but instead defined in a package or module)
module InnerTest
( input wire Clock
, input wire Reset
, MyInterface.SNK Sink
) ;
localparam BIT_WIDTH_SINK_DATA = $bits( Sink.bus.data ) // this line errors out b/c sink is 'virtual'
RAM # ( .DATA_WIDTH( BIT_WIDTH_SINK_DATA ) ) ram ( ... // etc
... other code to drive output ready of interface ...
endmodule
There are many reasons why a designer would want to make a module "parameterizable" and I have taken that approach in the past, but I am very interested in not duplicating information. If I were to take the easy approach, I would simply parameterize my inner test module so that I provided it DATA_WIDTH, but I would then have two numbers to update and a lot of parameters that I feel I do not need. I think it would be most elegant if I could simply infer characteristics of the parameterized struct somehow. The information I am looking for is truly known at compile time in my opinion. I just can't seem to access it, or this is another shortfall of SystemVerilog.
Follow up Q, in Simulation
The workaround mentioned by Dave was very useful when using QuestaSim, but now running into different issue in QuestaSim:
parameter reference "sink.bus.data" through interface port "sink" is not valid when the actual interface in the instance is an arrayed instance element or below a generate construct
What is the workaround for this, I don't understand why simply being in a generate statement would impact things way downstream. In this case i use a generate statement to choose between different FIFO implementations, a few layers above the line of code where the error happens.
typedef sink.bus.data questasim_workaround;
localparam width = $bits(questasim_workaround);
Follow Up Experiment
I have experimented with passing in type instead of restricting myself to passing in DATA_W.
interface MyInterface #(int ADDR_W, type DATA_TYPE) () ;
typedef struct packed
{ logic valid
; logic [ADDR_W-1:0] addr
; DATA_TYPE data
; } SimpleStruct;
SimpleStruct bus;
logic ready;
modport SNK (input bus, output ready);
modport SRC (output bus, input ready);
endinterface
This allows for more flexibility. I have observed that Vivado Simulator and Synthesis tools can handle an example like this without issue.
module SomeModule
( MyInterface myInt
blah...
);
localparam SOMETHING = $bits(myInt.DATA_TYPE);
// or equivalently
localparam SOMETHING_ELSE = $bits(myInt.data);
// or even this, for needs of a internal signal for pipelined processing steps
MyInterface # ($bits(myInt.addr), myInt.DATA_TYPE) internal_0 () ;
In place of this in QUestaSim we have had to implement Dave's work around:
module SomeModule
( MyInterface myInt
blah...
);
// this gets less elegant :/
typedef myInt.data questasim_workaround_data;
typedef myInt.addr questasim_workaround_addr;
localparam SOMETHING = $bits(questasim_workaround_data);
// or equivalently
localparam SOMETHING_ELSE = $bits(questasim_workaround_data);
// or even this, for needs of a internal signal for pipelined processing steps
MyInterface # ($bits(questasim_workaround_addr), questasim_workaround_data) internal_0 () ;
The current SystemVerilog BNF does not allow any dotted "." names in a parameter initialization. But you can get around this by using a typedef instead
interface MyInterface #(int DATA_W=0, ADDR_W=0) () ;
typedef logic [DATA_W-1:0] data_t;
...
endinterface
module InnerTest
( input wire Clock
, input wire Reset
, MyInterface.SNK Sink
) ;
typedef Sink.data_t data_t;
localparam BIT_WIDTH_SINK_DATA = $bits( data_t );
...
endmodule
Whenever you see an error that is unexpected in Vivado around "[Synth 8-27] scoped/hierarchical type name not supported" ... check to see if the instantiation port map matches all the names of the actual module definitions portmap. That was the issue in Vivado only with this code. The spelling didn't match and instead of a "[Synth 8-448] named port connection 'clkkkk' does not exist" I got a "[Synth 8-27] scoped/hierarchical type name not supported" error
As explained in: https://forums.xilinx.com/t5/Synthesis/Parameterizing-the-Bit-Widths-of-fields-in-a-packed-struct-so/td-p/1191678

What are the common and good usage of pre_randomize() and post_randomize() in systemverilog?

How can I change/add constraint or constraint_mode in pre_randomize()?
I know I can overwrite results in post_randomize and I can call rand_mode on and off in pre_randomize, but I am looking for some additional functionality especially related to constraints.
pre_randomize is generally used to set some pre-conditions before object randomization. Here one can print the results of previous randomization, set some variables on which the constraints are dependent etc.
As you mentioned, pre_randomize can be used to set rand_mode(0) for any variable. It can be used to manipulate constraints as well.
post_randomize is used to manipulate some variables like ECC check, print randomization result, manipulate some non-random fields based on existing randomization etc.
One another usage of post_randomize is to generate 'x' or 'z' in randomization process. The randomization by default generates 0 and 1 known values only. But one can use the existing randomized variable to generate x/z values also.
Here is a dummy example of what we can do in pre_randomize and post_randomize functions. Here, depending on non_rand_var, we can enable/disable the constraint mode and set the rand mode of any variable. In the post_randomize function, one can overwrite 'my_x' variable byt 'x' or 'z'.
class A;
int non_rand_var;
rand int rand_var;
rand int rand_var2;
rand logic my_x;
constraint c1{non_rand_var==1 -> rand_var=='h5;}
function new(int non_rand_var);
this.non_rand_var = non_rand_var; // set non random variable
endfunction
function void pre_randomize();
if(non_rand_var==5) begin // set randomization mode of rand_var2
rand_var2.rand_mode(0);
c1.constraint_mode(0); // disable constraint
end
$display("In pre randomize, non_rand_var=0x%0x rand_var=0x%0x",non_rand_var, rand_var);
endfunction
function void post_randomize();
// my_x = $urandom_range(0,1) ? 0 : 'x;
my_x = (non_rand_var==1) ? 0 : 'x; // Manipulate my_x to generate 'x' values
$display("In post randomize, rand_var=0x%0x",rand_var);
endfunction
endclass
module top();
A a=new(1);
initial begin
a.randomize();
$display("Initial block:\na.my_x = 0x%0x\na.rand_var=0x%0x\na.non_rand_var=0x%0x\na.rand_var2=0x%0x",a.my_x,a.rand_var,a.non_rand_var,a.rand_var2);
end
endmodule
pre_randomize & post_randomize function can have several uses based on the application.
Here is the list of few usage of those functions.
Both functions can be overridden, and hence it can be possible to modify the randomization behavior with extended class
Turn on/off few random variables
Turn on/off few constraints
Assignments to other nonrandom variable, on which the randomization is dependent
Changing the weight of random variables, based on certain conditions
Typical usage of pre_randomization function is to generate an array of unique values.
class helper;
randc bit [7:0] a;
endclass
class original;
bit [7:0] unique[64];
function void pre_randomize();
helper h = new();
foreach (unique[i])
begin
void'(h.randomize());
unique[i] = h.a;
end
endfunction
endclass

Pack individual signal into an array

I have a bunch of signals like this:
logic [7:0] in0;
logic [7:0] in1;
logic [7:0] in2;
logic [7:0] in3;
That I want to assign to an array:
logic [7:0] in_array [4];
assign in_array[0] = in0;
assign in_array[1] = in1;
assign in_array[2] = in2;
assign in_array[3] = in3;
Easy enough, but if instead of 4 items I have 128 this gets annoying. I am sure there is a combination of defines and generates that can do this in a loop. Something like:
`define IN(x) inx
genvar i;
generate
for(i = 0; i<4; i++) begin
assign in_array[i] = `IN(i);
end
endgenerate
The above code doesn't work, but I think that I have done something like this before.
Simplifying that code is something that cannot be done in SystemVerilog. You can reduce you typing by creating a macro like below (note the double backticks ``), but you will still need to manually write each index. Macros are are resolved before generate loops and the input variable to the macro is treated as a literal.
// short named macro for reduced typing
// Note: using short named macro is typically a bad practice,
// but will be removed latter with an undef
`define A(idx) assign array_in[idx] = out``idx
//This works
`A(0);
`A(1);
`A(2);
`A(3);
// doesn't work. For example # gidx==0 will eval to 'assign array_in[0] = outgidx;'.
// There is not outgidx
genvar gidx;
generate
for(gidx=0; gidx<4; gidx++) begin
`A(gidx);
end
endgenerate
`undef A // prevent macro from from being used latter on
If it is just a small number of entries, it is best to do it manually. If it is large number of entries, then you need to consider a way to generate the for you, such as embedded coded.
There are also various embedded code (such as Perl's EP3, Ruby's eRuby/ruby_it, Python's prepro, etc.) that can generate the desired code. Pick your preference. You will need to per-process these files before giving to the compiler. Example with EP3 generating 400 assignments:
#perl_begin
foreach my $idx (0..400) {
printf "assign array_in[%0d] = out%0d;", $idx, $idx;
}
#perl_end
Use `` to separate text from argument.
`define IN(x) in``x
But there is another issue with the variable i not being declared at the time when the macro is evaluated. Thus the whole generate loop just connects to ini, because i is just another letter. Because of this macros cannot be assigned by dynamically allocated values.
The environment of your module already has to connect explicitly to each input assign in0 = out0; ... assign in127 = out127. So the simplest solution would be to have in_array as your modules input and let the environment connect to it assign array_in[0] = out0.
Something like this:
module parent_module();
/*some other stuff that has outputs out0, out1 etc.*/
logic [7:0] array_in[4];
assign array_in[0] = out0;
assign array_in[1] = out1;
assign array_in[2] = out2;
assign array_in[3] = out3;
my_module(.array_in(array_in));
endmodule

How do you join several input wires into one wire in systemverilog (all wires except one hi-Z)

Suppose that all input wires except one is supposed to be in Hi-Z. We want to connect these wires into a single wire (e.g. data bus). How can this be done in SystemVerilog?
If you mean module inputs, the alias construct can do this:
module a(input wire a,b,c);
wire bus; // use if you want a different internal name
alias a = b = c = bus;
endmodule
I'm sure this will not be synthesizable.
Assuming your condition that only one input is not Z holds true then we could just loop through and find the last non-Z input and assign its value to the output. No idea if this would synthesize.
module merge_bus
#(parameter BUSW = 8)
(
input [BUSW-1:0] bus_in,
output wire_out
);
always_comb begin
wire_out = 1'bz;
for(int i = 0; i<BUSW; i++) begin
if(bus_in[i] !== 1'bz) wire_out = bus_in[i];
end
end
endmodule
You can make use of wor/wand data types from verilog.