news 2026/8/15 9:38:53

11. UVM Test [uvm_test]

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
11. UVM Test [uvm_test]

现在,你已经准备好学习UVM的“总指挥”了——uvm_test。它是整个验证工厂的最高指挥官,负责设定任务、调配资源并下令开工。

简单来说,uvm_test不是一个具体的测试动作,而是一个可配置、可重用的“测试方案”或“作战计划”。它不直接驱动信号,而是通过配置环境、启动不同的“作战指令”(序列)来验证DUT的不同功能。

🎯 Test的本质:一个可执行的“验证方案”

你可以把验证计划(Verification Plan)里的一条条功能点(Feature)想象成需要攻克的“战略目标”。一个uvm_test就是一个针对其中一个战略目标的完整作战计划。它包含:

  • 作战环境:需要哪些“部队”(Agent)参与。
  • 部队配置:各部队用什么“装备”、以什么“模式”(主动/被动)作战。
  • 作战指令:具体的进攻顺序和策略(Sequence)。
  • 战果评估:如何判断目标是否达成(通过Scoreboard等检查)。

🏗️ 编写一个UVM Test的四步核心流程

下图展示了创建一个“作战计划”(Test)从搭建指挥部到下达作战指令的完整流程:

第一步:建立指挥部
你的 Test 类需要从uvm_test继承,并进行工厂注册。注意,它虽然不叫uvm_component,但本质上是一个顶级组件

class my_base_test extends uvm_test;`uvm_component_utils(my_base_test)// 使用组件宏注册functionnew(string name=“my_base_test”,uvm_component parent=null);super.new(name,parent);endfunction// ... 后续步骤写在这里endclass

第二步:组建与配置部队 (核心)
build_phase中,你需要:

  1. 创建环境(env)和配置对象(cfg)
  2. 为配置对象赋值(如设置工作模式、获取虚拟接口等)。
  3. 通过uvm_config_db将配置对象“下发”到环境中的具体Agent
virtual functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);// 1. 创建m_env=my_env::type_id::create(“m_env”,this);m_cfg=my_cfg::type_id::create(“m_cfg”);// 2. 配置if(!uvm_config_db #(virtual dut_if)::get(this,“”,“dut_vif”,m_cfg.vif))`uvm_error(“CFG”,“Interface not found!)m_cfg.is_active=UVM_ACTIVE;// 3. 下发uvm_config_db #(my_cfg)::set(this,“m_env.m_agent”,“cfg”,m_cfg);endfunction

第三步:战前检阅(调试)
end_of_elaboration_phase中,所有组件已创建并连接好。此时打印拓扑结构,可以清晰地看到你搭建的整个“部队编制”是否正确。

virtual functionvoidend_of_elaboration_phase(uvm_phase phase);uvm_top.print_topology();// 打印出完整的UVM组件树endfunction

第四步:下达作战指令(核心)
run_phase(这是一个task,会消耗仿真时间)中,你需要:

  1. 申请作战时间:通过raise_objection(this)防止仿真立刻结束。
  2. 创建并启动主序列:这是测试的灵魂,决定了要发送什么数据。
  3. 结束作战:序列执行完毕后,drop_objection(this)
virtual taskrun_phase(uvm_phase phase);my_main_sequence main_seq;super.run_phase(phase);// 好习惯phase.raise_objection(this);main_seq=my_main_sequence::type_id::create(“main_seq”);main_seq.start(m_env.v_sqr);// 通常启动在虚拟序列器上phase.drop_objection(this);endtask

🚀 如何“启动”一个Test:两种方式

Test 的启动不是在你代码里调用一个函数,而是在顶层模块(tb_top)的initial块中,通过run_test()这个全局任务来“召唤”。

方式一:代码内指定(不灵活)

initial beginrun_test(“my_base_test”);// 固定运行 my_base_testend

方式二(推荐):命令行指定

initial beginrun_test();// 参数为空,从命令行获取end

然后在仿真时通过命令行指定:

# 使用不同的 +UVM_TESTNAME 来运行不同的测试,无需重新编译!<simulator_command>+UVM_TESTNAME=my_base_test<simulator_command>+UVM_TESTNAME=test_feature_a<simulator_command>+UVM_TESTNAME=test_stress

这是UVM框架灵活性的重要体现,一定要掌握。

🔄 衍生测试:实现极致的重用

这是UVM测试策略最强大的地方。你不需要为每个功能点从头写一个Test,而是通过继承来复用和调整。

假设你有一个验证寄存器读写功能的测试reg_test

// 1. 基础测试:搭建通用环境,启动基础序列class reg_test extends uvm_test;`uvm_component_utils(reg_test)reg_env m_env;reg_cfg m_cfg;virtual functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);m_env=reg_env::type_id::create(“m_env”,this);m_cfg=reg_cfg::type_id::create(“m_cfg”);m_cfg.reg_model=“RAL”;// 使用寄存器模型uvm_config_db #(reg_cfg)::set(this,*,“cfg”,m_cfg);endfunction virtual taskrun_phase(uvm_phase phase);base_reg_seq seq=base_reg_seq::type_id::create(“seq”);phase.raise_objection(this);seq.start(m_env.v_sqr);phase.drop_objection(this);endtask endclass// 2. 衍生测试A:复用环境,但更换更复杂的“作战指令”class test_reg_stress extends reg_test;// 关键:继承`uvm_component_utils(test_reg_stress)virtual taskrun_phase(uvm_phase phase);stress_reg_seq seq=stress_reg_seq::type_id::create(“seq”);// 更换序列phase.raise_objection(this);seq.start(m_env.v_sqr);// 环境 m_env 是从父类继承来的!phase.drop_objection(this);endtask endclass// 3. 衍生测试B:复用环境和序列,但调整“部队配置”class test_reg_passive extends reg_test;`uvm_component_utils(test_reg_passive)virtual functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);// 先调用父类,完成通用搭建m_cfg.is_active=UVM_PASSIVE;// 然后覆盖配置:将Agent设为被动模式endfunction endclass

⚠️ 核心要点与总结

概念说明关键点
uvm_test角色验证环境的总指挥和配置中心不直接干活,只负责“排兵布阵”。
配置下发通过uvm_config_db::setbuild_phase将配置对象(cfg)下发。实现环境行为的动态控制。
启动序列run_phasecreatestart序列。必须使用raise/drop_objection包裹。
运行方式通过run_test()++UVM_TESTNAME命令行参数实现不修改代码、不重新编译即可切换测试。
测试重用通过类继承创建衍生测试。重写build_phase以修改配置,或重写run_phase以更换主序列

给你的最终建议:
现在,请将你之前搭建的envagentsequence组合起来。

  1. 创建一个my_base_test,在build_phase中创建你的env
  2. run_phase中启动你的序列,并确保有raise/drop_objection
  3. tb_top中,使用run_test(“my_base_test”)启动仿真。
  4. 成功后,尝试用run_test()+ 命令行参数+UVM_TESTNAME=my_base_test的方式再次运行。
  5. 最后,创建一个衍生测试,仅仅更换另一个不同的序列,并通过命令行运行它。

当你成功运行起第一个由Test指挥的完整UVM环境时,你就真正打通了UVM从底层到顶层的任督二脉。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/14 0:58:03

Cocos Engine性能监控全攻略:从入门到精通

Cocos Engine性能监控全攻略&#xff1a;从入门到精通 【免费下载链接】cocos-engine Cocos simplifies game creation and distribution with Cocos Creator, a free, open-source, cross-platform game engine. Empowering millions of developers to create high-performanc…

作者头像 李华
网站建设 2026/8/15 3:24:39

终极方案:OneDrive深度卸载工具完整使用指南

想要彻底清理Windows系统中的OneDrive组件吗&#xff1f;OneDrive-Uninstaller是一个简单高效的批处理脚本工具&#xff0c;专门用于完全卸载OneDrive&#xff0c;释放系统资源&#xff0c;让电脑运行更流畅。这个开源工具能够深度清理OneDrive的所有相关文件、服务配置和注册表…

作者头像 李华
网站建设 2026/8/14 14:34:26

【Java毕设全套源码+文档】基于springboot的个人博客系统的设计与实现(丰富项目+远程调试+讲解+定制)

博主介绍&#xff1a;✌️码农一枚 &#xff0c;专注于大学生项目实战开发、讲解和毕业&#x1f6a2;文撰写修改等。全栈领域优质创作者&#xff0c;博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java、小程序技术领域和毕业项目实战 ✌️技术范围&#xff1a;&am…

作者头像 李华
网站建设 2026/8/15 9:33:17

26、Linux 文件操作与库 I/O 函数全解析

Linux 文件操作与库 I/O 函数全解析 1. 文件操作基础 在 Linux 系统里,文件操作至关重要,它涵盖文件的读取、写入、复制等操作。下面我们先来看一个基础的文件读取与显示程序: if (fd < 0) exit(1); } while (n = read(fd, buf, BLKSIZE)){m = write(1, buf, n); } }…

作者头像 李华
网站建设 2026/8/14 21:55:30

轻松上手Llama-Factory:五分钟完成第一个LoRA微调任务

轻松上手Llama-Factory&#xff1a;五分钟完成第一个LoRA微调任务 你有没有遇到过这样的场景&#xff1f;想为自己的业务定制一个专属的对话模型&#xff0c;比如让大模型学会回答公司产品的售后问题&#xff0c;或者生成符合行业风格的技术文档。但一想到要动辄几十GB显存、数…

作者头像 李华
网站建设 2026/8/14 15:59:09

eino框架结构化数据解析完整指南

eino框架结构化数据解析完整指南 【免费下载链接】eino Go 语言编写的终极大型语言模型&#xff08;LLM&#xff09;应用开发框架&#xff0c;强调简洁性、可扩展性、可靠性与有效性。 项目地址: https://gitcode.com/CloudWeGo/eino 引言&#xff1a;为什么需要结构化输…

作者头像 李华