news 2025/12/26 8:45:07

19.1 UVM Phase流程详解?

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
19.1 UVM Phase流程详解?


🎯 一、两张图的关系:整体 vs 局部

图1=图2中run_phase的"放大镜"

图2 (总览)对应的图1 (详细分解)
run_phase(一个大盒子)拆成12个小phase:pre_resetresetpost_resetpre_configure→ … →post_shutdown

这就像是:

  • 图2“学校一天的作息时间表”
  • 图1“上午8:00-11:30的语文课上,老师具体怎么安排45分钟”

💡 二、为什么需要这么多Phase?

生活问题:如果让你"一天只做一件事"会怎样?

  • 没法边吃饭边睡觉(功能冲突)
  • 没法先写作业再起床(顺序混乱)
  • 没法不刷牙就出门(状态不对)

验证平台同理

  • reset:必须先让DUT(被测设计)复位到初始状态,才能发数据
  • configure:必须先配置寄存器,再启动功能
  • main:必须先完成配置,再跑核心业务
  • shutdown:必须先停止数据流,再检查最终结果

💻 三、技术代码示例

示例1:DUT初始化(对应reset系列phase)

class my_test extends uvm_test;// 1. pre_reset:复位前的准备工作(不耗时)taskpre_reset_phase(uvm_phase phase);`uvm_info("PRE_RESET","⚠️ 准备拉低复位信号...",UVM_LOW);// 配置:复位信号初始为高电平dut_vif.reset<=1'b1;endtask// 2. reset:真正的复位操作(消耗仿真时间)taskreset_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("RESET","🔥 复位DUT开始(持续100ns)",UVM_LOW);@(posedge dut_vif.clk);// 等待时钟上升沿dut_vif.reset<=1'b0;// 拉低复位repeat(10)@(posedge dut_vif.clk);// 保持10个周期dut_vif.reset<=1'b1;// 释放复位phase.drop_objection(this);endtask// 3. post_reset:复位后的检查(不耗时)taskpost_reset_phase(uvm_phase phase);`uvm_info("POST_RESET","✅ 检查DUT是否从复位状态恢复",UVM_LOW);if(dut_vif.state!=IDLE)begin `uvm_error("POST_RESET","DUT未回到IDLE状态!");end endtask endclass

示例2:配置寄存器(对应configure系列phase)

class uart_test extends uvm_test;// pre_configure:计算配置参数taskpre_configure_phase(uvm_phase phase);`uvm_info("PRE_CFG","📊 计算波特率分频系数...",UVM_LOW);// 假设系统时钟50MHz,波特率9600baud_divisor=50_000_000/9600;// 结果:5208endtask// configure:通过总线配置DUT(耗时)taskconfigure_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("CONFIGURE","🔧 配置UART寄存器...",UVM_LOW);// 写波特率寄存器reg_model.baud_div.write(status,baud_divisor);// 配置8位数据,无校验reg_model.ctrl.write(status,'h03);phase.drop_objection(this);endtask// post_configure:读取回显确认taskpost_configure_phase(uvm_phase phase);uvm_reg_data_tread_val;reg_model.baud_div.read(status,read_val);if(read_val!=baud_divisor)begin `uvm_error("POST_CFG","寄存器配置失败!");end endtask endclass

示例3:主测试(main_phase)

taskmain_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("MAIN","🚀 开始压力测试!",UVM_LOW);// 创建并启动测试序列(就像运动员按指令跑步)uart_tx_seq seq;seq=uart_tx_seq::type_id::create("seq");// 发送1000个随机数据包seq.num_trans=1000;seq.start(env.agent.sequencer);// 等待所有传输完成wait(env.scoreboard.packet_count>=1000);`uvm_info("MAIN","✅ 1000个数据包发送完成",UVM_LOW);phase.drop_objection(this);endtask

场景:测试一个FIFO(先进先出缓冲区)完整示例

class fifo_test extends uvm_test;fifo_env env;// 环境:包含driver, monitor, scoreboard// 1️⃣ build_phase:造人、造设备functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);`uvm_info("BUILD","=== 造FIFO测试环境 ===",UVM_LOW);env=fifo_env::type_id::create("env",this);// 配置:FIFO深度=32,数据宽度=32位uvm_config_db#(int)::set(this,"env","fifo_depth",32);endfunction// 2️⃣ connect_phase:连电线functionvoidconnect_phase(uvm_phase phase);super.connect_phase(phase);`uvm_info("CONNECT","=== 连接monitor和scoreboard ===",UVM_LOW);env.agent.monitor.item_port.connect(env.scoreboard.item_export);endfunction// 3️⃣ run_phase的12个子phase(重点!)// 🔄 reset_phase:复位DUTtaskreset_phase(uvm_phase phase);phase.raise_objection(this);// 说:"我要干活!"`uvm_info("RESET","=== 拉低复位信号100ns ===",UVM_LOW);env.driver.reset_dut();// 驱动器拉低reset_n信号#100ns;// 等待100nsphase.drop_objection(this);// 说:"我干完了"endtask// ⚙️ configure_phase:配置FIFOtaskconfigure_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("CONFIGURE","=== 配置FIFO工作模式 ===",UVM_LOW);env.driver.write_reg(CONFIG_REG,8'h01);// 使能FIFO#10ns;phase.drop_objection(this);endtask// 🚀 main_phase:压力测试(核心!)taskmain_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("MAIN","=== 开始疯狂写数据 ===",UVM_LOW);// 连续写1000个随机数据for(inti=0;i<1000;i++)begin env.driver.push_data($random());#5ns;end #100ns;// 等待FIFO处理完phase.drop_objection(this);endtask// 📊 report_phase:打印成绩单functionvoidreport_phase(uvm_phase phase);super.report_phase(phase);`uvm_info("REPORT","=== FIFO测试报告 ===",UVM_LOW);`uvm_info("REPORT",$sformatf("写入: %0d个",env.scoreboard.push_count),UVM_LOW);`uvm_info("REPORT",$sformatf("读出: %0d个",env.scoreboard.pop_count),UVM_LOW);`uvm_info("REPORT",$sformatf("错误: %0d个",env.scoreboard.error_count),UVM_LOW);endfunction endclass

🔍 四、时序解剖:精确到ns

时间轴可视化

时间: 0ns 50ns 100ns 150ns 200ns 250ns |-------|-------|-------|-------|-------|→ Phase: [build,connect,start_of_simulation] (瞬间完成) [ reset_phase: 0-50ns ] // 复位信号 [ post_reset, pre_configure ] (各5ns) [ configure_phase: 60-80ns ] // 寄存器配置 [ post_configure, pre_main ] (各5ns) [ main_phase: 90-240ns ] // 核心测试 ❗最长❗ [ post_main, pre_shutdown ] (各5ns) [ shutdown_phase: 250-300ns ] // 收尾 [ extract,check,report,final ] (瞬间完成)

关键观察

  • build/connect0ns就瞬间做完了
  • main_phase占据了**80%**的仿真时间
  • 其他phase都是准备工作或收尾工作

⚠️ 五、常见错误与调试技巧

错误1:在reset_phase发数据

taskreset_phase(uvm_phase phase);phase.raise_objection(this);driver.send_data();// ❌ 错误!DUT还没准备好!#100ns;phase.drop_objection(this);endtask

后果:数据丢失,测试无意义!

正确做法

taskmain_phase(uvm_phase phase);phase.raise_objection(this);driver.send_data();// ✅ 正确!在main_phase发数据phase.drop_objection(this);endtask

错误2:多个组件同时raise objection

// env.driver:taskmain_phase(uvm_phase phase);phase.raise_objection(this);// +1#100ns;phase.drop_objection(this);// -1endtask// env.scoreboard:taskmain_phase(uvm_phase phase);phase.raise_objection(this);// +1 (现在总数=2!)#50ns;phase.drop_objection(this);// -1 (还剩1个)endtask

结果:仿真跑到150ns才结束(等所有objection都drop)

调试技巧:打印objection计数

`uvm_info("OBJ", $sformatf("Objections: %0d", phase.get_objection_count()), UVM_DEBUG);

错误3:为什么要拆这么细?为什么不直接用一个大run_phase搞定所有事?(设计哲学)

答案:为了可重用性可维护性
场景对比

❌ 粗暴方式(所有代码塞在run_phase)
taskrun_phase(uvm_phase phase);phase.raise_objection(this);// 复位(100行代码)dut_vif.reset<=0;#100;dut_vif.reset<=1;// 配置(200行代码)reg_model.baud_div.write(...);reg_model.ctrl.write(...);// 测试(300行代码)seq.start(...);phase.drop_objection(this);endtask

问题:如果10个测试都要复位,代码重复10次!哪天复位信号从低有效改成高有效,要改10个地方!

✅ 优雅方式(按phase拆分)
// 定义一个base_test,专门处理复位和配置class base_test extends uvm_test;taskreset_phase(uvm_phase phase);// 统一复位逻辑(1处维护)endtask taskconfigure_phase(uvm_phase phase);// 统一配置逻辑(1处维护)endtask endclass// 具体测试只关心main_phaseclass test1 extends base_test;taskmain_phase(uvm_phase phase);// 只写test1特有的测试endtask endclass class test2 extends base_test;taskmain_phase(uvm_phase phase);// 只写test2特有的测试endtask endclass

好处:修改复位逻辑,只改base_test,所有子类自动生效!


🎓 六、设计模式:如何优雅地使用Phase

通用测试模板

class your_test extends uvm_test;your_env env;// 构建环境functionvoidbuild_phase(uvm_phase phase);super.build_phase(phase);env=your_env::type_id::create("env",this);endfunction// 复位DUT(必需)taskreset_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("RESET","=== 复位DUT ===",UVM_LOW);// TODO: 拉低复位信号#100ns;phase.drop_objection(this);endtask// 配置DUT(可选)taskconfigure_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("CONFIGURE","=== 配置DUT ===",UVM_LOW);// TODO: 写寄存器phase.drop_objection(this);endtask// 主测试(核心)taskmain_phase(uvm_phase phase);phase.raise_objection(this);`uvm_info("MAIN","=== 开始测试 ===",UVM_LOW);// TODO: 启动sequenceyour_sequence seq;seq=your_sequence::type_id::create("seq");seq.start(env.agent.sequencer);// 等待完成wait(env.scoreboard.all_done());phase.drop_objection(this);endtask// 打印报告functionvoidreport_phase(uvm_phase phase);super.report_phase(phase);`uvm_info("REPORT","=== 测试完成 ===",UVM_LOW);// TODO: 统计结果endfunction endclass

推荐模板(适用于90%的项目):

class my_base_test extends uvm_test;// 1. 【固定套路】在base_test中实现通用逻辑virtual taskreset_phase(uvm_phase phase);// 所有测试都需要的复位endtask virtual taskconfigure_phase(uvm_phase phase);// 所有测试都需要的配置endtask// 2. 【留空给子类】main_phase声明为virtual,但不实现virtual taskmain_phase(uvm_phase phase);`uvm_fatal("MAIN","子类必须重写main_phase!");endtask endclass// 具体测试只需关注main_phaseclass test_tx_only extends my_base_test;taskmain_phase(uvm_phase phase);// 只测试发送功能endtask endclass class test_rx_only extends my_base_test;taskmain_phase(uvm_phase phase);// 只测试接收功能endtask endclass

🎯 七、互动题目(检验掌握程度)

选择题

  1. DUT必须在哪个phase后处于ready状态?

    • A) build_phase
    • B) reset_phase
    • C) main_phase
    • D) report_phase

    答案:B) reset_phase后DUT才复位完成

  2. 核心业务逻辑应该放在哪个phase?

    • A) configure_phase
    • B) main_phase
    • C) shutdown_phase
    • D) final_phase

    答案:B) main_phase是核心测试阶段

  3. 提取仿真结果应该在哪个phase?

    • A) extract_phase
    • B) check_phase
    • C) report_phase
    • D) final_phase

    答案:A) extract_phase专门收集数据


🎓 八、终极记忆口诀

“Build环境,Connect线路,Reset清零,Configure配置,Main干活,Report汇报!”

🎁 记忆卡片(打印贴墙上)

Phase类型作用耗时常见用途
build创建❌ 不耗时new()组件
connect连接❌ 不耗时port.connect()
reset复位✅ 耗时拉低复位信号
configure配置✅ 耗时写寄存器
main测试✅ 耗时发激励、收响应
extract提取❌ 不耗时收集覆盖率
check检查❌ 不耗时assert检查
report报告❌ 不耗时打印日志

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

LangFlow工作流导出为API接口的完整流程

LangFlow工作流导出为API接口的完整流程 在AI应用开发日益普及的今天&#xff0c;一个核心挑战浮出水面&#xff1a;如何让非程序员也能参与构建智能系统&#xff1f;数据科学家、产品经理甚至业务专家常常能清晰描述他们想要的逻辑——比如“先检索知识库&#xff0c;再用大模…

作者头像 李华
网站建设 2025/12/25 5:06:11

25、Linux 系统通信指南:网络连接、传真与调制解调器使用

Linux 系统通信指南:网络连接、传真与调制解调器使用 在当今数字化时代,计算机之间的通信变得至关重要。无论是连接互联网、收发传真还是使用调制解调器进行串行连接,都是常见的需求。本文将详细介绍在 Linux 系统中实现这些通信功能的方法。 1. 连接到互联网 连接 Linux…

作者头像 李华
网站建设 2025/12/22 16:44:00

22、Linux系统中的提醒工具使用指南

Linux系统中的提醒工具使用指南 在Linux系统中,当我们花费大量时间在工作上时,提醒功能就显得尤为重要。它能帮助我们合理安排时间,确保不会错过重要的事情。下面将介绍一些常见的提醒工具及其使用方法。 日期和时间显示 在Linux中, date 命令可以用来输出当前系统的日…

作者头像 李华
网站建设 2025/12/25 22:42:11

加密已死?不,它正在重生:为什么加密仍然是数据安全的终极堡垒

当数据成为数字时代的血液&#xff0c;加密就是保护生命线的最坚固血管。深夜&#xff0c;某互联网公司的服务器监控中心&#xff0c;警报声刺破了平静。安全团队迅速发现&#xff0c;攻击者已突破层层防线&#xff1a;防火墙规则被篡改、零日漏洞被利用、管理员账户被接管。但…

作者头像 李华
网站建设 2025/12/21 22:33:07

26、负载均衡与高可用集群搭建指南

负载均衡与高可用集群搭建指南 1. LVS IP 虚拟服务器路由规则 可以使用以下命令查看 LVS IP 虚拟服务器路由规则: #ipvsadm -L –n示例输出如下: IP Virtual Server version x.x.x (size=4096) Prot LocalAddress:Port Scheduler Flags-> RemoteAddress:Port Forwar…

作者头像 李华