如何在有效的使用uvm_config_db来搭建uvm验证环境对于许多验证团队来说仍然是一个挑战。一些验证团队完全避免使用它,这样就不能够有效利用它带来的好处;另一些验证团队却过多的使用它,这让验证环境变得不稳定。
本文讨论如何简单有效平衡的在验证环境中使用uvm_config_db,让它验证环境贡献最多的力量,却又不会成为验证环境搭建的累赘。
接下来=通过三部分来进行阐述:
-
uvm_config_db的使用方法
-
uvm_config_db的作用对象
-
uvm_config_db的具体应用
第一部分:uvm_config_db的使用方法
uvm_config_db作用是把验证环境的一些资源配置为类似全局变量一样,使得它对于整个验证环境来说是可见的。最常见的是在验证环境顶层通过它对一些组件进行配置,组件可以在建立的阶段读取这些配置实现组件不同工作模式的切换,下面是使用uvm_config_db的语法:
uvm_config_db#(T)::set(uvm_component cntxt, string inst_name, string field_name, T value);
uvm_config_db#(T)::get(uvm_component cntxt, string inst_name, string field_name, ref T value);
例子:
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if)
uvm_config_db#(virtual my_if)::get(this, "", "vif", vif)
在以上两个函数set和get是使用它时要调用的函数,set表示把要处理的资源放进全局可见的数据库,get表示从全局可见的数据库输出需要的资源,使用set和get函数时有五个参数需要制定,第一个是uvm_config_db类的参数#(T),T表示要set或get的资源的类型,它可以是虚拟接口,sequencer等等,第二个cntxt和第三个参数inst_name一起定义了uvm_config_db中set或get函数的作用范围。第四个参数决定了是对作用范围中的哪个对象或变量进行操作,第五个参数value会存储当前操作对象的句柄或者操作变量的值。需要注意的是,当制定第二个参数是一个uvm组件时,uvm会用它的全局名字取替换它,而全局名字会通过uvm的get_full_name来获取。
第二部分:uvm_config_db的作用对象
在uvm验证环境中并不是所有的资源我们都会用uvm_config_db去配置,这样会让我们的验证环境变得不可维护,下面是一些典型的在验证环境中使用uvm_config_db去配置的资源:
-
虚拟接口,虚拟接口提供了一个访问真实虚接口的入口,我们会把虚拟接口的句柄放到全局的数据库中,uvm中的这些组件会通过get函数拿到虚拟接口的句柄对接口数据进行操作。
-
配置数据,负责配置环境的类中包含许多可以控制验证环境的变量,它会改变这些变量,并且通过set函数把它放到我们全局的数据库中,其他的组件通过get函数来取到这些变量的值,再根据这些值去改变工作模式
-
sequencers,在uvm中sequencers负责把我们写的sequence进行排队送到driver上去,所以sequence需要有对sequencer的访问入口,我们同样通过uvm_config_db的方式来把sequencer的句柄传给sequence
第三部分:uvm_config_db的具体应用
上图是我们的一个设计实例,下面介绍针对上图的实例我们应该如何使用uvm_config_db,首先我们把上图中可以配置的资源用一张更清晰的表列出来,如下:
在上面的表中,一共有四列,分别列出了五个接口的描述,类型,传送的数据类型以及它们自己唯一性的名字,下面把它对应到uvm_config_db的调用函数set和get中。
对于第二列type类型,我们会把它放入uvm_config_db的第一个参数#(T)的位置,表示处理的数据类型是axi_if,如下:
uvm_config_db#(virtual vif axi_if)::get(...
uvm_config_db#(virtual vif axi_if)::set(..
而对于第三列,transaction是在配置sequencer时需要制定的传送类型,然后再把sequencer放到全局数据库中去,如下:
typedef uvm_sequencer#(axi_transaction) axi_sequencer_t;
uvm_config_db#(axi_sequencer_t)::set(...
uvm_config_db#(axi_sequencer_t)::get(...
对于identifier这一列会被放在前面语法解析部分中的inst_name部分,这一列的名字要求是唯一的,但是如果接口或者sequencer的类型不同,则允许重复,例如如果都是axi_if类型,那么要求名字必须具有唯一性,因为它们处于同一个数据库(axi_if数据库)中,而如果一个类型是ahb_if,另一个是axi_if,则不要求唯一性,因为它们处于不同数据库的区域中。
typedef uvm_sequencer#(axi_transaction) axi_sequencer_t;
uvm_config_db#( virtual axi_if )::set(cntxt, inst_name, "primary_master",...
uvm_config_db#(axi_sequencer_t)::set(cntxt, inst_name, "primary_master",...
uvm_config_db#( virtual axi_if )::get(cntxt, inst_name, "primary_master",...
uvm_config_db#(axi_sequencer_t)::get(cntxt, inst_name, "primary_master",...
上面为了能够更清楚的展示细节,所有的identifier都是用字符串,但在实际项目中为了以后的复用,我们会考虑定义一个package文件,把所有的identifier都用一个全局变量代替,这样方便以后维护,具体如下图:
// In a test level parameters package
// This code defines the unique string used to identify each interface in the resource database
// A parameter is used to eliminate runtime errors due to typing errors.
parameter string PRIMARY_MASTER = "primary_master";
parameter string SECONDARY_MASTER = "secondary_master";
parameter string CONFIG_MASTER = "config_master";
parameter string MEMORY_SLAVE = "memory_slave";
parameter string PERIPHERAL_SLAVE = "peripheral_salve";
配置interface到全局数据库
// In the module where the interfaces reside
uvm_config_db#(virtual axi_if)::set(null, "VIRTUAL_INTERFACES", PRIMARY_MASTERS, primary_axi_master_bus);
uvm_config_db#(virtual axi_if)::set(null, "VIRTUAL_INTERFACES", SECONDARY_MASTER, secondary_axi_master_bus);
uvm_config_db#(virtual apb_if)::set(null, "VIRTUAL_INTERFACES", CONFIG_MASTER, config_apb_master_bus);
uvm_config_db#(virtual axi_if)::set(null, "VIRTUAL_INTERFACES", MEMORY_SLAVE, axi_memory_slave_bus);
uvm_config_db#(virtual ahb_if)::set(null, "VIRTUAL_INTERFACES", PERIPHERAL_SALVE, peripheral_ahb_slave_bus);
配置sequencer为默认sequencer
//In the agents class declaration file
//Each protocol would have its own agent class: axi_agent, apb_agent, etc.
//For this example xxx represent axi,apb or ahb
class xxx_agent extends uvm_agent #(...
//This variable is a handle to thihs agents configuration class
xxx_configuration xxx_config;
function void build_phase(uvm_phase phase);
//Get this agents configuration
uvm_config_db#(xxx_configuration)::get(this, "", "AGENT_CONFIG", xxx_config);
...
//Place this agents sequencer in the uvm_config_db using the interface identifier
uvm_config_db#(uvm_sequencer #(xxx_transaction))::set(null, "SEQUENCERS", xxx_config.interface_name, xxx_sequencer);
配置发送数据波形可见
总结
本篇论文详细介绍了uvm_config_db在uvm验证环境中的应用(interface,config,sequencer),它的使用可以极大的方便我们对一些需要全局可见的变量或类的处理,让我们能够更容易的去管理验证环境中的一些组件,也在一定程度上方便了构建测试激励的验证人员,他们无须关注验证环境的细节便可以构建测试场景对设计行为进行验证。