弄UVM的验证工程的过程种,发现之前resource这块学得不到位,来这补充一下,先补充uvm_config_db,就拿我自己那个工程的代码来举例子说明uvm_config_db是怎么用的。
我的工程中主要是对virtual interface用到了config_db,流程是这样的:
1.在testbench中例化了interface,然后通过config_db::set来把interface这个样的一个virtual interface放进resource_pool里面,resource_pool也就是两张表。放起来的目的是为了让需要使用virtual interface的component可以获取这个virtual interface。
module tb;
reg clk;
always #10 clk =~ clk;
DUT_if _if (clk);
float64_add_DUT_RTL float64_add_DUT_RTL_1(
.clk(clk),
.rstN(_if.rstN),
.start(_if.start),
.fin(_if.fin),
.a(_if.a),
.b(_if.b),
.return_value(_if.return_value));
test t0;
initial begin
clk <= 0;
uvm_config_db#(virtual DUT_if)::set(null, "uvm_test_top", "DUT_vif", _if);//注册interface
run_test("test");
end
initial begin
$dumpfile ("dump.vcd");
$dumpvars;
end
endmodule
config_db::set一共有4个参数。
头两个参数的作用是合起来形成一个范围,只有在这个范围内的单位才可以通过config_db::get来获得我们set的东西。
第三个参数是我们自己瞎起的一个名字,就是个string型的标签,到时候系统在执行config_db::get的时候,就依据我们起的这个名字以及我们规定的类型(在这里类型就是virtual DUT_if)来在资源池里找我们要的东西。
第四个参数就是我们正儿八经要放进资源池的东西,我想应该就是放了个地址进type表和name表里面。
2.那么谁要用到virtual interface呢,是agent中的monitor,driver。于是我们这么做,在test中通过config_db::get拿到已经写入资源池的virtual interface,然后通过config_db::set来重新设置一个virtual interface,并且设置它的范围是例化出来的ageent a0下面的所有单元都有权利调用它。
class test extends uvm_test;
`uvm_component_utils(test)
function new(string name = "test", uvm_component parent=null);
super.new(name, parent);
endfunction
env e0;
virtual DUT_if vif;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
e0 = env::type_id::create("e0", this);
if (!uvm_config_db#(virtual DUT_if)::get(this, "", "DUT_vif", vif))
`uvm_fatal("TEST", "Did not get vif")
uvm_config_db#(virtual DUT_if)::set(this, "e0.a0.*", "DUT_vif", vif);//
endfunction
这里我们注意,get函数的头两个参数就很简单了,因为跟set一样,头两个参数的作用只是指定范围,正常情况下都是在哪用在哪get,应该不会有故意要在A类里给B类get一个资源的这种操作,所以无脑this和""就可以了。
后两个参数就是怎么set的怎么get。
这样config_db做资源的传递就完成了。
3.最后是具体到真正的使用者怎么用这个virtual interface。
monitor的run_phase里面:
class monitor extends uvm_monitor;//monitor属于agent(a0),所以它是有权限来取vif的
`uvm_component_utils(monitor)
function new(string name="monitor", uvm_component parent=null);
super.new(name, parent);
endfunction
uvm_analysis_port #(transaction_item) mon_analysis_port;
virtual DUT_if vif;
uvm_event ev;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual DUT_if)::get(this, "", "DUT_vif", vif))
`uvm_fatal("MON", "Could not get vif")
mon_analysis_port = new ("mon_analysis_port", this);
endfunction
driver的run_phase里面:
class driver extends uvm_driver #(transaction_item);
`uvm_component_utils(driver)
function new(string name = "driver", uvm_component parent=null);
super.new(name, parent);
endfunction
//声明interface
virtual DUT_if vif;
uvm_event ev;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if (!uvm_config_db#(virtual DUT_if)::get(this, "", "DUT_vif", vif))
`uvm_fatal("DRV", "Could not get vif")
ev = uvm_event_pool::get_global("ev_ab");
endfunction
下面是之前记录的内容。
首先要梳理一下resource相关几个类的逻辑关系。
首先我们看看官方对于uvm_config_db的介绍:
The uvm_resource_db class provides a convenience interface for the resources facility. In many cases basic operations such as creating and setting a resource or getting a resource could take multiple lines of code using the interfaces in uvm_resource_base or uvm_resource#(T). The convenience layer in uvm_resource_db reduces many of those operations to a single line of code.
可以看出来,这个uvm_config_db提供的是对uvm_resource_base和uvm_resource#(T)这两个类里的内容的一些整体操作。具体可以看下面的图:
往下看uvm_resource_base和uvmresource#(T),首先是从uvmobject分出来的uvm_resource_base。
再往下是uvm_resource,它的作用是对uvm_resource_pool进行操作。
最下一层就是正主了,我们的uvm_resource_pool,它里面就是两个放资源的联合数组。一个按名字排的,一个按类型排的。
接下来梳理一下uvm_config_db背后的逻辑,一个常见的使用方法是这样的:先在testbench里面调用uvm_config_db::set方法做出设置,然后在子模块比如driver中调用uvm_config_db::get方法取得上级做好的设置。
下面是在testbench中的设置:
module top;
import uvm_pkg::*;
reg clk;
jelly_bean_if jb_slave_if(clk);
jelly_bean_taster jb_taster(jb_slave_if);
initial begin // clock generation
clk = 0;
#5ns ;
forever #5ns clk = ! clk;
end
initial begin
uvm_resource_db#(virtual jelly_bean_if)::set
(.scope("ifs"), .name("jelly_bean_if"), .val(jb_slave_if));
run_test();
end
endmodule: top
我们还是老办法,从这里的set函数开始,慢慢剥洋葱往里看里面到底在干什么。
下面来康康set函数内部的实现:(属于uvm_config_db类)
// function: set
//
// Create a new resource, write a ~val~ to it, and set it into the
// database using ~name~ and ~scope~ as the lookup parameters. The
// ~accessor~ is used for auditing.
static function void set(input string scope, input string name,
T val, input uvm_object accessor = null);
rsrc_t rsrc = new(name, scope);
rsrc.write(val, accessor);
rsrc.set();
if(uvm_resource_db_options::is_tracing())
m_show_msg("RSRCDB/SET", "Resource","set", scope, name, accessor, rsrc);
endfunction
其中用到的rsrc_t是在类里面定义的自己uvm_resource类型 (属于uvm_config_db类)
class uvm_resource_db #(type T=uvm_object);
typedef uvm_resource #(T) rsrc_t;
所以现在我们明白了,我们所使用的uvm_resource_db ::set函数,本质是调用了uvm_resource里面的write和set函数。
// Function: write
// Modify the object stored in this resource container. If the
// resource is read-only then issue an error message and return
// without modifying the object in the container. If the resource is
// not read-only and an ~accessor~ object has been supplied then also
// update the accessor record. Lastly, replace the object value in
// the container with the value supplied as the argument, ~t~, and
// release any processes blocked on
// <uvm_resource_base::wait_modified>. If the value to be written is
// the same as the value already present in the resource then the
// write is not done. That also means that the accessor record is not
// updated and the modified bit is not set.
function void write(T t, uvm_object accessor = null);
if(is_read_only()) begin
uvm_report_error("resource", $sformatf("resource %s is read only -- cannot modify", get_name()));
return;
end
// Set the modified bit and record the transaction only if the value
// has actually changed.
if(val == t)
return;
record_write_access(accessor);
// set the value and set the dirty bit
val = t;
modified = 1;
endfunction
write的作用主要是把uvm_resource类定义的val值设成通过write函数传递进来的,val这个值是给uvm_resource类下其它一些功能使用的,跟我们今天讨论的内容关系倒不大。
class uvm_resource #(type T=int) extends uvm_resource_base;
typedef uvm_resource#(T) this_type;
// singleton handle that represents the type of this resource
static this_type my_type = get_type();
// Can't be rand since things like rand strings are not legal.
protected T val;
下面是正主,就是set函数,它就是正经往uvm_resource_pool里面放东西的人了。
//-----------
// Group: Set
//-----------
// Function: set
//
// Add a new resource to the resource pool. The resource is inserted
// into both the name map and type map so it can be located by
// either.
//
// An object creates a resources and ~sets~ it into the resource pool.
// Later, other objects that want to access the resource must ~get~ it
// from the pool
//
// Overrides can be specified using this interface. Either a name
// override, a type override or both can be specified. If an
// override is specified then the resource is entered at the front of
// the queue instead of at the back. It is not recommended that users
// specify the override parameter directly, rather they use the
// <set_override>, <set_name_override>, or <set_type_override>
// functions.
//
function void set (uvm_resource_base rsrc,
uvm_resource_types::override_t override = 0);
uvm_resource_types::rsrc_q_t rq;
string name;
uvm_resource_base type_handle;
// If resource handle is ~null~ then there is nothing to do.
if(rsrc == null)
return;
// insert into the name map. Resources with empty names are
// anonymous resources and are not entered into the name map
name = rsrc.get_name();
if(name != "") begin
if(rtab.exists(name))
rq = rtab[name];
else
rq = new();
// Insert the resource into the queue associated with its name.
// If we are doing a name override then insert it in the front of
// the queue, otherwise insert it in the back.
if(override & uvm_resource_types::NAME_OVERRIDE)
rq.push_front(rsrc);
else
rq.push_back(rsrc);
rtab[name] = rq;
end
// insert into the type map
type_handle = rsrc.get_type_handle();
if(ttab.exists(type_handle))
rq = ttab[type_handle];
else
rq = new();
// insert the resource into the queue associated with its type. If
// we are doing a type override then insert it in the front of the
// queue, otherwise insert it in the back of the queue.
if(override & uvm_resource_types::TYPE_OVERRIDE)
rq.push_front(rsrc);
else
rq.push_back(rsrc);
ttab[type_handle] = rq;
endfunction
到此我们就明白了,整个调用uvm_resource_db中set的过程,就是一层层往下调用最后往整个工程所共用的两张资源表里面放东西的过程。