4. SVA序列操作:序列的延迟、重叠、连接,序列的重复与逻辑操作
好,咱们今天聊聊SVA序列操作。说实话,刚接触SystemVerilog Assertion那会儿,我觉得序列就是简单的「信号A拉高后信号B拉高」。但实际项目一上手,发现远没那么简单。延迟、重叠、连接、重复、逻辑操作——这些才是真正让序列变得强大的地方。
我个人习惯把序列操作分成两大类:时间控制类和逻辑组合类。时间控制类管的是「什么时候发生」,逻辑组合类管的是「怎么组合这些事件」。咱们一个一个来。
4.1 序列的延迟操作
延迟操作,说白了就是「等几个时钟周期」。SVA里用 ## 来表示。
核心语法:
##n:延迟n个时钟周期##[m:n]:延迟范围,从m到n个周期##[*]:延迟0到无穷(慎用!)
举个例子:
// 信号a拉高后,正好过3个周期,信号b拉高
sequence s_delay3;
a ##3 b;
endsequence
// 信号a拉高后,在1到5个周期内,信号b拉高
sequence s_delay_range;
a ##[1:5] b;
endsequence
我在项目中遇到过一个问题:用 ##[*] 做超时检测,结果仿真跑了一整天没结束。为什么?因为 ##[*] 匹配0到无穷个周期,如果信号一直不来,仿真器会一直尝试匹配下去。嗯,这里要注意——能用具体范围就别用无穷。
4.2 序列的重叠与连接
重叠和连接,这两个概念容易搞混。我刚开始也分不清,后来用一句话记住了:重叠是「同时发生」,连接是「先后发生」。
4.2.1 重叠操作
重叠用 and 关键字。两个序列在同一时刻都满足,才算匹配。
// 序列A和序列B在同一时刻都成立
sequence s_overlap;
seq_a and seq_b;
endsequence
你想想看,这就像两个人同时到达约定地点。一个人到了,另一个人没到,就不算重叠。
4.2.2 连接操作
连接用 ## 直接连。序列A结束后,紧接着序列B开始。
// 序列A结束后,下一个周期序列B开始
sequence s_concatenation;
seq_a ##1 seq_b;
endsequence
注意这里的 ##1 表示「序列A结束后的下一个时钟周期」。如果序列A本身占用了3个周期,那序列B从第4个周期开始。
小技巧: 我习惯把连接操作想象成「流水线」。上一级处理完,下一级立刻接手。这在验证数据通路时特别有用。
4.3 序列的重复操作
重复操作,就是让某个事件或序列重复发生。SVA提供了三种重复方式:
| 操作符 | 含义 | 示例 |
|---|---|---|
[*n] |
连续重复n次 | a[*3] 表示a连续拉高3个周期 |
[*m:n] |
连续重复m到n次 | a[*2:5] 表示a连续拉高2到5个周期 |
[->n] |
非连续重复n次(goto重复) | a[->3] 表示a总共出现3次,中间可以隔周期 |
[=n] |
非连续重复n次(non-consecutive) | a[=3] 类似goto,但结束时不要求a保持 |
我曾经在验证一个DMA控制器时,需要检查「连续4个周期都有数据请求」。用 req[*4] 一行搞定。如果用传统波形检查,得盯着看半天。
// 连续重复:信号a连续拉高3个周期
sequence s_consecutive;
a[*3];
endsequence
// goto重复:信号a总共出现3次,不要求连续
sequence s_goto;
a[->3];
endsequence
避坑指南: 我曾经用 a[->3] 检查中断信号,结果发现仿真一直不匹配。后来才意识到,goto重复要求「最后一次出现后立即结束序列」。如果后面还有别的条件,得小心处理。
4.4 序列的逻辑操作
逻辑操作让序列可以组合成更复杂的条件。常用的有 and、or、not、intersect。
4.4.1 and 和 or
and 要求两个序列同时结束。or 只要其中一个结束就算匹配。
// 两个序列同时结束
sequence s_and;
seq_a and seq_b;
endsequence
// 任一序列结束即可
sequence s_or;
seq_a or seq_b;
endsequence
说白了,and 是「都要」,or 是「有就行」。我在验证握手协议时经常用 and 检查「valid和ready同时拉高」。
4.4.2 intersect 操作
intersect 比 and 更严格。它不仅要求两个序列同时结束,还要求它们长度完全一致。
// 两个序列必须长度相同且同时结束
sequence s_intersect;
seq_a intersect seq_b;
endsequence
你想想看,and 允许一个序列长一个序列短,只要同时结束就行。但 intersect 要求「你俩从开始到结束,每一步都得同步」。这在检查严格时序对齐时非常有用。
4.4.3 not 操作
not 就是取反。但注意,SVA里的 not 是对整个序列取反,不是对单个信号。
// 序列seq_a不应该发生
sequence s_not;
not seq_a;
endsequence
我曾经用 not 检查「写操作期间不应该有读请求」。结果发现误报了一堆——原来 not 检查的是「整个仿真期间都没有seq_a」,而不是「某个时间段内没有」。嗯,这里要注意作用域。
4.5 知识体系总览
下面这张图总结了SVA序列操作的核心内容,我画成了流程图,方便你对照理解:
4.6 综合示例:一个实际场景
最后,我分享一个实际项目中的例子。验证一个AXI总线从设备,需要检查「写地址通道和写数据通道同时准备好后,下一个周期写响应必须回来」。
// 写地址有效且写数据有效
sequence s_wr_addr_data;
awvalid and wvalid;
endsequence
// 写地址和数据同时准备好后,下一个周期写响应
property p_wr_response;
@(posedge clk) disable iff (!rst_n)
s_wr_addr_data |=> ##1 bvalid;
endproperty
assert property(p_wr_response);
这里用了 and 做重叠检查,|=> 做延迟一个周期。你看,序列操作组合起来,就能描述复杂的时序行为。
好了,关于SVA序列操作就聊到这儿。记住:延迟控制时间,逻辑控制组合,重复控制次数。把这三点吃透了,写断言就跟搭积木一样简单。