SVA中的基本元素sequence和蕴含操作

VA描述的断言和其他语言一样,都是由很多元素组件构成的,本文将通过示例说明构建SVA的常用基本元素。

序列块sequence

序列是断言最基本的组成结构之一,主要描述一些逻辑行为关系,但是在实际的操作过程中该结构也可以省略,当然其存在就有其合理之处,其中一点就是从复用性的角度来说,将可重用的逻辑结构定义于sequence中,然后在property中通过时钟采样触发sequence,这样可以使sequence不依赖于特定的时钟而存在于不同的时钟设计中。当然这并不是说sequence中不能使用时钟等采样事件,只是一般在构建断言时其时钟都是继承于使用调用其的property或者assert的,这个在后续的部分将进行详细的说明。实际中是为了复用性不在其中增加特定时钟而已。另外还需要说明的是property中可以包含sequence,但是sequence中不能包含propertysequence中可以包含其他的sequence

【示例】
`timescale 1 ns / 1 ps
module top_tb;
logic clk;
logic sig1,sig2,sig3;

initial begin
    clk = 1'b0;
    forever #1 clk = ~clk;
end

initial begin
    sig1 = 1'b0;sig2 = 1'b0;sig3 = 1'b0;
    #2 sig1 = 1'b1;
    #2 sig1 = 1'b0;sig2 = 1'b1;
    #4 sig3 = 1'b1;
    #4 $stop;
end
// sequence
sequence sp;
    sig2 ##2 sig3;
endsequence // sp
// property
property pr;
    @(posedge clk) sig1 |=> sp;
endproperty // pr
// no sequence in property
property pr1;
    @(posedge clk) sig1 |=> sig2 ##2 sig3;
endproperty // pr1
// sequence triggered in property
a1 : assert property(pr) $display("@%0t | a1 : PASSED!",$time);
     else $display("@%0t | a1 : FAILED!",$time);
// sequence triggered in assert-property
a2 : assert property(@(posedge clk) sig1 |=>sp) $display("@%0t | a2 : PASSED!",$time);
     else $display("@%0t | a2 : FAILED!",$time);
a3 : assert property(pr1) $display("@%0t | a3 : PASSED!",$time);
     else $display("@%0t | a3 : FAILED!",$time);     
endmodule // top_tb
【仿真结果】

示例中,sequenceproperty本身在设计中并不会进行检查,而是需要使用assert property断言他们才会被激活进行相应的检查。sequence不仅可以通过propertyassert property触发,也可以直接跳过property直接被assert property触发,但是需要注意的是如果直接在assert property中使用sequence,那么需要在使用的时候指定对应的采样事件(clocking_event),否则将会compile error。如果在property调用sequence时指定了clocking_event,那么在assert property时完全可以不同再次指定clocking_event。可见断言一个序列并不一定要独立的定义一个property,但是如果跳过property直接将sequence加入到assert property中,会造成代码的冗繁不易理解,可重用性和可维护性都很差。所以,出于代码的简单易懂、高可重用性和可维护性建议采用sequence-property-assert property三步式定义和使用。

sequence除了可以被property调用,还可以被其他的sequence调用,其使用格式比较灵活方便。

【示例】

`timescale 1 ns / 1 ps
module top_tb;
logic clk;
logic sig0,sig1,sig2,sig3;

initial begin
    clk = 1'b0;
    forever #1 clk = ~clk;
end
initial begin
    sig0 = 1'b0;sig1 = 1'b0;
    sig2 = 1'b0;sig3 = 1'b0;
    #2 sig0 = 1'b1;
    #2 sig1 = 1'b1;sig0 = 1'b0;
    #2 sig2 = 1'b1;sig1 = 1'b0;
    #2 sig3 = 1'b1;sig2 = 1'b0;
    #4 $stop;
end
// base sequence
sequence s0;
    sig2 ##1 sig3;
endsequence // s0
// upper sequence
sequence s1;
    sig1 ##1 s0;
endsequence // s1
// property
property pr;
    @(posedge clk) sig0 |-> ##1 s1;
endproperty // pr
ap : assert property(pr) $display("@%0t | ap : PASSED!",$time);
     else $display("@%0t | ap : FAILED!",$time);
endmodule // top_tb
【仿真结果】
示例中s0s1中被调用,其实从仿真结果可以看出,示例中的“sig1 ##1 s0”等价于“sig1 ##1 sig2 ##1 sig3”。即互相调用的sequence相当于一个sequence中将多个sequence中的表达式按照相互调用的顺序连接起来执行。

蕴含操作

蕴含操作在并行断言中可以说是无处不在,基本上所有的并行断言中都会用到蕴含操作,那么为什么要使用蕴含操作呢?首先先看下面的这个示例。

【示例】

`timescale 1 ns / 1 ps
module top_tb;
logic clk;
logic sig0,sig1;

initial begin
    clk = 1'b0;
    forever #1 clk = ~clk;
end

initial begin
    sig0 = 1'b0;sig1 = 1'b0;
    #2 sig0 = 1'b1;#2 sig0 = 1'b0;
    #2 sig1 = 1'b1;#2 sig1 = 1'b0;
    #4 $stop;
end

property p;
    @(posedge clk) sig0 ##2 sig1;
endproperty // p

a : assert property(p) $display("@%0t | a : PASSED!",$time);
    else $display("@%0t | a : FAILED!",$time);
endmodule // top_tb
【仿真结果】

示例中属性在每个时钟的上升沿都是待检测序列的有效开始,即会在每个时钟周期的上升沿检测sig0是否为高。如果sig0在某个时钟周期的上升沿被采样到不为高,就会产生错误信息,这些信息有时我们并不关心。如果sig0在一段时间内一直保持低,那么将会产生大量的我们不关心的信息,而我们关心的是序列起始点有效后,其后续的序列是否满足要求。示例中,我们关心的是在sig0为高后的第二个时钟周期sig1也为高的情况,如果此时sig1不为高我们才认为序列不匹配。可见在每个采样周期对sig0进行判断后输出信息是不需要的,为了避免冗余的信息,SVA中引入了蕴含操作,当起始序列不满足时将不会输出冗余的不关心的信息,即蕴含操作可以有效的控制时序建模的起点。

【示例】

`timescale 1 ns / 1 ps
module top_tb;
logic clk;
logic sig1,sig2,sig3;

initial begin
    clk = 1'b0;
    forever #1 clk = ~clk;
end

initial begin
    sig1 = 1'b0;sig2 = 1'b0;sig3 = 1'b0;
    #2 sig1 = 1'b1;sig3 = 1'b1;
    #2 sig1 = 1'b0;sig2 = 1'b1;sig3 = 1'b0;
    #2 sig2 = 1'b0; #4 $stop;
end
// sig1在时钟上升沿时为高,在当前时钟上升沿的下一个时钟上升沿时sig2也必须为高
property p1;
    @(posedge clk) sig1 |=> sig2;
endproperty // p1
// sig1在时钟上升沿时为高,在当前时钟上升沿sig2也必须为高
property p2;
    @(posedge clk) sig1 |-> sig3;
endproperty // p2

a1 : assert property(p1) $display("@%0t | a1 : PASSED!",$time);
     else $display("@%0t | a1 : FAILED!",$time);
a2 : assert property(p2) $display("@%0t | a2 : PASSED!",$time);
     else $display("@%0t | a2 : FAILED!",$time);
endmodule // top_tb
【仿真结果】

示例中的仿真可以看出,此时的检测虽然也是在每一个时钟上升沿去检测判断sig1的情况,但是检查结果仅在“|=>”和“|->”前的表达式为真的情况下才会输出,不会出现前例中的每个采样时钟上升沿都输出信息的现象。在这个示例中,我们注意到在属性property中使用了一些特殊的语法结构,其具体含义如下:

示例

说明

property sp1;

    sig1 |=> sig2;

endproperty // sp1

property sp2;

    sig3 |-> sig4;

endproperty // sp2

property sp3;

    sig5 ##n sig6;

endproperty // sp3

 

property pr1;

    clocking_event sp1 |=> sp2 |-> sp3;

endproperty // pr1

 

assert property(pr1)

|=>|->是蕴含操作符,分别表示非交叠蕴含(Non-Overlapped Implication)和交叠蕴含(Overlapped Implication),位于|=>|->左侧的称为“先行算子(Antecendent)”,右侧的称为“后续算子(Consequent)”,先行算子为约束条件,当先行算子匹配成功时,后续算子才会被计算,如果此时后续算子也匹配成功了,那么该断言“真”成功,如果此时后续算子计算失败,那么该断言失败。这里需要注意的是如果先行算子匹配失败,那么整个属性并不会认为失败,会被认为“成功”,这个成功为“假”成功,叫做空成功(Vacuous Success)。同时需要注意蕴含操作只能使用在property或者assert property中,不能在sequence中使用。

##n,表示延迟n个采样周期

不管是sequence还是property都需要assert property才会被激活进行check,否则他们本身在设计中是不会执行的。

这里的sig1sig2sig3sig4等可以为各种复杂的序列表达式,从而可以实现复杂的序列功能。

这里需要非常注意的是,“|=>|->”不能用于sequence中,作为蕴含操作符的|=>|->语法结构完全一样,都可以用if-else结构替换,但是从示例中可以看到两者的断言成功时间不一样,那么这个差别怎么表现出来呢?请看下例。

【示例】

`timescale 1 ns / 1 ps
module top_tb;
logic clk;
logic sig1,sig2,sig3;

initial begin
    clk = 1'b0;
    forever #1 clk = ~clk;
end

initial begin
    sig1 = 1'b0;sig2 = 1'b0;sig3 = 1'b0;
    #2 sig1 = 1'b1;sig2 = 1'b1;
    #2 sig1 = 1'b0;sig3 = 1'b1;
    #4 $stop;
end
// |=>
property pr0;
    @(posedge clk) sig1 |=> sig3;
endproperty // pr0
// |->
property pr1;
    @(posedge clk) sig1 |-> sig2;
endproperty // pr1
// |-> ##1
property pr2;
    @(posedge clk) sig1 |-> ##1 sig3;
endproperty // pr2

a1 : assert property(pr0) $display("@%0t | p0 : PASSED!",$time);
     else $display("@%0t | p0 : FAILED!",$time);
a2 : assert property(pr1) $display("@%0t | p1 : PASSED!",$time);
     else $display("@%0t | p1 : FAILED!",$time);
a3 : assert property(pr2) $display("@%0t | p2 : PASSED!",$time);
     else $display("@%0t | p2 : FAILED!",$time);
endmodule // top_tb
【仿真结果】

a1a3的检查结果一样,这是因为“|=>”和“|-> ##1”等效,两者都是在sig1(先行算子)匹配后,在下一个时钟周期sig3(后续算子)匹配,断言成功。a2中,sig1sig2在同一个时钟周期计算,如果此时sig1sig2同时满足要求,则断言成功。由a1a2a3的仿真结果可以看出,“|=>”描述的是先行算子比后续算子提前一个时钟周期,“|->”描述的是先行算子和后续算子在同一个时钟进行计算判断匹配情况。这里有个小建议:在编写断言时尽量使用“|->”而不是“|=>”,因为你可以通过“|->”后增加时钟数实现“|=>”的功能,但是不能通过“|=>”实现“|->”的功能。下面两图示意了“|=>”和“|->”的判断机理。

 s1 |->s2

s1  |=> s2





全部评论

相关推荐

不愿透露姓名的神秘牛友
08-08 13:37
点赞 评论 收藏
分享
爱睡觉的冰箱哥:你是我今晚见过的最美的牛客女孩
点赞 评论 收藏
分享
评论
点赞
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务