问题引入
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 平台了,其他的方法后续再更新。
END
文章来源:处芯积律
推荐阅读
- Vim 超实用指南,收藏这一篇就够了!
- PCIe 6.0 :Virtual Channel 总结
- 告别繁琐 JSON,wavedraw 用 Python 重新定义时序图绘制
- VCD文件秒变专业时序图:wavedraw让仿真波形更美观
更多 IC 设计干货请关注IC设计专栏。欢迎添加极术小姐姐微信(id:aijishu20)加入技术交流群,请备注研究方向。