初版内容:
1)基础用法:我们在工程中主要是对virtual的interface用到了config_db,大概流程如下:
首先我们在顶层中例化了interface,然后就用config_db::set把interface这样一个virtual interface放入resource_pool中,resource_pool也就是两张表,之后在所需要的时候调用config_db::get获取即可,config_db::set有4个参数,前两个组成一个范围,这个范围内的单位才可以通过get获取我放入资源池的数据,第三个参数是标签参数,在get时放入对应标签才能找到对应数据,第四个参数是我们需要放入资源池的数据。
2)项目用法升级:
上面是基础用法,实际中我们在virtual sequencer中放入,在子agent中get后,再次set到子agent的各个子component中,完成数据在各个agent之间的随机调用。
3)底层原理分析:
Virtual_interface解释:
interface 和 module是一样的, 都是静态的变量, 也就是在程序开始时, 内存中就有了其实例.但是在class里使用virtual interface时之前有两部必须提前完成:
定义:将接口作为一个类进行定义。
实例化:在RTL级的顶层中对接口进行实例化。
总结:定义一个interface,且实例化多个后,如果没有定义virtual,则在任何一个实例中修改了某个信号值,在其他实例中都会受到影响。如果定义了virtual,则每个实例独立。如果该interface只有一个实例,可用可不用virtual,有多个实例,需要virtual。更好的办法是,都加上virtual。virtual interface只是申明一个handle, 就好像一个指针一样, 可以再程序进行中进行construct, 所以class里必须是virtual interface
Config解释:
首先在官网的介绍中,uvm_config_db提供的是对uvm_resource_base和uvm_resource#(T)这两个类里(由uvm_object分出来)的内容的一些整体操作,如下图:
图中的type和name就是刚才所说的放入数据的类型和标签。
最下一层就是uvm_resource_pool了,它里面就是两个放资源的联合数组。一个按名字排的,一个按类型排的。
最后梳理一下整个配置流程:
首先在顶层调用config_db::set,该源码中主要是对rsrc_t(uvm_config_db类)的初始化,然后调用uvm_resource里面的write和set函数。write的作用主要是把传递进来的数据赋值给uvm_resource类的val,这个值是给下其它一些功能使用的(这个我们不关心)。然后是set函数,这就是真正把数据放入资源池的操作了。
相应的config_db::get就是相应的逆向操作。获取配置的类型和标签,去资源池搜索目标数据,最后通过uvm_resource#(T)中的read函数读出数据。
二次更新内容:
我的工程中主要是对virtual interface用到了config_db,流程是这样的:
1.在TB(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个参数。
a. 头两个参数的作用是合起来形成一个范围,只有在这个范围内的单位才可以通过config_db::get来获得我们set的东西。
b. 第三个参数是我们自己瞎起的一个名字,就是个string型的标签,到时候系统在执行config_db::get的时候,就依据我们起的这个名字以及我们规定的类型(在这里类型就是virtual DUT_if)来在资源池里找我们要的东西。
c. 第四个参数就是我们正儿八经要放进资源池的东西,我想应该就是放了个地址进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
以上都是初级使用,在项目中不可能只有一个agent,下面升级下难度:(实用干货,高能注意)
当我们使用virtual_sequencer将多个agent包括起来时,你考虑过如何让config_db正确下发到对应的agent的组件没有?
这里我们可以采用分层法,如下所示:
可以将结构拆成三层,顶层下发到各个agent,agent收到后再次下发,最后使driver等拿到数据。
virtual_sequencer中的db进行set ---> 各个agent中的db先get再set ---> 各个组件的db进行get
暂时写到这,之后有空再补充。