The code guideline for our verification environment is one class per file.
Sometimes a uvm_object is only needed by 1 other uvm_component, so, following object-oriented theory, we should use nested/inner classes.
Nested classes are fully supported by SystemVerilog. However, are they supported by UVM?
Is it possible to compile something like the following:
class inception_level_1 extends uvm_test;
`uvm_component_utils(inception_level_1)
function new(string name = "inception_level_1", uvm_component parent = null);
super.new(name, parent);
endfunction
class inception_level_2 extends uvm_object;
int a;
`uvm_object_utils_begin(inception_level_2)
`uvm_field_int(a, UVM_DEFAULT)
`uvm_object_utils_end
function new(string name = "inception_level_2");
super.new(name);
endfunction
endclass
endclass
Currently the above code gives a compile error:
** Error: testbench.sv(20): (vlog-2889) Illegal to access non-static method 'uvm_report_warning' outside its class scope.
Full code example here: http://www.edaplayground.com/x/3r8
SystemVerilog has packages, which is the preferred mechanism to "hide" class declarations from other packages.
You will have problems using the field macros, or anything else that tries to reference identifiers from inside the inner class that are defined with the same name in both the global uvm_pkg and the outer class. All the uvm_report_... methods are defined in both because uvm_component is extended from uvm_report_object, and uvm_report_... is in the global uvm_pkg.
You will also have problems using the factory with nested classes. Only the outer class will be able to provide overrides by type, but string based overrides by name are global. So even if you nested the inner class, scopes other than the outer class will be able to provide it as an override by string name.
I changed the code to remove the field macros and this runs. So it seems like this is supported if you can give up the field automation macros: http://www.edaplayground.com/x/i5
class inception_level_1 extends uvm_test;
`uvm_component_utils(inception_level_1)
function new(string name = "inception_level_1", uvm_component parent = null);
super.new(name, parent);
endfunction
class inception_level_2 extends uvm_object;
int a;
`uvm_object_utils(inception_level_2)
function new(string name = "inception_level_2");
super.new(name);
endfunction
endclass
endclass
in general it does work. however there are situations where UVM uses shortcuts which conflict with the class-in-class scenario. examples are
string based factory (inception_level_2 can only be registered once despite that foo:inception_level_2 and bla::inception_level_2 would be different classes)
name lookup collision (here for uvm_report_warning which should goto uvm_pkg::uvm_report_warning and not to the enclosing class uvm_component::uvm_report_warning)
... etc
Related
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
I have the following transaction:
typedef enum {READ = 0, WRITE = 1} direction_enum;
//Transaction
class axi_transaction extends uvm_sequence_item();
bit id = 0; //const
bit [31:0] addr;
bit [2:0] size = 0'b100;//const
direction_enum rw;
bit [31:0] transfers [$];
//factory registration
`uvm_object_utils_begin(axi_transaction)
`uvm_field_int(id, UVM_ALL_ON)
`uvm_field_int(addr, UVM_ALL_ON)
`uvm_field_int(size, UVM_ALL_ON)
`uvm_field_enum(rw, UVM_ALL_ON)
`uvm_field_int(transfers, UVM_ALL_ON)
`uvm_object_utils_end
//constructor
function new(string name = "axi_transaction");
super.new(name);
endfunction: new
endclass: axi_transaction
I want to extend the new function, so I can initializes the transaction in the sequence with arguments which initialize some of the transaction members (like addr, transfers) by:
ax_trx = axi_transaction::type_id::create();
How to write the constructor of the transaction and how do I initialize the transaction from the sequencer?
You cannot add arguments to the class constructor when using the UVM factory. In general this is not good OOP programing practice for re-use because if you do add arguments to either the base class or extended class, you have to modify every place where the class gets constructed.
A better option is to use the uvm_config_db or set the individual fields you need to after constructing the object.
ax_trx = axi_transaction::type_id::create();
ax_trx.addr = some_address
ax_trx.transfers = '{word1,word2,word3};
You can use uvm_config_db class for initialisation.
You can set the value using following syntax. And then you can get that value inside the constructor of that class.
uvm_config_db#(int)::set(this,“my_subblock_a”,“max_cycles”,max_cycles)
uvm_config_db#(int)::get(this,“”, “max_cycles”,max_cycles)
For more information of "uvm_config_db", you can refer to the following paper.
https://www.synopsys.com/Services/Documents/hierarchical-testbench-configuration-using-uvm.pdf
class my_a;
int member1 = 1;
endclass
class my_ea extends my_a;
int member1 = 2;
endclass
Now when I do
my_a A;
my_ea EA;
EA =new();
A=EA;
EA = new(); has given handle to object of type my_ea to class variable EA.
A=EA; passes the same handle (pointer value which points to object of my_ea) to A. So, A.member1 should refer to value 2.
But it refers to value 1. Why?
So far, System-Verilog does not allow overriding of class variables. Only virtual methods can be overridden.
There is nothing like virtual for class members, so parent class can never directly access them. When using class_object.member, the particular class is referred to. Henceforth, this is not possible.
You cannot redefine an existing member by extending a class. You should use virtual methods to access (get/set) them. For instance, I added "get_member1" function to your code, and it returns 2 when called from a base class handle as you wanted.
class my_a;
int member1 = 1;
virtual function int get_member1();
return member1;
endfunction
endclass
class my_ea extends my_a;
int member1 = 2;
virtual function int get_member1();
return member1;
endfunction
endclass
module tb;
initial begin
my_a A;
my_ea EA;
EA =new();
A=EA;
$display("%0d", A.get_member1());
end
endmodule
You can similarly define "set_member1(int value)" function and use it to change its value.
In your case A.member1 should return the original member of its own class. When you are overriding a class members, you are basically hiding the overridden members. Super/base class can never access overridden member in its subclass.
As far as I know, only method identified with virtual, randomize() function and class constraint can be overridden without hiding them from the base class - thus they allow base class to reference to them (polymorphism)
For more info, please find here IEEE 1800-2012 in section 8.14 Overridden members.
I'm trying to override a sequence by instance.
An example code will describe it best:
class my_vir_seq extends base_vir_seq;
my_seq_c seq1, seq2;
`uvm_object_utils_begin(my_vir_seq)
`uvm_field_object(seq1, UVM_ALL_ON)
`uvm_field_object(seq2, UVM_ALL_ON)
`uvm_object_utils_end
`uvm_declare_p_sequencer(v_seqr)
function new(string name = "my_vir_seq");
super.new(name);
endfunction // new
virtual task body();
`uvm_do_on(seq1, p_sequencer.my_seqr)
`uvm_do_on(seq2, p_sequencer.my_seqr)
endtask // body
endclass
class my_err_vir_seq extends my_vir_seq;
my_err_seq_c seq3;
`uvm_object_utils_begin(my_err_vir_seq)
`uvm_field_object(seq3, UVM_ALL_ON)
`uvm_object_utils_end
`uvm_declare_p_sequencer(v_seqr)
function new(string name = "my_err_vir_seq");
super.new(name);
my_seq_c::type_id::set_inst_override(my_err_seq_c::get_type(), "sve.v_seqr.my_err_vir_seq.seq2" );
endfunction // new
endclass
My aim is to only override seq2 with seq3 (its type extends seq2's type).
I don't get any errors, but the original sequence runs,
What am I doing wrong?
Thanks in advance,
Izhar
Doing type overrides by instance is (I think) conceptually intended for instances of classes that derive from uvm_component, because they have a specific hierarchical path.
There is a trick to do it for sequences as well, using the sequencer's path as an argument to set_inst_override(...) (kind of what you tried). You need to do a few changes to your sequence to support this, though. When creating seq1 and seq2 you have to give them a context (shown only for seq2) so that the factory can find them:
// get_full_name() is the third argument
// - the second argument is empty, it's not a typo
seq2 = my_seq_c::type_id::create("seq2", , get_full_name());
After you created your sequence, you can start it using start(...):
seq2.start(p_sequencer.my_seqr, this);
The idea is from a DVCon 2013 paper that you can find here: DVCon 2013 paper
I am confused with the following SystemVerilog construct used for registering the UVM test with the factory:
class random_test extends uvm_test;
`uvm_component_utils(random_test);
...
function new (...
Here we have a definition of the class random_test, and inside of the definition we call a method whereas its argument is the class that is being defined.
So here are my questions:
Is `uvm_component_utils being called at time 0 even before any object was constructed out of random_test class?
How can we pass a class to `uvm_component_utils in that class definition?
Thanks.
`uvm_component_utils is not a method, it is a macro which is evaluated at compile time.
You can see what the macro does in the UVM source code. Take a look at src/macros/uvm_object_defines.svh within the UVM distribution.
Your example for class random_test will expand to something like this:
typedef uvm_component_registry #(random_test,"random_test") type_id;
static function type_id get_type();
return type_id::get();
endfunction
virtual function uvm_object_wrapper get_object_type();
return type_id::get();
endfunction const static string type_name = "random_test";
virtual function string get_type_name ();
return type_name;
endfunction
I was studying UVM and building some tests and had the same error. After some time searching about i figure out a detail that is your code too.
in:
class random_test extends uvm_test;
`uvm_component_utils(random_test);
...
function new (...
We don't need the semicolon after `uvm_component_utils(random_test)
So, the correct code is:
class random_test extends uvm_test;
`uvm_component_utils(random_test)
...
function new (...
Best Regards