4. 激励生成与场景建模

激励是验证平台真正“驱动”DUT 的部分。一个验证环境即使连接器、监视器、参考模型都齐全,如果激励无法覆盖协议允许的组合、无法命中边界条件、无法复现失败场景,覆盖率仍然会停在低位。本节围绕 UVM sequence 机制,说明如何用 SystemVerilog 构建定向激励与随机激励,并借助 Synopsys VC SpyGlass 对激励代码做静态检查,提前发现约束冲突、死代码和不可达分支。

一、激励的两个来源:定向与随机

定向激励(directed stimulus)指明确指定每个字段取值的激励,例如固定发送一个长度为 1 的短包、一个 CRC 错误的包、一个地址越界的访问。它的优点是意图清晰、可复现,适合验证复位序列、错误注入、协议违例等“必须发生”的场景。缺点是手工枚举组合,覆盖有限。

随机激励(constrained random)指通过 rand/randc 声明随机变量,用 constraint 描述合法取值范围,由求解器在约束空间内产生激励。它的优势是能在合法空间内自动探索,配合覆盖率驱动可以持续逼近未覆盖区域。工程实践中两者不是二选一,而是“随机为主、定向为辅”:随机负责广度,定向负责深度和特定错误路径。

二、UVM sequence 的层次结构

UVM 把激励组织成 sequence item、sequence、sequencer、driver 四层。item 是事务对象,sequence 是 item 的集合与生成逻辑,sequencer 负责仲裁与转发,driver 把 item 转换成引脚级信号。理解这个层次,才能知道约束应该写在哪里、随机化应该在哪个阶段发生。

UVM 激励生成链路与 VC SpyGlass 静态检查点 Sequence Item rand 字段 + constraint len / addr / data / crc Sequence body() 生成与调度 start() / 嵌套 / 分层 Sequencer 仲裁与转发 seq_item_port Driver item → 引脚时序 驱动 DUT 激励质量静态检查(VC SpyGlass) 约束冲突 求解失败 / 空解 不可达分支 if/else 死代码 随机稳定性 seed 依赖 / 分布 覆盖率可达性 coverpoint 命中 激励策略组合 随机激励:广度探索 constraint 约束合法空间 定向激励:深度命中 错误注入 / 边界场景 覆盖率反馈闭环 未覆盖 → 调整约束

三、用 SystemVerilog 写约束:从合法到有意图

约束不是“随便写几条”,而是把协议规则翻译成求解器能理解的形式。以一个 AXI 风格写事务为例,假设地址 4KB 对齐、突发长度 1~16、数据宽度 32 位:

class axi_wr_item extends uvm_sequence_item;
  rand bit [31:0] addr;
  rand bit [7:0]  len;      // 1~16 beats
  rand bit [2:0]  size;     // 2^size bytes per beat
  rand bit [31:0] data[];

  constraint c_addr_align {
    addr[11:0] == 0;                 // 4KB 对齐
  }
  constraint c_len_range {
    len inside {[0:15]};             // 实际 beats = len+1
  }
  constraint c_size_fixed {
    size == 3'd2;                    // 4 bytes
  }
  constraint c_data_size {
    data.size() == len + 1;
  }
  constraint c_data_dist {
    foreach (data[i])
      data[i] dist { 32'h0 := 2, [32'h1:32'hFFFF_FFFE] :/ 8, 32'hFFFF_FFFF := 2 };
  }
endclass

这里 dist 权重让全 0 和全 F 各占约 20%,其余值均分剩余权重,目的是让边界数据更容易被命中。inside 用于范围约束,foreach 用于数组元素约束。注意 data.size() == len + 1 把动态数组长度与 len 绑定,避免求解出长度不匹配的非法事务。

四、定向激励与随机激励的编排

在 body() 中,可以先用定向 sequence 完成复位后的一次基准写读,再用随机 sequence 做批量探索,最后用定向 sequence 注入错误。典型写法:

class wr_scenario extends uvm_sequence #(axi_wr_item);
  `uvm_object_utils(wr_scenario)

  task body();
    axi_wr_item it;
    // 1) 定向:单拍写,地址 0
    `uvm_do_with(it, { addr == 0; len == 0; })
    // 2) 随机:200 次随机写
    repeat (200) `uvm_do(it)
    // 3) 定向:越界地址,验证错误响应
    `uvm_do_with(it, { addr == 32'hFFFF_F000; })
  endtask
endclass

这种“定向—随机—定向”的编排,既保证基准场景可复现,又让随机部分覆盖大量合法组合,最后用定向激励命中错误路径。实际项目中,随机次数、定向场景数量应根据覆盖率缺口动态调整,而不是固定写死。

五、VC SpyGlass 对激励质量的静态检查

激励代码本身也可能有缺陷,而且这些缺陷往往在仿真中表现为“随机化失败”或“某些分支永远不命中”,排查成本高。Synopsys VC SpyGlass 可以在不运行仿真的情况下对 SystemVerilog/UVM 代码做静态分析,重点检查以下几类问题:

  • 约束冲突:两条约束对同一变量给出互斥范围,求解器永远无解。例如同时要求 len == 0 和 len inside {[1:15]}。
  • 不可达分支:约束把某变量限定在固定值,而后续 if 分支却判断该变量为其他值,形成死代码。
  • 随机稳定性:未固定 seed 时,同一 sequence 在不同仿真中分布差异过大,导致覆盖率波动。SpyGlass 可提示随机变量的分布依赖。
  • 覆盖率可达性:coverpoint 的 bin 在约束空间内无法被命中,例如约束要求 addr[11:0]==0,但 coverpoint 却采样 addr[11:0] 的非零值。

使用方式通常是在项目脚本中调用 SpyGlass 的 lint/CDC/约束检查规则集,把激励文件加入分析列表,输出报告后逐条确认。对于约束冲突和不可达分支,应直接修改约束或分支条件;对于随机稳定性问题,应在测试中固定 seed 或使用 uvm_top.set_report_id_action 记录随机种子,便于复现。

建议把“约束冲突”和“覆盖率不可达”作为激励代码提交前的必查项。前者会导致随机化失败,后者会让覆盖率长期停滞,两者都会显著拖慢验证收敛。

六、一个可执行的检查清单

检查项检查内容判断依据
约束可解性所有 constraint 是否存在互斥范围SpyGlass 无冲突告警,随机化成功
分支可达性if/else、case 分支是否被约束排除SpyGlass 无 unreachable 告警
随机分布dist 权重是否覆盖边界值覆盖率报告中边界 bin 命中
seed 可复现失败用例能否用同一 seed 复现记录 seed 后重跑结果一致
覆盖率反馈未覆盖 bin 是否可通过调整约束命中调整后覆盖率提升

把上述检查嵌入日常流程:每次修改 sequence 或 constraint 后,先跑 SpyGlass 静态检查,再跑随机仿真,最后看覆盖率报告。这样激励质量不再依赖个人经验,而是有明确的检查点和判断依据。