断言assertion被放在verilog设计中,方便在仿真时查看异常情况。当异常出现时,断言会报警。一般在数字电路设计中都要加入断言,断言占整个设计的比例应不少于30%。以下是断言的语法:
module ABC ();
rtl 代码
SVA断言
endmodule
注意:不要将SVA写在enmodule外面。
【例】 断言名称1:
assert property(事件1) //没有分号
$display("........",$time); //有分号
else
$display("........",$time); //有分号
断言名称2:
assert property(事件2)
$display("........",$time);
else
$display("........",$time);
注:以下介绍的SVA语法,既可以写在sequence中,也可以写在property中,语法是通用的。
sequence name;
...
endsequence
(2) 属性块:
property name;
...;
endproperty
从定义来讲,sequence块用于定义一个事件(砖),而property块用于将事件组织起来,形成更复杂的一个过程(楼)。sequence块的内容不能为空,你写乱字符都行,但不能什么都没有。sequence也可以包含另一个sequence, 如:
sequence s1;
s2(a,b);
endsequence //s1和s2都是sequence块
sequence s1;
s2(a,b);
endsequence
sequence s1;
s2(a,b);
endsequence
property p1;
int cnt;
......
endproperty
$rose( a );———— 信号上升
$fell( a );———— 信号下降
$stable( a );———— 信号值不变
(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事件发生。
if a
b;
else
succeed;
因此,一旦 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];
endproperty
@ (posedge clk1) a |-> ##1 @ (posedge clk2) b
@ (posedge clk1) a |=> @ (posedge clk2) b
总线就是好多根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是高,其他是低。
如:@(posedge clk) (q == $past(d)),当未复位时报错,屏蔽方法是将该句改写为:
@(posedge clk) disable iff (!rst_n) (q == $past(d)) //rst是低电平有效
name: cover property (func_name)
(1)【编译verilog代码时按照system verilog进行编译】 vlog -sv abc.v
(2)【仿真命令加一个-assertdebug】 vsim -assertdebug -novopt testbench
(3)【如果想看断言成功与否的分析,使用打开断言窗口的命令】 view assertions
在fsdb文件中加一句话:$fsdbDumpSVA
在VCS编译参数:system "vcs $VCS_SIMULATION" 中加入一些options:
-assert enable_diag\
-assert vpiSeqBeginTime\
-assert vpiSeqFail\
-assert report=路径\
-assert finish_maxfail=100
(1)案例验证法:写一个地址,再写一段数据,然后读取该地址,看输出的是不是刚才写的数据。
(2)断言法:不需要专门设计地址和数据,当发起写时,在地址传输的时间里将地址存储到一个变量里,在数据传输的时间里将数据存储到一个变量里,观察RAM中该地址是否存在该数据就可以了。
if a1
{
if a2
then b
}
/
wr: assert property(wr_p)
$display("succeed:",$time/10);
else
$display("error: ",$time/10);
/
//断言可以声明一个int数组arr[4],
//“@(posedge clk) !vld_pulse_r[0] && !DataIn”是真实的预备条件
//“##4 (read==read, arr[0] = DataIn)”只是为了在特定时间内赋值,有用的语句是“arr[0] = DataIn”,//“read==read”是废话,为了编译通过。
//arr赋值完毕后,进入function进行处理,判断实际地址addr跟junc处理过的数据是否相同。
//“addr == junc(arr[0],arr[1],arr[2],arr[3]);”就是junction调用。
property wr_p;
int arr[4];
@(posedge clk) !vld_pulse_r[0] && !DataIn
##4 (read==read, arr[0] = DataIn)
##1 (read==read, arr[1] = DataIn)
##1 (read==read, arr[2] = DataIn)
##1 (read==read, arr[3] = DataIn) |=>
addr == junc(arr[0],arr[1],arr[2],arr[3]);
endproperty
//
function [3:0] junc;
input a,b,c,d;
reg [3:0] a1;
reg [3:0] b1;
reg [3:0] c1;
reg [3:0] d1;
a1 = {3'b0,a};
b1 = {3'b0,b};
c1 = {3'b0,c};
d1 = {3'b0,d};
junc = a1+(b1<<1)+(c1<<2)+(d1<<3);
$display(junc);
endfunction
##4 (废话, cnt = 0, arr[cnt] = DataIn, cnt++) //初始化一下,
##1 (read==read, arr[cnt] = DataIn, cnt++)[*3] //循环3次
property ept_p;
@(posedge rd_clk) ((rd_num == 0) |-> rd_ept)
&& (rd_ept |-> (rd_num == 0));
endproperty
是错误的,写了|->,就不能再用 && 等事件组合逻辑了。
解决方法是使用2个断言,没更好的方法。