How to use let with typedef signals? - system-verilog

I'm trying to use let-constructs instead of `define in my TB to access some signals via hierarchical paths. It's not going that great. "Normal" signals seems to work and I can access them, but typedef-signals act strange.
Here's a super simple example code that I use to trigger the error:
module dut();
// Create a type
typedef struct {
int foo;
int bar;
} ty_fooBar;
// Instantiate the type
ty_fooBar fooBar;
// Create an "alias" for fooBar
let fooBar2 = fooBar;
// Assign some values and try to access the struct members
initial begin
fooBar.foo = 3;
fooBar.bar = 7;
$display("fooBar: %p", fooBar );
$display("fooBar2: %p", fooBar2 );
$display("fooBar.fooBar: %p", fooBar.foo );
// $display("fooBar2.fooBar: %p", fooBar2.foo ); <- ERROR
end
endmodule
Simulation gives this result:
# fooBar: '{foo:3, bar:7}
# fooBar2: '{foo:3, bar:7}
# fooBar.fooBar: 3
So, fooBar should now be the same as fooBar2, ModelSim shows this with the $display command, but for some reason I can access fooBar.foo but not fooBar2.foo. I tried reading the IEEE standard but that didn't enlighten me.
What's the deal with let and typedef? Do I have some profound misunderstanding?

The let statement combines the flexibility of a `define macro with the well formed structure of function.
Like a macro. the let arguments get replaced into the body of its definition. they may be typeless.
Like a function, a let declaration is local to a scope, including a package that can be imported. References to identifiers that are not arguments are searched from the point of the declaration. And finally the problem that you are facing is that a let statement can only be called where an expression is allowed. In fact it places parenthesis around the body before substituting.
So your reference to fooBar2.foo gets expanded as (fooBar).foo which is not legal.

Related

System Verilog Conditional Type Definition

Is there a way to conditionally select between two types based on a parameter value?
typedef struct packed {
logic a;
} my_type_1_t;
typedef struct packed {
logic [1:0] a;
} my_type_2_t;
parameter type type_t = my_type_1_t;
if (MY_PARAM == 1) begin
typedef my_type_1_t type_t;
do something...
end else begin
typedef my_type_2_t type_t;
do the same something as above with different struct...
end
type_t my_signal;
As you can see, I need to do the same operations in the else clause as in the if clause, but on a different struct. This seems redundant to me, and I am curious if there is a way to avoid duplication.
No, you can't do this because the type_t name will be local to the generate-if block. But it wouldn't make much sense if you could because code that references my_signal is going to want to access my_signal.b and it would not exist in certain cases.
The only way to mimic the behavior you want is to use parameterized modules.
Here is a simple example:
typedef struct {
logic a, b;
} type1_t;
typedef struct {
logic [1:0] a1, b1;
} type2_t;
module top(input logic clk);
pmod#(.T(type1_t)) inst1(clk);
pmod#(.T(type2_t)) inst2(clk);
endmodule
module pmod#(type T=int) (input logic clk);
logic [1:0] a, b;
T t;
if ($typename(T) == "type1_t") begin
always #(posedge clk) begin
a = t.a;
b = t.b;
end
end
else if ($typename(T) == "type2_t") begin
always #(posedge clk) begin
a = t.a1;
b = t.b1;
end
end
// do something with a and b
endmodule
First, question to ask yourself is whether you are needing synthesizable code?
If not, you simply can use a parameterized class with different struct definitions, effectively giving you parameterized structs.
Note that it would be possible for synthesis tools to synthesize such code, yet your mileage with each tool varies, and last time I checked, most synthesis tools didn't even support this limited use of class construct as a scoping mechanism.
Packages, which have much more synthesis tool support unfortunately do not have an option for parameterizing packages/structs. The best I've done is to emulate parameterizable structs using macros, e.g.:
// Macro and struct definitions could be moved to common header file if shared across multiple modules
// Acts like a parameterized typedef
// Note: You could even extend multiple nested levels if needed
// This mechanism requires that the struct definitions take the same form, i.e., logic[0:0] instead of just logic
`define my_type_t(W) struct packed { logic [W-1:0] a; }
// module used as example, but would work in most contexts
module mod;
parameter MY_PARAM = 1;
// In a particular parameterized context, can resolve the macro
// into a real typedef if desired
typedef my_type_t(MY_PARAM) type_t;
type_t my_signal;
// do something that works with either struct definition
endmodule;
Please check my syntax above as I took a previously working example and modified it to match the question from OP, without testing final code segment.

Store reference to array/queue in SystemVerilog

I'd like to store a reference to an array/queue inside a class. It's doesn't seem possible to do this, though.
I'd like to do something like this:
class some_class;
// class member that points to the 'q' supplied as a constructor arg
??? q_ref;
function new(ref int q[$]);
this.q_ref = q;
endfunction
endclass
If q_ref is merely defined as int q_ref[$], then the assignment operator will create a copy, which isn't what I want. I'd like changes in 'q' to be visible inside the class.
Is there some hidden section in the LRM that shows how this can be done?
I'm not looking for the obvious "you have to wrap the array/queue in a class answer", but for something that allows me to interact with code that uses native arrays/queues.
There are only three variable types in SystemVerilog that can store references: class, event, and virtual interfaces variables.
You have to wrap the array/queue as a member in a class object. Then, any method of that class can be used in an event expression. Any change to a member of the class object causes a re-evaluation of that method. See the last paragraph and example in section 9.4.2 Event control of the 1800-2012 LRM.
So, the only solution for you would be to wrap the queue in a class. The latter is always assigned by a reference, as in this example:
class QueueRef #(type T = int);
T queue[$];
function void push_back(T t);
queue.push_back(t);
endfunction // push_back
endclass // Queue
class some_class;
QueueRef q_ref;
function new(QueueRef q);
this.q_ref = q;
endfunction
endclass
program test;
QueueRef q = new;
some_class c = new (q);
initial begin
q.push_back(1);
q.push_back(2);
$display(c.q_ref.queue);
end
endprogram // test

How do I convert strings to enums in SystemVerilog?

I have command-line plusargs that I want to map to enumerated values.
vsim foo +MY_PLUSARG=BAR
How do I get the string "BAR" to become the enum BAR?
If you are using UVM 1.2 or have access to the library, you may want to use uvm_enum_wrapper class to do the conversion. It is a template class wrapper defined in uvm_globals.svh and you can use it as follows:
typedef enum {BISTRO, COFFEE_SHOP, BAR} places_e;
typedef uvm_enum_wrapper#(places_e) places_wrapper;
places_e place;
places_wrapper::from_name("BAR", place);
Quite like the code you provided in this solution, the wrapper class works by traversing the enum entries and creating an assoc array for a enum[string] map for the given enum (supplied as template parameter). So if you are using UVM 1.2, don't repeat.
The idea behind this solution is to avoid a case statement that hardcodes the members of your enumerated type. You want to be able to change the type in one play.
Let's say you have the following enum:
typedef enum {BISTRO, COFFEE_SHOP, BAR} places_e;
You want your user to be able to type:
vsim top +MEET_PLACE=BAR
Now you want to translate the string "BAR" to the enum 'Bar'.
You can do this:
typedef enum {BISTRO, COFFEE_SHOP, BAR} places_e;
module top;
places_e place_map[string];
function void make_map;
places_e pl;
pl = pl.first();
do begin
place_map[pl.name()]=pl;
pl = pl.next();
end while (pl != pl.first());
endfunction // make_map
function string get_list;
string ss;
places_e pl;
pl = pl.first();
ss = "";
do begin
ss = {ss, " ",pl.name()};
pl = pl.next();
end while (pl != pl.first());
return ss;
endfunction // get_list
initial begin
string place_str;
places_e place;
make_map;
if (!$value$plusargs("MEET_PLACE=%s", place_str)) begin
$display("You must choose a MEET_PLACE");
$finish;
end
if (! place_map.exists(place_str)) begin
$display("You must choose from this list: %s", get_list());
$finish;
end
place = place_map[place_str];
$display("Let's meet at a %s!", place.name());
end // initial begin
endmodule // top

Is there any method to know whether a member is declared random or not in a class in SV

// Current Class
class x;
rand int a;
int b; // b is nonrandom as of now
function new();
endfunction
function abc;
// if a != ref.a, where ref is reference object of class x, declared somewhere else
a.rand_mode(0);
endfunciton
// Future Possible Class
class x;
rand int a;
rand int b; // b is also a random variable now
function new();
endfunction
function abc;
// if a != ref.a, where ref is reference object of class x, declared somewhere else
a.rand_mode(0);
// if b != ref.b, where ref is reference object of class x, declared somewhere else
b.rand_mode(0);
endfunciton
So in function abc, depending upon whether a rand member value matches or doesn't match with the value of that member in reference class, that rand declared members of class x, should be active or inactive accordinly.
Purpose - I need to check if a rand variable matches with reference class value then only it should be randomized, otherwise not.
I want to generalize method abc, for all possible future variations (So I don't need to modify it, as done in the above example), and as I don't know, when a class member may become rand or nonrand member, Is there any inbuilt method to know, whether a member of a class is declared as rand or not in that class?
You could change your perspective on the problem slightly. Instead of trying to disable randomization for fields that are declared rand, why not say that when they get randomized, they should keep their value?
According to this nice post, there's a new construct in SV 2012, const'(...) that would work in this case. Unfortunately I don't think many vendors support it. Your randomize() call would look like this:
if (!rand_obj.randomize() with {
const'(a) != ref_obj.a -> a == const'(a);
})
$fatal(0, "rand error");
Let's dissect this code. const(a) will sample the value of a prior to doing any sort of randomization. If the value of a before randomization is not equal to the reference value, then we have the second part of the constraint that says a should keep its value. I've tried this code on two simulators but it wasn't supported by either (though it should be legal SV 2012 syntax). Maybe you're lucky enough to have a vendor that supports it.
You can write such constraints even for state variables, as they will still hold.
If you can't get the const syntax to work in your simulator, then the same post shows how you could work around the issue. You could store the values prior to randomization inside the object and use those in the constraint:
class some_class;
rand bit [2:0] a;
bit [2:0] b;
bit [2:0] pre_rand_a;
bit [2:0] pre_rand_b;
function void pre_randomize();
pre_rand_a = a;
pre_rand_b = b;
endfunction
endclass
When you want to randomize, you'd add the following constraints:
if (!rand_obj.randomize() with {
pre_rand_a != ref_obj.a -> a == pre_rand_a;
pre_rand_b != ref_obj.b -> b == pre_rand_b;
})
$fatal(0, "rand error");
You can find a full example on EDAPlayground.
You mention that your function that does randomization is defined outside of the object. Because of that, the pre_rand_* fields can't be local/protected, which isn't very nice. You should consider making the function a class member and pass the reference object to it, so that you can enforce proper encapsulation.
This isn't possible as SystemVerilog doesn't provide any reflection capabilities. You could probably figure this out using the VPI, but I'm not sure how complete the implementation of the VPI is for classes.
Based on what you want to do, I'd say it anyway doesn't make sense to implement such a query just to future proof your code in case some fields will one day become rand. Just as how you add the rand modifier to the field, you can also add it to the list of fields for which randomization should be disabled. Both code locations reside in the same file, so it's difficult to miss.
One certain simulator will return -1 when interrogating a state variable's rand_mode(), but this is non-standard. The LRM explicitly states that it's a compile error to call rand_mode() on non-random fields.

System Verilog: enum inside interface

I have an interface:
interface my_intf();
typedef enum logic [1:0] {
VAL_0 = 2'b00,
VAL_1 = 2'b01,
VAL_2 = 2'b10,
VAL_3 = 2'b11
} T_VAL;
T_VAL val;
endinterface
My module uses this interface:
my_intf intf;
The problem is to assign the val with a value from the enum.
I can assign it as:
intf.val = 0; (and receiving warning or error)
but not as:
intf.val=VAL_0;
Nor as
intf.val = my_intf.T_VAL.VAL_0
How I overcome that problem?
I have only dealt with packages for containing enums before, and avoid interfaces. This is how I use packages. Import the package before the module definition with which you want to use it:
import my_intf_pkg::* ;
module bla(
output my_val_t intf
);
initial begin
intf = VAL_0 ;
end
endmodule
The package containing enums might look like:
package my_intf_pkg;
typedef enum logic [1:0] {
VAL_0 = 2'b00,
VAL_1 = 2'b01,
VAL_2 = 2'b10,
VAL_3 = 2'b11
} my_val_t;
endpackage : my_intf_pkg
Note that the VAL_0 etc are global and not tied to the T_VAL typedef. Therefore I often make them a bit more unique including the typedef in the name. T_VAL_0 for T_VAL typedefs etc.
Here is an example on EDAplayground.
intf.val = 0; should be an error because you are trying to assign a integral type to an enum without a cast.
intf.val = VAL_0; is an error because VAL_0 is not defined in the current scope.
You should be able to do
intf.val = intf.VAL_0;
However, the best solution in to put shared types in a package, an import the package where needed.