Sequence定义
1. 基本操作符号
-
##
用来表示周期延迟符号,例如##n表示在n个时钟周期后,##0表示在当前周期,即交叠周期;sequence a_b; @(posedge clk) a ##1 b; endsequence
-
##[min:max]
表示在一个范围内的时钟周期延迟。min、max必须是非负数,序列会在从min到max时间窗口中最早的时间来匹配;sequence a_b; @(posedge clk) a ##[1:5] b; endsequence
-
$
用来表示无穷大的周期(在仿真结束前),但是一般不建议这么做,因为它会增大仿真评估序列的负担;sequence a_b; @(posedge clk) a ##[1:$] b; endsequence
-
事件
也可以通过[*n]
操作符号来表示重复。n须为非负数,其不能为$;sequence a_b; @(posedge clk) a ##1 b[*2]; // a拉高一拍后,b要连续拉高两拍 endsequence
-
类似地,也可以用使用
[*m:n]
来表示一定范围内的重复事件;sequence a_b; @(posedge clk) a ##1 b[*2:5]; // a拉高一拍后,b要连续拉高两拍到五拍 endsequence
-
[=m]
用来表示一个事件的连续性,需要重复发生m次,但是并不需要在连续周期内发生;sequence a_b; @(posedge clk) a ##1 b[=3]; // b[=3]表示b必须在三个周期内为1,但是并不需要是连续的三个周期 endsequence
-
例如,执行了一次burst read操作,期望读的数据和ack信号在接下来的4个周期内返回,但又不需要连续的4个周期,即可以使用[=4]来表示;
-
类似地,
[=m:n]
用来表示从最小m到最大n的重复发生的非连续周期次数; -
a[*0]
用来表示没有在任何正数时钟周期内有效; -
例如 a[*0:3] ##1 b ##1 c 与哪些情形匹配呢?(都匹配)
- (b ##1 c)
- (a ##1 b ##1 c)
- (a ##1 a ##1 b ##1 c)
- (a ##1 a ##1 a ##1 b ##1 c)
2. and操作符号
- and用来表示两个序列需要保持匹配
- 用法: SEQ1 and SEQ2
- 下列情形将满足此操作符:
- 在从同一起始点开始后,seq1和seq2均满足;
- 满足的时刻发生在两个序列都满足的周期,即稍晚序列的满足时刻;
- 两个序列的满足时间可以不同;
- 如果操作符两边的序列都是用来衡量采样信号而非事件时序,那么则要求在相同周期内,and左右两边的序列都应该满足条件;
3. intersect操作符号
- 和and操作符类型,只是需要两边的序列在同一时钟周期内匹配;
- interact和and都要求同一时刻开始,但是interact多了一个条件,必须在同一时刻结束
4. or操作符号
- or用来表示两个序列至少需要有一个满足
- 用法: SEQ1 or SEQ2
- 下列情形将满足次操作符
- seq1和seq2都从同一个时刻被触发
- 最终满足seq1或者满足seq2
- 每一个序列的结束时间可以不同,结束时间以序列满足的最后一个序列时间为准
5. and / or应用
- 如果burst write长度为1,那么写的长度可以为1、2或者4;
property BurstLengthValid
@(posedge clk) disable iff(!rst)
((burstLen == 4) |-> (wrlen ==1) or (wrlen == 2) or (wrlen == 4))
endproperty
assert property (BurstLengthValid);
6. first_match操作符号
-
first_match用来从多次满足的序列中选择第一次满足时刻,从而放弃其他满足时刻;
sequence t1; te1 ##[2:5] te2; endsequence
-
t1序列可以用来匹配te1 ##2 te2,te1 ##3 te2,te1 ##4 te2,或者te1 ##5 te2;
sequence t1; first_match(te1 ##[2:5] te2); endsequence
-
此序列用来选择第一次匹配的时刻;
sequence t2; (1 ##[2:3] b) or (c ##[1:2] d); endsequence // a ##2 b // a ##3 b // c ##1 d // c ##2 d sequence ts2; first_match(t2); endsequence
-
对于上述的集中可能满足的sequence,ts2只会选择第一个满足上述sequence的时刻点;
-
每一次PCI总线进入idle状态时,状态机也应该返回IDLE状态。由此,在时序信息上要求,如果frame和irdy信号保持至少两个周期以上为高时,系统的状态应该为idle状态;
sequence checkBusIdle; (##[2:$] (frame && irdy)); endsequence property first_match_idle; @(posedge clk) first_match(checkBusIdle) |-> (state == busidle); endproperty
7. throughout操作符号
- 可以用来检查一个信号或者一个表达式在贯穿(throughout)一个序列时是否满足要求;
- 用法: Sig1/Exp1 throughout Seq
- 例如,在burst模式信号拉低以后的两个周期时,irdy/trdy也应该在连续七个周期内保持为低,同时burst模式信号也应该在这一连续周期内保持为低;
sequence brust_rule1; @(posedge mclk) $fell(burst_mode) ##0 // 触发前置条件 (!burst_mode) throughtout (##2 ((trdy == 0) && (irdy == 0)) [*7]); endsequence
8. within操作符号
- 可以用来检查一个序列与另外一个序列在部分周期长度上的重叠;
- 用法:SEQ1 within SEQ2
- 如果当seq1满足在seq2的一部分连续时钟周期内成立,seq1 within seq1成立;
- 例如,trdy需要在irdy下拉的一个周期后保持7个周期为低,同时irdy也将保持8个周期为低,一下序列会在低11个周中周期满足
9. if操作符号
- 可以在sequence中使用if…else;
- 例如,当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
- 在cache访问时,如果有cache lookup满足,那么状态机的状态应该为READ_CACHE,否则应该为REQ_OUT;
property cache_hit_check; @(posedge clk) (state == CACHE_LOOKUP) ##1 (CHit || CMiss) |-> if(CHit) ##1 (state == CACHE_READ); else ##1 (state == REQ_OUT); endproperty assert property(cache_hit_check) else $error;
10. 检测序列的终点
- 用法: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
- 在c拉起的下一个周期,a拉低b拉高的序列也应该结束;
sequence aRbseq(aFell, bRose); @(posedge clk) #fell(aFell) ##1; $rose(bRose); endsequence property endCycle; @(posedge clk) $rose(c) |=> aRbseq(a, b).ended; endproperty // 上面的逻辑也可以这样描述: // $rose(c) && $fell(a) |=> $rose(b);
11. 局部变量
- 局部变量可以在sequence或者property中使用;
- 这些变量会伴随着sequence、property动态创建;
- 每一个sequence实例都会有它自己的变量拷贝;
- 例如,在cache rdDone拉高后,读出rdDone会在两个周期后,在其基础上加1,并作为wrData写入;
sequence rd_cache_done; ##[1:5] rdDone; endsequence sequence check_reg_wr_data; int local_data; (rd_cache_done, local_data = cache_rd_data) ##2 (reg_wr_data == (local_data + 1)); endsequence
- 例如,如果read拉高,伴随readId,则下一次read必须在这一次read对应的readAck返回之后,才可以发起;
sequence checkReadIdAck; int loc_id; ($rose(read), loc_id == readId) |=> not (($rose(read)&& readId == loc_id) [*1:$]) ##0 /* ##0即就是&& */ $rose(readAck) && readAckId == loc_id); endsequence
- 需要先记录上一次read的readId,继而在接下来的周期,检查没有相同readId的read发起,直到对应readId的上一次read的readAck拉起,并且readAckId与之相同;
12. 调用方法
- 在序列匹配时,可以调用task,void function和系统函数;
- 例如,可以在s1序列末尾,分别打印出e和f变量被采样时的数值;
sequence s1; local v, w; (a, v = 2) ##1 (b[->1], w = f, $display("b after a with = %h, w = %h\n", v, w)); endsequence
13. 访问采样方法
- 一些系统函数可以用来访问指定类型的变量采样:
- 用来访问当前周期采样值
- 用来访问上一个采样周期之
- 用来检测采样变量的变化
$rose(expression[, clocking_event])
和$fell(expression[, clocking_event])
用来表示与上一个采样周期相比,变量最低位是否跳变为1或者0,满足条件返回1,否则返回0;- clocking_event在sequence中不需要单独提供,因为sequence中一般会指定采样时钟;
- 例如,在Req拉起的2个周期以后,Ack也应该拉高;
- 例如,在write_en拉低后(表征发起写访问),write_data的数据内容不应该为X;
property check_data_we; @(posedge clk) $fell(write_en) |-> not ($isunknown(wr_data)); endproperty
-
$stable(expressin[, clocking_event])
,用来表示在连续两个采样周期内,表达式的值保持不变,如果满足,返回1,否则,返回0; -
例如,在rd_en为高时,寄存器的值应该保持不变;
property reg_rd_data_stable; @(posedge clk) rd_en |-> $stable(reg_data); endproperty
-
$past(expr[, num_cycles][, gating_expr][,clocking_event])
用来访问在过去若干采样周期前的数值; -
默认情况下。num_cycles = 1,即采样1个周期前的数值;
-
例如,在ack拉高时的前两个周期,req信号应该为高;
property ReqCauseAck; @(posedge clk) $rose(ack) |-> $past(req, 2); endproperty
-
例如,如果当前状态时CACHE_READ,那么上一个状态不应该是CACHE_MISS(如果cache丢失,那么无法从中读取数据)
property cache_read_chk;
@(posedge clk) (state == CACHE_READ) |-> ($past(state) != CACHE_MISS);
endproperty
- $rose / $fell / $stable也可以在过程块语句和连续赋值语句中使用,在类中class中的function中使用,编译报错,软件task中是没有办法指定时钟的;
always @(posedge clk) TestDone <= stimulus_over & $rose(unit_done); always @(posedge clk) begin if($stable(my_sig)) begin $display($time, "my_sig is stable from previous clk"); end end assign intr_cleared = $fell(intr, @(posedge clk)); assign intr_set = $rose(intr, @(posedge clk));
14. 系统函数和方法
-
类似于之前的访问采样方法,还可以使用其他一些系统函数和方法,在sequence / property / assertion中使用:
- $countbits
- $onehot
- $isunknown
- $countones
-
这些系统函数(不依赖时钟)也可以用在一般过程语句块和赋值语句中
-
$countbits(expression, control_bit)
用来计算expression中匹配control_bit数值的位数; -
$countones(expression)
与$countbits(expression, ‘1)
—致,即计算expression中为1的位数 -
$onehot(expression)
与$countbits(expression, ‘1)==1
—致,即检查expression中是否有且只有1位为1 -
$isunknown(expression)
与$countbits(expression, ‘x, ‘z)!=0
一致,即检查expression中是否有x或者z。 -
在assertion或者property中,可以通过”disable iff“来给assertion做局部的条件控制;
-
在全局控制方面,我们也可以通过系统函数对property模块或者实例做出控制:
$asserton
,默认控制,用来打开所有的assertion$assertoff
,暂时停止assertion运行$assertkill
,终止所有执行的assertion
$assertoff (level,[list of module, instance or assertion_identifier]); $assertkill (level,[list of module, instance or assertion_identifier]); $asserton (level,[list of module,instance or assertion_identifier] );
-
level=0,表示当前模块或者层次下的所有assertion;
-
level=n,表示当前模块或者层次下n层范围中de assertion;
-
assertion_identifier表示property的名字或者assertion的label;
-
例如,在reset阶段停止所有的assertion,并且在其复位之后,使能所有的assertion
module assert_control(); initial begin: disable_assert_during_reset @(negedge top_tb.reset_n) // 复位时暂停 $display("Disabling assertion during reset"); $assertoff(0, top_tb.cpu_inst1); @(posedge top_tb.reset_n) // 复位后开启 $display("enabling assertions after reset" ); $asserton(0, top_tb.cpu_inst1); end endmodule