流程
在任何设计模型中,功能总是由多个逻辑事件的组合表示。这里的事件可以是简单的同一个时钟沿的布尔表达式,即即使断言。也可以是经过几个时钟周期后在求值的事件,即并发断言。在数字电路中,事件就是多个信号值的组合,我们可以使用布尔代数来表示。
我们可以把布尔表达式封装成序列sequence,序列是一个涉及一个或者多个周期的布尔表达式。其好处是可以提高重用性,就是我们在调用逻辑表达式时,我们可以使用sequence替代。sequence声明如下:
sequence name_of_sequence;
……
endsequence
多个序列可以组成一个更复杂的序列。systemverilog使用属性(property)来表示,语法形式如下:
property name_of_property;
test expression or
complex sequence expressions
endproperty
电路时序建立好了之后,为了可以让仿真工具检查,需要加入关键词assert,使仿真器可以检查属性。语法形式如下:
assertion_name:assert_property(property_name)
流程图如下,总的来说是一个往上封装的过程。
sequence
看下面的例子,seq_1在每个时钟的上升沿检查a是否为1。如果这个信号a在每个上升沿不是1的话,那么断言就会失败。
sequence seq_a;
@(posedge clk) a = 1;
endsequence
下面的例子中,检查的是一个逻辑表达式,即在上升沿检查a||b的值是否为真。
sequence seq_2;
@(posedge clk) a || b;
endsequence
为了提高序列的重用性,我们可以在序列定义中定义所需要的参数,可以被其它类似的行为调用。如下面例子所示:
sequence seq_lib(a,b);
a || b;
endsequence
我们在调用的时候,可以在其它序列中传入参数:
sequence s_lib_inst;
seq_lib(req1,req2);
endsequence
我们还可以检查多个周期的行为,即时序问题,为了表达不同周期的行为,采用“##”符号,比如说 ##2表示仿真事件后的两个周期,对于多个周期的约束,不同周期之间的逻辑表达式使用空格隔开。在下面这个例子中,首先判断是在每个时钟的上升沿,信号a是否为高电平,如果a时低电平,那么整个sequence失败。但是如果a时高电平在任意一个上升沿,而且检查在这之后的后两个周期b是否为高电平,若信号b不是高电平,那么此次断言失败。
sequence seq;
@(posedge clk) a ##2 b;
endsequence
我们也可以把sequence和property分开,在下面例子中,在property中定义时钟,将序列和时钟互相隔离,可以增加基本序列定义的重用性。
sequence seq;
a ##2 b;
endsequence
property p;
@(posedge clk seq);
endproperty
a_1 : assert property (p);
此外我们也可以对断言条件取反,下面这个例子中,如果检测到a在一个给定的时钟上升沿为高电平,那么在过了两个时钟周期后,信号b不能为高电平。
sequence seq;
@(posedge clk) a ##2 b;
endsequence
property p;
not seq;
endproperty
a_1:assert property(p);
implication operator
有时候,我们期望和约束一样,满足一定条件才检查,同样使用蕴涵操作符实现。蕴涵操作符等价于if-then结构,操作符的坐标叫做前因(antecedent),操作符的右边称作后果(consequent)。如门控时钟一般,前因满足时,才会检查后果,前因不满足,则不会检查后果。需要注意的是,蕴涵操作符只能在property内部使用,无法再sequence内部使用。蕴涵操作符分为两类:
- 重叠蕴涵
- 非重叠蕴涵
重叠蕴涵使用|->表示,如果前因得到匹配时,那么这个后果将会在同一个时钟周期中进行检查。如下面的例子所示,如果a是高电平的话,那么信号也应高在相同的时钟周期内也为高电平。
property p;
@(posedge clk) a |-> b;
endproperty
a:assert property(p);
非重叠蕴涵使用|=>表示,如果前因得到匹配时,那么这个后果将会在下一个时钟周期内进行检测。如下面的例子所示,如果a在一个给定的时钟上升沿为高电平,那么在写一个时钟周期信号b也应该是高电平。
property p;
@(posedge clk) a|=> b;
endproperty
a:assert property(p);
此外我们也可以在后果前加一个固定的延时语句。如下面的例子所示,如果信号a在一个给定的时钟上升沿为高电平,那么应该在两个时钟周期之后信号b也为高电平。
property p;
@(posedge clk) a |-> ##2 b;
endproperty
a:assert property(p);ww
实质上,蕴涵操作符的前后都是序列,下面的例子体现了sequence到property的封装。
sequence seq_1;
(a && b) ##1 c;
endsequence
sequence seq_2;
##2 !d;
endsequence
property p;
@(posedge clk) seq_1 |-> seq_2;
endpeoperty
a: assert property(p);
当然,固定延时不能满足所有需求,还可能是一个范围。下例中,通过[1:4]表示在1-4个周期内,b=1就通过断言,是一个或的关系,即可以经过1个周期为1,也可以经过2个、3个、4个周期b=1。中括号内周期数目,可以设置为0,表示当前时钟沿,也可以写$表示无穷,即后果任意个周期。
property p;
@(posedge clk) a |-> ##[0:4] b;
endproperty
a:assert property(p);