问题引入
UVM中,phase机制是一个基本而又非常核心的特性之一,uvm phase将验证环境的运行划分为不同的阶段,如build, configue, reset, main等。每个phase阶段可对应着环境或者DUT的初始化或者处理过程。
phase的执行机制遵循UVM的"树"结构,从top-down角度看,有自上而下和自下而上的phase区别。也有function phase 和 task phase之分,区别在于是否消耗仿真时间。uvm中定义的phase如下所示:
UVM已有的phase机制已经可以解决大家绝大部分的场景需求。
近期项目验证架构调整,遇到这样的需求。简化来说,SoC验证环境中会集成数十个子系统的验证UVC(有uvm_test, uvm_env,agent), 其中env A 和env B的初始化build有约束,需要保证env B的build phase内容在env A之前完成。
解决方案:
根据这个需求,其实有这样几个解决办法。
将env A和env B build_phase中有约束的部分上移,将需要协调同步的内容放到uvm_test中的build_phase中实现。这样就需要Block level和SoC level在集成时进行区别处理。
将uvm_env的实例名按字典排序。在uvm_phase或者树的遍历中,按照字典序进行,因此可以将env B和env A的实例名按字典序命名。
如果是类似随机的过程的代码,可以尝试将env B的该部分代码放到new函数中。new函数会在build_phase前执行。
给env B建立一个自定义phase, 该phase在build_phase之前执行,完成同步。
因此就对自定义phase的实现进行了小小的尝试和solution的package设计,有几种方路线可选:
UVM推荐的经典实现 基于UVM预先埋入的回调函数实现 基于interface class的插件式方法 基于AOP(Aspect Oriented Programming)实现
先介绍第一种的经典做法。
自定义phase实现之: UVM经典推荐
自定义phase的实现,也能找到一些开源的代码,主要步骤:
根据所需,选择function phase还是task phase。并实现对应的exec_function或者exec_task。
获取uvm_domain句柄。function phase对应uvm_domain::get_common_domain()。task phase使用uvm_domain::get_uvm_domain()。
获取domain中需要插入phase的位置
调用uvm_domain的add函数,将自定义的phase插入到指定位置。
扩展包含新的phase的uvm组件
下面以建立一个pre_build_phase为例,相关代码如下。
user_pre_build_test
先新建一个包含pre_build_phase的base component, 根据需求继承uvm_test或者uvm_env。然后用户的component均此component扩展。
class user_pre_build_test extends uvm_test;
`uvm_component_utils(pre_build_test)
functionnew (string name="pre_build_test",uvm_component parent=null);
super.new(name,parent);
endfunction
virtualfunctionvoid pre_build_phase(uvm_phase phase);
endfunction
endclass
class user_test extends user_pre_build_test;
`uvm_component_utils(user_test)
functionnew (string name="pre_build_test",uvm_component parent=null);
super.new(name,parent);
endfunction
virtualfunctionvoid build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info(get_type_name, "Here is build_phase", UVM_NONE)
endfunction
virtualfunctionvoid pre_build_phase(uvm_phase phase);
`uvm_info(get_type_name, "Here is pre_build_phase", UVM_NONE)
endfunction
endclass
user_pre_build_phase
实现pre_build_phase的class, 可以参考uvm_build_phase的实现。如果是task类选的phase,需要继承uvm_task_phase, 实现exec_task, 可以参考uvm_main_phase的实现。
class user_pre_build_phase extends uvm_topdown_phase;
localstatic user_pre_build_phase m_inst;
staticconststring type_name = "user_pre_build_phase";
protectedfunctionnew(string name="user_pre_build_phase");
endfunction
staticfunction user_pre_build_phase get();
if(m_inst == null) m_inst = new();
return m_inst;
endfunction
virtualfunctionstring get_type_name();
return type_name;
endfunction
virtualfunctionvoid exec_func(uvm_component comp, uvm_phase phase);
user_pre_build_test t;
if($cast(t,comp) ) begin
t.pre_build_phase(phase);
end
endfunction
endclass
uvm_domain.add(...)
最后在TB的initial里将user_pre_build_phase插入到build_phase之前即可。
module tb;
import uvm_pkg::*;
initialbegin
uvm_domain sim_common_domain;
uvm_phase target_phase;
sim_common_domain = uvm_domain::get_common_domain();
target_phase = sim_common_domain.find(uvm_build_phase::get() );
sim_common_domain.add(
.phase(user_pre_build_phase::get() ),
.with_phase(null ),
.after_phase(null ),
.before_phase(target_phase )
);
end
initialbegin
run_test();
end
endmodule
根据上面的代码可以逐步实现所需要的phase实现,可以看到期望的输出,也是比较基础和经典的代码。后几种方法可以进一步提升自定义phase的易用性和集成性。因为最近找不到可用的VCS平台了,其他的方法后续再更新。