在 SystemVerilog 和 UVM 验证环境中,force 和 deposit 是两种用于控制信号值的强大机制,主要用于调试、驱动特定场景和绕过常规验证流程。
核心机制对比
| 特性 | force |
deposit |
|---|---|---|
| 主导权 | 强制驱动,最高优先级,覆盖所有其他驱动源 。 | 写入驱动,参与仲裁,需与其他驱动源(如其他进程、assign语句)竞争。 |
| 作用对象 | 主要针对线网(net),也可用于变量(variable/reg)。 | 可对变量(reg) 或线网(net) 进行赋值操作。 |
| 作用时间 | 持续有效,直至被 release 释放 。 |
立即生效一次,无持续性。 |
| UVM对应 | uvm_hdl_force() / uvm_hdl_release() 。 |
uvm_hdl_deposit() 。 |
| 验证意图 | 强制设置特定状态,无视设计逻辑。 | 模拟一个驱动源的输入,遵循设计的仲裁逻辑。 |
Verilog/SystemVerilog 原生语法与应用
force与release
原生语法用于在测试平台中直接操作信号。
-
语法 :
force <路径> = <值>; -
释放 :
release <路径>; -
示例 :强制时钟信号暂停。
verilogmodule tb; reg clk = 0; always #5 clk = ~clk; // 正常时钟产生,周期10ns initial begin #100; // 等待100ns force clk = 0; // 强制clk为0,时钟"暂停" #50; // 保持50ns release clk; // 释放clk,时钟恢复由always块驱动 #50 $finish; end endmodule
deposit
原生 SystemVerilog 没有直接的 deposit 命令,其概念通过过程赋值(=) 或持续赋值(assign) 来体现。deposit 的核心思想是作为一个驱动源参与竞争。UVM 的 uvm_hdl_deposit 函数则通过 DPI 接口在 C/SystemVerilog 边界实现此功能。
UVM DPI 函数详解与实例
UVM 通过一组 DPI-C 函数(uvm_hdl_*)提供对 HDL 信号的后门访问能力,这些函数封装了与仿真器的交互细节 。
uvm_hdl_deposit(path, value)
该函数将一个值"存入"指定路径的信号,其效果等同于在仿真时间点对该信号进行一次非阻塞赋值 。
-
应用场景:在测试序列中模拟一个驱动源的输入值,例如向一个多路器(MUX)的某个输入端口写入数据,该值将与其他端口驱动进行仲裁。
-
代码示例 :控制一个寄存器模型的字段值。
systemverilog// 假设DUT中有一个状态寄存器:dut.u_core.ctrl_reg[3:0] import uvm_pkg::*; task my_test_seq::body(); bit [63:0] rw_data; string hdl_path = "tb.dut.u_core.ctrl_reg"; // 场景:模拟配置状态为 4'b0011 rw_data = 64'h3; // 注意:DPI函数常使用64位数据宽度 if (uvm_hdl_deposit(hdl_path, rw_data)) begin `uvm_info("DEPOSIT", $sformatf("Successfully deposited 0x%0h to %s", rw_data, hdl_path), UVM_LOW) end else begin `uvm_error("DEPOSIT", $sformatf("Failed to deposit to %s", hdl_path)) end // 此时,若DUT内没有其他更高优先级的驱动,ctrl_reg的值将变为3。 // 若有其他持续的force或assign语句,则结果取决于仲裁。 endtask
uvm_hdl_force(path, value)与uvm_hdl_release(path)
这对函数用于强制驱动和释放信号,是调试和错误注入的利器 。
-
应用场景 :
- 错误注入:强制一个错误信号(如 fifo_full)为 1,验证 DUT 的错误处理机制。
- 跳过复杂逻辑:在验证初期,强制某个模块的输出为期望值,以隔离并验证下游逻辑。
- 控制难以触发的状态:强制状态机进入一个难以通过正常激励到达的状态。
-
代码示例 :注入一个单比特错误。
systemverilog// 假设需要向一个数据总线 tb.dut.data_bus[31:0] 注入错误 task error_injection_task(); bit [63:0] fault_value; string path = "tb.dut.data_bus"; int force_ok, release_ok; // 1. 首先读取当前值,以便后续恢复(可选) if (!uvm_hdl_read(path, fault_value)) begin `uvm_error("HDL", "Read failed before force") return; end `uvm_info("PRE_FORCE", $sformatf("Current value of %s is 0x%0h", path, fault_value), UVM_HIGH) // 2. 强制注入错误:将第7位翻转 fault_value = fault_value ^ (1 << 7); // 翻转bit7 force_ok = uvm_hdl_force(path, fault_value); if (!force_ok) begin `uvm_error("FORCE", $sformatf("Force failed on %s", path)) return; end `uvm_info("FORCE", $sformatf("Forced 0x%0h onto %s for error injection", fault_value, path), UVM_LOW) // 3. 保持错误一段时间(例如2个时钟周期) #200ns; // 4. 释放强制,让总线恢复由正常逻辑驱动 release_ok = uvm_hdl_release(path); if (!release_ok) begin `uvm_error("RELEASE", $sformatf("Release failed on %s", path)) end else begin `uvm_info("RELEASE", $sformatf("Released force on %s", path), UVM_LOW) end endtask
关键区别与选用原则
-
驱动竞争 vs. 绝对控制:
deposit写入的值会与设计中的其他驱动(如always块、assign语句)进行竞争。如果设计中有持续驱动该信号的逻辑,deposit可能无法改变最终值。force拥有最高优先级,会覆盖所有其他驱动源,确保信号值在强制期间完全符合预期 。
-
验证目的:
- 使用
deposit当你想 "模拟一个输入" ,并观察设计在正常仲裁逻辑下的反应。这更贴近真实硬件行为。 - 使用
force当你想 "设定一个条件" 或 "制造一个异常" ,用于调试、错误注入或绕过某些难以构造的激励逻辑。
- 使用
-
对仿真行为的影响:
force可能掩盖某些设计缺陷,因为它屏蔽了正常的信号传播路径。过度使用force会降低测试的置信度。deposit更安全,因为它尊重设计内部的驱动逻辑,但其效果可能因设计内部的竞争而不可预测。
实践建议 :在构建定向测试(Direct Test)或调试时,可谨慎使用 force。在构建随机约束测试时,应优先通过正常的验证环境(如 uvm_sequence、uvm_driver)产生激励,仅在极端情况下使用 deposit 作为补充。uvm_hdl_read 函数常与这两者配合使用,用于读取信号当前状态以做出决策或记录 。