目录

  • Assertion介绍
  • Sequence定义
  • 1.蕴含操作符
  • 2.基本操作符号
  • and操作符
  • intersect操作符
  • or操作符
  • first_match操作符
  • throughout操作符
  • within操作符
  • if操作符
  • 检测序列的终点
  • 局部变量
  • 调用方法
  • Property使用
  • 1.时钟声明
  • 2.bind语句
  • 3.expect语句


Assertion介绍

断言(Assertion):用来与设计功能和时序作比较的属性描述。
可以分为:

  • 立即断言:非时序的、执行时如同过程语句、可以在initial/always过程块或者task/function中使用。
time t;
always@(posedge clk)begin
	if(state == REQ)
	//如果state为REQ,但是req1或req2均不为1时,断言将失败。
	assert(req1||req2)
	else begin
		t = $time;
		#5 $error("assert failed at time %0t",t);
	end     
end
//立即断言可以结合$fatal/$error/$warning/$info给出不同严重级别的消息提示。    
assert(myfunc(a,b)) count1 = count + 1; else -> event1;                                       
assert(y==0) else flag=1;      
always@(state) assert(state == $onehot) else $fatal;
  • 并行断言:时序性的、关键词property用来区分立即断言和并行断言、与设计模块一同并行执行。
//Rquest-Grant协议描述:request拉高,在2个周期后grant 拉高,在一个周期后property拉低,在一个周期后grant拉低。
property req_grant_prop
	@(posedge clk) req ##2 gnt ##1 !req ##1 !gnt;
endproperty
assert property rq_grant_prop else $error("Req-Gnt Protocol violoation");

Sequence定义

sequence是用来表示在一个或者多个时钟周期内的时序描述,是property的基本构建模块,并经过组合来描述复杂的功能属性。
sequence可以在module、interface、program、clocking块和package中声明。

sequence s1;
	@(posedge clk) a ##1 b ##1 c;
endsequence
sequence t1;
	(a ##[2:3] b) or (c ##[1:2] d);
endsequence

1.蕴含操作符

  • 蕴含操作符用来表示,如果property中左边的先行算子成立,那么property右边的后续算子才会被计算。
  • 如果先行算子不成功,那么整个属性就默认地被认为成功,这叫做“空成功”。
  • 蕴含结构只能用在属性定义中,不能在序列中使用。
  • 蕴含可以分为两类:交叠蕴含和非交叠蕴含。
  1. |->交叠交错符号:
    如果条件满足,则评估其后续算子序列。
    如果条件不满足,则表现为空成功,不执行后续算子。
property p_req_ack;
	@(posedge clk) mem_en |-> (req ##2 ack);
endproperty:p_req_ack
  1. |=>非交叠交错符号:
    如果条件满足,则在下一个周期评估其后续算子序列。
    如果条件不满足,则表现为空成功,不执行后续算子。
property p_req_ack;
	@(posedge clk) mem_en |-> (req ##2 ack);
endproperty:p_req_ack

2.基本操作符号

  • ##用来表示周期延迟符号,例如##n表示在n个时钟周期后,##0表示在当前周期,即当前周期。
  • ##[min:max]表示在一个范围内的时钟周期延迟。min、max必须是非负数,序列会从min到max时间窗口中最早的时间来匹配。
  • $用来表示无穷大的周期(在仿真结束前),不建议使用。
  • [*n]用来表示事件的重复,n须为非负数,其不能为$。
sequence a_b;
	@(posedge clk) a ##1 b[*2];
endsequence
//b[*2]means 'b' must be true for 2 consecutive clocks
  • 也可以用[*m:n]来表示一定范围内的重复事件。
sequence a_b;
	@(posedge clk) a ##1 b[*2:5];
endsequence
//b[*2:5] means 'b'must be true for minimum 2 consecutive cycles
//and maximum 5 consecutive cycles
  • [=m]用来表示一个事件的连续性,需要重复发生m次,但是并不需要在连续周期内发生。
  • [=m:n]用来表示从最小m到最大n的重复发生的非连续周期次数。
  • a[*0]用来表示没有在任何正数时钟周期内有效。

and操作符

  • 用法:SEQ1 and SEQ2
  • 用来表示两个序列需要保持匹配。
  • 下列情形将满足此操作符:
    (1)在从同一个起始点开始后,seq1和seq2均满足。
    (2)满足的时刻发生在两个序列都满足的周期,即稍晚序列的满足时刻。
    (3)两个序列的满足时间可以不同。

    上图中在第八拍时,te1 ##2 te2、te3 ##2 te4 ##2 te5以及(te1 ##2 te2) and (te3 ##2 te4 ##2 te5)触发,所以将第八拍作为序列的起始点,上面三个序列分别在第十拍、第十二拍、第十二拍结束。
    如果操作符两边的序列都是用来衡量采样信号而非事件时序,那么则要求在相同周期内,and左右两边的序列都应该满足条件。

intersect操作符

与and操作符类似,只是需要两边的序列时序在同一时钟周期内匹配。

moviepy SubtitlesClip ass文件_操作符


因为两个序列的结束时间需要相同,所以左边的序列在[1:5]的区间中取4,在第十二拍结束。

or操作符

  • 用法:SEQ1 or SEQ2
  • 用来表示两个序列至少需要有一个满足。
  • 下列情形将满足此操作符:
    (1)seq1和seq2都从同一个时刻被触发。
    (2)最终满足seq1或seq2。
    (3)每一个序列的结束时间可以不同,结束时间以序列满足的最后一个序列时间为准。

    上图中两个序列在第八拍被触发,由于结束时间可以不同,所以在第十拍或者第十二拍结束都可以。

    上图中由于左边的序列中有[1:5],所以在第九、十、十一、十二、十三拍结束都可以。
    and/or的应用:
//要求burst write长度为4,写的长度可以为1、2、4。
property BurstLengthValid
    @(posedge clk) disable iff(!rst)
    ((burstLen == 4) |-> (wrlen==1) or (wrlen==2) or (wrlen==4) );
endproperty
assert property(BurstLengthValid)

first_match操作符

first_match用来从多次满足的时序中选择第一次的满足时刻,从而放弃其他满足的时刻。

//要求frame和irdy信号保持两个周期以上为高时,系统的状态应该进入idle状态。
sequence checkBusIdle;
    (##[2:$] (frame && irdy));
endsequence

property first_match_idle;
    @(posedge clk) first_match(checkBusIdle) |-> (state==busidle);    
endproperty

throughout操作符

  • 用法:Sig1/Exp1 throughout Seq
  • 用来检查一个信号或者一个表达式在贯穿(throughout)一个序列时是否满足要求。
//要求在burst_mode信号拉低后的两个周期内,irdy/trdy也应该在连续七个周期内保持为低,
//同时burst_mode信号也应该在这一连续周期内保持为低。
sequence burst_rule1;
	@(posedge mclk)
		$fell(burst_mode) ##0
		(!burst_mode) throughout(##2 ((trdy == 0) && (irdy ==0)) [*7]);
endsequence

moviepy SubtitlesClip ass文件_时钟周期_02

within操作符

  • 用法:SEQ1 within SEQ2
  • 用来检查一个序列与另外一个序列在部分周期长度上的重叠。
  • 如果当SEQ1满足在SEQ2的一部分连续时钟周期内成立,则SEQ1 within SEQ2成立。
  • 与thoughout的区别在于within左右两边都是序列,且操作符右边的序列要包住左边的序列;而thoughout左边是信号或表达式,右边是序列,且操作符左边的信号或表达式要包住右边的序列。
!trdy[*7] within (($fell irdy) ##1 !irdy[*8])

要求trdy需要在irday下拉的1个周期后保持7个周期为低,同时irday也将保持8个时钟周期为低,上面的序列会在下图中的第十一拍满足。

moviepy SubtitlesClip ass文件_systemverilog_03

if操作符

//要求当master_req为高时,下一个周期,req1或者req2应该为高,
//如果req1为高,则下一个周期ack1为高,如果req2为高,则下一个周期ack2为高。
property master_child_reqs;
	@(posedge clk) master_req ##1 (req1 ||req2)
	if(req1)
		(##1 ack1)
	else
		(##1 ack2);
endproperty

检测序列的终点

  • 用法:SEQ.ended。
  • 在某一时刻,序列如果即使抵达终点,那么条件满足。
    例如 ,在inst为高的下一个周期,序列e1应该结束或者已经结束。
sequence e1;
	@(posedge sysclk) $rose(ready) ##1 proc1 ##1 proc2;
endsequence
sequence rule;
	@(posedge sysclk) reset ##1 inst ##1 e1.ended ##1 branch_back;
endsequence

局部变量

  • 局部变量可以在sequence或者property中使用。
  • 这些变量会伴随着sequence、property动态创建。
  • 每一个sequence实例都会有它自己的变量拷贝。
sequence rd_cache_done;
	##[1:5] rdDone;
endsequence

sequence check_reg_wr_data;
	int local_data;
	(rd_cache_done, local_data = rd_data) ##2 (wr_data == (lacal_data+1));
endsequence
//在rd_cache_done拉高后,读出的rd_data会在两个周期后在其基础上加1,并作为wr_data写入

调用方法

  • $display:打印数值。
  • $isunknown :检查数据内容是否是X或Z。
  • $stable :用来表示在连续两个采样周期内,表达式的值保持不变,如果满足返回1,否则返回0.
  • $past :用来访问在过去若干采样周期前的数值。
  • $rose / $fell :用来表示与上一个采样周期相比,变量最低位是否跳变为1/0,满足条件返回1,否则返回0。
  • 系统函数和方法
    $countbits:用来计算匹配control_bit数值的位数。
    $countones:计算为1的位数。
    $onehot:检查是否有且只有1位为1。
    对于以下三个方法,level = 0表示当前模块或者层次下的所有assertion;level = n 表示当前模块或者层次n层范围内的所有assertion;assertion_identifier表示property的名字或者assertion的label。
    $asserton(level, [list of module, instance or assertion_identifier]):默认设置,打开所有的assertion
    $assertoff(level, [list of module, instance or assertion_identifier]) :暂停assertion执行
    $assertkill(level, [list of module, instance or assertion_identifier]) : 终止所有执行的assertion

Property使用

  • 结合sequence对时序和逻辑的描述,property可以用来描述设计的确切行为。
  • property可以在验证中用来做assumption、checker以及coverage:
    1.使用assert关键词时,可用作checker来检查设计是否遵循property的描述。
    2.使用assume关键词时,可作为环境的假设条件,对于仿真环境和形式验证均起到对激励进行假设的作用。
    3.使用cover关键词时,可以将property是否通过作为断言覆盖率来衡量。
  • property可以在module、interface、clocking块或者package中声明。
  • 一共有七种property:sequence、negation、disjunction、conjunction、if…else、implication、instantiation。
    sequence:只有当出现满足该sequence条件时,property才可以通过。
    negation:即not property_expr,如果 property_expr不满足,则negation类型的property通过。
    disjunction:即property_expr1 or property_expr2,当至少一个property_expr满足时,property通过。
    conjunction:即property_expr1 and property_expr2,当两个property_expr均满足时,property通过。
    if…else:条件选择。
    implication:蕴含(|->,|=>),同sequence中用法一致。
    instantiation:即一个命名后的property可以在另外一个property_expr中使用。

1.时钟声明

  • 一般对于sequence或者property,默认情况下使用同一个时钟对数据做采样,但用多个时钟采样也可以。
  • 如果一个sequence或者property需要声明多个时钟用来做数据采样,可以使用##1结合第二个时钟沿采样。
  • sequence操作符号如and、or、intersect等无法被使用在多时钟sequence,而property中的and、or、not则可以用在多时钟的property声明中,,因为它们代表逻辑运算,并不参与sequence之间的时序关系。
  1. 在sequence中独立指定时钟:
sequence s2; 
	@(posedge clk) a ##2 b;
endsequence
property p2;
	not s2;
endproperty
assert property(p2);
  1. 在property中独立指定时钟:
property p3;
    @(posedge clk) not (a ##2 b);
endproperty
assert property(p3);
  1. 分别在过程块和时钟块中继承时钟:
always@(posedge clk)
begin
	assert property(not(a ##2 b));
end
//在时钟块中继承
clocking master_clk @(posedge clk)
    property p3;
        not(a ##2 b);
    endproperty
endclocking
assert property(master_clk.p3);

断言的时钟优先级由以下条件逐级判定:

  1. 显示声明断言时钟。
  2. 继承断言所嵌入环境的时钟。
  3. 继承默认的时钟。
    对于并行断言,必须满足上述条件之一;对于多时钟的断言,必须显示声明时钟,无法继承或使用默认时钟,且无法嵌套入由时钟驱动的过程块语句。

2.bind语句

使用方法:bind design_block_or_instance_name block_with_assertion
1.bind方法可以满足在设计外部定义断言,而将断言绑定到设计内部或者接口上面。
2.bind可以将包含断言的模块与设计模块或者实例进行绑定,既可以满足对设计信号的可视性,又能满足断言模块的独立性。
3.其优势在于,无法修改原有设计代码,也不需要添加监测信号,即可以实现断言的添加。

interface range(input clk, enable, input int minval, expr);
    property crange_en;
        @(posedge clk) enable |-> (minval <= expr);
    endproperty
    range_chk:assert property(crange_en);
endinterface
bind cr_unit range r1(c_clk, c_en, v_low, (in1 && in2));

3.expect语句

  • 之前的assert、assume和cover都是非阻塞的方式,即它们本身并不会阻塞后续的语句,然而expect是一种阻塞的property使用方式。
  • expect的语法同assert一致,不过它会等待property执行通过,才会执行后续的语句。
  • 简单来看,可以使用wait语句的地方,即可以使用expect语句。
task automatic wait_for(integer value, output bit success);
	expect(@(posedge clk) ##[1:10] data == value) success =1;
	else success = 0;
endtask