Systemverilog const ref arg position when constructing an object - system-verilog

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).

Related

System Verilog: ref class member

It should be possible to have a reference as a class member, like in C++. However...
covergroup smurf_car_covergroup(ref smurf_transaction tx)
;
covpt_tx_direction_TURN_LEFT :
coverpoint tx.tx_direction {
bins left[] = {[ENUM_C_LEFT_TURN:ENUM_C_LEFT_TURN]
}
;
}
endgroup
class smurf_coverage_container extends
smurf_coverage_object;
ref smurf_transaction m_ref_smurf_transaction ;
function new(string name, ref smurf_transaction) ;
m_ref_smurf_transaction=smurf_transaction ;
super.new(smurf_transaction) ;
// does the following:
// // m_smurf_bicycle_covergroup
//= new(smurf_transaction) ;
// etc
endfunction : new
function void smurf_sample() ;
// important
if (m_ref_smurf_transaction.is_bicycle() )
m_smurf_bicycle_covergroup.sample () ;
if (m_ref_smurf_transaction . is_car () )
m_smurf_car_covergroup .sample () ;
endfunction : smurf_sample
endclass : smurf_coverage_container
This doesn't work because ref isn't allowed to apply to class fields - the compiler complains "Following verilog source has a syntax problem : NNN: token is smurf_transaction."
Clearly holding a reference (so that when another class assigns its handle to a new object, the sampled object is updated automatically) is technically possible, so what syntax would acheive this effect?
Note I can't add arguments to the sample method owing to coding guidelines and common sense.
The ref keyword only applies as connection type to arguments like in a task or function. It is not needed(allowed) in a class variable declaration because a class variable is always a reference to a class object. See my short class on classes.
A ref argument is a variable passed by reference. This type of argument are not a copy but a reference to the original variable(in this case the object of the class).
Arguments passed by reference are not copied into the subroutine area, rather, a reference to the original argument is passed to the subroutine. The subroutine can then access the argument data via the reference.
From section 13.5.2 in [IEEE Std 1800-2012].
Thus using ref for a class variable is not needed .You can directly pass the object of the class .

Error: passing 'const xxx' as 'this' argument discards qualifiers

I am trying to implement bigint class in c++, it's not completed yet, i have encountered some errors that i am unable understand.
I have erased all other functions (as they are unnecessary in this case)
and karatsuba is not yet completed (but that should't pose a problem in this case).
In the multiply function (overloaded * ) my compiler gives an error:
passing 'const BigInt' as 'this' argument discards qualifiers [-fpermissive]
at line
ans.a = karatsuba(n,m);
I understand that this would occur when i am trying to change a constant object or object passed to a constant function, in my case i am merely creating a new vector and passing it to karatsuba function.
Removing const from overloded * gets rid of this error.
So,does this mean that a constant function can't change anything at all? (including local variables?)
class BigInt {
typedef long long int ll;
typedef vector<int> vi;
#define p10 1000000000;
#define range 9
vi a;
bool sign;
public:
BigInt operator * (const BigInt &num) const
{
vi n(a.begin(),a.end()),m(num.a.begin(),num.a.end());
BigInt ans;
ans.sign = !(sign ^ num.sign);
while(n.size()<m.size()) n.push_back(0);
while(n.size()>m.size()) m.push_back(0);
ans.a = karatsuba(n,m);
return ans;
}
vi karatsuba(vi a,vi b)
{
int n = a.size();
if(n <= 16)
{
// some code
}
// some code
return a;
}
};
Ok so after googling a bit more, i realized that this pointer is implicitly passed to the oveloaded * and then on to karatsuba (as it is a member function of the class), and as karatsuba is not a constant function, there is no guarantee that it won't change the object contents, hence this error is triggered.
One solution is to declare karatsuba as static, as static member functions don't receive this pointer (they can even be called with out a class object simply using :: operator) , read more about them from here Static data members and member functions.
All that is needed to be changed is :-
static vi karatsuba(vi a,vi b)
{
int n = a.size();
if(n <= 16)
{
// some code
}
// some code
return a;
}

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.

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

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.

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.