后仿中 UVM Monitor 采集 DUT 输出及中间节点信号的修改指南

背景

现有一个 UVM 验证 Monitor,采集 DUT 输出以及中间节点信号。从 RTL 仿真迁移到后仿(门级仿真 + SDF 反标)时,Monitor 需要针对性修改。核心修改集中在三个方面:

  1. 信号路径的层次化引用
  2. 采样时机与 X 态处理
  3. 中间节点的可观测性保障

RTL 仿真中通过 vif 直接采样的信号,在后仿网表中可能因层次变化、assign 语句或未初始化状态而无法正确获取。


一、中间节点信号的可观测性:解决 assign 导致的层次断裂

后仿网表中,综合工具可能将某些中间信号用 assign 语句连接到其他 net,导致原始信号名在层次结构中“消失”,Monitor 通过 vif 采样的路径失效。这是后仿中采集中间节点最常见的障碍。

修改方式

  • 预防手段:在 DC 综合时设置 set_fix_multiple_port_nets -all -buffer_constants,强制工具用 buffer 替代 assign,保留信号的可观测层次。
  • 适配手段:若网表已生成,则在 Monitor 中改用网表实际的 net 名,或通过 generate 层次路径采样,或直接采样该信号驱动的下游寄存器 D 端作为替代观测点。

代码示例:Monitor 采样路径的条件化适配

// RTL 仿真:直接采样接口中的中间信号
`ifdef RTL_SIM
  @(posedge vif.clk);
  data_sample = vif.mid_signal;
`else
  // 后仿:mid_signal 可能被 assign 替换,改用实际网表路径或下游寄存器 D 端
  @(posedge vif.clk);
  data_sample = dut.u_sub.u_reg.d;  // 层次化引用网表内部节点
`endif

二、采样时机:处理后仿的延迟偏移与 X 态

后仿中 SDF 反标引入了门延迟和线延迟,信号到达 Monitor 采样点的时间晚于 RTL 仿真。若 Monitor 仍按 RTL 的时钟沿立即采样,可能采到尚未稳定的值。此外,网表中的触发器在上电后可能处于 X 态,直到复位有效才确定。

修改方式

  • 在 Monitor 的采样逻辑中增加对 X 态的过滤。
  • 在时钟沿后留出延迟余量再采样:可在 @(posedge clk) 之后加一个小延迟(如 #1step 或基于时钟周期的百分比),或改用 @(negedge clk) 采样以避免建立时间窗口的竞争。

代码示例:Monitor 采样时序调整

task my_monitor::main_phase(uvm_phase phase);
  forever begin
    @(posedge vif.clk);
    
    // 后仿中等待信号稳定,避免采到 X 或过渡值
    `ifdef GATE_SIM
      #1ns;  // 或 #(CLK_PERIOD*0.1)
      // 跳过复位期间的无效采样
      if (vif.rst_n !== 1'b1) continue;
      // X 态过滤:若采样值为 X,记录警告并跳过
      if ($isunknown(vif.data_out)) begin
        `uvm_warning("MON", "Sampled X on data_out, skipping")
        continue;
      end
    `endif
    
    trans.data = vif.data_out;
    ap.write(trans);
  end
endtask

三、中间节点的层次化引用:vif 的局限性

Monitor 通常通过 virtual interface 获取信号,但 vif 只能绑定到接口中声明的信号,无法直接访问 DUT 内部层次。当需要采集的“中间节点”位于 DUT 网表内部、未被引出到接口时,vif 无能为力。

修改方式

  • 在 Monitor 中增加基于层次化路径的直接引用(dut.u_internal.signal)。
  • 通过 uvm_config_db 传递 DUT 顶层路径,或使用 $root 层次名。
  • 后仿中需确保仿真器开启了足够的调试可见性(如 VCS 的 -debug_region+cell、Xcelium 的 -access),否则网表内部节点可能不可见。

代码示例:Monitor 中层次化采集中间节点

class my_monitor extends uvm_monitor;
  // 接口信号用于端口采集
  virtual my_if vif;
  // 层次化路径用于内部中间节点采集
  string dut_path = "tb_top.dut";
  
  task run_phase(uvm_phase phase);
    forever begin
      @(posedge vif.clk);
      `ifdef GATE_SIM
        // 通过层次化路径直接采集网表内部信号
        if ($value$plusargs("DUT_PATH=%s", dut_path)) begin
          // 使用 uvm_hdl_read 或直接层次引用
          uvm_hdl_read({dut_path, ".u_core.state_reg"}, state_val);
        end
      `endif
    end
  endtask
endclass

注意:uvm_hdl_read 需要仿真器支持 DPI 或 HDL 后门访问,且目标信号在网表中必须存在(未被优化掉)。对于被综合优化掉的信号,只能从 SDF 或网表中寻找功能等效的替代观测点。


总结

后仿中修改 Monitor 的核心清单:

问题 修改位置 具体措施
中间信号被 assign 吞掉 综合或 Monitor 采样路径 综合加 set_fix_multiple_port_nets;或改用网表实际路径/下游寄存器 D 端
采样采到 X 态 Monitor 采样逻辑 复位期间跳过;$isunknown 过滤
采样时机偏早 Monitor 时钟块 沿后加延迟余量,或用 negedge 采样
内部节点 vif 够不到 Monitor 增加层次化引用 uvm_hdl_read 或直接 dut.path.signal,配合仿真器 debug 选项

补充说明

  • 后仿中 Monitor 本身的功能逻辑(将采集到的 bit 流组装成 transaction)不需要改变,改变的只是信号获取的路径和时机。
  • 验证平台的其他组件(scoreboard、reference model)通常无需因后仿而修改,前提是 Monitor 输出的 transaction 格式保持一致。

拓展 verilog 中module output 声明规则

这个输出端口在模块内部怎么赋值?
│
├─ 用 assign 连续赋值
│    └─> output wire(或省略,默认 wire)
│
├─ 用 always @(*) 组合逻辑赋值
│    └─> output reg
│
├─ 用 always @(posedge/negedge) 时序赋值
│    └─> output reg
│
├─ 由子模块实例输出驱动
│    └─> output wire(顶层连接也用 wire)
│
├─ 需要多驱动 / 三态总线
│    └─> output wire(必须)
│
└─ inout 双向
     └─> 只能是 wire / tri 等 net 类型
input:net
posted @ 2026-10-27 11:20  shiguang9870  阅读(4)  评论(0)    收藏  举报

© 博客园 · 个人博客

Powered by 博客园  ·  Theme Customized