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的覆盖率,也能在失败时快速定位是激励、时序、参考模型还是比对逻辑的问题。

UVM签核平台架构与通信/配置关系 Test层 base_test soc_signoff_test sequence启动与配置 config_db::set Env层 AXI Agent sequencer driver monitor coverage virtual interface绑定 DDR Agent sequencer driver monitor coverage virtual interface绑定 Scoreboard / Ref Model expected transaction actual transaction比对 错误计数与签核判据 DUT / SoC顶层 AXI / DDR / 中断 / 时钟复位接口 配置下发

二、组件通信: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行为不变,回归结果可复现
签核平台的可复现性来自“配置可追溯、事务边界明确、检查点独立”。当回归失败时,先确认config_db是否按预期传递、monitor是否在正确时刻产生transaction,再检查scoreboard和覆盖率,往往比直接调试DUT更快定位问题。

总结来看,SystemVerilog与UVM基础在签核语境下并不是语法复习,而是平台工程的三块基石:架构决定检查能力放在哪里,通信决定数据如何无损流动,配置决定平台能否覆盖多种签核场景。把这三者落实到agent、scoreboard、config_db和factory的具体实现中,后续的覆盖率收敛、断言签核和回归分析才有可靠基础。