如果我们用SystemVerilog构建验证平台,构建好了之后,想改变平台中的某个组件,例如将driver改成driver_new,我们需要重新定义一下driver_new,当然也可以直接从driver继承。但是我们还需要在driver对象例化的地方将driver drv; 改成driver_new drv;,如果需要多个组件的更新、以及多次的平台复用,那代码量巨大,而且每次改变都要深入平台内部,非常繁琐。基于上述问题,UVM提出的factory机制通过将拓展类在工厂注册,可实现环境内部组件的创建与对象的重载。
factory机制主要针对构成验证环境层次的uvm_component及其子类,以及构成环境配置属性和数据传输的uvm_object及其子类。
1. 注册
使用factory机制的第一步首先需要将类注册到工厂。这个factory是整个全局仿真中存在且唯一的"机构”,所有被注册的类才能使用factory机制。这很好理解,想要使用工厂机制,你首先得在工厂中注册这个类。
一些注册示例:
`uvm_component_utils(my_agent) //component注册macro
`uvm_component_utils_begin(my_agent)//注册factory的同时,可注册field automation
//...
`uvm_component_utils_end
`uvm_component_param_utils(my_driver) //参数化的component注册,例class my_driver #(int width = 32) extends uvm_driver;
`uvm_component_utils_param_begin(my_driver) //注册factory的同时,可注册field automation
//...
`uvm_component_utils_param_end
`uvm_object_utils(my_transaction) //object注册macro
`uvm_object_utils_begin(my_transaction) //注册factory的同时,可注册field automation
//...
`uvm_object_utils_end
`uvm_object_param_utils(my_sequence) //参数化的object注册,例class my_sequence #(int width = 32) extends uvm_sequence;
`uvm_object_utils_param_begin(my_sequence) //注册factory的同时,可注册field automation
//...
`uvm_object_utils_param_end
注册只需要遵照范式,使用utils宏对相应对象完成注册,注意不要弄错component和object。
2. 创建
完成了注册之后还要进行创建,即实例化对象,所有注册到factory的类均可通过factory独特的方式实例化对象。但factory的独特方式,实际上也是调用了new函数,也是先创建句柄再赋予对象。
class my_agent extends uvm_agent;
`uvm_component_utils(my_agent) //注册
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
//...
endclass
my_agent agt; //创建my_agent句柄
agt = my_agent::type_id::create("agt", env);//factory独特且最常用的例化方式,创建了my_agent实例并返回句柄,本质还是调用的my_agent::new(name,parent);
class my_sequence extends uvm_sequence;
`uvm_object_utils(my_sequence) //注册
function new(string name); //object不属于uvm层次结构,不需要parent
super.new(name);
endfunction
//...
endclass
my_sequence seq; //创建my_sequence句柄
seq = my_sequence::type_id::create("seq");//factory独特且最常用的例化方式
建议使用create的方式创建,便于后续可能进行的重载等操作。
3. 重载
重载的意思就是覆盖、替换。对于UVM中的factory机制,可实现factory机制中创建函数的重载,即将父类的创建函数重载成子类的创建函数。前提是必须将父类和子类均在factory中注册,必须使用factory的例化方式,这也显示了factory机制的优点。
class bird extends uvm_object;
`uvm_object_utils(bird);
//...
endclass
class parrot extends bird;
`uvm_object_utils(parrot);
//...
endclass
function void my_case0::build_phase(uvm_phase phase);
bird bird_inst;
parrot parrot_inst;
bird_inst = new("bird_inst"); //bird句柄指向了名叫"bird_inst"的bird对象,注意UVM中不允许这样例化!!
parrot_inst = new("parrot_inst"); //parrot句柄指向了名叫"parrot_inst"的parrot对象,UVM中不允许这样例化!!
bird_inst = parrot_inst; //bird句柄指向了名叫"parrot_inst"的parrot对象,SV的多态
//...
endfunction
function void my_case1::build_phase(uvm_phase phase);
bird bird_inst;
parrot parrot_inst;
set_type_override_by_type(bird::get_type(),parrot::get_type()); //将父类bird重载成子类parrot
bird_inst = bird::type_id::create("bird_inst"); /此处返回的是名叫"bird_inst"的parrot实例!但bird_inst依旧是bird句柄
parrot_inst = parrot::type_id::create("parrot_inst ");
//...
endfunction
注意,原验证环境都是通过父类句柄实现方法的,重载之后父类句柄指向的是子类对象,此时只有父类中为virtual型,且在子类中被重新实现的方法,原验证环境才会使用子类中的该方法。
并且,component和object之间不能重载!
3.1 连续重载
如有需要,我们可以将父类的创建函数重载成子类的创建函数,还可将子类的创建函数继续重载成孙类的创建函数,以此类推。
class big_parrot extends parrot;
`uvm_object_utils(big_parrot);
//...
endclass
function void my_case1::build_phase(uvm_phase phase);
bird bird_inst;
parrot parrot_inst;
big_parrot big_parrot_inst;
set_type_override("bird","parrot");
set_type_override("parrot","big_parrot");
bird_inst = bird::type_id::create("bird_inst"); //bird句柄指向的是"bird_inst"的big_parrot实例
parrot_inst = parrot::type_id::create("parrot_inst"); //parrot句柄指向的是"parrot_inst"的big_parrot实例!
//...
endfunction
3.2 重载的意义
factory机制的重载具有这些特点:
顶层修改时,非常方便,无需修改原始代码,继而保证了原有代码的封装性,安全性;
新的替换类型必须与被替换类型相兼容,否则稍后的句柄赋值将失败,所以需要使用继承。
具体来说,当使用create创建对象时,工厂会检查原有类型是否要被覆盖。如果是,它会创建一个新类型的对象;如果不是,它会创建一个原有类型的对象。在覆盖时可以选择是类型覆盖或是实例覆盖:类型覆盖是UVM层次结构下的所有原有类型都被覆盖类型所替换,对应语句为set_type_override();实例覆盖是指某些位置中原有类型会被覆盖类型所替换,对应语句为set_inst_override()。