后仿中 UVM Monitor 采集 DUT 输出及中间节点信号的修改指南
背景
现有一个 UVM 验证 Monitor,采集 DUT 输出以及中间节点信号。从 RTL 仿真迁移到后仿(门级仿真 + SDF 反标)时,Monitor 需要针对性修改。核心修改集中在三个方面:
- 信号路径的层次化引用
- 采样时机与 X 态处理
- 中间节点的可观测性保障
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
浙公网安备 33010602011771号