3. SystemVerilog与UVM基础回顾
在进入复杂SoC验证签核之前,需要把SystemVerilog与UVM中与签核直接相关的三条主线重新梳理清楚:验证平台架构决定了激励、监测、检查和覆盖率如何分层;组件通信决定了事务、时序和参考数据如何在各组件之间流动;配置机制决定了同一套验证平台如何在不同测试、不同DUT配置和不同签核场景下复用。签核不是“跑完仿真”,而是要在可重复、可追溯、可收敛的平台上,给出覆盖率、断言、回归和签核判据的完整证据链。
一、面向签核的验证平台架构
UVM验证平台通常由测试层、环境层、代理层和序列层构成。面向签核时,架构设计要额外关注三点:第一,激励与检查分离,driver只负责把transaction按时序驱动到接口,monitor只负责采样并还原transaction,scoreboard和reference model负责判断对错;第二,覆盖率与检查点独立,功能覆盖率、断言覆盖率和代码覆盖率分别由covergroup、SVA和工具统计,不能混在scoreboard里;第三,可配置边界清晰,哪些参数通过config_db传递,哪些通过virtual interface绑定,哪些通过factory override替换,必须在架构阶段就确定。
一个典型的签核级agent包含sequencer、driver、monitor和coverage collector。sequencer产生sequence item,driver将其转换为pin级时序,monitor从接口采样并重建transaction,coverage collector基于monitor输出采样covergroup。scoreboard则从monitor和reference model分别获取实际结果与期望结果,完成比对并记录错误。这样的分层使签核时能够单独统计每个agent的覆盖率,也能在失败时快速定位是激励、时序、参考模型还是比对逻辑的问题。
二、组件通信:transaction、TLM与采样时序
UVM组件之间的通信主要依赖TLM端口。sequence通过sequencer把item交给driver,driver通过virtual interface驱动DUT;monitor通过analysis port把采样到的transaction广播给scoreboard和coverage collector。签核场景下,通信设计要特别注意采样时刻和事务边界。例如AXI的写通道,monitor应在WLAST有效且握手完成时形成一个完整写事务,而不是在每个beat都发一个transaction,否则scoreboard会收到碎片化数据,覆盖率也会重复统计。
下面给出一个简化的monitor采样片段,用于说明事务边界如何确定。示例参数为假设值,仅用于教学演示:
class axi_monitor extends uvm_monitor;
virtual axi_if vif;
uvm_analysis_port #(axi_transaction) ap;
task run_phase(uvm_phase phase);
axi_transaction tr;
forever begin
@(posedge vif.aclk);
if (vif.aresetn === 1'b1 && vif.awvalid && vif.awready) begin
tr = axi_transaction::type_id::create("tr");
tr.addr = vif.awaddr;
tr.len = vif.awlen;
// 等待写数据通道完成,WLAST作为事务结束标志
collect_wdata(tr);
ap.write(tr); // 广播给scoreboard与coverage
end
end
endtask
endclass
这段代码的关键点在于:ap.write(tr)只在整个写事务完成后调用一次。如果放在每个beat之后调用,scoreboard的比对队列会与reference model的期望队列长度不一致,导致误报。签核时,这类通信边界错误往往表现为“功能覆盖率很高但scoreboard错误数持续增长”,因此必须把事务边界作为平台检查项之一。
三、配置机制:config_db、factory与virtual interface
UVM配置机制的核心是config_db。它通过层次化路径把配置对象从test传递到env、agent、driver和monitor。签核平台通常需要同时支持多种DUT配置,例如AXI数据位宽为64位或128位、DDR频率为1600Mbps或3200Mbps、是否使能ECC等。这些参数不应硬编码在driver里,而应通过config_db设置,使同一套平台在不同测试中复用。
配置传递的典型顺序是:test在build_phase中调用config_db::set,把配置对象按路径写入;env、agent、driver在build_phase中调用config_db::get取出配置。若get失败,应通过uvm_config_db#(T)::get的返回值判断并报错,而不是静默使用默认值。签核平台中,静默默认值是最危险的隐患之一,因为它会让错误配置在回归中长期不被发现。
// test中设置
cfg = axi_cfg::type_id::create("cfg");
cfg.data_width = 128;
cfg.has_ecc = 1;
uvm_config_db#(axi_cfg)::set(this, "env.axi_agent.*", "cfg", cfg);
// driver中获取
if (!uvm_config_db#(axi_cfg)::get(this, "", "cfg", cfg))
`uvm_fatal("CFG", "axi_cfg not found")
除config_db外,factory override也是签核平台的重要机制。当需要替换某个agent的driver或monitor时,可以通过set_type_override_by_type或set_inst_override_by_type实现,而不修改原有类。例如在低功耗签核场景中,可以用一个带电源状态检查的driver替换默认driver,从而在不影响其他测试的情况下增加检查能力。virtual interface则通过config_db传递,确保driver和monitor绑定到正确的接口实例,避免多实例SoC中接口错绑。
四、签核视角下的平台检查清单
把上述内容落到签核实践,可以在平台搭建完成后按以下清单逐项确认。该清单为示例性检查项,具体项目应结合SoC接口和签核目标调整。
| 检查项 | 关键问题 | 判断依据 |
|---|---|---|
| 事务边界 | monitor是否在完整事务结束时才ap.write | scoreboard期望队列与实际队列长度一致 |
| 配置获取 | config_db::get是否检查返回值 | 无静默默认值,缺失配置立即报fatal |
| 接口绑定 | virtual interface是否按实例路径绑定 | 多实例SoC中无接口错绑、无空指针 |
| 覆盖率采样 | covergroup是否基于monitor事务采样 | 覆盖率与scoreboard使用同一事务源 |
| factory替换 | override是否只影响目标实例 | 其他agent行为不变,回归结果可复现 |
总结来看,SystemVerilog与UVM基础在签核语境下并不是语法复习,而是平台工程的三块基石:架构决定检查能力放在哪里,通信决定数据如何无损流动,配置决定平台能否覆盖多种签核场景。把这三者落实到agent、scoreboard、config_db和factory的具体实现中,后续的覆盖率收敛、断言签核和回归分析才有可靠基础。