Systemverilog: Is it possible to treat a macro like an array that can be indexed? - macros

In SystemVerilog, is it possible to index a macro for a long hierarchical reference? i.e.
`define CONDENSED top.DUT.mod.sub_module.register_map
then do something like:
`CONDENSED.reg1[0]

A macro is just simple text substitution. The text pre-processor doing the substitition knows nothing about SystemVerilog syntax other than what a token is (a string, a numeric literal, an identifier, a comment). You can use any macro as long as the resulting text is legal SystemVerilog text. (and mind the rules about splitting text that makes up a token).

You can even include indices in your macro:
`define MY_SELECTION(index0, index1) c0_a[index0].c1_a[index1].a
class c1;
int a;
endclass
class c0;
c1 c1_a[10];
function new();
foreach(c1_a[idx]) begin
c1_a[idx] = new();
end
endfunction
endclass
module top;
initial begin
automatic c0 c0_a[10];
foreach(c0_a[idx]) begin
c0_a[idx] = new();
end
`MY_SELECTION(5, 6) = 8;
$display("my_value: %0d", `MY_SELECTION(5, 6));
`MY_SELECTION(5, 6)[0] = 1;
$display("my_value: %0d", `MY_SELECTION(5, 6));
end
endmodule
The output of this code is this:
my_value: 8
my_value: 9
You can run this example on EDA Playground - https://www.edaplayground.com/x/4EZZ

Related

How to slicing array interface in system verliog

I try to use array interface mapping through always_comb procedure instead of generate a statement.
You can see my test codes is in below(https://www.edaplayground.com/x/5cLt)
interface tintf;
bit valid;
bit data;
bit stall;
endinterface: tintf
module top;
tintf intf_a[0:3]();
bit valid, data, stall;
always_comb begin
loop_for_mapping:
for(int i = 0; i < 4; i++) begin
intf_a[i].valid = valid;
intf_a[i].data = data;
end
end
endmodule
But I see the error message below.
intf_a[i].valid = valid;
| ncelab: *E,NOTPAR (./testbench.sv,18|13): Illegal operand for constant expression [4(IEEE)].
I don't know that is why illegal...As I know 'i' in for-loop is considered constant.
Would you let me know what I missing??
It may be true for synthesis the for loop gets unrolled into a constant set of iterations, but it's not a constant from a language point of view. Simulation tools don't know which portions of your code you plan to synthesize. You need to write this as a generate-for loop.
for(genvar i = 0; i < 4; i++) begin : loop_for_mapping
always_comb begin
intf_a[i].valid = valid;
intf_a[i].data = data;
end
end
instantiating an interface (or a module) as an array (arrayed instance) is a part of the generate feature of verilog. As a result, you can only access those by using literal constants (0,1,2...) or from other generate blocks (see dave_59's answer).
It can be more flexible if you make arrays of the interface variables instead:
interface tintf;
bit [3:0] valid;
bit [3:0] data;
bit [3:0] stall;
endinterface: tintf
module top;
tintf intf_a();
bit valid, data, stall;
always_comb begin
loop_for_mapping:
for(int i = 0; i < 4; i++) begin
intf_a.valid[i] = valid;
intf_a.data[i]= data;
end
end
endmodule

Way to have a function like urandom_range(); which will return unique values?

I want to have a urandom_range(); which will not repeat a value once its picked in a simulation ? If it has exhausted its supply of 'available' numbers, then perhaps it can repeat .
Is there any keyword in systemverilog which will help quickly to get around this ?
Not a SV expert here so an example would really help! Thanks
randc does exactly this. (cyclic randomization)
class A;
randc bit[7:0] m;
endclass
Each time you call randomize() on the same object, it will not repeat value for m until all possible values have been given.
Simulators have limits on how large the cyclic value can be, but the standard requires a minimum of 8-bits. If you have a larger value, then you can use the inside operator.
class A;
rand bit[23:0] r;
bit [23:0] list[$];
constraint c { !(r inside {list}); }
function void post_randomize();
list.push_back(r);
endfunction
endclass
If you really expect to cycle through the list, it might be simpler to build the list first, and then shuffle through the list.
bit [7:0] list[20];
for(int i=0;i<20;i++) list[i] = i+10; // range 10-29
list.shuffle();
// cycle through list[0] ... list[29]
list.shuffle();
// cycle through list[0] ... list[29]
You can declare a variable with randc identifier. This is called 'cyclical random' and will ensure exactly what you are requiring.
Note: This requires a license that supports randomization and random variables. Most commercial simulators do provide this but at a higher cost. If you are constrained by this and need to only use the system calls - $urandom or $urandom_range, I would implement something like a queue that tracks all the values returned.
function automatic void find_unique_num();
int c;
int vals[$];
bit found;
do begin
c = $urandom_range(10, 1);
foreach(vals[i])
if (c == vals[i]) found = 1;
end
while (!found);
vals.push_back(c);
return c
endfunction

Is there a way to cast an SystemVerilog assignment pattern into a packed struct?

Per the System Verilog LRM "Assignment pattern format", a data structure can be printed into a string as follows:
module top;
typedef enum {ON, OFF} switch_e;
typedef struct {switch_e sw; string s;} pair_t;
pair_t va[int] = '{10:'{OFF, "switch10"}, 20:'{ON, "switch20"}};
initial begin
$display("va[int] = %p;",va);
$display("va[int] = %0p;",va);
$display("va[10].s = %p;", va[10].s);
end
endmodule : top
This example may print:
va[int] = '{10:'{sw:OFF, s:"switch10"}, 20:'{sw:ON, s:"switch20"}} ;
va[int] = '{10:'{OFF, "switch10"}, 20:'{ON, "switch20"}} ;
va[10].s = "switch10";
Is there a way to do the reverse? What I'd like to do is to take an assignment pattern string as a plusarg or a line read from a file, and assign that to a variable at run time, e.g.:
string assign_pattern = "'{10:'{sw:OFF, s:"switch10"}, 20:'{sw:ON, s:"switch20"}}";
$cast(va, assign_pattern); // ** This doesn't work **
If not generally possible, is there a way to do that specifically for packed struct types?
You can't do the reverse. SystemVerilog was designed as a compiled languageā€”there's no parser available at run-time. You would have to create a parser in SystemVerilog or C smart enough to decode the assignment patterns you expect to read in.
Another option is converting the file of assignment patterns into code that could be compiled in with the rest of your code.
Another option based on your comments
You can use a bit-stream or streaming operator to parse a bit-string into a struct. The struct does not need to be packed, it just needs to be made up from fixed-sized, integral values.
module top;
typedef enum bit [1:0] {ON, OFF, INBETWEEN} switch_e;
typedef struct {switch_e sw; bit [8*8:1] s; bit [5:0] value;} trio_s; // 72 bits
typedef bit [71:0] uint72_t;
trio_s v,x;
uint72_t l;
initial begin
x = '{sw:OFF, s:"switch10", value:'h0a};
l = uint72_t'(x);
$displayh(l);
v = trio_s'(l);
$displayh("v = %p",v);
$display("v.s = %s",v.s);
end
endmodule
This displays
# 5cddda5d18da0c4c0a
# v = '{sw:OFF, s:8320234785195176240, value:10}
# v.s = switch10
pair_t va[int] = '{10:'{OFF, "switch10"}, 20:'{ON, "switch20"}};
is the same as
pair_t va[int]
initial begin
va[10].sw = OFF;
va[10].s = "switch10";
..
Saying that, you can write your own parser of a +arg string (or strings) which will assign values to the array fields in a task. This is the only possibility. For exmple:
string indx = "1";
string sw = "off";
initial begin
int i = indx.atoi();
va[i].sw = sw == "off" ? OFF : ON;
...

How to pass signal name through $value$plusargs in system verilog

I am writing assertions in system verilog. This assertion check for a signal "lock" which is passed as an test argument through switch $value$plusargs as GPIO_SEL="test_bench.gpio"
So my code is :
module ab
string abc;
$value$plusargs("GPIO_SEL=%s" , abc);
reg lock;
always #*
begin
lock = abc;
end
endmodule
The problem here is that signal lock is not getting value as test_bench.gpio. Is there any way i can pass this signal value from testplaus args
SystemVerilog is a compiled language, not interpreted. You can't access identifiers directly using a string. You can use compiler directives on the command line
module ab;
bit lock;
always_comb
begin
lock = `GPIO_SEL;
end
endmodule
Then when compiling your code use a +define switch
vlog your_file.sv +define+GPIO_SEL=test_bench.gpio
If you think re-compiling your testbench/dut is a significant burden (most tools offer incremental compilation options), and you have a fixed number of paths to access, then you can use a case statement
module ab
string abc;
initial $value$plusargs("GPIO_SEL=%s" , abc);
reg lock;
always_comb
case (abc)
"test_bench.gpio": lock = test_bench.gpio;
"test_bench.gpio1": lock = test_bench.gpio1;
endcase
end
And you could just use a simple number instead of a string to select.
Looks like you need to conver the string to an integer type. Look at the string conversion functions: atoi, atobin, ...
for example
module parg;
string abc;
bit [3:0] sig;
initial begin
$value$plusargs("GPIO_SEL=%s" , abc);
sig = abc.atoi();
$display("sig = %b", sig);
end
endmodule // parg

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