Is randomize an inbuilt function in SystemVerilog? - system-verilog

class sample;
randc bit[2:0]count;
endclass
module top;
sample sample_test;
initial begin
sample_inst=new();
repeat(20) begin
sample_inst.randomize();
sample.print();// this is assumed to be written in class
end
end
endmodule
I understood what rand and randc are, but I don't understand how we are able to use randomize() without writing a function in SystemVerilog class. I assume it is an inbuilt function. I have seen many codes online which don't have randomize function written in the class.

Yes, randomize is a built-in function, as defined in the IEEE Std 1800-2017, section 18.6.1 Randomize():
Variables in an object are randomized using the randomize() class
method. Every class has a built-in randomize() virtual method,
declared as follows:
virtual function int randomize();
This means that all the code examples you see online do not declare a randomize function.
Free copies of the IEEE Std are available. This should be your first source for all information.

Related

How to control the property rand_mode in a SystemVerilog class?

Suppose there is a class A like below:
class A;
rand logic [3:0] a;
rand logic [3:0] b;
rand logic [3:0] c;
constraint a_const{
a<'h4;
}
constraint b_const{
b<'h4;
}
endclass
When I use :
A at = new();
at.b_const.constraint_mode(0);
assert(at.randomize());
b is also randomized. But, I don't want that.
Is there a way I can only randomize a without randomizing b and c?
Because there can be many logics in a class, sometimes I just want to rand some of them. Put some of the logics in one class like A while some in other class B is one of the solutions, but it is too complicated.
If you only want one of the rand variables in a class to be randomized, then you can pass the variable to the randomize function:
assert(at.randomize(a));
Alternately, as you mentioned in the title to your question, you can use rand_mode to disable randomization of individual class variables:
at.b.rand_mode(0);
at.c.rand_mode(0);
assert(at.randomize());
Refer to IEEE Std 1800-2017, section 18.8 Disabling random variables with rand_mode().
With either of the above approaches, only a will be randomized.
I suspect you expected b_const.constraint_mode(0) to disable randomization of variable b. That line simply disables the named constraint, leaving b unconstrained. This means that b will be randomized in your original code (which is what you observed).
SystemVerilog gives you two mode controls rand_mode() and constraint_mode, and they operate independently. Active constraints must be satisfied regardless of whether the variables are random or not(state-variables).
You should probably separate the variables you want randomized into different groups of different classes and perhaps put them into a hierarchy.
If you only want to randomize one variable with a single constraint, use std::randomize()
assert( std::randomize(a) with { a < 'h4;});

passing generated modports to instances of the same module

I'm pretty sure there is no way to do what I am trying, but just in case there is an interesting clever solution, I thought I'd ask around. I have a parameterized SystemVerilog interface, inside of which I am generating modports
interface some_interface #(parameter NUM_READERS=3);
logic [`kBitsPerProgramCounter-1:0] read_addr[NUM_READERS];
logic [`kBitsPerProgramCounter-1:0] write_addr;
genvar i;
generate
// generates Reader[n].Modport
for (i = 0; i < NUM_READERS; ++i) begin : Reader
modport Modport
(
output .read_addr(read_addr[i])
);
end
endgenerate
endinterface
Using this is easy if I have different module types taking different modports. However, what I wanted to try doing is to have multiple instances of the same module. I tried this by parameterizing on type
module some_block#(parameter type SOMETYPE) (
input logic CLK200MHZ,
SOMETYPE aarrgghh);
But getting it to work syntactically has been challenging. Giving SOMETYPE a default value doesn't work because Vivado complains about not allowing hierarchical types, so right off the bat I don't think this will work. When instantiating, I tried using the full modport name, the full modport name with the instantiated interface, and a few others, but nothing seems to work.
So I am curious if there is a clever way I can have multiple instantiations of some_block, each taking a different generated modport. And if not, is there some fun clever trick I can do to use the generated modports? The idea in the first place was that I have a thing that has one writer and multiple readers. I wanted to generate a modport for each reader so that it could only touch it's own read address. I guess I could always just parameterize some_block on an integer, pass some_block the whole interface, and then rely on some_block to only touch the read address corresponding to the passed in integer, but that might be error prone.
Assuming that 'generate' works, there is nothing to be concerned about modules. There is no need to pass a type parameter. The module port is just supposed to be of the type of your interface.
module top();
some_interface ifc;
for (genvar gi = 0; gi < NUM_REASDERS; gi++) begin: inst
some_block sb(ifc.Reader[gi].Modport);
end
endmodule
module some_block (some_interface ifc);
always_comb myvar = ifc.read_addr;
some_block just always references the 'read_addr' which is the modport var. You can use a generate block in the 'top' module.

What is the benefit of automatic variables?

I'm looking for benefits of "automatic" in Systemverilog.
I have been seeing the "automatic" factorial example. But I can't get though them. Does anyone know why we use "automatic"?
Traditionally, Verilog has been used for modelling hardware at RTL and at Gate level abstractions. Since both RTL and Gate level abstraction are static/fixed (non-dynamic), Verilog supported only static variables. So for example, any reg or wire in Verilog would be instantiated/mapped at the beginning of simulation and would remain mapped in the simulation memory till the end of simulation. As a result, you can take dump of any wire/reg as a waveform, and the reg/wire would have a value from the beginning till the end, since it is always mapped. In a programmers perspective, such variables are termed static. In C/C++ world, to declare such a variable, you will have to use storage class specifier static. In Verilog every variable is implicitly static.
Note that until the advent of SystemVerilog, Verilog supported only static variables. Even though Verilog also supported some constructs for modelling at behavioural abstraction, the support was limited by absence of automatic storage class.
automatic (called auto in software world) storage class variables are mapped on the stack. When a function is called, all the local (non-static) variables declared in the function are mapped to individual locations in the stack. Since such variables exist only on the stack, they cease to exist as soon as the execution of the function is complete and the stack correspondingly shrinks.
Amongst other advantages, one possibility that this storage class enables is recursive functions. In Verilog world, a function can not be re-entrant. Recursive (or re-entrant) functions do not serve any useful purpose in a world where automatic storage class is not available. To understand this, you can imagine a re-entrant function as a function which dynamically makes multiple recursive instantiations of itself. Each instance gets its automatic variables mapped on the stack. As we progress into the recursion, the stack grows and each function gets to make its computations using its own set of variables. When the function calls return the computed values are collated and a final result made available. With only static variables, each function call will store the variable values at the same common locations thus erasing any benefit of having multiple calls (instantiations).
Coming to the factorial algorithm, it is relatively easy to conceptualize factorial as a recursive algorithm. In maths we write factorial(n) = n(factial(n-1))*. So you need to calculate factorial(n-1) in order to know factorial(n). Note that recursion can not be completed without a terminating case, which in case of factorial is n=1.
function automatic int factorial;
input int n;
if (n > 1)
factorial = factorial (n - 1) * n;
else
factorial = 1;
endfunction
Without automatic storage class, since all variables in a function would be mapped to a fixed location, when we call factorial(n-1) from inside factorial(n), the recursive call would overwrite any variable inside the caller context. In the factorial function as defined in the above code snippet, if we do not specify the storage class as automatic, both n and the result factorial would be overwritten by the recursive call to factorial(n-1). As a result the variable n would consecutively be overwritten as n-1, n-2, n-3 and so on till we reach the terminating condition of n = 1. The terminating recursive call to factorial would have a value of 1 assigned to n and when the recursion unwinds, factorial(n-1) * n would evaluate to 1 in each stage.
With automatic storage class, each recursive function call would have its own place in the memory (actually on the stack) to store variable n. As a result consecutive calls to factorial will not overwrite variable n of the caller. As a result when the recursion unwinds, we shall have the right value for factorial(n) as n*(n-1)(n-2) .. *1.
Note that it is possible to define factorial using iteration as well. And that can be done without use of automatic storage class. But in many cases, recursion makes it possible for the user to code algorithms in a more intuitive fashion.
I propose 1 example as below(using fork...join_none):
Ex.1 (non-using automatic) : value output will be "3 3 3 3". because i take the latest value after exit for loop, i is stored in static memory location. This may be a bug in your code.
initial begin
for( int i =0; i<=3 ; i++)
fork
$write ("%d ", i);
join_none
end
Ex.2 (using automatic) : value output will be "0 1 2 3". Because in each loop, value of i is copied to k, and fork..join_none spawn a thread with each value of k ( each loop will locate 1 memory space for k : k0, k1, k2, k3):
initial begin
for( int i =0; i<=3 ; i++)
fork
automatic int k = i;
$write ("%d ", k);
join_none
end
Another example is the use of fork join inside for loop -
Without the use of automatic, the fork join inside the for loop will not work correctly.
for (int i=0; i<`SOME_VALUE ; i++) begin
automatic int id=i;
fork
task/function using the id above ;
...
join_none
end

How to pass a class between two modules?

I have two modules and a class and I would like to move that class from one module to the other. Something like this:
class foo;
int x;
int y;
endclass
module mod_A(output foo foo_inst, output event trig);
initial begin
foo my_foo = new;
my_foo.x = 1;
my_foo.y = 2;
foo_inst = my_foo;
->trig;
end
endmodule
module mod_B(input foo foo_inst, input event trig);
always #(trig) begin
$display("%d%d, is a funky number.", foo_inst.x, foo_inst.y);
end
endmodule
module top();
event trig;
foo foo_inst;
mod_A mod_A(.trig, .foo_inst);
mod_B mod_B(.trig, .foo_inst);
endmodule
Of course there're also some functions defined in the class which are used in each module.
The issue with this setup is that I'm seeing errors for each ports of mod_B:
Error-[RIPLIP] Register in low conn of input port
Non-net variable 'foo_inst' cannot be an input or inout port.
Non-net variable 'trig' cannot be an input or inout port.
EDAplayground does not support class objects as module ports and 1800-2012 only mentions interface declarations, events, arrays structures or unions in Port declarations (23.2.2) so my questions are:
Is it even legal to pass classes through ports? If not, what is an elegant
method of accomplishing this?
What does "Register in low conn of
input port" mean? I'm aware that this might be a compiler specific
error and nothing indicative but if I knew what it was trying to tell me I might be a step closer to fixing this.
A variable of any type can be an input or output port. You might have to write for your compiler
input var foo foo_inst,
But it would be better to use a ref when a port is really a handle.
module mod_A(ref foo foo_inst, ref event trig);
Note that you have a typo with foo_o or foo_inst and a race condition between a trigger ->trig and an event control #(trig).

about matlab's Adaptfilt.<algorithm> function

Matlab's DSP toolbox has a function called adaptfilt., where calling adaptfilt is not enough, but you must add the .< algorithm> where the algorithm can be one of the many things, which we can view using help adaptfilt. What kind of Matlab structure has been used here (or what is the . operator and how can I make my own function that has to be called using a dot).
Also, the result of doing, say, adaptfilt.fdaf, gives a result that seems like a structure. How can I view all the elements of this structure (that is, if there are any more members besides the values that are returned on the screen by the function itself)?
adaptfilt is a class definition, of which fdaf is a member. Then, you use the dot operator to access the static member of the class. See Static Methods in the MATLAB documentation. In summary, to define a similar function yourself use
classdef MyClass
...
methods(Static)
function y = yourFunc(x)
...
end
end
end
The result you're getting from adaptfilt.fdaf is in fact an object. The adaptfilt.fdaf documentation page outlines the members of the object.