SystemVerilog 断言(SVA)检查器库(OVL)
前言:SystemVerilog 断言(SVA)检查器库由如下两部分组成:
- 由检查器组成的SystemVerilog验证库(SVL),这些SVL库与OVL(Open Verification Library)的行为和控制方式相同。——查看Accellera Open Verification Library Reference Manual,相关文档在http://www.verificationlib.org/。这些检查器还包含有额外的特性,例如SVA库中的覆盖率信息。
- SVA 高级检查器,这些所谓的高级检查器能够验证更复杂的行为。它们与 SVL 检查器有类似的控制方式,但是,这些高级的检查器能够让用户来选取采样时钟沿——正沿/负沿。并且还支持覆盖率信息。 注意在每一个检查器的文件头都包含有对应检查器的相关行为和参数的描述。所以如果你不清楚这个检查器是做什么的,那就需要阅读清楚每个检查器头文件的内容。本文的目的就是为了能够清晰的讲解每一个库文件,这样一来你只需要根据这个文件的描述,选取适当的检查器例化到你的设计中,进行某些表达式或者说是功能的检查就可以了。这样一来给你提供了一个更为方便的了解每一个检查器功能的目的。当然如果你想要验证的功能,这些检查器不能够实现,那没有办法,只能自己实现对应的检查器,详细参见 SystemVerilog 的语法讲解。
一、全局控制
1.1 检查库的路径
检查器库放置在如下路径中(由于本文所讲解的是针对 Synopsys 的 VCS 工具,所以有很多内容都是和 Synopsys 的 VCS 相关的,详细的 VCS 使用文档可以参阅相关的文档,VCS 的讲解不是本文档的最终目的):
$VCS_HOME/packages/sva_cg/ 或
$PIONEER_HOME/packages/sva_cg/
1.2 检查器的两种功能:
- 作为一个使用 assert property 描述的验证对象。这种形式是用来验证检查器中所定义的行为。
- 作为一个使用 cover property描述的对象和使用 covergroup 描述等价的覆
盖点。 这种形式是用来获取被观测的设计中某些行为 pattern 存在性的相关信息(也
就是说,看看想要观测的这些行为 pattern 是不是真的出现了)——某些 pattern
的覆盖率。
1.3 全局控制的宏
如下标记参数都是使用`define 进行定义的宏,并且这些宏被应用到所有的检测器。
No. | Macro | Description |
1 | ASSERT_ON | 当这个宏被定义的时候,检查器中的断言和相关变量都会被包含进去,也就是说——检查功能被使能。 |
2 | COVER_ON | 当这个宏被定义的时候,检查器中的覆盖项目就会被包含进去。也就是说——覆盖率功能被使能。注意:coverage_level_1, coverage_level_2 和coverage_level_3 这些参数是专门提供给检查器的额外控制参数,它们实现了不同的控制功能。 |
3 | ASSERT_INIT_MSG | 当这个宏被定义的时候,所有的检查器实例都将在标准输出上输出一个信息,用来表明这些检查器的层次名。 |
4 | ASSERT_GLOBAL_RESET | 当这个宏被定义的时候,它的定义表达式将被作为所有检查器的复位信号。否则,reset_n 端口信号将被作为复位信号。 |
5 | ASSERT_MAX_REPORT_ERROR | 当这个宏被定义的时候,如果出错数超过了这个宏所定义的范围,断言实例将停止继续报告信息。使用这个宏要求修改 sva_std_task.h 文件中的报告任务。 |
6 | SVA_CHECKER_INTERFACE | 如果没有定义,interface 是 SystemVerilog―module‖。如果定义了,检查器的interface 将是―interface‖。在这两种情况下参数和端口保持一致。 |
7 | SVA_CHECKER_NO_MESSAGE | 当定义了这个宏,它将会在断言失败的时候摒弃用户信息的报告。但它不会控制来自仿真器的默认失败信息。 |
二、SVA 基本检查器
本章包含了 31 个检查器,所有的检查器,除了 assert_proposition 以外,都是在触发信号或表达式 clk 的上升沿触发的。检查器的端口中,所有实际信号或表达式的值都是在 clk 的上升沿之前被采样。因此发生在 clk 连续沿之间的信号脉冲式不能够被检查器监测到的。
此外,无论什么时候,将一个检查器端口上的 signal/expression 上的沿(时钟除外)被用在检查器中的时候,其值是被检测的沿的采样形式——其检测方式是通过查询一个信号的两个连续采样点来实现的。 检查器 assert_proposition 在整个时间段持续检测一个表达式,直到这个表达
式 test_expr 的下降沿。
2.1 检查器列表
No. | Assert | Description |
1 | assert_always | 在每一个时钟(clk)的上升沿持续监测 test_expr。它将验证test_expr 值持续为真,如果 test_expr 值为假,断言将会报错。test_expr 可以是任何有效的 verilog 表达式。 |
2 | assert_always_on_edge | 检查器将在每一个 sampling_event 的指定沿持续检查 test_expr。test_expr 在每一个 sampling_event 都应该为真。如果 test_expr 为假,断言将会报错。 |
3 | assert_change | 在每一个时钟 clk 的上升沿持续监测 start_event。当 start_event为真的时候,检查器确认表达式 test_expr 在指定时钟周期内的改变值——时钟周期的数量可以使用 num_cks 来指定。这个断言将在违例的时候报错。 |
4 | assert_cycle_sequence | |
5 | assert_decrement | 在每个时钟 clk 的上升沿持续检查 test_expr。它的检查目的是:保证 test_expr 在每个时钟周期递减值不会超过参数定义的 value。 test_expr 可以是任何有效的 verilog 表达式。 检查器直到 reset_n 为 1 后的第二个时钟沿才开始检查。也就是说此处需要注意检查器的检查时刻。 |
6 | assert_delta | 在每一个时钟 clk 的上升沿连续检查 test_expr。 它的检查目的:是为了保证 test_expr 的改变值不小于 min,也不会大于 max。 test_expr 可以是任何有效的 verilog 表达式。 |
7 | assert_even_parity | 在每个时钟 clk 的上升沿连续的监测 test_expr。它的验证目的是:test_expr 将一直有偶数个位有效——也就是说有偶数个位是 1。 |
8 | assert_fifo_index | 是确保 FIFO 元素:(1)不会上溢,也不会下溢 ;(2)并且控制/检查 push 和 pop 是否同时进行; |
9 | assert_frame | 验证设计中两个事件之间的时序相关性。当 start_event 值为真,test_expr 必须在一个设定的时钟周期范围(是由 min_cks 和 max_cks 设定的)内为真。——也就是说,在启动时间有效以后, 待检测的表达式一定要在一定时钟周期内有效。 |
10 | assert_handshake | 在每一个时钟 clk 上升沿连续监测 req 和 ack 信号。req 和 ack 必须要在开启一轮新的握手之前进入到非激活状态(0)。 |
11 | assert_implication | 会连续监测 antecedent_expr。如果值为真,检查器将验证consequent_expr 是否为真。 当 antecedent_expr 为假的时候,consequent_expr 表达式将不会被检查,并且断言表明通过。 |
12 | assert_increment | 在每一个时钟 clk 的上升沿连续的监测 test_expr。 它的验证目的是:test_expr 的增量将永远不会超过 value。 test_expr 可以是任意有效的 verilog表达式。 |
12 | assert_never | 在每个时钟 clk 的上升沿连续的监测 test_expr。它的检查目的是:test_expr 的值永远不能为真。test_expr 可以是任意有效的 verilog 表达式。当 test_expr 为真的时候,检查器将会报错。 |
14 | assert_next | 设计中两个事件之间的正确时序关系。当 start_event 值为真,test_expr 必须要在确定的 num_cks 周期后为真。 支持重叠序列。例如,如果你断言 test_expr 在 start_event 的四个 周期后为真,在下一个序列开始检查之前,它没必须等待前一个序列的结束。 |
15 | assert_no_overflow | 在每一个时钟 clk 的上升沿连续的监测 test_expr。 它的验证目的是保证 test_expr 永远不能够: (1)发生改变的时候其值从 max 开始超过指定的 max(默认(2**width)-1)值; |
16 | assert_no_transition | 在每一个触发事件的上升沿且 clk 的上升沿,持续的监测test_expr。当变量值到达 start_state 状态值的时候,监测器确保 test_expr 将不会再转换到 next_state 状态的值。 |
17 | assert_no_underflow | 在触发事件或者时钟 clk 的上升沿连续的检查 test_expr。 它的验证目的是,保证 test_expr 永远不能: (1)从 min 值(默认为 0)改变到小于 min 值; |
18 | assert_odd_parity | 在每一个时钟 clk 的上升沿持续监测 test_expr 存在奇数个 1。test_expr 可以是任何有效的 verilog 表达式。 |
19 | assert_one_cold | 确认变量 test_expr 在任何时钟的上升沿都只有一个低电平位。 |
20 | assert_one_hot | 在每一个时钟 clk 的上升沿都会去确保 test_expr 只能有一个 bit为 1。这个 test_expr 可以是任意有效的 verilog 表达式。 |
21 | assert_proposition | 会连续的监测 test_expr。因此,这个断言不像 assert_always;也就是说,test_expr 不会被时钟采样。 这个 assert_proposition 断言将会验证 test_expr始终为真。如果 reset_n 为 1 的时候,test_expr 的值为假,断言就会失效(也就是说,这个时候检测到代码中出现了错误) |
22 | assert_quiescent_state | 将要验证变量 state_expr 的值等于指定的 check_value,并且在仿真的结点处可以使用宏`ASSERT_END_OF_SIMULATION。当检测到sample_event 事件上升沿的时候(被 clk 采样)验证开始执行。 |
23 | assert_range | 能够在每一个时钟 clk 的上升沿监测 test_expr。它是为了确保test_expr 的值是在 min 和 max 之内。min 和 max 应该是一个有效的值,也就是说 min 的值一定要小于等于 max。 |
24 | assert_time | 会持续的检查 start_expr。当这个信号值为真的时候,断言监测器确保 test_expr 的值在之后的 num_cks 周期为真。 |
25 | assert_transition | 在每一个触发事件的上升沿持续的监测 test_expr。当 test_expr的值等于 start_state 的时候,检查器将会确认 test_expr 是否会变到前一周期采样到的 next_state 值。 |
26 | assert_unchange | 在每一个时钟 clk 的上升沿持续监测 start_event。当这个信号为真的时候,检查器将会确保在接下来的 num_cks 周期内 test_expr 不会改变。 |
27 | assert_width | 会持续的检查 test_expr。当这个信号为真的时候,它将确保test_expr 为真的脉冲会 min_cks<=Ttest_expr=1<=max_cks。 |
28 | assert_win_change | 在每一个时钟 clk 的上升沿持续的监测 start_event。当这个信号为真的时候,检查器将确保 test_expr 的值在 end_event 之前改变。 |
29 | assert_win_unchange | 在每一个时钟 clk 的上升沿持续检查 start_event。当这个信号为真的时候,检查器将会确保 test_expr 在 end_event 有效之前,其值不会发生变化。 |
30 | assert_window | 在每一个时钟的上升沿持续检查 start_event。当这个信号为真的时候,检查器将会确保 test_expr 的值在每一个 clk 的上升沿都为真,包括end_event 表达式时刻。 |
31 | assert_zero_one_hot | 在每一个时钟 clk 的上升沿连续监测 test_expr。它将验证test_expr 严格的有一位为 1,或没有为 1 的 bit。test_expr 可以是任何有效的 verilog表达式。如果 SVA_CHECKER_FORMAL 标志没有被定义,检查器也会检查test_expt 的值不等于 Z 或 X。 |
三、SVA 高级检查器
No. | Assert | Description |
1 | assert_arbiter | 确保源仲裁器提供给相应请求信号的应答在 min_lat 和 max_lat 周期内就已经有效。 |
2 | assert_bits | 确保 exp 的值有 min 和 max 个位被有效(为 1)或无效(为 0)——具体通过 asserted 进行控制。为了指定一个定值,而不是一个范围,可以 min==max。 |
3 | assert_code_distance | 确保当 exp 改变的时候,与 exp2 相比至少有 min 但不能超过 max 个 bit 是不同的。 |
4 | assert_data_used | 确保来自 src[sleft:sright]的数据在 start 指定的周期到 finish 指定的周期窗口中在 dest[dleft:dright]出现过。 |
5 | assert_driven | 确保 exp 的所有为被驱动(没有浮空态 z,也没有 x 态)。 |
6 | assert_dual_clk_fifo | 实现双时钟,单入/单出的输出端口队列检查。 |
7 | assert_fifo | 实现单时钟,单入和单出端口队列检查。 |
8 | assert_hold_value | 确保 exp 在 min 到 max 周期内维持 value 值。也就是说,这个信号必须要维持 value 值 min 个周期,并且知道 max 周期后才能够改变。 |
9 | assert_memory_async | 确保整个异步 memory 内容被访问。 |
10 | assert_memory_sync | 确保整个同步 memory 的内容被访问。 |
11 | assert_multiport_fifo | 实现但时钟,多端口输入和多端口输出度队列的检查。 |
12 | assert_mutex | 确保 a 和 b 不会同时为真。这个检查只有在 reset_n 为 1 的时候才开始检查。检查操作在指定 clk 的沿开始执行。 |
13 | assert_next_state | 确保当 exp 处在当前态 cs 的时候,exp 的下一个状态将是一个有效状态,有效状态个数 ns。 |
14 | assert_no_contention | 确保 buf 时钟只有一个激活的驱动,也就是说不存在 X 和 Z。en_vector 位在同一时刻只能有一位为 1。 |
15 | assert_packet_flow | 这个检查其会在每一个 clk 的上升沿持续检查 sop 和 eop。它会在序列 sop和 eop 上执行如下检查: (1)assert_no_eop_till_sop (2)assert_no_sop_till_eop |
16 | assert_rate | 在每一个时钟的上升沿持续检查 test_expr 和 trigger。一旦trigger 有效,检查器就会确认 test_expr 在 period 周期内,间歇性或持续[min:max]时钟周期持续为高。 |
17 | assert_reg_loaded | 确保寄存器 dst_reg 加载了 src 数据。 |
18 | assert_req_ack_unique | 验证每一个 req 在指定的 min_time 和 mac_time 时钟周期接收到一个 ack。 |
19 | assert_req_requirs | 如果 trig_req 表达式为真,确保 follow_req 表达式和 follow_resp 表达式在trig_resp 表达式为真之前为真。 |
20 | assert_stack | 检查栈上操作。 |
21 | assert_valid_id | 信号 issued_sig 被有效为 1 以后将会验证 issued_id 中的 ID 请求。这个请求应该是被 ret_id 所公认的,并且 ret_id 在[min_lat:max_lat] sva_v_delay 内被 ret_sig有效后进行检查。issued_id 和 ret_id 都是在 reset_n 为 1 后才开始验证。如果 rets_sig 没有在 issued_sig 有效以后的 max_lat 个时钟周期之前,达到正确的 ret_id,断言将会在 max_lat+1 周期发送错误。这是有关于 max_lat 报告的唯一失效情况,直到带有 id 的 ret_sig 出现,才能够重新使能这个 id 的检查。必须要保持 max_lat>= min_lat >=1 关系。 |
22 | assert_value | 确保 exp 只能是 vals 中的一个被指定的值。 |