中断验证:中断控制器的置位、清除、mask 与脉冲丢失
中断是外设告诉 CPU「我有事」的主要通道:定时器到点、DMA 传完、FIFO 溢出,都靠一根中断线或一个中断控制器上报。中断路径在硅片上是异步、低频、长延迟的,正常激励下可能几千个周期才发生一次,纯随机仿真很难把边角情况撞全;而一旦有 bug,现象就是「中断丢了,系统挂起等一个永远不来的事件」,属于芯片带回来后最难查的那一类问题。所以中断验证值得单独立项:把置位、清除、屏蔽、聚合的每个状态转换都显式驱动、显式检查。验证方法基础可回顾 SystemVerilog 测试平台基础,寄存器访问路径可参考 UVM 寄存器模型集成。
本文要回答的问题
- 电平触发和脉冲触发的中断,在置位与清除语义上有什么本质差别?
- mask、raw status、使能位串在同一条链路上,怎么验证它们各自的职责不串味?
- 中断丢失、重复上报、多源聚合、服务后再触发,分别怎么构造激励并自动比对?
一、两种触发方式:电平与脉冲
中断源到控制器的物理接口只有两种主流约定。电平触发(level-triggered):源端把中断线拉高并保持,直到外设被服务(例如读出数据后)才释放;控制器在输入有效期间持续看到请求。脉冲触发(pulse-triggered,也称 edge):源端只打一个时钟周期(或更短)的脉冲,发生过这件事必须由控制器内部的寄存器锁存下来,否则脉冲过去就无从查考。
| 维度 | 电平触发 | 脉冲触发 |
|---|---|---|
| 源端行为 | 保持有效直到被清零 | 只打一拍 |
| 控制器是否需要 latch | 不强制(线本身有记忆) | 必须,边沿后置位 raw status |
| 典型清除方式 | 先清除源条件,再清状态位 | 直接 W1C 状态位即可 |
| 主要风险 | 源未释放时清不掉、立刻复活 | 脉冲太窄被漏采样 |
一个常见的 DUT bug 是把电平源直接当脉冲用:状态位清掉的同一拍源还高着,下一拍 raw status 又被置位,中断「怎么清都清不掉」。这不是清除逻辑错,而是验证时没有把「源保持」这个条件建模进去。
二、置位与清除:latch、W1C 与 pulse-clear
中断控制器内部通常有三类寄存器协同工作,理解数据流是写检查器的前提:
- raw status(原始状态):中断源条件或脉冲到达即置位,不受 mask 影响,回答「到底有没有发生过」;
- mask/enable:逐源屏蔽位,只决定请求往不往 CPU 送,不影响 raw status 的记录;
- status/clear:面向软件的状态视图与清除入口,常见方式是 W1C(write-1-to-clear,写 1 清零、写 0 无效果),少数设计用脉冲清除(写一次 clear 寄存器产生一拍清除脉冲)。
下图画出了从源到 CPU 中断输出再到软件清除的完整链路:

这张图里有两个必须验证的回路:正向回路是「源到 IRQ」,任何一环断开都会丢中断;反向回路是「W1C 到 latch」,清除必须精确作用于被写的那一位,不能误伤同源其他位。W1C 的语义检查尤其要做反例:写 0 的位必须保持,连续两次写 1 第二次读回仍应是 0(不能自相矛盾地置位)。
三、mask、raw status 与使能链路
mask 的黄金法则是:屏蔽只改变输出,不改变记录。中断在 mask 期间到达,raw status 照常置位;软件随后读 raw status 能看到这条悬挂中断,解除 mask 后 IRQ 应立即(同一拍或约定延迟内)拉起,不需要源再来一次。很多子系统 bug 就出在这里——实现图省事把 mask 放在了 latch 之前,导致屏蔽期间的事件被永久丢弃。
| 场景 | raw status | CPU IRQ |
|---|---|---|
| mask=0,源有效 | 置位 | 输出有效 |
| mask=1,源有效 | 置位 | 保持无效 |
| mask 期间置位,随后解除 | 保持置位 | 解除后立即有效 |
| mask=1,W1C 清除 | 清零 | 无效,解除后也不再来 |
使能开关还要验证全局 enable(控制器总开关)与逐源 mask 的嵌套关系:全局关断时任何源都不出线,但 raw status 行为应与单独 mask 一致;全局重新使能后,所有悬挂源按聚合规则一次性或按优先级依次上报。
四、中断丢失、重复与聚合
脉冲中断最凶险的失效模式是丢失。源端打了一拍,但控制器采样逻辑(尤其经过两级同步器后)要求脉冲至少满足两个接收时钟沿的宽度;跨时钟域时源拍过窄,同步器输出根本不翻转,事件蒸发。这正是 CDC/RDC 报告分析 要与功能验证联动的地方:验证中要主动驱动「最短合法脉冲」与「一拍以下非法脉冲」两种边界。
另一组风险围绕重复与聚合:
- 服务后再触发:W1C 生效的同一拍或相邻拍源再次有效,正确行为是状态位先归零再重新置位、IRQ 重新上报;若清除优先级高于置位,新事件会被吞掉;
- 多源同时:两个以上源同一拍置位,聚合逻辑若只编码一路,另一路悬挂但不出线;
- 电平源未释放:清除后立刻复活,软件看到无限重入,需区分「合理再触发」与「清不掉」;
- 脉冲重复:源端故障连打两拍,latch 只记录一位是正常的,但软件清除后不应有残留脉冲再次置位。
下图对比「清除后安全再触发」与「清除吞掉新事件」两种时序结局:

五、testplan 怎么列
中断 testplan 应按「状态位生命周期 + 链路配置矩阵」组织,每个点映射到 coverpoint 或断言。映射方法见 testplan 与覆盖映射。
| 编号 | 测试点 | 期望 |
|---|---|---|
| IP-01 | 单源电平置位,mask 放行 | raw=1,IRQ 有效 |
| IP-02 | 单拍脉冲置位 | raw=1,脉冲结束后仍保持 |
| IP-03 | W1C 写 1 清除,写 0 位不受影响 | 精确清对应位 |
| IP-04 | mask 期间到达,随后解除 | raw 已置位,解除即出线 |
| IP-05 | 最短合法脉冲 / 过窄脉冲 | 前者捕获,后者按规格拒收 |
| IP-06 | 多源同拍置位与聚合 | 每源独立悬挂,逐一上报 |
| IP-07 | 清除同拍/次拍再触发 | 1-0-1,二次 IRQ |
| IP-08 | 电平源未释放时清除 | 按规格立即复活或拒绝清除 |
| IP-09 | 全局 enable 开关嵌套 | 关断不出线,记录不丢 |
| IP-10 | 饱和压力:高频随机脉冲+随机 mask/清除 | scoreboard 全程一致 |
六、SVA:置位条件、清除归零、masked 不输出
断言直接钉住三条核心性质。写法基础见 SVA 断言实践。
// 1) 置位条件:源有效(经同步后的采样脉冲或电平)后,下拍 raw 必须为 1
a_set: assert property (@(posedge clk) disable iff (!rst_n)
src_pulse |=> raw_status);
// 2) 清除后归零:W1C 写 1 且当拍无新请求,下拍 raw 必须为 0
a_clear: assert property (@(posedge clk) disable iff (!rst_n)
(w1c && !src_pulse) |=> !raw_status);
// 3) masked 不输出:该源被屏蔽时,它绝不能单独拉起 IRQ
a_masked: assert property (@(posedge clk) disable iff (!rst_n)
(mask || !global_en) |-> !(irq && $past(raw_status) && !other_pending));
// 4) 解除屏蔽后及时上报:raw 悬挂且解除 mask,下拍 IRQ 必须有效
a_unmask: assert property (@(posedge clk) disable iff (!rst_n)
(raw_status && !mask && global_en) |=> irq);第四条配合多源聚合时要小心写 other_pending 条件:聚合输出只要任一路放行就为高,断言应检查「存在一条放行路径」,而不是把某一源和输出一一硬绑,否则同拍多源场景会误报。
七、功能覆盖与 scoreboard
覆盖点要显式采样正常随机撞不到的边界:
covergroup cg_irq @(posedge clk);
cp_width: coverpoint pulse_width { bins min = {2}; bins one = {1}; }
cp_multi: coverpoint {src_req[3:0]} { bins all4 = {4'b1111}; option.weight = 5; }
cp_clear_timing: coverpoint w1c_to_pulse {
bins same = {0}; bins next = {1}; bins far = {[2:10]}; }
cp_mask_during: coverpoint (mask && src_pulse);
endgroup至少要覆盖:最短合法脉冲、过窄脉冲、多源同时(全置位单独加权)、清除与新请求同拍/次拍/远距离三个时间档、mask 窗口内到达。
scoreboard 侧维护一份每源的期望状态(exp_raw[N]、exp_masked_output),在每个时钟沿按同一套规则更新:源事件置位、W1C 且无新事件时清零、再按 mask 与聚合规则计算期望 IRQ,与监视器采样的实际值逐拍比对。关键纪律有两条:清除与置位同拍时的优先级必须照 RTL 规格建模(推荐「置位优先」,但要以规格为准);软件侧事务(W1C 写操作)与 RTL 实际生效拍之间有总线延迟,scoreboard 要在「信号生效拍」而非「发起拍」更新,否则会产生假的失配。跑完 IP-10 这类长随机压力用例仍全程零失配,中断路径才算真正闭环。
总结
中断验证的核心是把中断当成一个有记忆的小状态机来验:电平与脉冲决定置位方式,raw status 忠实记录、mask 只门控输出、W1C 精确清除;用 SVA 钉死「置位、归零、masked 不输出、解除即上报」四条性质,用覆盖去撞最短脉冲、多源同时和清除同拍再触发的边角,再用逐拍维护期望状态的 scoreboard 兜住随机压力,中断丢失与「清不掉」这两类最隐蔽的硅后 bug 就能在仿真阶段提前拦住。