3. SVA基础语法(一):断言的概念、立即断言与并发断言的基本结构
各位同学,今天我们正式开始啃SVA这块硬骨头。说实话,我刚接触断言那会儿,也觉得这东西不就是个检查器吗?后来在项目中吃过亏才明白——断言是芯片验证的“眼睛”,没有它,你就像在黑夜里摸路。
3.1 断言到底是什么?
断言,说白了就是一段“监视代码”。它时刻盯着你的信号,看它们是不是符合预期。如果不符合,就立刻报警。
我习惯把断言比作“交通摄像头”。你开车经过路口,摄像头拍到你闯红灯,咔嚓一张照片,罚单就来了。断言也一样——信号违规了,它就“咔嚓”一下,告诉你这里出问题了。
在SystemVerilog里,断言用 assert 关键字来写。但根据检查的时机不同,又分成两种:
- 立即断言(Immediate Assertion):像“实时监控”,信号一变它就检查。
- 并发断言(Concurrent Assertion):像“定时巡逻”,只在时钟沿检查。
嗯,这里要注意:两种断言各有各的用武之地,千万别混着用。
3.2 立即断言:最简单的检查器
立即断言长什么样?我直接给你看代码:
always_comb begin
assert (data_valid == 1'b1) else $error("data_valid 应该为高,但现在是低!");
end
这段代码的意思是:只要 data_valid 不是高电平,就报错。注意,它写在 always_comb 里,所以是组合逻辑,信号一变就触发。
我在项目中遇到过一种情况:有人把立即断言写在 always_ff 里,结果仿真器在每个时钟沿都检查一次,但信号可能在时钟沿之间变化,导致漏报。所以记住——立即断言只适合组合逻辑或过程块。
立即断言的基本语法很简单:
assert (expression) [else action_block]
expression:要检查的条件,必须为真。else action_block:可选,条件为假时执行的动作,比如打印错误、停止仿真。
你想想看,如果条件为真,什么都不做;条件为假,才触发动作。这就是“断言”的本质——只关心异常情况。
3.3 并发断言:时序检查的主力军
并发断言才是SVA的核心。它和时钟绑定,只在时钟沿采样信号,然后判断序列是否满足。
为什么需要并发断言?举个例子:你想检查“请求信号拉高后,两个时钟周期内必须收到应答”。用立即断言根本写不了,因为它不关心时间。但并发断言可以:
property p_req_ack;
@(posedge clk) req |=> ##[1:2] ack;
endproperty
assert property (p_req_ack);
这段代码的意思是:在每个时钟上升沿,如果 req 为高,那么接下来1到2个时钟周期内,ack 必须为高。这就是并发断言的威力——它能描述时序关系。
并发断言的基本结构分三部分:
- 序列(sequence):描述信号的变化模式。
- 属性(property):把序列组合成完整的检查条件。
- 断言(assert):把属性挂到仿真器上。
我习惯把这三部分比作“菜谱”:序列是食材,属性是烹饪步骤,断言是最后端上桌的菜。缺一个都不行。
3.4 立即断言 vs 并发断言:一张表说清楚
| 对比项 | 立即断言 | 并发断言 |
|---|---|---|
| 检查时机 | 信号变化立即检查 | 只在时钟沿检查 |
| 适用场景 | 组合逻辑、简单条件 | 时序逻辑、复杂序列 |
| 能否描述时序 | 不能 | 能(用 ## 操作符) |
| 性能开销 | 小 | 稍大(但可接受) |
| 典型写法 | assert (a & b); |
assert property (@(posedge clk) a |=> b); |
我个人建议:能用并发断言就别用立即断言。因为并发断言更规范,而且形式化验证工具(比如VC Formal)只支持并发断言。但如果你只是临时检查一个组合逻辑,立即断言更轻量。
3.5 一个完整的例子:从零写一个并发断言
咱们来写一个实际点的例子。假设有个握手协议:valid 拉高后,ready 必须在3个时钟内拉高,否则协议违规。
第一步,写序列:
sequence s_valid_ready;
@(posedge clk) valid ##[1:3] ready;
endsequence
第二步,写属性:
property p_valid_ready;
@(posedge clk) $rose(valid) |=> s_valid_ready;
endproperty
这里用了 $rose(valid),意思是“valid 的上升沿”。为什么不用 valid 本身?因为如果 valid 一直为高,你不想在每个时钟沿都触发检查,对吧?
第三步,挂断言:
assert property (p_valid_ready)
else $error("valid 拉高后,ready 没有在3个时钟内响应!");
你看,三步走,清晰明了。我在项目中教新人写断言,都是让他们先写序列,再写属性,最后挂断言。这个顺序千万别搞反。
ready 在第4个时钟才拉高,断言就会报错——这正是我们想要的。
3.6 常见错误与避坑
最后,分享几个我踩过的坑:
- 忘了写时钟:并发断言必须指定时钟,否则仿真器不知道什么时候采样。我见过有人写
assert property (a |=> b);没加@(posedge clk),结果仿真报错。 - 立即断言用在时序逻辑里:前面说过,立即断言不关心时钟,用在时序逻辑里会漏报或误报。
- 断言写得太复杂:一个断言里塞了十几个信号,出了问题都不知道是哪个信号违规。我建议一个断言只检查一个行为。
嗯,今天就先讲到这里。下一节我们会深入SVA的序列操作符,比如 ##、and、or 这些。到时候我会结合VC Formal工具,给大家演示怎么调试断言。咱们下节课见!