断言概念
一般,断言是基于时序逻辑的,单纯进行组合逻辑的断言很少见,因为太费内存(时序逻辑是每个时钟周期判断一次,而组合逻辑却是每个时钟周期内判断多次,内存吃不消)。
因此,写断言的一般规则是: time + event,要断定发生什么event,首先要指定发生event的时间,例如:
- 每个时钟上升沿 + 发生某事;
- 某信号下降时 + 发生某事;
sequence / property
- 序列块:
sequence name;
....;
endsequence
- 属性块:
property name;
...;
endproperty
从定义来讲,sequence块用于定义一个事件(砖),而property块用于将事件组织起来,形成更复杂的一个过程(楼)。sequence块的内容不能为空,你写乱字符都行,但不能什么都没有。sequence也可以包含另一个sequence, 如:
sequence s1;
s2(a,b);
endsequence //s1和s2都是sequence块
sequence块和property块都有name,使用assert调用时都是:assert property(name);
在SVA中,
- sequence块一般用来定义组合逻辑断言,
- property一般用来定义一个有时间观念的断言,它会常常调用sequence,
- 一些时序操作如“|->”只能用于property就是这个原因。
注:以下介绍的SVA语法,既可以写在sequence中,也可以写在property中,语法是通用的。
Table 1: SVA system functions
Table 2: SVA operators
Table 3: Repetition operators
NOTE: The table above lists operators most frequently used in SVAs. There are several more -
intersect, throughout, within
, etc. In my opinion, while these operators are powerful, they lead to confusion. Most assertions can be written using the above table.
断言的时间点
断言要熟记两个时间点:采样时刻和匹配时刻,断言在preponed域采样,在observed域执行检查(关于时钟域也有很多介绍,之后我再写一个简单明了的吧)。简单来说断言的采样点处于时钟上升沿之前、module中@clk采样时刻之后,也就是说采样到的是上升沿之前的”旧值”。验证环境中,@clk进行采样时的采样顺序可以简记为module @clk采样(旧值)->断言采样(旧值)->program @clk采样(新值,跳变后的)。
and运算符
and运算符用来连接两个事件(多为序列),当两个事件均成功时匹配成功,需要注意的是两个事件必须有相同的起始点,但是可以有不同的结束点。举个简单的例子,(a ##[2:4] b) and (a ##5 c)在波形中可见,100ns处(a ##[2:4] b)匹配成功,140ns处整个序列匹配成功,其他时间都是匹配失败的(假“失败”)。
说一下and和&在断言中的区别与联系。a & b也是要a和b同时成立在是成功,不过此式中a和b必须为表达式,匹配也只能在每一拍内进行;而a and b中的a、b既可以是表达式又可以是序列,是可以进行跨拍匹配的。例如(sig_a == 5) & (sig_b == 3)也可以写成(sig_a == 5) and (sig_b == 3),但是(a ##[2:4] b) and (a ##5 c)不可以写作(a ##[2:4] b) & (a ##5 c)。
or运算符
and运算符用来连接两个事件(多为序列),当两个事件任一匹配成功则整体成功,与|的区别和之前类似,or可以连接两个表达式或是两个序列,|只能连接两个表达式。
intersect运算符
我认为intersect运算符是最重要的一个运算符,后面会大量的用到。intersect和and有一些类似,都是连接两个事件,两个事件均成功整个匹配才成功。不过intersect多了一个条件,那就是两个事件必须在同一时刻结束(and已经需要保证两个事件同一时刻开始了),换句话说a intersect b能匹配成功,a、b两个序列的长度必须相等。
通过几个例子来深入理解下,(a ##[1:2] b) intersect (c ##[2:3] d),见波形如下图。图中黄色点为a ##[1:2] b匹配成功时刻点,蓝色为c ##[2:3] d匹配成功点,根据我们之前的分析,intersect连接的两个序列必须有同样的结束时刻,因此整个序列匹配的时间点一定在黄色和蓝色重合的点中,那么我们需要分析下起始点。对于180ns,a ##[1:2] b起始点为160ns或140ns,c ##[2:3] d的起始成功点为140ns或120ns(如果觉得这样比较乱,那就顺着从开头找),因此起点在140ns终点在180ns的波形符合序列(a ##[1:2] b) intersect (c ##[2:3] d),匹配成功;同理起点在160ns终点在200ns的波形符合序列,起点在180ns终点在220ns的波形也符合序列。
记住intersect最重要的特点:连接打两个序列有相同的起点相同的起点相同的终点和必然相同的序列长度,这一点之后会非常好用。
within运算符
a within b含义是在事件b(序列b)匹配期间,事件a(序列a)至少出现1次则匹配成功,否则失败。再正规一些的表述就是序列a在序列b开始到结束的范围内发生,序列b开始匹配点必去在序列a开始 匹配点之前发生,序列a的结束匹配点必须在序列b结束匹配点之前结束。不过在此我自己有一个疑点,序列b应当包含序列a,那么序列b和序列a的起始时间或结束时间相同是否可以,对于这点暂做遗留问题,我亲自仿真下寻找答案(暂时认为是不可以的)。
还是举一个简单的栗子看看波形吧,(a ##1 b[=3]) within (c ##1 d[->1])。
40ns~200ns是一次成功的匹配,(c ##1 d[->1])在40ns开始在200ns成功,(a ##1 b[=3])在80ns开始在160ns(和180ns、200ns…)匹配成功,(a ##1 b[=3])完美包裹在(c ##1 d[->1])之中;240ns~320ns是一次典型的匹配失败,请自行分析下。
因为后面用到within很少,我就不再深入探究了。
throughout运算符
throughout运算符和intersect有些接近(我个人认为throughout是可以完全被intersect取代的,所有throughout都可以写成intersect),不过区别在于throughout必须连接一个表达式和一个序列即req throughout seq,含义是在seq匹配起始到结束期间,req都必须成立。例如a throughout (b ##1 c[->1])就要求从b有效开始,直到c第一次有效结束,这段期间a必须始终保持有效,如下波形图60ns~140ns就是一次典型的匹配成功。
这个我用的的确不多,就写这么多吧。
first_match
含义是只使用第一次匹配,丢弃其他时刻点的匹配。
在之前所写的事件描述中我们提到过,仿真器会在每个时钟窗口去匹配,大概率会匹配成功多次。在一些情景下,我们只关心第一次出现的状况,例如我们认定某一些信号组合是不能出现的,一旦出现了就应该报error提醒我们进行检查,这种情况下我们只需要关注第一次出现的时间点就可以了,那么我们就可以使用first_match了。不过说实话这个运算符在断言中用的不多。
语法1:信号(或事件)间的“组合逻辑”关系:
- 常见的有:&&, ||, !, ^
- a和b哪个成立都行,但如果都成立,就认为是a成立:firstmatch(a||b),与“||”基本相同,不同点是当a和b都成立时,认为a成立。
- a ? b:c ———— a事件成功后,触发b,a不成功则触发c
语法2:在“时序逻辑”中判断独立的一根信号的行为:
@ (posedge clk) A事件; // ———— 当clk上升沿时,如果发生A事件,断言将报警。
边沿触发内置函数:(假设存在一个信号a)
$rose( a );———— 信号上升
$fell( a );———— 信号下降
$stable( a );———— 信号值不变
-
@ (posedge clk) a |=> b ———— 断定clk上升沿后,a事件开始发生,下一个时钟沿后,b事件开始发生。
-
@ (posedge clk) a |=>##2b ———— 断定clk上升沿后,a事件开始发生,下三个时钟沿后,b事件开始发生。
-
overlapped implication采用"|->"操作符,只有先行词判断为真后,后继才会在同一个时钟沿被检测.例子:
property p;
@(posedge clk) a |-> b;//在上升沿到来时,若a=1,则在同一个时钟周期继续检查b是否为1,若为1,则判断为真
endproperty
a: assert property(p);
- Non-overlapped implication采用"|=>"操作符,只有先行词判断为真后,后继才会在下一个时钟沿被检测.例子:
property p;
@(posedge clk) a |=> b;//当时钟沿到来时检测a是否为1,若为1,则在下一个时钟沿继续检测b
endproperty
a: assert property(p);
sequence seq_rose;
@(posedge clk) $rose(a);//如果在时钟有效沿a跳变为1,则断言成功
endsequence
语法5:总线的断言函数
总线就是好多根bit线,共同表示一个数。SVA提供了多bit状态一起判断的函数,即总线断言函数:
- $onehot(BUS) ————BUS中有且仅有1 bit是高,其他是低。
- $onehot0(BUS) ————BUS中有不超过1 bit是高,也允许全0。
- $isunknown(BUS) ————BUS中存在高阻态或未知态。
- countones(BUS)==n ————BUS中有且仅有n bits是高,其他是低。
语法6:屏蔽不定态
当信号被断言时,如果信号是未复位的不定态,不管怎么断言,都会报告:“断言失败”,为了在不定态不报告问题,在断言时可以屏蔽。如:
@(posedge clk) (q == $past(d)) //,当未复位时报错,屏蔽方法是将该句改写为:
@(posedge clk) disable iff (!rst_n) (q == $past(d)) //rst是低电平有效
Forbidding a property 禁止一个property
sequence seq;
@(posedge clk) a ##2 b;
endsequence
property p;
not seq;//not表示对sequence取反,即sequence成功时,property失败
endproperty
a_1: assert property(p);
断言编译modelsim
在modelsim中开启断言编译和显示功能:
- 【编译verilog代码时按照system verilog进行编译】 vlog -sv abc.v
- 【仿真命令加一个-assertdebug】 vsim -assertdebug -novopt testbench
- 【如果想看断言成功与否的分析,使用打开断言窗口的命令】 view assertions
带时序关系的sequence
在SVA中时钟延时用符号"##"来表示,如“##2”表示延时两个时钟周期。看例子:
sequence seq;
@(posedge clk) a ##2 b; //在时钟上升沿到来是检查a是否为1,若不为1则断言失败
//若a为1,则检查两个周期之后b是否为1,若不为1,断言失败
endsequence
Repetition Operators 重复操作
signal [*n]
或
sequence [*n]
property p;
@(posedge clk) a |-> ##1 b ##1 b ##1 b;//在当前时钟沿若a=1,则在接下来的3个时钟b=1
endproperty
a: assert property(p);
//所以上面的语句可以重写为:
property p;
@(posedge clk) a |-> ##1 b[*3];
endproperty
a: assert property(p);
[*n : $], [*] [+]连续循环,是针对sequence的连续循环
[* n:$]结构类似于[* n:m],除了m被$符取代,$符代表无限的意思,并且n意味着重复最少n个周期。
[*]是[* 0:$]的缩写,表示重复至少0次,最多无限次。
[+]是[* 1:$]的缩写,表示重复至少1次,最多无限次。
property p;
@(posedge clk) a |-> ##1 b ##1 b ##1 b;
endproperty
a: assert property(p);
//The above property checks that, if the signal “a” is high on given posedge of the clock, the signal “b” should be high for 3 consecutive clock cycles.
//the same with repetition operator above sequence can be re-written as,
property p;
@(posedge clk) a |-> ##1 b[*3];
endproperty
a: assert property(p);
go to repetition
The go-to repetition operator is used to specify that a signal will match the number of times specified not necessarily on continuous clock cycles.
signal [->n]
//重复n次,且不必是连续时钟周期
property p;
@(posedge clk) a |-> ##1 b[->3] ##1 c;
endproperty
a: assert property(p);
//The above property checks that, if the signal “a” is high on given posedge of the clock, the signal “b” should be high for 3 clock cycles followed by “c” should be high after ”b” is high for the third time.
SystemVerilog Assertions (SVA)
Nonconsecutive repetition
This is very similar to “go to” repetition except that it does not require that the last match on the signal repetition happens in the clock cycle before the end of the entire sequence matching.
signal [=n]
Only expressions are allowed to repeat in “go to” and “nonconsecutive” repetitions. Sequences are not allowed.
它与上面go-to类似,唯一的区别就是它不要求信号重复的最后一次匹配发生在整个序列匹配结束之前的时钟周期中,格式如下:
signal [=n]
[=n]和[->n]的区别:
a ##1 b[->1:3] ##1 c // E.g. a !b b b !b !b b c
//也就是说最后一次必须匹配
a ##1 b [=1:3] ##1 c // E.g. a !b b b !b !b b !b !b c
//在跳变到c之前的最后一次不一定要匹配
/*--------------------------------------
// AXI virtual interface
// description : axi virtual interface which is a connection pool interface for DUT and Virtual test
// file : axi_vif.sv
// author : SeanChen
// date : 2013/04/10
---------------------------------------*/
`timescale 1ns/10ps
interface AXI_vif #(
parameter integer C_AXI_ID_WIDTH = 10, // default 4
parameter integer C_AXI_ADDR_WIDTH = 32,
parameter integer C_AXI_REG_WITH = 4,
parameter integer C_AXI_DATA_WIDTH = 32,
parameter integer C_AXI_LEN_WIDTH = 8, // default 4
parameter integer C_AXI_SIZE_WIDTH = 3,
parameter integer C_AXI_BURST_WIDTH = 2,
parameter integer C_AXI_CACHE_WIDTH = 4,
parameter integer C_AXI_PROT_WIDTH = 3,
parameter integer C_AXI_QOS_WIDTH = 4,
parameter integer C_AXI_STRB_WIDTH = 4,
parameter integer C_AXI_RESP_WIDTH = 2,
parameter integer C_AXI_LOCK_WIDTH = 1,
parameter integer C_AXI_VALID_WIDTH = 1,
parameter integer C_AXI_READY_WIDTH = 1,
parameter integer C_AXI_LAST_WIDTH = 1,
parameter string name = "vif"
)( input AXI_ACLK, input AXI_ARESET_N);
// control flags
bit has_checks = 1;
bit has_coverage = 1;
// AXI global signals
// logic [0:0] AXI_ARESET_N;
// logic [0:0] AXI_ACLK;
// AXI address write phase
logic [C_AXI_ID_WIDTH-1:0] AXI_AWID; //axi4 remove it
logic [C_AXI_ADDR_WIDTH-1:0] AXI_AWADDR;
logic [C_AXI_REG_WITH-1:0] AXI_AWREG;
logic [C_AXI_LEN_WIDTH-1:0] AXI_AWLEN;
logic [C_AXI_SIZE_WIDTH-1:0] AXI_AWSIZE;
logic [C_AXI_BURST_WIDTH-1:0] AXI_AWBURST;
logic [C_AXI_LOCK_WIDTH-1:0] AXI_AWLOCK;
logic [C_AXI_CACHE_WIDTH-1:0] AXI_AWCACHE;
logic [C_AXI_PROT_WIDTH-1:0] AXI_AWPROT;
logic [C_AXI_QOS_WIDTH-1:0] AXI_AWQOS;
logic [C_AXI_VALID_WIDTH-1:0] AXI_AWVALID;
logic [C_AXI_READY_WIDTH-1:0] AXI_AWREADY;
// AXI data write phase
logic [C_AXI_ID_WIDTH-1:-0] AXI_WID;
logic [C_AXI_DATA_WIDTH-1:0] AXI_WDATA;
logic [C_AXI_STRB_WIDTH-1:0] AXI_WSTRB;
logic [C_AXI_LAST_WIDTH-1:0] AXI_WLAST;
logic [C_AXI_VALID_WIDTH-1:0] AXI_WVALID;
logic [C_AXI_READY_WIDTH-1:0] AXI_WREADY;
// AXI response write phase
logic [C_AXI_ID_WIDTH-1:0] AXI_BID;
logic [C_AXI_RESP_WIDTH-1:0] AXI_BRESP;
logic [C_AXI_VALID_WIDTH-1:0] AXI_BVALID;
logic [C_AXI_READY_WIDTH-1:0] AXI_BREADY;
// AXI address read phase
logic [C_AXI_ID_WIDTH-1:0] AXI_ARID;
logic [C_AXI_ADDR_WIDTH-1:0] AXI_ARADDR;
logic [C_AXI_REG_WITH-1:0] AXI_ARREG;
logic [C_AXI_LEN_WIDTH-1:0] AXI_ARLEN;
logic [C_AXI_SIZE_WIDTH-1:0] AXI_ARSIZE;
logic [C_AXI_BURST_WIDTH-1:0] AXI_ARBURST;
logic [C_AXI_LOCK_WIDTH-1:0] AXI_ARLOCK;
logic [C_AXI_CACHE_WIDTH-1:0] AXI_ARCACHE;
logic [C_AXI_PROT_WIDTH-1:0] AXI_ARPROT;
logic [C_AXI_QOS_WIDTH-1:0] AXI_ARQOS;
logic [C_AXI_VALID_WIDTH-1:0] AXI_ARVALID;
logic [C_AXI_READY_WIDTH-1:0] AXI_ARREADY;
// AXI data read phase
logic [C_AXI_ID_WIDTH-1:0] AXI_RID;
logic [C_AXI_DATA_WIDTH-1:0] AXI_RDATA;
logic [C_AXI_RESP_WIDTH-1:0] AXI_RRESP;
logic [C_AXI_LAST_WIDTH-1:0] AXI_RLAST;
logic [C_AXI_VALID_WIDTH-1:0] AXI_RVALID;
logic [C_AXI_READY_WIDTH-1:0] AXI_RREADY;
// write data count
// read data count
/*-------------------------------------------
// modport for each module type, like master, slave...
// please add your modports here
---------------------------------------------*/
// modports in slave interfaces
modport Slave ();
// modports in master interfaces
modport Master ();
// modports in monitor interface
modport Monitor ();
/*-----------------------------------------------
// Assertions -> axi_assertioms.sv
// please add your assertion rules here
------------------------------------------------*/
always @(negedge AXI_ACLK)
begin
// write address must not be X or Z during address write phase
assertWriteAddrUnknown:assert property (
disable iff(!has_checks)
($onehot(AXI_AWVALID) && $onehot(AXI_AWREADY) |-> !$isunknown(AXI_AWADDR)))
else
$error({$psprintf("ERR_AXI_AWADDR %s went to X or Z during address write phase when AXI_AWVALID=1", name)});
// write data must not be X or Z during data write phase
assertWriteDataUnknown:assert property (
disable iff(!has_checks)
($onehot(AXI_WVALID) && $onehot(AXI_WREADY) |-> !$isunknown(AXI_WDATA)))
else
$error({$psprintf("ERR_AXI_WDATA %s went to X or Z during data write phase when AXI_WVALID=1", name)});
// write resp must not be X or Z during resp write phase
assertWriteRespUnKnown:assert property (
disable iff(!has_checks)
($onehot(AXI_BVALID) && $onehot(AXI_BREADY) |-> !$isunknown(AXI_BRESP)))
else
$error({$psprintf("ERR_AXI_BRESP %s went to X or Z during response write phase when AXI_BVALID=1", name)});
// read address must not be X or Z during address read phase
assertReadAddrUnKnown:assert property (
disable iff(!has_checks)
($onehot(AXI_ARVALID) && $onehot(AXI_ARREADY) |-> !$isunknown(AXI_ARADDR)))
else
$error({$psprintf("ERR_AXI_ARADDR %s went to X or Z during address read phase when AXI_ARVALID=1", name)});
// read data must not be X or Z during read data phase
assertReadDataUnKnown:assert property (
disable iff(!has_checks)
($onehot(AXI_RVALID) && $onehot(AXI_RREADY) |-> !$isunknown(AXI_RDATA)))
else
$error({$psprintf("ERR_AXI_AWDATA %s went to X or Z during data read phase when AXI_RVALID=1", name)});
// assert each pin has value not unknown
end
endinterface : AXI_vif
//----------------------------------------------------------------------
// Copyright 2012 Mentor Graphics Corporation
// All Rights Reserved Worldwide
//
// Licensed under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in
// compliance with the License. You may obtain a copy of
// the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in
// writing, software distributed under the License is
// distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
// CONDITIONS OF ANY KIND, either express or implied. See
// the License for the specific language governing
// permissions and limitations under the License.
//----------------------------------------------------------------------
//
// APB Protocol Monitor
//
interface apb_monitor #(int no_slaves = 16, int addr_width = 32, int data_width = 32)
(
input PCLK,
input PRESETn,
input[addr_width-1:0] PADDR,
input[data_width-1:0] PWDATA,
input[data_width-1:0] PRDATA,
input[no_slaves-1:0] PSEL,
input PWRITE,
input PENABLE,
input PREADY,
input PSLVERR);
// Check for unknown signal values:
// Reuseable property to check that a signal is in a safe state
property SIGNAL_VALID(signal);
@(posedge PCLK)
!$isunknown(signal);
endproperty: SIGNAL_VALID
RESET_VALID: assert property(SIGNAL_VALID(PRESETn));
PSEL_VALID: assert property(SIGNAL_VALID(PSEL));
// Reuseable property to check that if a PSEL is active, then
// the signal is valid
property CONTROL_SIGNAL_VALID(signal);
@(posedge PCLK)
$onehot(PSEL) |-> !$isunknown(signal);
endproperty: CONTROL_SIGNAL_VALID
PADDR_VALID: assert property(CONTROL_SIGNAL_VALID(PADDR));
PWRITE_VALID: assert property(CONTROL_SIGNAL_VALID(PWRITE));
PENABLE_VALID: assert property(CONTROL_SIGNAL_VALID(PENABLE));
// Check that write data is valid if a write
property PWDATA_SIGNAL_VALID;
@(posedge PCLK)
($onehot(PSEL) && PWRITE) |-> !$isunknown(PWDATA);
endproperty: PWDATA_SIGNAL_VALID
PWDATA_VALID: assert property(PWDATA_SIGNAL_VALID);
// Check that if PENABLE is active, then the signal is valid
property PENABLE_SIGNAL_VALID(signal);
@(posedge PCLK)
$rose(PENABLE) |-> !$isunknown(signal)[*1:$] ##1 $fell(PENABLE);
endproperty: PENABLE_SIGNAL_VALID
PREADY_VALID: assert property(PENABLE_SIGNAL_VALID(PREADY));
PSLVERR_VALID: assert property(PENABLE_SIGNAL_VALID(PSLVERR));
// Check that read data is valid if a read
property PRDATA_SIGNAL_VALID;
@(posedge PCLK)
($rose(PENABLE && !PWRITE && PREADY)) |-> !$isunknown(PRDATA)[*1:$] ##1 $fell(PENABLE);
endproperty: PRDATA_SIGNAL_VALID
//PRDATA_VALID: assert property(PRDATA_SIGNAL_VALID);
// Check that only one PSEL line is valid at a time:
property PSEL_ONEHOT0;
@(posedge PCLK)
$onehot0(PSEL);
endproperty: PSEL_ONEHOT0
PSEL_ONLY_ONE: assert property(PSEL_ONEHOT0);
// PENABLE goes low once PREADY is sampled
property PENABLE_DEASSERTED;
@(posedge PCLK)
$rose(PENABLE && PREADY) |=> !PENABLE;
endproperty: PENABLE_DEASSERTED
PENABLE_DEASSERT: assert property(PENABLE_DEASSERTED);
// From PSEL active to PENABLE active is 1 cycle
property PSEL_TO_PENABLE_ACTIVE;
@(posedge PCLK)
(!$stable(PSEL) && $onehot(PSEL)) |=> PENABLE;
endproperty: PSEL_TO_PENABLE_ACTIVE
PSEL_2_PENABLE: assert property(PSEL_TO_PENABLE_ACTIVE);
// FROM PSEL being active, then signal must be stable until end of cycle
property PSEL_ASSERT_SIGNAL_STABLE(signal);
@(posedge PCLK)
(!$stable(PSEL) && $onehot(PSEL)) |=> $stable(signal)[*1:$] ##1 $fell(PENABLE);
endproperty: PSEL_ASSERT_SIGNAL_STABLE
PSEL_STABLE: assert property(PSEL_ASSERT_SIGNAL_STABLE(PSEL));
PWRITE_STABLE: assert property(PSEL_ASSERT_SIGNAL_STABLE(PWRITE));
PADDR_STABLE: assert property(PSEL_ASSERT_SIGNAL_STABLE(PADDR));
PWDATA_STABLE: assert property(PSEL_ASSERT_SIGNAL_STABLE(PWDATA & PWRITE));
// Functional Coverage for the APB transfers:
//
// Have we seen all possible PSELS activated?
// Have we seen reads/writes to all slaves?
// Have we seen good and bad PSLVERR results from all slaves?
covergroup APB_accesses_cg();
option.per_instance = 1;
RW: coverpoint PWRITE {
bins read = {0};
bins write = {1};
}
ERR: coverpoint PSLVERR {
bins err = {1};
bins ok = {0};
}
APB_CVR: cross RW, ERR;
endgroup: APB_accesses_cg
// Array of these covergroups
APB_accesses_cg APB_protocol_cg[no_slaves];
// Creation the covergroups
initial begin
foreach(APB_protocol_cg[i]) begin
APB_protocol_cg[i] = new();
end
end
// Sampling of the covergroups
sequence END_OF_APB_TRANSFER;
@(posedge PCLK)
$rose(PENABLE & PREADY);
endsequence: END_OF_APB_TRANSFER
cover property(END_OF_APB_TRANSFER) begin
foreach(APB_protocol_cg[i]) begin
if(PSEL[i] == 1) begin
APB_protocol_cg[i].sample();
end
end
end
endinterface: apb_monitor
//uart tx
property busy_bit;
@(posedge PCLK)
tx_state == IDLE ##1 tx_state == START |->
((busy == 1) && (tx_state != IDLE))[*1:$] ##1 (tx_state == START);
endproperty: busy_bit
TX_BUSY_CHK: assert property(busy_bit);
//uart rx
property rx_parity;
@(posedge PCLK)
$rose(rx_state == STOP1) |=> parity_error == rx_buffer[9];
endproperty: rx_parity
RX_PE_CHK: assert property(rx_parity);
property rx_framing;
@(posedge PCLK)
$rose((rx_state == STOP1) && (enable == 1) && (bit_counter == 4'hf)) |=>
rx_buffer[8] == ~filtered_rxd;
endproperty: rx_framing
RX_FE_CHK: assert property(rx_framing);
property rx_break;
@(posedge PCLK)
$rose((rx_state == STOP2) && ~(|{filtered_rxd, rx_buffer})) |=> break_error;
endproperty: rx_break
RX_BE_CHK: assert property(rx_break);
property rx_overrun_prop;
@(posedge PCLK)
$rose((rx_state == STOP2) && (rx_fifo_count == 4'hf)) |=> (rx_overrun == 1);
endproperty: rx_overrun_prop
RX_OE_CHK: assert property(rx_overrun_prop);
//uart_tx_fifo
property fifo_full_prop;
@(posedge clk)
fifo_full |-> (count == 5'b10000);
endproperty: fifo_full_prop
property fifo_empty_prop;
@(posedge clk)
fifo_empty |-> (count == 0);
endproperty: fifo_empty_prop
property fifo_niether;
@(posedge clk)
(!fifo_full && !fifo_empty) |-> ((count > 0) && (count < 5'b10000));
endproperty: fifo_niether
TX_FIFO_FULL_CHK: assert property(fifo_full_prop);
TX_FIFO_EMPTY_CHK: assert property(fifo_empty_prop);
TX_FIFO_OK_CHK: assert property(fifo_niether);
/* File name : bus_arb_assertions.svh */
prop_a_cnt: assert property (
@(posedge clk) disable iff (rst)
a_vld |-> (a_cnt == $past(a_cnt) + 1));
property valid_state;
@(posedge clk) disable iff (rst)
$onehot0(current_state);
endproperty
prop_valid_state: assert property (valid_state);
// ... other assertions and cover props
// FIFO level cannot go down without a pop.
property FifoLevelCheck;
@(posedge clk) disable iff (rst)
(!rd_vld) |->
##1 (fifo_level >= $past(fifo_level));
endproperty
FifoLevelCheck_C: assume property (FifoLevelCheck);
// when there's a no_space_err, the no_space_ctr_incr signal is flagged
// for exactly once clock
property NoSpaceErrCtr;
@(posedge clk) disable iff (rst)
(no_space_err) |-> (no_space_ctr_incr ^ $past(no_space_ctr_incr));
endproperty
NoSpaceErrCtr_A: assert property (NoSpaceErrCtr);
// if there's an uncorrectable err during an ADD request,
// err_cnt should be incremented in the same cycle and an interrupt
// should be flagged in the next cycle
property AddUncorErrCheck;
@(posedge clk) disable iff (rst)
(uncor_err && (req_type == ADD)) |->
(err_cnt_incr ##1 intr);
endproperty
AddUncorErrCheck_A: assert property (AddUncorErrCheck);
// INIT and FLUSH transactions should complete within MAX_LATENCY.
property LatencyCheck;
@(posedge clk) disable iff (rst)
((req_type == INIT) ||
(req_type == FLUSH)) |->
(block_latency < MAX_LATENCY);
endproperty
LatencyCheck_A: assert property(LatencyCheck);
// interrupt should never get set
property InterruptCheck;
@(posedge clk) disable iff (rst)
(!intr);
endproperty
InterruptCheck_A: assert property (InterruptCheck);
// wr_data should be stable until wr_ack arrives
property WriteData;
@(posedge clk) disable iff (rst)
(wr && !wr_ack) |->
##1 (wr_data == $past(wr_data));
endproperty
WriteData_A: assert property (WriteData);
// wr_ack should be asserted only when there's a wr request
property WriteAck;
@(posedge clk) disable iff (rst)
(!wr) |-> (!wr_ack);
endproperty
WriteAck1_C: assume property (WriteAck1);
// if wr is asserted, it should remain high until wr_ack is received
property WriteAck2;
@(posedge clk) disable iff (rst)
(wr && (!wr_ack)) |-> ##1 wr;
endproperty
WriteAck2_A: assert property (WriteAck2);
// output is not x or z when valid is high
DoutCheck: assert property (@(posedge clk) valid |-> (!$isunknown(dout)));
// Check if ack arrives 3 to 5 clocks after a request
assert property (@(posedge clk) req |-> ##[3:5] ack);
// check if interrupt propagates when intr is enabled
generate
for (i=0; i < 16; i++) begin: INTR0
Intr0 : assert property (@(posedge clk) disable iff (rst)
((intr_enable[i] & intr_status[i] ) |-> ##1 intr))
else `uvm_error ("INTR_ERR", $sformatf ( "[%m] : Interrupt not propagating"))
end
endgenerate
// When vld rises high -
// .. a is repeated twice then
// .. after 2 clocks b is repeated 3 to 4 times with gaps in between,
// .. after last occurence of b, exactly 1 clock later c occurs
// .. one clock after c, d occurs 3 times non-consecutively,
// .. after last occurence of d, there are a variable number of empty
// .. clocks, then e happens
property Repeat1;
@(posedge clk) disable iff (rst)
$rose(vld) |-> (a[*2] ##2 b[->3:4] ##1 c ##1 d[=3] ##1 e);
endproperty
Repeat1_A: assert property (Repeat1);
// Going crazy with repetition operators
property Repeat2;
@(posedge clk) disable iff (rst)
$fell(reset) |-> ##[3:5] ((st1 ##6 st2) [*2]) ##2 (ready [*1:5]);
endproperty
Repeat2_A: assert property (Repeat2);