Updating a classes' variable in a constructor through pass by reference? - system-verilog

Blazing ahead with newfound knowledge of SystemVerilog's inner workings I've set out to use one of these fandangled pass-by-reference features to update a classes' counter in the constructor of another class. The setup (stripped to the basics) looks somewhat like this:
class my_queue;
int unsigned num_items; //Want to track the number of items this Queue has seen.
function push_new_item();
item new_item = new(num_items);
endfunction
endclass
class parent_item;
int unsigned x_th_item;
function new(ref int unsigned num_items);
x_th_item = num_items;
num_items += 1; //This should increase the counter in num_items.
endfunction
endclass
class item extends parent_item;
function new(ref int unsigned num_items);
super.new(num_items);
endfunction
endclass
The issue is that my compiler is complaining about an
Illegal connection to the ref port 'num_items' of function/task parent_item::new, formal argument should have same type as actual argument.
I have an idea on how to fix this: Moving the increment after the call to new() in push_new_items.
But then I still won't know how to correctly use pass-by-refrence in SV so what's causing the error?
Is it the other pass-by-reference or maybe a syntactical error?

You do not need ref semantics for this, use an inout argument.
inout's are copied-in upon entry and copied-out upon return of a task or function. The type compatibility requirements are much stricter as you have seen for ref arguments.
The only occasion you must use a ref argument isin time consuming tasks and you need to see active updates to the arguments before the task returns.
task my_task(ref bit tclock);
#(posedge tclock) // this would hang if tclock was an input
endtask
Another place you might want to use a ref argument is as an optimization when the argument type is a large object like an array. But passing a single int by reference is actually slower than copying its value directly.

Qiu did point me to the issue with my code. My problem was that, whilst the variables were declared correctly on both ends, one of my constructors was written:
function new(ref int num_items);
where it should have rather been
function new(ref int unsigned num_items);
Thank you Qiu.

Related

Modifying queue of class in systemverilog function

I met a problem when I trying to modify a queue of class in systemverilog function.
Here are the codes:
module my_module;
class dscr;
logic mode;
function void print_dscr;
$display("mode = %d", this.mode);
endfunction
endclass
dscr a_dscr_q[$];
dscr b_dscr_q[$];
initial begin
descriptor_decode(0, a_dscr_q);
for (int I=0; I<a_dscr_q.size(); i++)
a_dscr_q[i].print_dscr();
descriptor_decode(1, b_dscr_q);
for (int I=0; I<a_dscr_q.size(); i++)
a_dscr_q[i].print_dscr();
for (int I=0; I<b_dscr_q.size(); i++)
b_dscr_q[i].print_dscr();
end
function void descriptor_decode(logic mode, ref dscr dscr_q[$]);
dscr dscr_dec = new;
dscr_dec.mode = mode;
dscr_q.pushback(dscr_dec);
endfunction
endmodule
I am trying to create different class queue in function "descriptor_decoder", with different value of input mode. In function, I firstly create a new dscr class and then push it to a class queue. However the simulation result are:
mode = 0
mode = 1
mode = 1
The first time I call the function, it did push back the correct class into a_dscr_q. But the second function call, it seems the class is push back into both a_dscr_q and b_dscr_q. I am quite confused, What happened in here?
Your code was made illegal syntax in the IEEE 1800-2009 LRM because of the very problem you are experiencing. Most tools now report this as an error.
Your descriptor_decode is function with a static lifetime, and the dscr_dec variable declared inside it has a static lifetime as well.
You are not allowed to have an initialization on a variable whose lifetime is implicitly static and has the option to be declared automatic. This is because unlike most programming languages, the default lifetime of variables in a SystemVerilog function is static, and initialization of static variables happens once before time 0, not each occurrence of calling the function. In your example, you are expecting dscr_dec to behave as an automatic.
So you need to make one of the following code changes:
explicitly declare dscr_dec automatic
declare the function automatic, which makes variables declared inside it implicitly automatic
declare the module automatic, which makes functions declared inside it implicitly automatic
split the declaration and initialization do that the initialization happens when the function gets called.

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

Systemverilog const ref arg position when constructing an object

I am creating a class that needs a reference to the test bench's configuration object. Since the configuration must be intact throughout the simulation, I pass it as a const ref object. Here is a sudo code that I want to run:
class tb_config;
int unsigned rate;
int unsigned chnls[];
const int unsigned nb_chnls;
function new (int unsigned rate, int unsigned nb_chnls);
this.rate = rate;
this.nb_chnls = nb_chnls;
chnls = new[nb_chnls];
endfunction
endclass
class tx_phy;
static int phy_id;
tb_config cfg;
function new (int unsigned phy_id, const ref tb_config cfg);
this.phy_id = phy_id;
this.cfg = cfg;
endfunction
endclass
module test;
tb_config cfg = new(100, 4);
tx_phy phy = new( 1234, cfg);
endmodule
The code above works perfectly fine and it meets my expectation. But if I change the arguments in tx_phy::new to function new (const ref tb_config cfg, int unsigned phy_id); and pass the values to the constructor accordingly I get the following error in Cadence Incisive:
invalid ref argument usage because actual argument is not a variable.
Also same thing happens when I test it with Aldec in edaplayground: https://www.edaplayground.com/x/5PWV
I assume this is a language limitation, but is there any other reason for that??
The reason for this is because the argument kind is implicit if not specified. You specified const ref for the first argument, but nothing for the second argument, so it is also implicitly const ref. Adding input to the second argument declaration fixes this.
function new (const ref tb_config cfg, input int unsigned phy_id);
I also want to add const ref tb_config cfg is equivalent to writing
function new (tb_config cfg, int unsigned phy_id);
Both of these arguments are implicitly input arguments, which means they are copied upon entry.
A class variable is already a reference. Passing a class variable by ref means that you can update the handle the class variable has from within the function. Making the argument a const ref means you will not be able to update the class variable, but you can still update members of the class the variable references. There is no mechanism to prevent updating members of class object if you have a handle to it other than by declaring them protected or local.
The only place it makes sense to pass function arguments by ref in SystemVerilog is as an optimization when the arguments are large data structures like an array, and you only need to access a few of the elements of the array. You can use task ref arguments when the arguments need to be updated during the lifetime of the task (i.e. passing a clock as an argument).

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.

Timer Thread with passed Function* and Param

I'm working on finishing up my server for my first iPhone application, and I want to implement a simple little feature.
I would like to run a function (perhaps method as well), if another function returns a certain value after a certain waiting period. Fairly simple concept.... right?
Here's my basic foundation.
template <typename T,class TYP>
struct funcpar{
T (*function)(TYP);
TYP parameter;
funcpar(T (*func)(TYP),TYP param);
funcpar& operator=(const funcpar& fp);
};
The goal here is to be able to call funcpar::function(funcpar::parameter) to run the stored function and parameter, and not have to worry about anything else...
When I attempted to use a void* parameter instead of the template, I couldn't copy the memory as an object (because I didn't know what the end object was going to be, or the beginning for that matter) and when I tried multiple timers, every single object's parameter would change to the new parameter passed to the new timer... With the previous struct I have a
question:
Is it possible to make an all-inclusive pointer to this type of object inside a method of a class? Can I templatize a method, and not the whole class? Would it work exactly like a function template?
I have a managing class that holds a vector of these "jobs" and takes care of everything fairly well. I just don't know how to use a templatized function with the struct, or how to utilize templates on a single method in a class..
I'm also utilizing this in my custom simple threadpool, and that's working fairly well, and has the same problems...
I have another question:
Can I possibly store a function with a parameter before it's run? Something like toRun = dontrunmeyet(withThisParameter);? Is my struct even necessary?
Am I going about this whole thing incorrectly?
If this is overly ambiguous, I can set you up with my whole code for context
In order to create a class method that takes a template parameter, yes, it would work almost exactly like a function template. For example:
class A
{
public:
template<typename T>
void my_function(const T& value) { }
};
int main()
{
A test;
test.my_function(5);
return 0;
}
Secondly, for your structure, you can actually turn that into a functor-object that by overloading operator(), lets you call the structure as-if it were a function rather than having to actually call the specific function pointer members inside the structure. For instance, your structure could be re-written to look like this:
#include <iostream>
template <class ReturnType, class ParameterType>
class funcpar
{
private:
ReturnType (*function)(ParameterType);
ParameterType parameter;
public:
funcpar(ReturnType (*func)(ParameterType),ParameterType param):
function(func), parameter(param) {}
funcpar& operator=(const funcpar& fp);
//operator() overloaded to be a function that takes no arguments
//and returns type ReturnType
ReturnType operator() ()
{
return function(parameter);
}
};
int sample_func(int value)
{
return value + 1;
}
int main()
{
funcpar<int, int> test_functor(sample_func, 5);
//you can call any instance of funcpar just like a normal function
std::cout << test_functor() << std::endl;
return 0;
}
BTW, you do need the functor object (or your structure, etc.) in order to bind a dynamic parameter to a function before the function is called in C/C++ ... you can't "store" a parameter with an actual function. Binding a parameter to a function is actually called a closure, and in C/C++, creating a closure requires a structure/class or some type of associated data-structure you can use to bind a function with a specific parameter stored in memory that is used only for a specific instance of that function call.