Need to put UVM reg model handles in an array - system-verilog

I'm trying to combine the register block handles into an array so that I can access them using an index.
For example lets say I have the below register class :
class ral_sys_traffic extends uvm_reg_block;
rand ral_block_traffic_cfg1 cfg_1;
rand ral_block_traffic_cfg2 cfg_2;
`uvm_object_utils(ral_sys_traffic)
function new(string name = "traffic");
super.new(name);
endfunction
function void build();
this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
this.cfg1 = ral_block_traffic_cfg1::type_id::create("cfg1",,get_full_name());
this.cfg1.configure(this, "");
this.cfg1.build();
this.default_map.add_submap(this.cfg1.default_map, `UVM_REG_ADDR_WIDTH'hF4402000);
this.cfg2 = ral_block_traffic_cfg2::type_id::create("cfg2",,get_full_name());
this.cfg2.configure(this, "");
this.cfg2.build();
this.default_map.add_submap(this.cfg2.default_map, `UVM_REG_ADDR_WIDTH'hF4403000);
endfunction
endclass
I will have to access the registers with something like this:
regs.cfg_1.REG1_EN....
regs.cfg_2.REG1_EN....
However, I want to keep both the register blocks in an array so that I can do something like :
regs.cfg[0].REG1_EN...
regs.cfg[1].REG1_EN...
To accomplish this, I modified my code as follows:
class ral_sys_traffic extends uvm_reg_block;
//rand ral_block_traffic_cfg1 cfg_1;
//rand ral_block_traffic_cfg2 cfg_2;
uvm_reg_block ral_cfg[];
`uvm_object_utils(ral_sys_traffic)
function new(string name = "traffic");
super.new(name);
ral_cfg = new[2];
endfunction
function void build();
this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
this.ral_cfg[0] = ral_block_traffic_cfg1::type_id::create("ral_cfg[0]",,get_full_name());
this.ral_cfg[0].configure(this, "");
this.ral_cfg[0].build();
this.default_map.add_submap(this.ral_cfg[0].default_map, `UVM_REG_ADDR_WIDTH'hF4402000);
this.ral_cfg[1] = ral_block_traffic_cfg2::type_id::create("ral_cfg[1]",,get_full_name());
this.ral_cfg[1].configure(this, "");
this.ral_cfg[1].build();
this.default_map.add_submap(this.cfg2.default_map, `UVM_REG_ADDR_WIDTH'hF4403000);
endfunction
endclass
ral_block_traffic_cfg1 and ral_block_traffic_cfg2 classes extend from uvm_reg_block class.
The above code is throwing an error at
this.ral_cfg[0].build();
saying that "build is not a class item" and there is no error at:
this.ral_cfg[0].configure(this, '');
The original source is in the EDA playground here : edaplayground.com/x/4Xab
Is there any way around this issue or any other workarounds?
Any help is appreciated.
EDIT : made modification to the edaplayground link for more accurate representation of the issue

You need to create an array of ral_block_traffic_cfg objects:
rand ral_block_traffic_cfg cfg [2];
You created an array of type uvm_reg_block, named it ral_cfg, but it has nothing to do with the ral_block_traffic_cfg object.
Then, inside the build function, use this:
function void build();
this.default_map = create_map("", 0, 4, UVM_LITTLE_ENDIAN, 0);
this.cfg[0] = ral_block_traffic_cfg::type_id::create("cfg[0]",,get_full_name());
this.cfg[1] = ral_block_traffic_cfg::type_id::create("cfg[1]",,get_full_name());
this.cfg[0].configure(this, null);
this.cfg[1].configure(this, null);
this.cfg[0].build();
this.cfg[1].build();

It is because uvm_reg_block class is extended from uvm_object and build is not a function in uvm_reg_block class.
Check details on https://verificationacademy.com/verification-methodology-reference/uvm/docs_1.2/html/files/reg/uvm_reg_block-svh.html

Related

DPI-C export of a task defined inside a SystemVerilog class

Would it be possible to export to C a task defined inside a SystemVerilog class as the following?
class packet_bfm_t;
int id = 0;
export "DPI-C" task send; // Is this possible and legal to call from C code?
function new (int my_id = 0);
id = my_id;
endfunction : new
task send (int data);
#1ns;
$display ("data = %h", data);
endtask : send
endclass : packet_bfm_t
The DPI does not alow exporting a class method. Same problem as calling a C++ class method from C. You would have to define and export a non-class task wrapper that uses an Id or whatever is needed to look up the class object in a table. The you can call the method on that class object.

Distinguishing between local data member and child-class data member in an inline constraint

I have a class with a rand data member i. This class (child) is a member of class parent, which also has a data member i. I would like to constrain the value of i in the child class to be the same as the value of i in the parent class. I want to do something like:
c.randomize with {i==this.i;};
but the this.i doesn't seem to refer to the i data member of the parent class. (Why?)
I can do this:
function void f;
int dummy = i;
c.randomize with {i==dummy;};
endfunction
or this:
function void f;
c.randomize with {i==m.blk.p.i;}; // yuck!
endfunction
but wonder if there is a better (built-in, non-hacky) way of distinguishing between the two is.
MCVE:
class child;
rand int i;
endclass
class parent;
child c = new;
int i=1;
function void f;
c.randomize with {i==this.i;};
endfunction
endclass
module m;
initial begin : blk
parent p = new;
p.f;
$display("%p", p);
end
endmodule
https://www.edaplayground.com/x/2_8P
You want {i==local::i}. See section 18.7.1 of the 1800-2017 LRM
The reason this.i does not do what you expect is the combination of these two rules:
all class methods, including the built-in randomize method, have a built-in this argument. So c.method(args) is really method(args, c) and this becomes a variable local to the method set to the value of c
Identifiers within the with clause try to bind into the scope being randomized first before searching locally at the point where calling randomize().
So i and this.i refer to the same class variable just as if you wrote
class A;
bit i;
function void method;
i = 1;
this.i = 2;
endfunction
endclass

Why should be used in twice "new" in systemverilog?

Would you let me know why do we have to have the "new" keyword in twice in systemverilog?
​
class MyClass;
int number;
function new();
number = 0;
endfunction
endclass
module test;
MyClass test1 = new();
endmodule
As you can see, there are used in twice "new" keyword.
Could you let me know why do we need to use in twice?
function new();
number = 0;
endfunction
provides implementation of the function new(). When we call:
MyClass test1 = new();
we are creating test1. In order to create it, we call function new(), whose implementation we defined above. When we call new() it will ensure that property number of test1 is initialized to 0 (because that is what is happening inside of new()).
I hope my explanation is clear.

Is it possible to call new in SystemVerilog outside an assign statement?

This is how I usely call new in SystemVerilog:
class A;
endclass
A a = new();
But sometimes, I don't need a local object, I just want to send it directly to a function taking an A. Is there a way to call the new function explicitly here:
function use_a(A obj);
endfunction
use_a(new()); // <--- How to write this call to specify which new to call?
use_a(A::new()); // <--- new not expected here :(
Unfortunately, SystemVerilog's syntax does not allow this. The special new method is not a static method, and a class handle has to exist in some variable because of the way class memory management is defined. You could get around this by wrapping new around a static method:
class A;
static function A create();
create = new();
endfunction
endclass
...
use_a(A::create());
BTW, the UVM has create methods in the BCL and you almost never need to call new() directly.

SystemVerilog- How to write a constructor with initialization?

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