verilog 断言(SVA)语法与练习

一、转载链接

https://blog.csdn.net/qq_41394155/article/details/83117143
自己看理解不太深刻,便照着写了一下啦

二、SVA介绍

  • 断言assertion可以用来判断仿真时,是否出现异常情况,某些情况是否执行过,用于覆盖率的收集。
  • 可以把断言理解为if语句,但是比if更复杂,不同的断言的语法可以构成多种条件
  • 断言可以分为立即断言(条件表达式)和并发断言(连续运行,整个仿真中都在运行,基于时钟周期,用于描述一个跨时钟周期的行为)
//立即断言:类似if语句
immediately_assert:assert(expression)
	$display("passed");
else $error("failed");
//并发断言:property…endproperty描述事件
property  p_event; 
   @(posedge clk) disable iff(rst);//clk上升沿断言开始,屏蔽rst高电平时的情况,
   $isunknown(request) == 0;      //检查request是否为0状态         
endproperty
concurrent_assertion:assert property(p_event);//assert启动断言检查

三、SVA使用

3.1 SVA位置

在一个.v文件中

module ABC ();
    rtl 代码;
    SVA 断言;
endmodule

3.2 SVA格式

  • sequence和property可以理解为定义一个函数,assert时调用这个函数,调用方式为
    name : assert property(name2)(只有property这种调用方式)
  • 两种格式分别为
    sequence块:用于定义一个事件(砖),一般用于定义组合逻辑断言
    property块:用于将事件组织起来,形成更复杂的一个过程,一般用来定义一个有时间观念的断言,它会常常调用sequence。
    sequence块的内容不能为空;sequence也可以包含另一个sequence
  • 写断言的一般规则是: time + event,要断定发生什么event,首先要指定发生event的时间,例如:
    每个时钟上升沿 + 发生某事;某信号下降时 + 发生某事
sequence
...
end sequence
property
...
endproperty
assert property(事件2)
    $display("........",$time);
else
    $display("........",$time);

四、基本语法

4.1 信号(或事件)间的“组合逻辑”关系

   (1) 常见的有:&&, ||, !, ^

   (2) a和b哪个成立都行,但如果都成立,就认为是a成立:firstmatch(a||b),与“||”基本相同,不同点是当a和b都成立时,认为a成立。

   (3) a ? b:c ———— a事件成功后,触发b,a不成功则触发c

4.2 “时序逻辑”中判断独立的一个信号的行为

@ (posedge clk) A事件; ———— 当clk上升沿时,如果发生A事件,断言将报警。

  边沿触发内置函数:(假设存在一个信号a)

 $rose( a );———— 信号上升

 $fell( a );———— 信号下降

 $stable( a );———— 信号值不变

4.3 “时序逻辑”中判断多个事件/信号的行为

(1) intersect(a,b)———— 断定a和b两个事件同时产生,且同时结束。

(2) a within b    ———— 断定b事件发生的时间段里包含a事件发生的时间段。

 (3) a ##2 b       ———— 断定a事件发生后2个单位时间内b事件一定会发生。

   a ##[1:3] b   ———— 断定a事件发生后1~3个单位时间内b事件一定会发生。

   a ##[3:$] b   ———— 断定a事件发生后3个周期时间后b事件一定会发生。

   (4) c throughout (a ##2 b)    ———— 断定在a事件成立到b事件成立的过程中,c事件“一直”成立。

   (5) @ (posedge clk) a |-> b   ———— 断定clk上升沿后,a事件“开始发生”,同时,b事件开始发生。

   (6) @ (posedge clk) a.end |-> b ———— 断定clk上升沿后,a事件执行了一段时间“结束”后,同时,b事件开始发生。
	
	一旦 a 发生,b 必须发生,断言才成功。如果a没发生,走else,同样成功。  

   (7) @ (posedge clk) a |=> b   ———— 断定clk上升沿后,a事件开始发生,下一个时钟沿后,b事件开始发生。      

   (8) @ (posedge clk) a |=>##2b ———— 断定clk上升沿后,a事件开始发生,下三个时钟沿后,b事件开始发生。

   (9) @ (posedge clk) $past(a,2) == 1'b1 ———— 断定a信号在2个时钟周期“以前”,其电平值是1。

   (10) @ (posedge clk) a [*3] ———— 断定“@ (posedge clk) a”在连续3个时钟周期内都成立。

    	@ (posedge clk) a [*1:3] ———— 断定“@ (posedge clk) a”在连续1~3个时钟周期内都成立。

    	@ (posedge clk) a [->3] ———— 断定“@ (posedge clk) a”在非连续的3个时钟周期内都成立。
property ABC;
    int tmp;
    @(posedge clk) ($rose(a),tmp = b) |-> ##4 (c == (tmp*tmp+1)) ##3 d[*3];
    //当clk上升沿时,断言开始。首先断定信号a由低变高,将此时的信号b的值赋给变量tmp,4个时钟周期后,断定信号c的值是4个周期前b^2+1,再过3个周期,断定信号d一定会起来,再过3个周期,信号d又起来一次。。。。。。。只有这些断定都成功,该句断言成功。otherwise,信号a从一开始就没起来,则断言也成功。
endproperty 

4.4 多时钟域联合断言:一句断言可以表示多个时钟域的信号关系

            @ (posedge clk1) a |-> ##1 @ (posedge clk2) b

   当clk1上升沿时,事件a发生,紧接着如果过来第二个时钟clk2的上升沿,则b发生。
   “##1”在跨时钟时不表示一个时钟周期,只表示等待最近的一个跨时钟事件。
   所以此处不能写成##2或其他。但是可以写成:
                   
            @ (posedge clk1) a |=> @ (posedge clk2) b

4.5 总线的断言函数

   总线就是好多根bit线,共同表示一个数。SVA提供了多bit状态一起判断的函数,即总线断言函数:

   (1) $onehot(BUS)      ————BUS中有且仅有1 bit是高,其他是低。

   (2) $onehot0(BUS)     ————BUS中有不超过1 bit是高,也允许全0。

   (3) $isunknown(BUS)   ————BUS中存在高阻态或未知态。

   (4) countones(BUS)==n ————BUS中有且仅有n bits是高,其他是低。

4.6 屏蔽不定态

当信号被断言时,如果信号是未复位的不定态,不管怎么断言,都会报告:“断言失败”,为了在不定态不报告问题,在断言时可以屏蔽。

如: @(posedge clk) (q == $past(d)),当未复位时报错,屏蔽方法是将该句改写为:

     @(posedge clk) disable iff (!rst_n) (q == $past(d))  //rst是低电平,关闭属性检查

	 not: 表示not后的序列不能出现

4.7 断言覆盖率检测:

name: cover property (func_name)

4.8 循环

for(){…}的功能,别忘了语法中介绍的[*3],这是在断言中实现for的唯一方式。

            ##4 (read==read, cnt = 0, arr[cnt] = DataIn, cnt++)   //初始化一下,

            ##1 (read==read, arr[cnt] = DataIn, cnt++)[*3]  //循环3次
property p;
  @(posedge clk) a |-> ##1 b ##1 b ##1 b;
endproperty
a: assert property(p);

property p;
  @(posedge clk) a |-> ##1 b[*3];//等价,重复三次
endproperty
a: assert property(p);

property p;
  @(posedge clk) a |-> ##1 b[->3] ##1 c;
endproperty
a: assert property(p);
//其中##1 b[->3]表示在a为高的上升沿后,经过一个周期,b在接下来要有三个上升沿为高,而且不一定要连续。(可以不连续)
//但有个要求,就是在##1 c发生之前的上升沿b是刚好完成最后一次匹配
//如果改为:b[=3],就是非连续重复,区别是,不要求b的最后一次匹配发生在c发生之前。也就是b完成了三次匹配后,可以等若干周期c才发生

五、仿真软件中加入断言编译和显示功能

5.1 在modelsim中开启断言编译和显示功能

(1)【编译verilog代码时按照system verilog进行编译】  vlog -sv abc.v

(2)【仿真命令加一个-assertdebug】   vsim -assertdebug -novopt testbench

(3)【如果想看断言成功与否的分析,使用打开断言窗口的命令】 view assertions

5.2 在VCS中加入断言编译和显示功能

在fsdb文件中加一句话:$fsdbDumpSVA

在VCS编译参数:system "vcs $VCS_SIMULATION" 中加入一些options:

       -assert enable_diag\

       -assert vpiSeqBeginTime\

       -assert vpiSeqFail\

       -assert report=路径\

       -assert finish_maxfail=100

六、练习

  1. b<=a逻辑的断言

     @ (posedge clk) (a==a,tmp=a) |=> (b==tmp);
    
  2. 每句断言都是一个小程序:如上例,在##4时间点上,(read==read, cnt = 0, arr[cnt] = DataIn, cnt++)就是一个小程序,信号断言必须是第一句,其他运算按照顺序进行。

  3. 断言的变量除了可用C语言中的int,float外,还可以是reg [n:0]等数字电路类型。

  4. 写了|->,就不能再用 && 等事件组合逻辑

property ept_p;
    @(posedge rd_clk) ((rd_num == 0) |-> rd_ept)
                     && (rd_ept |-> (rd_num == 0));
endproperty
//是错误的
//解决方法是使用2个断言,没更好的方法。
property ept_p1;
    @(posedge rd_clk) (rd_num == 0) |-> rd_ept);
                      
endproperty
property ept_p2;
    @(posedge rd_clk) rd_ept |-> (rd_num == 0);
endproperty
  1. a为高时,b为高
property p1
@(posedge clk) a |-> b;
endproperty
ast_1:assert property(p1)
  1. c为高时,下一个周期d为高
property p2
@(posedge clk) c |=> d;
endproperty
ast_2:assert property(p2)
  1. e为高时,下1-10个周期内f为高
property p3
@(posedge clk) e ##[1:10] f;
endproperty
ast_3:assert property(p3)
  1. a1和a2的检查效果完全一样
property p1:
	@(posedge clk) a|=>b|=>c;
endproperty
a1: assert property(p1);

property p2:
	@(posedge clk)a ##1 b ##1|-> c;
endproperty
a2: assert property(p2);
  1. 在时钟上升沿时采样到start有效开始,两个时钟周期后,信号“a”连续或者间断地出现3次为高电平,紧接着信号"stop"在下一个时钟周期为高电平,转换成断言描述
property p0: @(posedge clk) 
    $rose(start) 1->##2 (a[->3])##1 stop:  
endproperty
a0 : assert property(p0).