IC 验证周报知识合集|第 37–43 周
第 37–43 周的七期周报一共收录了 30 多条上游动态,但大部分篇幅花在“哪天、哪个仓库、合入了什么”上。新闻本身很快就会过时,真正值得留下的是这些修复背后暴露出来的验证知识:覆盖率数字怎么算、断言为什么会假通过、UVM 测试为什么会多跑 40 μs 才结束。
这篇文章把七期内容拆散,按主题重新组织。每一节都回答三个问题:
- 原理是什么:这个点在 SystemVerilog / UVM 里是怎么工作的;
- 哪里容易出错:上游项目真实踩过的坑;
- 你可以怎么检查:能直接放进自己环境的最小用例或检查清单。
读完这一篇,就不需要再回去翻七期周报了。原始链接都保留在文末,想追溯细节时再查。
阅读说明
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_busy | busy_run | busy_to_any |
|---|---|---|---|
只有一个样本 BUSY | 0 | 0 | 0 |
IDLE BUSY | 1 | 0 | 0 |
BUSY BUSY BUSY BUSY | 0 | 2(重叠匹配) | 0 |
IDLE IDLE BUSY | 1 | 0 | 0 |
以上是 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 结果带进动态仿真的思路:
- 从静态工具的复位约束中自动生成 SVA,检查复位分组和顺序假设;
- 对静态工具识别出的跨复位路径做 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 会:
- 等待 drain time;
- 调用所有组件的
phase_ready_to_end(); - 如果此时有组件 raise 了 objection,就等它 drop 之后重新计时 drain time,再次调用
phase_ready_to_end(); - 这个循环最多重复 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 | 实现位置 | checker | stimulus | 结果 |
|---|---|---|---|---|
| REQ-012 | fifo.sv: full 逻辑 | a_no_write_when_full | test_fill_overflow | PASS @ seed 42 |
| REQ-013 | fifo.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 |
| 多实例合并 | 两实例互补 bin | 1.3 |
| 覆盖率口径 | line-only / branch-only / combined 三份报告 | 1.4 |
| 断言负向 | 多周期前件 + 析取后件,预期失败 | 2.2 |
disable iff | 沿上 / 沿间 / 重新使能 | 2.3 |
| NBA 与 fork | disable 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、cocotbsize、I3C 环境、Surelog 多实例
上游提交链接中的项目方数据(如编译时间、覆盖率百分比)均为项目方或作者报告,本站未独立复现。