UVM(一)—— field automation、config_db、factory机制

1. field_automation机制

1.1 介绍
利用`uvm_field_* 宏,将类中的成员变量注册到UVM中,UVM可自动为其实现compare、copy、print、pack/unpack等方法,不需要自己去定义。解决 逐字段对transaction进行操作 的问题,极大地简化了验证平台的搭建,提高了效率。

1.2 用法
使用uvm_object_utils_begin和uvm_object_utils_end来实现my_transaction的factory注册,在这两个宏中间,使用uvm_field宏注册所有字段。uvm_field系列宏随着transaction成员变量的不同而不同。

1.3 相关宏

简单的uvm_field系列宏
`define uvm_field_int(ARG,FLAG)
`define uvm_field_real(ARG,FLAG)
`define uvm_field_enum(T,ARG,FLAG)
`define uvm_field_object(ARG,FLAG)
`define uvm_field_event(ARG,FLAG)
`define uvm_field_string(ARG,FLAG)
动态数组:
`define uvm_field_array_enum(ARG,FLAG)
`define uvm_field_array_int(ARG,FLAG)
`define uvm_field_array_object(ARG,FLAG)
`define uvm_field_array_string(ARG,FLAG)
静态数组:
`define uvm_field_sarray_int(ARG,FLAG)
`define uvm_field_sarray_enum(ARG,FLAG)
`define uvm_field_sarray_object(ARG,FLAG)
`define uvm_field_sarray_string(ARG,FLAG)
队列:
`define uvm_field_queue_enum(ARG,FLAG)
`define uvm_field_queue_int(ARG,FLAG)
`define uvm_field_queue_object(ARG,FLAG)
`define uvm_field_queue_string(ARG,FLAG)
联合数组:(第一个类型是存储数据类型,第二个类型是索引类型)
`define uvm_field_aa_int_string(ARG, FLAG)
`define uvm_field_aa_string_string(ARG, FLAG)
`define uvm_field_aa_object_string(ARG, FLAG)
`define uvm_field_aa_int_int(ARG, FLAG)
`define uvm_field_aa_int_int_unsigned(ARG, FLAG)
`define uvm_field_aa_int_integer(ARG, FLAG)
`define uvm_field_aa_int_integer_unsigned(ARG, FLAG)
`define uvm_field_aa_int_byte(ARG, FLAG)
`define uvm_field_aa_int_byte_unsigned(ARG, FLAG)
`define uvm_field_aa_int_shortint(ARG, FLAG)
`define uvm_field_aa_int_shortint_unsigned(ARG, FLAG)
`define uvm_field_aa_int_longint(ARG, FLAG)
`define uvm_field_aa_int_longint_unsigned(ARG, FLAG)
`define uvm_field_aa_string_int(ARG, FLAG)
`define uvm_field_aa_object_int(ARG, FLAG)

2. config_db机制

2.1 介绍
用来传递参数,一般有以下三种应用场景:
(1)传递virtual interface到环境中。
(2)设置单一变量值,例如int、string、enum等。
(3)传递配置对象到环境。

2.2 用法
set函数是寄信,而get函数是收信。如在某个测试用例的build_phase中可以使用如下方式寄信:

uvm_config_db#(int)::set(this,"env.i_agt.drv","pre_num",100);

其中第一个和第二个参数联合起来组成目标路径,与此路径符合的目标才能收信。第一个参数必须是一个uvm_component实例的指针,第二个参数是相对此实例的路径。第三个参数表示一个记号,用以说明这个值是传给目标中的哪个成员的,第四个参数是要设置的值

在driver中的build_phase使用如下方式收信:

uvm_config_db#(int)::get(this,"","pre num",pre num);

get函数中的第一个参数和第二个参数联合起来组成路径。第一个参数也必须是一个uvm_component实例的指针,第二个参数是相对此实例的路径。一般的,如果第一个参数被设置为this,那么第二个参数可以是一个空的字符串。第三个参数就是set函数中的第三个参数,这两个参必须严格匹配,第四个参数则是要设置的变量

2.3 使用实例
2.3.1 interface传递
在实现接口传递的过程中需要注意:
(1)在传递过程中的类型应当为virtual interface,即实际接口的句柄。
(2)接口传递应该发生在run_test()之前。这保证了在进入build phase之前,virtual interface已经被传递到uvm_config_db中。

interface intf1; 
    logic enable=0; 
endinterface 

class compl extends uvm component;
    `uvm_component_utils(comp1)
    virtual intf1 vif;

    function void build_phase(uvm phase phase); 
        if(!uvm config db#(virtual intf1):: get(this,"","vif", vif)) begin 
            uvm_error("GETVIF","no virtual interface is assigned")
        end 
        `uvm_info("SETVAL",$sformatf("vif. enable is %b before set", vif. enable), UVM LOW)
        vif.enable=1;
        `uvm info("SETVAL",$sformatf("vif. enable is b after set", vif. enable), UVM LOW)
    endfunction 
endclass

class testl extends uvm_test; 
    `uvm_component_utils(test1)
    comp1(c1) 

endclass 

intf1 intf(); 
initial begin 
    uvm_config_db#(virtual intf1)::set(uvm root::get(),"uvm test top. cl","vif", intf); 
    run test("test1"); 
end

//输出结果:
UVM_INFO @ 0:reporter [RNTST] Running test test1...
UVM_INFO @ 0:uvm_test_top.cl [SETVAL] vif.enable is 0 before set 
UVM_INFO @ 0:uvm_test_top.cl [SETVAL] vif.enable is 1 after set

2.3.2 变量传递
在各个test中, 可以在build phase对底层组件的变量加以配置, 进而在环境例化之前完成配置, 使得环境可以按照预期运行。
代码与2.3.1类似。

2.3.3 object传递
在test配置中,需要配置的参数不只是数量多,而且可能还分属于不同的组件,如果每个变量单独传递,容易出错,还不能复用。因此我们可以将每个组件中的变量加以整合,首先放置到一个uvm_object中,再对中心化的配置对象进行传递,会更有利于整体环境的修改维护。

class configl extends uvm object;
    int val1=1; 
    int str1="null"; 
    uvm_object_uti1s(config1)
    ...
endclass 

class comp1 extends uvm_component;
    `uvm_component_uti1s(comp1)
    configl cfg; 
    ...
    function void build_phase(uvm phase phase); 
        uvm_object_tmp; 
        uvm_config_db#(uvm_object):: get(this,"","cfg", tmp); 
        void'($cast(cfg, tmp)); 
        uvm_info("SETVAL",$sformatf("cfg.vall is %d after get", cfg. val1), UVM_LOW)
        uvm_info("SETVAL",$sformatf("cfg.str1 is %s after get", cfg. str1), UVM_LOW)
    endfunction
endclass

class test1 extends uvm_test;
    `uvm component utils(test1)
    comp1 c1,c2;
    config1 cfgl,cfg2; 
    ...
    function void build_phase(uvm_phase phase); 
        cfg1=config1::type id::create("cfg1"); 
        cfg2=configl::type_id::create("cfg2"); 
        cfg1.val1=30; 
        Cfg1.strl="c1"; 
        Cfg2.val1=50; 
        cfg2.strl="c2"; 
        uvm_config_db#(uvm_object):set(this,"cl","cfg", cfg1); 
        uvm_config_db#(uvm object):set(this,"c2","cfg", cfg2); 
        c1=comp1::type id::create("c1", this);
        c2=comp1::type_id::create("c2", this); 
    endfunction 
endclass

3. factory机制

3.1 介绍
工厂模式的主要解决的问题是,将原来分布在各个地方的对象创建过程单独抽离出来,交给工厂类负责创建。其他地方想要使用对象直接找工厂(即调用工厂的方法)获取对象。
对象由工厂生产是利用工厂生产模具可灵活替代的好处,在不修改原有验证环境层次和验证包的同时,实现对环境内部组件类型或者对象的覆盖(override,或译成重载)

3.2 用法
3.2.1 定义->注册->构建 缺一不可
component 构建函数
function new (string name = " name", uvm_component parent = null)
创建对象
comp_type::type_id::create(string name,uvm_component parent);

object 构建函数
function new (string name = “name”)
创建对象
object_type::type_id::create(string name);

class comp1 extends uvm_component;//1.定义
    `uvm_component_utils(comp1)//2.注册
    //3.构建函数
    function new (string name = "comp1", uvm_component parent = null);
        super.new(name,parent);
    endfunction:new
endclass
class obj1 extends uvm_object;//1.定义
    `uvm_object_utils(obj1)//2.注册
    function new(string name = "obj1");//3.构建函数
        super.new(name);
    endfunction:new
endclass

//创建对象
comp1 c1,c2;
obj1 o1,o2;
initial begin
    c1 = new("c1");
    o1 = new("o1");
    //建议使用下面方法创建对象
    c2 = comp1::type_id::create("c2",null);
    o2 = obj1::type_id::create("o2");
end

3.2.2 覆盖方法
覆盖机制可以将其原来所属类型替换为另一个新类型
在覆盖之后,原本用来创建原型类型的请求,将由工厂来创建新的替换类型。
(1)无需修改原始代码,保证了原有代码的封装性
(2)新的替换类型必须与被替换的类型相兼容,否则稍后的句柄赋值将失败,所以使用继承。
(3)做顶层修改时,非常方便

使用create()来创建对象时:
(1)工厂会检查,是否有类型被覆盖,如果是,它会创建一个新类型的对象,如果不是,它会创建一个原有类型的对象

覆盖发生时,可以使用“类型覆盖”或者“实例覆盖”:
(1)类型覆盖(set_type_override())指,UVM层次结构下的所有原有类型都被覆盖类型所替换。
(2)实例覆盖(set_inst_override())指,某些位置中原有类型会被覆盖类型所替换

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注