IC 验证周报知识合集|第 37–43 周

第 37–43 周的七期周报一共收录了 30 多条上游动态,但大部分篇幅花在“哪天、哪个仓库、合入了什么”上。新闻本身很快就会过时,真正值得留下的是这些修复背后暴露出来的验证知识:覆盖率数字怎么算、断言为什么会假通过、UVM 测试为什么会多跑 40 μs 才结束。

这篇文章把七期内容拆散,按主题重新组织。每一节都回答三个问题:

  1. 原理是什么:这个点在 SystemVerilog / UVM 里是怎么工作的;
  2. 哪里容易出错:上游项目真实踩过的坑;
  3. 你可以怎么检查:能直接放进自己环境的最小用例或检查清单。

读完这一篇,就不需要再回去翻七期周报了。原始链接都保留在文末,想追溯细节时再查。

阅读说明

2026-10-30 已用 Questa Sim-64 10.7c 验证覆盖率阈值、转移计数、多实例合并、断言负向用例、异步复位、非阻塞事件与 UVM drain time;NBA/fork 用例另在 Icarus Verilog 13.0 上对照通过。可复现源码和命令位于本站仓库的 examples/weekly-digest-w37-43/。clocking block、RAL、cocotb 与 parser 片段仍是示意,未运行;上游项目性能数据也未独立复现。

主题地图

主题核心问题涉及周次
一、覆盖率:百分比背后的口径同一份激励,换个工具或开关,覆盖率为什么会变39、40、41、42、43
二、断言:怎样证明它真的会失败断言全部通过,说明设计对了吗37、38、41、42
三、调度语义:同一时间槽里的先后fork、NBA、clocking block 的时序为什么会悄悄出错39、40、42
四、UVM 环境工程objection 收尾、RAL 复位值、scoreboard 过渡窗口、新 IP 骨架41、42、43
五、回归分层与门禁什么测试放 L0,迁移和换仿真器怎么验收39、40、41、42
六、需求追溯与证据链覆盖率之外,怎么证明“这条需求被验证过”37、38、43
七、cocotb 与前端工具Python testbench 迁移和 parser 错连40、41、42、43

一、覆盖率:百分比背后的口径

覆盖率门禁最常见的误区是只盯着一个百分比。这七周里 Verilator 有五项覆盖率相关修改,每一项都在提醒同一件事:百分比 = 分子 / 分母,而分子和分母都取决于规则和配置。

1.1 option.at_least:命中一次不等于覆盖

option.at_least 规定一个 bin 至少命中多少次才算覆盖,默认值是 1。

covergroup cg_len @(posedge clk iff valid);
  option.at_least = 4;            // 每个 bin 至少命中 4 次
  cp_len: coverpoint len {
    bins small = {[1:4]};
    bins big   = {[5:16]};
  }
endgroup

需要 at_least > 1 的典型场景:边界值要在不同上下文中多次出现才可信,或者某个状态必须在多种前序条件下进入过。

易错点:如果工具没有正确实现 covergroup 级或 cross 级的 at_least(Verilator 在第 43 周才补齐),命中一次就会被算作覆盖,而整体百分比看起来依然很合理,不会引起任何人怀疑。

检查方法:针对同一个 bin,构造“命中 3 次”“恰好 4 次”“5 次”三组激励,分别核对 coverpoint、cross 和 covergroup 汇总三层结果。

1.2 transition bin:第一个样本不是转移

transition bin 记录的是连续采样值之间的变化,常用于状态机覆盖率:

cp_state: coverpoint state {
  bins idle_to_busy = (IDLE => BUSY);
  bins busy_run     = (BUSY [*3]);     // 连续 3 次采样都是 BUSY
  bins busy_to_any  = (BUSY => IDLE, ERR);
}

转移至少需要两个样本。第一次 sample() 前面没有“上一个值”,因此不可能构成转移。Verilator 在第 43 周修复了两个问题:把第一次采样错算成转移,以及重叠序列的计数不正确。

为什么危险:这类错误不会让仿真失败,只会悄悄污染覆盖率收敛的判断。

检查方法:准备四类采样轨迹,先按 LRM 规则手算预期命中次数,再和工具报告逐项比对:

轨迹idle_to_busybusy_runbusy_to_any
只有一个样本 BUSY000
IDLE BUSY100
BUSY BUSY BUSY BUSY02(重叠匹配)0
IDLE IDLE BUSY100

以上是 Questa 10.7c 功能覆盖率数据库中的实际 bin 命中次数;每条轨迹使用独立 covergroup 实例,避免前一条轨迹的采样状态污染下一条。

1.3 多实例 covergroup:平均值还是并集

同一个 covergroup 有多个实例时,类型覆盖率可以有两种算法:

  • 默认:按 IEEE 1800-2023,对各实例的覆盖率求平均;
  • type_option.merge_instances = 1:先把各实例的 bin 求并集,再计算覆盖率。

举个例子,covergroup 有 4 个 bin,实例 A 命中 bin 0、1,实例 B 命中 bin 2、3:

算法结果
实例平均(50% + 50%) / 2 = 50%
bin 并集{0,1,2,3} / 4 = 100%

两个数字差了一倍,激励却完全相同。从商业仿真器迁移覆盖率门禁时,最容易在这里“覆盖率暴跌”或“覆盖率暴涨”。Verilator 在第 41 周支持了这一选项,并新增 --coverage-merge-instances 开关。

检查方法:迁移前写一个上面这种两实例的小例子,在新旧工具上分别读取 get_coverage()、get_inst_coverage() 和命令行报告,三处的分子、分母都要对上。

本机实测:默认类型覆盖率为 50%,开启合并后为 100%,每个实例仍各覆盖 50%。读取实例 API 时还要显式设置 option.per_instance = 1 和 option.get_inst_coverage = 1;只设置前者时,本机 Questa 的 get_inst_coverage() 在合并模式下返回 100%,而数据库的实例报告为 50%。API 配置必须一同记录,不能把它误判为统计错误。

1.4 行覆盖 vs 分支覆盖

第 42 周 Verilator 把分支覆盖(if/else、?:)从行覆盖里拆了出来:

开关统计内容
--coverage-line仅基本块行覆盖
--coverage-branch仅分支覆盖(新增)
--coverage两者都开

一行 assign y = sel ? a : b; 只要执行过就算行覆盖命中,但 sel=0 和 sel=1 两个分支是否都走过,只有分支覆盖能告诉你。这个例子说明行覆盖 100% 不代表所有分支都被执行过。

易错点:门禁脚本过去用 --coverage-line,实际拿到的是两者的合集;升级后分母突然变小,趋势图出现一个“台阶”。

1.5 同一个数据库,两种读法要一致

第 40 周 Verilator 修复了 verilator_coverage 命令行报告与仿真中 get_coverage() 结果不一致的问题。很多流水线一边在仿真结束时调用运行时 API 打印覆盖率,一边用命令行工具生成报告。两者对不上时,门禁就失去了意义。

原则:门禁只认一个统计入口,并在文档里写清分母定义。

1.6 大量 bin 的成本

bins b[] = {[0:16383]}; 这类写法会生成上万个 bin。第 39 周 Verilator 把数组 bins 和 auto bins 的采样改成区间查找。项目方报告,16,384 个 bin 的 C++ 编译时间从 81 秒降到约 1 秒,并新增 --coverage-max-bins 上限。

启示:bin 数量是有成本的。覆盖率模型应该按功能意义分组(边界、典型区间、非法值),不要因为写起来方便就把整个取值空间展开。

覆盖率检查清单

  • 门禁使用唯一统计入口,分母定义写进文档
  • 明确 at_least、merge_instances、权重、ignore_bins 的配置
  • 行覆盖、分支覆盖、功能覆盖分开记录趋势
  • 工具升级前后,在同一组小型 covergroup 上比对原始计数,不只比百分比
  • transition bin 有“单样本 / 重叠 / 重复前缀”测试轨迹

二、断言:怎样证明它真的会失败

断言全部通过,可能说明设计正确,也可能说明断言根本不会失败。这七周里有两次断言引擎修复都属于“本应失败却不报错”,这是验证里最危险的一类问题。

2.1 空成功(vacuous success)

// 如果 req 从来没有拉高过,这条断言永远“通过”
a_req_ack: assert property (@(posedge clk) req |-> ##[1:4] ack);

蕴含式 |-> 的前件不成立时,断言按空成功处理。仿真日志里看起来全是 PASS,实际上一次都没有真正检查过。

对策:给每条重要断言的前件配一条 cover:

c_req: cover property (@(posedge clk) req);           // 前件至少发生过
c_req_ack: cover property (@(posedge clk) req ##[1:4] ack);  // 完整路径发生过

cover 命中次数为 0 的断言,不能算作“已验证”。

2.2 负向用例:用故意的错误证明断言有效

第 41 周 Verilator 修复了一个问题:多周期前件后接布尔析取的后件永远不会失败。

// 多周期前件 + 析取后件
a_rsp: assert property (@(posedge clk) req ##1 gnt |-> ack || err);

如果只跑正向激励(ack 或 err 总会出现),这个 bug 永远发现不了。只有专门构造 req → gnt → 既无 ack 也无 err 的激励,才能验证断言确实会报错。

原则:每条关键断言至少配一个预期失败的负向用例。工具升级验收时,负向用例必须仍然失败;正向用例全过证明不了任何事。

2.3 disable iff 是异步的

a_done: assert property (@(posedge clk) disable iff (!rst_n)
                         start |-> ##[1:4] done);

disable iff 的条件不依赖采样时钟:只要复位在任意时刻生效,正在进行中的断言尝试(pending attempt)都应立即终止。第 42 周 Verilator 修复的正是复位在两个时钟沿之间生效时,待决尝试没有被终止的问题。

三类必测场景:

场景预期
复位恰好在时钟沿生效待决尝试终止,不报错
复位在两个时钟沿之间生效同上,这是最容易出错的情况
复位释放后再次触发 start断言重新开始检查,能正常失败

只验证“复位期间不报错”是不够的,还必须确认复位释放后断言能重新生效。

2.4 断言失败的三种根因

第 38 周 VerifAIX 的文章把回归中的断言失败分为三类,这个分类即使不用任何工具也值得直接采用:

根因典型表现正确处理
RTL 缺陷DUT 行为违反规格修 RTL
断言写错断言与规格不符修断言,并检查修改前后的触发条件
非法输入激励违反了接口假设修激励或补 assume 约束

最需要警惕的情况:把断言“修好”实际变成了把断言“改弱”。例如把 ##[1:4] 放宽到 ##[1:100],失败数确实清零了,检查力度也跟着没了。每次修改断言,都应该记录修改前后的触发条件,并重新检查 cover 是否仍然命中。

2.5 复位约束 → SVA → X 注入

第 37 周 DVCon Japan 的一篇论文展示了把静态 RDC 结果带进动态仿真的思路:

  1. 从静态工具的复位约束中自动生成 SVA,检查复位分组和顺序假设;
  2. 对静态工具识别出的跨复位路径做 X 注入,观察 X 是否会传播到可观测输出。

这个思路可以手工落地:如果设计要求 A 域先于 B 域释放复位,可以写成

a_rst_order: assert property (@(posedge clk) $rose(rst_b_n) |-> rst_a_n);

意思是 B 域释放复位的那一拍,A 域必须已经释放。静态工具告诉你“这里有风险”,动态断言和 X 注入则能告诉你“风险是否真的会被观测到”。

断言检查清单

  • 每条关键断言都有对应的前件 cover 与完整路径 cover
  • 每条关键断言至少有一个预期失败的负向用例
  • disable iff 覆盖“沿上 / 沿间 / 重新使能”三类场景
  • 断言修改要记录前后的触发条件,区分“修好”与“改弱”
  • waiver 有责任人审批,不能用清零失败数代替收敛

三、调度语义:同一时间槽里的先后

SystemVerilog 的一个时间槽内部还分为 Active、Inactive、NBA、Observed、Reactive 等区域。testbench 的许多“偶发”问题,根源都在这里。这七周 Verilator 修复了三处调度语义错误,正好对应三个经典知识点。

3.1 disable fork 不应吞掉已调度的 NBA

initial begin
  fork
    q <= 1'b1;          // 子线程调度一个 NBA 更新
  join_none
  #0 disable fork;      // 杀掉子线程
end
// 期望:q 最终仍变为 1。NBA 更新事件已经进入调度队列,
// 它不是一个“进程”,disable fork 不应撤销它

第 39 周修复的就是 disable fork / wait fork 错误影响待处理 NBA 更新的问题。在 UVM 环境中,fork ... join_any; disable fork; 是超时控制的常见写法,如果工具在这里处理错误,就会出现“偶尔少一次更新”的诡异现象。

本文这个最小用例在 Icarus 13.0 与 Questa 10.7c 中均得到 q=1。这个对照验证的是所示调度模式,不代表所有 fork/NBA 组合均已覆盖。

3.2 ->> 非阻塞事件触发

event e;
initial begin
  @(e);                        // 挂起等待
  $display("woken at %0t", $time);
end
initial #10 ->> e;              // 非阻塞触发:在 NBA 区域触发

-> 在当前区域立即触发,->> 则把触发推迟到 NBA 区域。两者的区别,正是为了避免“等待方还没开始 @(e),事件就已经触发”的竞争。第 40 周修复的问题是:在可挂起进程中,->> 根本没有触发。

检查点:事件是否唤醒,在哪个时间槽唤醒,唤醒后的 NBA 更新顺序是否正确。

3.3 clocking block 的驱动时机

clocking block 是 UVM driver 消除 testbench 与 DUT 之间竞争的标准手段:

clocking drv_cb @(posedge clk);
  default input #1step output #1;
  output valid, data;
  input  ready;
endclocking
 
// driver 中
@(vif.drv_cb);
vif.drv_cb.valid <= 1'b1;   // 按 output skew 驱动
vif.drv_cb.data  <= tr.data;

第 42 周 Verilator 修复的场景是:clocking 事件发生后,在同一时间步内发出的 clocking output 驱动被延后了一个周期。

为什么难发现:激励晚一拍,看起来像是 DUT 的协议延迟。如果 scoreboard 本来就允许一定的时序容差,这个错误会被完全掩盖。

调度语义检查清单

把以下最小用例放进“跨仿真器语义回归”,在两款仿真器上对照最终状态与时间戳:

  • fork 子线程调度 NBA 后被 disable fork
  • ->> 触发 + 挂起等待
  • clocking block 事件唤醒后在同一时间步驱动输出
  • fork...join_any 超时模式下的 NBA 与事件顺序

延伸阅读:第 7 章 · 线程控制与进程间通信、第 4 章 · 接口与测试平台连接


四、UVM 环境工程

4.1 phase_ready_to_end 与 drain time:一个 40 μs 的尾巴

这是七期里最值得细读的一条(第 41 周,OpenTitan)。

机制回顾:run phase 中所有 objection 都 drop 之后,UVM 会:

  1. 等待 drain time;
  2. 调用所有组件的 phase_ready_to_end();
  3. 如果此时有组件 raise 了 objection,就等它 drop 之后重新计时 drain time,再次调用 phase_ready_to_end();
  4. 这个循环最多重复 20 次(UVM 默认的 ready-to-end 迭代上限)。

问题写法:

virtual function void phase_ready_to_end(uvm_phase phase);
  if (phase.get_name() != "run") return;
  phase.raise_objection(this);
  fork begin
    if (!done) wait (done);     // 已经 done 也照样 raise / drop
    phase.drop_objection(this);
  end join_none
endfunction

即使不需要等待,它也会立即 raise 再 drop。每次 drop 都会重启 drain time 并再次触发 phase_ready_to_end(),直到用完 20 次迭代。drain time 为 2 μs 时,就会多出 40 μs,而且所有嵌入这个子环境的芯片级测试都要多付这笔时间。

在本机 UVM-1.1d 最小环境中,测试主体于 1 μs 结束:正确写法的尾部为 2 μs,错误写法为 42 μs(20 次 ready-to-end 回调),相差 40 μs。运行脚本同时检查这两个时间和 UVM error/fatal 计数;这是对机制的复现,不是 OpenTitan 完整环境的重跑。set_drain_time 参数还须按所用 UVM 包的时间单位换算,示例中已注明本机预编译包使用 1 ps。

修正写法:只有确实需要等待时才 raise。

virtual function void phase_ready_to_end(uvm_phase phase);
  if (phase.get_name() != "run") return;
  if (!cfg.en_scb || done) return;    // 不需要等待:什么都不做
  phase.raise_objection(this);
  fork begin
    wait (done);
    phase.drop_objection(this);
  end join_none
endfunction

排查方法:在回归报告中单独记录“测试主体结束 → run phase 真正结束”的时间。如果这段时间固定且偏长,就打开 objection trace(+UVM_OBJECTION_TRACE),检查哪个组件反复 raise / drop。

延伸阅读:第 5 章 · Phase 调度与 Objection 控制、UVM · objection

4.2 RAL:每个寄存器副本都有自己的复位值

第 42 周 OpenTitan 修复了 reggen 的一个问题:multireg(寄存器数组)的每个副本都复用了第一个副本的复位值。如果规格允许 CTRL_0 和 CTRL_1 的复位值不同,生成的 RAL 就是错的。

检查方法:复位后对整个寄存器模型做一次前门读取比对:

// 复位完成后
uvm_status_e status;
ral.reset();                                   // 模型镜像值回到复位值
ral.mirror(status, UVM_CHECK, UVM_FRONTDOOR);  // 逐个读回并与镜像值比较

也可以直接使用 UVM 内建的 uvm_reg_hw_reset_seq。关键是逐实例比对,而不是只抽查第一个副本的地址和位宽。

延伸阅读:UVM · 期望值、镜像值与预测、UVM · 寄存器内建测试与常用 API

4.3 scoreboard 的抽象状态会比 RTL 晚几拍

第 41 周 OpenTitan 的 OTBN 修复:设计执行 WFI(暂停等待)后恢复时,scoreboard 的抽象状态比 RTL 可观察事件晚了几个周期,导致开启功能覆盖率的 nightly 回归稳定失败。

错误的修法:暂停期间直接忽略所有检查。

正确的修法:把允许的过渡窗口写成范围明确、计数受限的规则,例如“恢复后 N 拍内允许观察到最多 M 条指令处于 PAUSED 状态”,超出窗口立即报错。覆盖模型也要跨越暂停边界持续跟踪指令对,不能在暂停时清空。

这个模式适用于所有带暂停、时钟门控、低功耗握手恢复的设计。

4.4 新 IP 验证环境的骨架清单

第 43 周 OpenTitan 为 I3C 搭建了第一个 block-level UVM 环境。从提交内容看,一个新 IP 环境在写第一个 sequence 之前,就要先处理好这些依赖:

依赖为什么要先处理
多时钟域复位(主时钟 + AON)复位顺序错了,公共 CSR 测试直接卡住
板级电气默认值(如总线上拉)没有上拉模型,总线信号是 X/Z
按 DUT 参数调整 RAL参数化后的寄存器复位值与 RAL 不一致
硬件自主写内存 / 寄存器硬件会更新的字段要设为 volatile 或排除比对,否则 RAL prediction 必然报错
CSR SVA bind + 公共 CSR testplan让读写、复位、别名等通用测试第一时间跑起来

经验:公共 CSR 测试失败时,先排查环境假设(复位、上拉、参数、volatile 字段),再怀疑 DUT。

4.5 验证计划要尽早成为追踪主线

第 43 周 OpenTitan 为 RRAM 控制器加入初始 testplan,时间早于大部分测试代码。对于存储控制器,评审时应重点确认以下类别是否已经落到可观测的 testpoint:错误注入、访问权限、跨复位持久性、擦写时序边界。testplan 如果只罗列功能点,就无法对接回归结果。


五、回归分层与门禁

5.1 分层回归:同一个测试,不同强度

Caliptra SS 在第 40、41 周展示了标准的分层结构:

层级触发时机用例特征
L0 Promote每次提交短、确定性强、超时短,能快速失败
L1 Nightly Directed每晚完整定向测试
L1 Nightly Strict每晚更严格的检查配置

两个值得借鉴的做法:

  • UART 环回冒烟测试同时进入 L0 和 L1。一个短小用例就覆盖了寄存器配置、发送、接收和数据一致性,性价比很高;
  • USB 状态测试提升到 L0 时,把轮询超时从 10000 降到 3000:压缩的是等待上限,不是检查点。测试在各层保持同一个身份,flaky 和性能变化才能跨层追踪。

5.2 大型验证资产迁移的五种状态

第 40 周 Caliptra SS 批量整合 USB 验证资产时,在提交说明中明确记录了仍待协调的 C API、地址映射和编译问题。这提醒我们:“文件进了主线”和“回归可信”之间还隔着好几步。

文件已进入主线 → 能编译 → 能运行 → 结果可信 → 已进入门禁

每个测试分别追踪处于哪一步。尚未到“结果可信”就加入夜间回归、却没有 waiver 和负责人的测试,只会放大回归噪声。资产数量不等于验证成熟度。

5.3 换仿真器或新增后端:三层验收

第 39 周 dvSim 1.54.0 加入 Verilator 后端。无论是哪种新后端接入,都可以按三层验收:

层级验收内容回答的问题
第一层:可运维编译、运行、超时、退出码、失败时的日志出错了能不能诊断
第二层:可归档seed、波形、覆盖率数据库、结果归档能不能复现和追溯
第三层:语义等价fork/NBA、零延迟事件、DPI/VPI、复杂 covergroup 的跨工具对照结果是否可信

前两层保证流水线能用,第三层才回答“结果是否等价”。第三层的用例可以直接复用本文第一、二、三节的最小用例。

5.4 失败路径也要测试

两个很小却很实用的修复:

  • dvSim 1.54.1(第 39 周):外部命令失败时补上了命令输出。验收方法是故意制造一次编译失败和一次工具缺失,确认日志中保留了真实的错误输出,同时检查凭据和绝对路径是否需要脱敏;
  • Xcelium FSDB 波形(第 42 周):旧配置没有加载 Verdi dumper,-w fsdb 根本生成不了波形。验收标准应是“波形文件存在且能打开”,而不是“仿真返回 0”。按需启用的调试格式,值得单独设一条轻量 smoke job。

5.5 按变更影响选择回归

第 37 周 DVCon Japan 的 IntelliVerifier 提出:diff 解析、调用图、数据流分析交给确定性脚本,只把语义映射等判断性工作交给 LLM。即使不用 AI,背后的原则也成立:

  • 能计算的部分必须可重放;
  • 被跳过的每个测试都要记录可审计的理由;
  • 硬门禁失败时不得宣称成功。

回归检查清单

  • L0 用例短、确定、超时明确,并在 L1 保持同一测试身份
  • 每个迁移资产标注当前所处的五种状态之一
  • 新后端按“可运维 / 可归档 / 语义等价”三层验收
  • 有专门的失败路径测试:编译失败、工具缺失、波形格式
  • 记录“测试主体结束 → 仿真结束”的尾部时间

六、需求追溯与证据链

6.1 覆盖率 100% 也可能什么都没证明

第 37 周 DVCon Japan 的 SpecGuard 区分了两种容易被忽视的情况:

  • 断言通过,但没有映射到任何需求:检查了某种行为,却说不清对应哪条规格;
  • 看似相关,实际检查了错误的行为:coverpoint 命中了,checker 却在验证别的东西。

作者报告,在 USB4 案例中,随着证据信任规则从宽松调到严格,需求覆盖率可以从 85.2% 降到 12.5%(作者数据,未经独立复现)。同一个项目,换一种“什么算证据”的口径,结论天差地别。

可以直接落地的做法:验证计划中每条需求都填写四类引用,任何一类为空都不能算已覆盖。

需求 ID实现位置checkerstimulus结果
REQ-012fifo.sv: full 逻辑a_no_write_when_fulltest_fill_overflowPASS @ seed 42
REQ-013fifo.sv: almost_full—test_fill_overflow未覆盖:缺 checker

6.2 单一来源:寄存器规格只写一次

第 37 周介绍的开源工具 RgGen,从一份可读的寄存器描述生成 RTL 和配套文件。它的价值不在“少写代码”,而在于 RTL、RAL、文档、寄存器测试共享同一份输入。

验收这类工具的首要用例:修改一个字段的访问属性(例如 RW → RO),检查所有下游产物是否同步变化。

6.3 安全相关项目的最小追溯表

第 38 周 Siemens EDA 讨论了 FMEDA 与验证证据分散维护的问题。不依赖任何商业工具,也可以从一张表开始:

安全机制需求编号RTL 基线故障模式注入 / 形式检查结果FMEDA 版本

设计或第三方 IP 更新时,先查这张表里哪些关联失效,再决定需要重跑哪些故障活动。


七、cocotb 与前端工具

7.1 len(handle) 的含义正在改变

第 43 周 cocotb 主线把“位宽”和“元素数量”拆开了:

width = dut.data.size     # 新:位宽(bit 数)
# len(dut.data)           # 旧:非数组句柄上的 len() 已被弃用

启用预览开关后,可索引 PackedObject 的 len() 返回最外层维度的元素个数,packed struct / union 则直接抛出 TypeError。

迁移风险:len() 依然返回一个整数,只是单位从 bit 变成了元素个数。这种“数值还在、单位变了”的错误最难发现。

迁移步骤:

grep -rn "len(dut" tests/        # 找出所有对句柄调用 len() 的地方

逐一判断意图:要位宽的改成 .size,要元素个数的保留,然后在 CI 中打开预览开关跑一轮。重点检查 packed array 和 packed struct。

7.2 测试结束时还在等待的任务

第 40 周 cocotb 修复了 @bridge / @resume 跨线程协程在测试结束时吞掉 CancelledError 的问题。旧行为下,测试结束时如果还有后台线程在等待,测试会因为“任务被取消却正常退出”而失败,看起来像随机失败。

通用经验:为“测试正常结束时仍有后台活动”专门设计清理用例。这同样适用于 UVM 的 objection、DPI 工作线程和外部协同仿真进程。

7.3 句柄数量与启动开销

第 41 周 cocotb 把 LogicArrayObject 的逐 bit 子句柄缓存改为按需创建,因为大多数向量从来不会被逐 bit 索引。大型设计的 Python testbench 做性能分析时,应拆开记录:句柄发现、对象包装、首次 bit 访问、稳态读写,才能判断开销到底落在哪里。

7.4 parser / elaborator 也会错连

前端工具的错误不会报编译错误,只会把电路连错:

  • Surelog(第 43 周):同一条语句中用逗号分隔的多个实例,全部绑定到第一个实例的端口连接。

    fifo u_fifo_tx (.din(tx_d), ...), u_fifo_rx (.din(rx_d), ...);
    // 错误行为:u_fifo_rx 用的是 u_fifo_tx 的连接
  • UHDM(第 42 周):常量折叠丢失 x/z/? 通配位、无显式基类型的枚举宽度被算成 0、拼接元素选择取错位置。

对策:为 parser 升级保留一组结构回归,覆盖“同语句多实例、参数化实例、实例数组、通配常量”,并比较 elaboration 后每个实例的连接。对于地址译码、参数化端口宽度这类静态配置,结构 diff 往往比跑仿真更早暴露问题。


附:工具升级验收总清单

把上面各节的检查点合并起来,每次升级仿真器、parser 或验证框架时逐项过一遍:

类别最小用例来源章节
覆盖率阈值at_least 少一次 / 恰好 / 超过1.1
覆盖率转移单样本 / 重叠 / 重复前缀1.2
多实例合并两实例互补 bin1.3
覆盖率口径line-only / branch-only / combined 三份报告1.4
断言负向多周期前件 + 析取后件,预期失败2.2
disable iff沿上 / 沿间 / 重新使能2.3
NBA 与 forkdisable fork 后 NBA 仍生效3.1
事件->> 唤醒挂起进程3.2
clocking block同时间步二次唤醒后驱动3.3
失败路径编译失败、工具缺失时日志完整5.4
波形产物文件存在且可打开5.4
结构连接同语句多实例的端口连接7.4

原始来源

各期周报保留了完整的日期与事实核验信息,需要追溯时可回看:

  • 第 37 周:DVCon Taiwan / Japan、CDC-RDC 标准、RgGen、SpecGuard、IntelliVerifier
  • 第 38 周:断言失败分诊、FMEDA 证据链
  • 第 39 周:dvSim Verilator 后端、covergroup bins 优化、fork 与 NBA
  • 第 40 周:->> 事件、覆盖率报告一致性、cocotb 取消传播、Caliptra SS 分层回归
  • 第 41 周:merge_instances、析取后件假通过、objection drain、WFI scoreboard
  • 第 42 周:disable iff、clocking block、分支覆盖拆分、RAL multireg、FSDB
  • 第 43 周:at_least、transition bin、cocotb size、I3C 环境、Surelog 多实例

上游提交链接中的项目方数据(如编译时间、覆盖率百分比)均为项目方或作者报告,本站未独立复现。