目录
3.1 使用config_db传递virtual interface
六、config_db机制
1 UVM中的层次和路径
图1 UVM树形结构
uvm_component在创建实例之后会形成类似于树形的层次结构,my_case的实例化名就是uvm_test_top,这是在调用run_test()自动指定的。对于层次结构而言,每一个component在UVM树的层次名都是它的指针名。比如apb_env就是一个my_env类型的指针,对应my_case来说,它的层次结构是env.apb_env。
路径的概念和层次结构有所不同。一个component对应的路径,是相对于创建实例时传入的实例名,通常来说我们将实例名设置为和指针名一样,因此路径和层次结构基本是一样的。假如在创建drv时传入的实例名是driver:
drv=my_driver::type_id::create("driver",this);
它们drv相对于my_case的层次结构是env.apb_env.i_agt.drv,而其相对路径变成了env.apb_env.i_agt.driver。应该尽量避免这种情况。
uvm类库中有几个与层次结构和路径相关的内置方法,如get_full_name()、get_type_name() get_full_name、get()等,调用这些方法可以返回不同的结果:
get()//返回当前component的指针,类型为uvm_component
get_name()//返回实例化时传入的实例名,类型为string
get_type_name()//返回component的类名,类型为string
get_full_name()//返回component相对于uvm_root的路径,类型为string
2 常用的config_db函数
config_db机制主要用于在UVM验证平台间传递参数。常用的函数有set、get、exists、wait_modified等:
//向数据库存放类型为T的数据对象value
uvm_config_db#(type T)::set(uvm_component cntxt,string inst_name,string field_name,T value);
//从数据库获取类型为T的数据对象并赋值给value
uvm_config_db#(type T)::get(uvm_component cntxt,string inst_name,string field_name,T value);
//检查某个类型为T的数据对象是否存在在数据库中,返回检查结果给spell_chk
uvm_config_db#(type T)::exists(uvm_component cntxt,string inst_name,string field_name,bit spell_chk);
//阻塞等待某个类型为T的数据对象被更新值
uvm_config_db#(type T)::wait_modified(uvm_component cntxt,string inst_name,string field_name,);
set和get通常是成对出现的。set是发送参数,get是获取参数。第一个和第二个参数联合起来组成目标组件路径,目标相符的component才可以收信。第一个参数必须是uvm_component类型实例的指针,第二个参数是相对此实例指针的路径。如果收信目标为uvm_object类型的实例(如sequence),第一个参数可以设置为null(即uvm_root的指针),第二个参数则为此sequence相对于uvm_root的路径,由于sequence不在树形结构上,其相对路径可以设置为对应的sequencer的路径下加*。如果第一个参数设置为this,则第二个参数相对路径为空。第三个参数类似于通信窗口,set和get必须一致才可以进行数据交换。第四个参数则是实际需要传递或接收的对象。
对于exists函数,第四个参数为检查结果的返回值。而wait_modified没有第四个参数。
3 config_db的应用
3.1 使用config_db传递virtual interface
config_db通常用来向driver和monitor传递interface。在UVM验证平台中,通常只有driver和monitor通过interface与DUT交互。由于DUT是硬件世界,使用的是硬件端口;而driver和monitor通常为类,是软件世界,使用软件端口。interface是属于硬件的,传递给driver和monitor的是virtual interface指针,其指向真实的interface:
//top_tb中将virtual interface存放到数据库
module top_tb;
my_if.INPUT in0;
my_if.OUTPUT ot0;
initial begin
//null为uvm_root的指针,其等同于uvm_root::get()
uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.apb_env.i_agt.drv","vif",in0);
uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.apb_env.i_agt.mon","vif",in0);
uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.env.apb_env.o_agt.mon","vif",ot0);
end
...
endmodule
//driver获取virtual interface
function void my_driver::build_phase(uvm_phase phase);
super.build_phase(phase);
...
uvm_config_db#(virtual my_if)::get(this,"","vif",vif);
endfunction
//monitor获取virtual interface
function void my_monitor::build_phase(uvm_phase phase);
super.build_phase(phase);
...
uvm_config_db#(virtual my_if)::get(this,"","vif",vif);
endfunction
3.2 sequence传递参数
通常在做一些异常用例测试时,在sequence发送了transaction之后,会向scoreboard传递一个参数,scoreboard根据这个参数决定是否对收到的transaction进行检查。由于sequence不属于uvm_component类型,因此使用config_db发送对象时第一个参数不能设置为sequence的指针。
virtual task case0_vseq::body();
fork
`uvm_do_on(seq0,p_sequencer.sqr0)
`uvm_do_on(seq1,p_sequencer.ser1)
begin
uvm_config_db#(bit)::set(null,"uvm_test_top.apb_env.scb","flag",0);
#1000;
uvm_config_db#(bit)::set(null,"uvm_test_top.apb_env.scb","flag",1);
end
join
...
endtask
task my_scoreboard::main_phase(uvm_phase phase);
...
fork
while(1) begin
uvm_config_db#(bit)::get(this,"","flag",flag);
end
while(1) begin
if(flag) begin
...
end
join
...
endtask
如上代码,在sequence中set函数的第一个参数设置为null,即uvm_root的指针,第二个参数就是scoreboard相对于uvm_root的相对路径,这样就完成了参数的传递。
由于sequence在task phase中发送参数,其设置的时间往往是不固定的,这样scoreboard获取的参数可能存在遗漏或获取不到的情况。此时可以使用wait_modified任务,当检查的类型对象值没有更新,进程就会一直阻塞,直到值被更新。这样就可以在wait_modified之后使用get获取更新之后的参数:
virtual task case0_vseq::body();
fork
`uvm_do_on(seq0,p_sequencer.sqr0)
`uvm_do_on(seq1,p_sequencer.ser1)
begin
uvm_config_db#(bit)::set(null,"uvm_test_top.apb_env.scb","flag",0);
#1000;
uvm_config_db#(bit)::set(null,"uvm_test_top.apb_env.scb","flag",1);
end
join
...
endtask
task my_scoreboard::main_phase(uvm_phase phase);
...
fork
while(1) begin
//使用wait_modified阻塞等待参数更新
uvm_config_db#(bit)::wait_modified(this,"","flag");
uvm_config_db#(bit)::get(this,"","flag",flag);
end
while(1) begin
if(flag) begin
...
end
join
...
endtask
当我们向一个sequence传递参数时,由于sequence不是uvm_component类型,因此其实例指针不能作为 config_db的第一个参数,使用null指针和get_full_name()获得完整路径:
virtual function void my_env::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(seq_cfg)::set(this,"env.apb_env.i_agt.sqr.*","seq_cfg",seq_cfg0);
endfunction
virtual task my_sequence::body();
uvm_config_db#(seq_cfg)::get(null,get_full_name(),"seq_cfg",cfg);
endtask
3.3 设置default_sequence
我们通常使用default_sequence来启动sequence。通过config_db将sequence指针作为某个sequencer的run_time phase的default_sequence,default_sequence会自动调用start任务启动该sequence。此时定义在sequence中的类型为uvm_phase的指针starting_phase就指向了此sequencer的run_time phase,因此可以使用starting_phase控制objection:
virtual function void case0::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_object_wrapper)::set(this,"env.apb_env.i_agt.sqr.main_phase",
"default_sequence",vseq::type_id::get());
endfunction
设置default_sequence后不需要在sequencer中调用get获取,uvm_sequencer_base中已自动获取了default_sequence并调用了start任务启动接收到的sequence。
3.4 设置cfg
config_db可以沟通两个component,使它们交换数据。因此常用来设置env_cfg、agt_cfg、seq_cfg等配置信息,比如env向agt配置is_active值等:
virtual function void my_env::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(uvm_active_passive_enum)::set(this,"i_agt","is_active",UVM_ACTIVE);
uvm_config_db#(uvm_active_passive_enum)::set(this,"o_agt","is_active",UVM_PASSIVE);
i_agt=my_agent::type_id::create("i_agt",this);
o_agt=my_agent::type_id::create("o_agt",this);
endfunction
还可以设置sequence_library配置:
function void case0::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_seq.library seq_lib;
seq_lib=new("seq_lib");
seq_lib.selection_mode=UVM_SEQ_LIB_RAND;
seq_lib.min_random_count=10;
seq_lib.max_random_count=50;
uvm_config_db#(uvm_sequence_base)::set(this,"env.i_agt.sqr.main_phase",
"default_sequence",
seq_lib);
endfunction
4 跨层次设置和多重设置
一般来说config_db的set和get是成对出现的,但如果出现设置多次,而只获取一次,得到的值是哪次设置的值呢?UVM采取了优先检查发信结点层次,其次检查发信时间的原则。发信结点越高,即越接近uvm_root,则获取其设置的信息;其次,当发信结点一样,则检查设置的时间,时间越新,则信息越准确,那么就获取其设置的信息。
virtual function void case0::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(int)::set(this,"env.apb_env.i_agt.drv","count",10);
endfunction
virtual function void my_env::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(int)::set(this,"apb_env.i_agt.drv","count",99);
endfunction
假设case0和env都向drv发送了count参数,第一个参数即为发信结点,由于case0层次高于env,因此drv接收到的count参数为10;
virtual function void case0::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(int)::set(null,"uvm_test_top.env.apb_env.i_agt.drv","count",10);
endfunction
virtual function void my_env::build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(int)::set(null,"uvm_test_top.env.apb_env.i_agt.drv","count",99);
endfunction
假设将case0和env设置的发信结点都改为一样,由于build_phase是自顶向下执行的,因此env中的set时间较新,则drv获取的count参数为99。因此,set的第一个参数尽量设置为this。
当在一个结点多次set参数,那么会接收到最后一次设置的数值。
5 非直线设置和获取
当设置的结点和接收的结点处在UVM树的同一条枝干上,称为直线设置和获取。比如说uvm_test_top→env→i_agt→drv。当设置的结点和接收的结点位于UVM树的不同枝干上,则称为非直线设置和获取,如i_agt的drv和o_agt的mon。此时需要仔细设置第一个和第二个参数。第一个参数需要设置为设置和接收交汇的结点。
此时出现一个问题,由于build_phase是自顶向下执行,并且深度优先的。当i_agt的drv获取参数时,o_agt的mon可能还没有执行build_phase。应该避免这种情况的出现。
6 check_config_usage
config_db机制的功能强大,可以实现不同层次的参数配置。但因第二个路径参数为string类型,当设置错误时UVM并不会提示错误,因此难以debug。
针对这种情况,UVM提供了一个check_config_usage函数,它可以显示出截止到此函数调用时哪些参数被设置过但没有被获取过。由于config_db的set和get一般在build_phase中使用,因此check_config_usage一般在connect_phase调用:
virtual function void case0::connect_phase(uvm_phase phase);
super.connect_phase(phase);
check_config_usage();
endfunction
当然,此函数也可以在connect_phase之后的phase中调用。当有config_db被set而没有被get时,UVM会给出提示信息。但是要注意default_sequence是在build_phase中设置,却是被获取在sequencer的main_phase中。
7 config_db的调试
config_db机制有一些用于调试功能的函数。如check_config_usage函数,可以显示出被调用前所有被设置了但是从没被获取的参数。除此之外,还提供了print_config函数。
virtual function void case0::connect_phase(uvm_phase phase);
super.connect_phase(phase);
print_config(1);
endfunction
函数的参数表示是否递归查询,即遍历当前component及其子类的信息。它会遍历当前结点及其子结点,将其中所有被设置过的信息都显示出来。
通过命令行参数可以打开config_db的调试功能,打印出默认信息:
<sim command> +UVM_CONFIG_DB_TRACE