Tuesday, 6 November 2018

Automatic configuration through uvm_config_db

UVM has an internal database table in which we can store values (of any data type including user defined data type) with unique name and can be retrieved later by some other testbench component.
There are two interface through which we can access configuration database.
1) uvm_resource_db, 2) uvm_config_db

Uvm_config_db is a great way to pass different resources (variable/object/virtual interface) within UVM testbench component hierarchy.

The uvm_config_db class provides a convenience interface on top of the uvm_resource_db to simplify the basic interface that is used for configuring uvm_component instances.

Let’s go through basic example:
-------------------------------------------------------------------------
class my_object extends uvm_object;
integer a;
`uvm_object_utils_begin(my_object)
`uvm_field_int(a, UVM_DEFAULT | UVM_DEC)
`uvm_object_utils_end
function new(string name = "my_object");
super.new(name);
endfunction : new
endclass : my_object
class my_cfg extends uvm_object;
int unsigned idx;
int unsigned idx1;
bit [47:0] idx2;
bit [47:0] idx22;
logic [127:0] idx3;
string abc;
my_object obj;
int ary_int[5];
real r1;
`uvm_object_utils_begin(my_cfg)
`uvm_field_int(idx, UVM_DEFAULT | UVM_DEC)
//`uvm_field_int(idx1, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(idx2, UVM_DEFAULT | UVM_HEX)
`uvm_field_int(idx22, UVM_DEFAULT | UVM_HEX)
`uvm_field_int(idx3, UVM_DEFAULT | UVM_HEX)
`uvm_field_string(abc, UVM_DEFAULT)
`uvm_field_object(obj, UVM_DEFAULT)
`uvm_field_sarray_int(ary_int, UVM_DEFAULT)
`uvm_field_real(r1, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "my_cfg");
super.new(name);
endfunction : new
endclass : my_cfg
class my_env extends uvm_env;
my_cfg cfg;
`uvm_component_utils_begin(my_env)
`uvm_field_object(cfg, UVM_DEFAULT)
`uvm_component_utils_end
function new (string name = "my_env", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
//Not required as we are getting resource using get method of uvm_config_db
//super.build_phase(phase);
if (!uvm_config_db#(my_cfg)::get(this, "", "cfg", cfg)) begin
`uvm_fatal(get_name(), $sformatf("Need to set cfg for this"))
end
if (cfg == null) begin
`uvm_fatal(get_name(), $sformatf("cfg is NULL"))
end
else begin
`uvm_info(get_name(), $sformatf("cfg is\n%s", cfg.sprint()), UVM_LOW)
end
endfunction : build_phase
endclass : my_env
class my_test extends uvm_test;
my_cfg cfg;
my_env env;
`uvm_component_utils_begin(my_test)
`uvm_field_object(env, UVM_DEFAULT)
`uvm_field_object(cfg, UVM_DEFAULT)
`uvm_component_utils_end
function new (string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
env = my_env :: type_id :: create ("env", this);
cfg = my_cfg :: type_id :: create ("cfg");
cfg.obj = my_object:: type_id ::create ("obj");
cfg.idx = 5;
cfg.idx1 = 25;
cfg.idx2 = 48'hFFFF_FFFF_FFFF;
cfg.idx22 = 48'hFFFF_FFFF_FFFF;
cfg.idx3 = 531;
cfg.abc = "auto_config";
cfg.obj.a = 10;
for(int unsigned i=0; i< $size(cfg.ary_int); i++) begin
cfg.ary_int[i] = i;
end
cfg.r1 = 543.123456;
uvm_config_db#(my_cfg)::set(this, "env", "cfg", cfg);
endfunction : build_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
//Output:
// UVM_INFO resource_get_set_config_db.sv(64) @ 0: uvm_test_top.env [env] cfg is
// ---------------------------------------------
// Name Type Size Value
// ---------------------------------------------
// cfg my_cfg - @359
// idx integral 32 'd5
// idx2 integral 48 'hffffffffffff
// idx22 integral 48 'hffffffffffff
// idx3 integral 128 'h213
// abc string 11 auto_config
// obj my_object - @360
// a integral 32 'd10
// ary_int sa(integral) 5 -
// [0] integral 32 'h0
// [1] integral 32 'h1
// [2] integral 32 'h2
// [3] integral 32 'h3
// [4] integral 32 'h4
// r1 real 64 543.123456
// ---------------------------------------------
-------------------------------------------------------------------------

UVM provides automatic configuration of resources (variable/object etc…) which are registered in the respective component (which is extended from uvm_component) by calling apply_config_settings method.
Any component which is extended from uvm_component should call super.build_phase(phase) to execute apply_config_settings method.
Let's go through basic example of automatic configuration.
-------------------------------------------------------------------------
class my_object extends uvm_object;
integer a;
`uvm_object_utils_begin(my_object)
`uvm_field_int(a, UVM_DEFAULT | UVM_DEC)
`uvm_object_utils_end
function new(string name = "my_object");
super.new(name);
endfunction : new
endclass : my_object
class my_cfg extends uvm_object;
int unsigned idx;
int unsigned idx1;
bit [47:0] idx2;
bit [47:0] idx22;
logic [127:0] idx3;
string abc;
my_object obj;
int ary_int[5];
real r1;
`uvm_object_utils_begin(my_cfg)
`uvm_field_int(idx, UVM_DEFAULT | UVM_DEC)
// "idx1" is not registered in factory but still get automatically configured
// as cfg (instance of my_cfg in env) is registered in factory using `uvm_field_object
//`uvm_field_int(idx1, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(idx2, UVM_DEFAULT | UVM_HEX)
`uvm_field_int(idx22, UVM_DEFAULT | UVM_HEX)
`uvm_field_int(idx3, UVM_DEFAULT | UVM_HEX)
`uvm_field_string(abc, UVM_DEFAULT)
`uvm_field_object(obj, UVM_DEFAULT)
`uvm_field_sarray_int(ary_int, UVM_DEFAULT)
`uvm_field_real(r1, UVM_DEFAULT)
`uvm_object_utils_end
function new (string name = "my_cfg");
super.new(name);
endfunction : new
endclass : my_cfg
class my_env extends uvm_env;
my_cfg cfg;
`uvm_component_utils_begin(my_env)
`uvm_field_object(cfg, UVM_DEFAULT)
`uvm_component_utils_end
function new (string name = "my_env", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
//if (!uvm_config_db#(my_cfg)::get(this, "", "cfg", cfg)) begin
// `uvm_fatal(get_name(), $sformatf("Need to set cfg for this"))
//end
if (cfg == null) begin
`uvm_fatal(get_name(), $sformatf("cfg is NULL"))
end
else begin
`uvm_info(get_name(), $sformatf("cfg is\n%s", cfg.sprint()), UVM_LOW)
end
endfunction : build_phase
endclass : my_env
class my_test extends uvm_test;
my_cfg cfg;
my_env env;
`uvm_component_utils_begin(my_test)
`uvm_field_object(env, UVM_DEFAULT)
`uvm_field_object(cfg, UVM_DEFAULT)
`uvm_component_utils_end
function new (string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
env = my_env :: type_id :: create ("env", this);
cfg = my_cfg :: type_id :: create ("cfg");
cfg.obj = my_object:: type_id ::create ("obj");
cfg.idx = 5;
cfg.idx1 = 25;
cfg.idx2 = 48'hFFFF_FFFF_FFFF;
cfg.idx22 = 48'hFFFF_FFFF_FFFF;
cfg.idx3 = 531;
cfg.abc = "auto_config";
cfg.obj.a = 10;
for(int unsigned i=0; i< $size(cfg.ary_int); i++) begin
cfg.ary_int[i] = i;
end
cfg.r1 = 543.123456;
//uvm_config_db#(my_cfg)::set(this, "env", "cfg", cfg); // Not working
uvm_config_db#(uvm_object)::set(this, "env", "cfg", cfg);
endfunction : build_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
//Output:
// UVM_INFO apply_config_settings_to_get_resource.sv(65) @ 0: uvm_test_top.env [env] cfg is
// ---------------------------------------------
// Name Type Size Value
// ---------------------------------------------
// cfg my_cfg - @358
// idx integral 32 'd5
// idx2 integral 48 'hffffffffffff
// idx22 integral 48 'hffffffffffff
// idx3 integral 128 'h213
// abc string 11 auto_config
// obj my_object - @359
// a integral 32 'd10
// ary_int sa(integral) 5 -
// [0] integral 32 'h0
// [1] integral 32 'h1
// [2] integral 32 'h2
// [3] integral 32 'h3
// [4] integral 32 'h4
// r1 real 64 543.123456
// ---------------------------------------------
-------------------------------------------------------------------------

But,
This is not recommended approach as it introduces extremely poor simulation performance and big confusion on what kind of field/resource it supports and does not supports.

Some of the types get automatically configured and while some are not.

Let’s go through one more example of automatic configuration,
-------------------------------------------------------------------------
class my_object extends uvm_object;
integer a;
`uvm_object_utils_begin(my_object)
`uvm_field_int(a, UVM_DEFAULT | UVM_DEC)
`uvm_object_utils_end
function new(string name = "my_object");
super.new(name);
endfunction : new
endclass : my_object
class my_env extends uvm_env;
int unsigned idx; // will get auto configured
// won't get auto configured as it is not registered in factory using `uvm_field_* macro
int unsigned idx1;
// will get auto configured as it is set using uvm_config_db#(int)::set
bit [47:0] idx2;
// won't get auto configured as it is set using uvm_config_db#(bit[47:0])::set
bit [47:0] idx22;
logic [127:0] idx3; // will get auto configured
string abc; // will get auto configured
// will get auto configured as it is set using base type (uvm_config_db#(uvm_object)::set)
my_object obj1;
// won't get auto configured as it is set using extended type (uvm_config_db#(my_object)::set)
my_object obj2;
// Unpack Array won't get auto configured
int ary_int[5];
// Real variable won't get auto configured
real r1;
`uvm_component_utils_begin(my_env)
`uvm_field_int(idx, UVM_DEFAULT | UVM_DEC)
//`uvm_field_int(idx1, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(idx2, UVM_DEFAULT | UVM_HEX)
`uvm_field_int(idx22, UVM_DEFAULT | UVM_HEX)
`uvm_field_int(idx3, UVM_DEFAULT | UVM_HEX)
`uvm_field_string(abc, UVM_DEFAULT)
`uvm_field_object(obj1, UVM_DEFAULT)
`uvm_field_object(obj2, UVM_DEFAULT)
`uvm_field_sarray_int(ary_int, UVM_DEFAULT)
`uvm_field_real(r1, UVM_DEFAULT)
`uvm_component_utils_end
function new (string name = "my_env", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
//if (!uvm_config_db#(my_cfg)::get(this, "", "cfg", cfg)) begin
// `uvm_fatal(get_name(), $sformatf("Need to set cfg for this"))
//end
`uvm_info(get_name(), $sformatf("In ENV build_phase \n%s", this.sprint()), UVM_LOW)
endfunction : build_phase
endclass : my_env
class my_test extends uvm_test;
my_env env;
int unsigned idx;
int unsigned idx1;
bit [47:0] idx2;
bit [47:0] idx22;
logic [127:0] idx3;
string abc;
my_object obj1;
my_object obj2;
int ary_int[10];
real r1;
`uvm_component_utils_begin(my_test)
`uvm_field_object(env, UVM_DEFAULT)
`uvm_field_int(idx, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(idx1, UVM_DEFAULT | UVM_DEC)
`uvm_field_int(idx2, UVM_DEFAULT | UVM_HEX)
`uvm_field_int(idx22, UVM_DEFAULT | UVM_HEX)
`uvm_field_int(idx3, UVM_DEFAULT | UVM_HEX)
`uvm_field_string(abc, UVM_DEFAULT)
`uvm_field_object(obj1, UVM_DEFAULT)
`uvm_field_object(obj2, UVM_DEFAULT)
`uvm_field_sarray_int(ary_int, UVM_DEFAULT)
`uvm_field_real(r1, UVM_DEFAULT)
`uvm_component_utils_end
function new (string name = "my_test", uvm_component parent = null);
super.new(name, parent);
endfunction : new
function void build_phase (uvm_phase phase);
super.build_phase(phase);
env = my_env :: type_id :: create ("env", this);
obj1 = my_object:: type_id ::create ("obj1");
obj2 = my_object:: type_id ::create ("obj2");
idx = 5;
idx1 = 25;
idx2 = 48'hFFFF_FFFF_FFFF;
idx22 = 48'hFFFF_FFFF_FFFF;
idx3 = 531;
abc = "auto_config";
obj1.a = 10;
obj2.a = 10;
for(int unsigned i=0; i< $size(ary_int); i++) begin
ary_int[i] = i;
end
r1 = 543.123456;
uvm_config_db#(int)::set(this, "env", "idx", this.idx);
uvm_config_db#(int)::set(this, "env", "idx1", this.idx1);
uvm_config_db#(int)::set(this, "env", "idx2", this.idx2);
uvm_config_db#(bit[47:0])::set(this, "env", "idx22", this.idx22); // Not working
uvm_config_db#(int)::set(this, "env", "idx3", this.idx3);
uvm_config_db#(string)::set(this, "env", "abc", this.abc);
uvm_config_db#(uvm_object)::set(this, "env", "obj1", this.obj1);
uvm_config_db#(my_object)::set(this, "env", "obj2", this.obj2); // Not working
for(int unsigned i=0; i< $size(ary_int); i++) begin
uvm_config_db#(int)::set(this, "env", $sformatf("ary_int_%0d", i), this.ary_int[i]);
end
uvm_config_db#(real)::set(this, "env", "r1", this.r1);
endfunction : build_phase
endclass : my_test
module top();
initial begin
run_test("my_test");
end
endmodule : top
//Output:
// UVM_INFO apply_config_setting_to_get_resource_2.sv(63) @ 0: uvm_test_top.env [env] In ENV build_phase
// ---------------------------------------------
// Name Type Size Value
// ---------------------------------------------
// env my_env - @352
// idx integral 32 'd5
// idx2 integral 48 'hffffffffffff
// idx22 integral 48 'h0
// idx3 integral 128 'h213
// abc string 11 auto_config
// obj1 my_object - @361
// a integral 32 'd10
// obj2 object - <null>
// ary_int sa(integral) 5 -
// [0] integral 32 'h0
// [1] integral 32 'h0
// [2] integral 32 'h0
// [3] integral 32 'h0
// [4] integral 32 'h0
// r1 real 64 0.000000
// ---------------------------------------------
-------------------------------------------------------------------------

Observation:
1)      If you set any object which is extended from uvm_object and set it into config_db using extended type it won't get automatically configured (obj2).
2)      If you set extended object into config_db using uvm_object as type then it will get automatically configured (obj1).
3)      Unpack array will not get automatically configured (ary_int).
4)      If resource (variable/object etc) is not registered in respective class using `uvm_field_* macro then it won't get automatically configured (idx1).
5)      Virtual interface won't get automatically configured as we can't register it using `uvm_field_* macro.
6)      If you set any vector (let’s say bit [47:0]) in config_db using uvm_config_db#(int)::set then it will get automatically configured. Even though width of int is 32 bit but still 48 bits will get automatically configured (idx2).
7)      But if you set same vector (bit [47:0]) in config_db using uvm_config_db#(bit[47:0])::set then it won't get automatically configured (idx22).


If you want to pass Unpack array using uvm_config_db then you can set it directly using for loop as done in above example. But this is not efficient approach, instead of that create a class (extended from uvm_object) that includes unpack arrays and then do set/get of that class using uvm_config_db.

Reference:
1) https://verificationacademy.com/forums/uvm/uvmconfigdb-usage-big-confusion

2 comments: