UVM中SVA使用指南
文章目录
前言
UVM能够搭建一个强大的功能验证环境,而SVA是一种描述性语言,可以描述时序状态。两个相结合,取长补短,能够更大程度完善验证。
一、SVA是什么,什么时候使用SVA
SVA全称是SystemVerilog Assertions,即用SV描述的断言。断言是设计的属性的描述,可以实现两个功能。
- 如果一个在模拟中被检查的属性不像期望发生,则断言失败。
- 如果一个被禁止在设计中出现的属性在模拟过程中发生,则断言失败。
因此,对于严格约束的时序,SVA可以在仿真过程中全程监视。可以验证总线时序,通信时序或者其他特定的时序逻辑。
二、SVA块
SVA使用“assert”关键词来表示一个断言属性。
assertion_name : assert property (property_name);
assertion_name : assert a && b;
当写下这一句,一个断言就已经实现了,而断言的属性,在SV中使用"property"关键字来表示。也可以直接用布尔表达式,语法在第四章中说明。
property property_name;
<text expression>;
endproperty
在property模块中写入时序语法,再使用assert语法进行断言,或者直接写一个布尔表达式,用assert进行断言,一个基本的SVA块就已经完成了。
事实上,在sva中除了property块,还有sequence块来进行更复杂的时序描述,但是在使用中,不带时序的sequence也可以通过assign等更简略的语法来表示,带简单时序逻辑的也可以只使用property来描述。而且在sequence中无法使用蕴含操作符,所以在应用中,只使用peoperty块来进行断言是常用用法。
三、SVA块嵌入UVM平台
SVA块可以直接在设计module中嵌入,但是UVM中的SVA模块,一般是作为单独的模块独立存在,便于后续更新迭代。
module sva_case(
input logic clk,
input logic in,
input logic out);
//在每一个时钟上升沿,信号in都应该是高电平,且两个时钟后,out也是高电平
//否则断言失败
property property_case;
@(posedge clk) in ##2 out;
endproperty
assertion_case : assert property (property_case);
endmodule
这样一个简单的sva模块就完成了,在property可以直接使用模块输入的信号来进行属性描述。这个模块可以作为一个独立的文件,或者嵌入其他的文件中。
这里提两种sva模块与设计模块连接的方法。
3.1 绑定方法
假如我们现在有一个sva模块:sva_case 和一个设计模块 design_case
实际的绑定语法如下:
bind <module name or instance name>
<checker name> <checker instance name>
<design signals>
使用如下:
module top();
design_case x_design_case(clk,in_1,in_2,out_1,out_2);
endmodule
bind top.x_design_case sva_case b1(clk,in_1,out_1);
3.2 例化方法
事实上还有第三种更简单的方法,即将sva模块作为独立的模块例化,将相应信号进行连接即可。比如我们有一个top模块:
module top();
virtual tb_interface vif;
design_case x_design_case(
.clk (vif.clk),
.in (vif.in ),
.out (vif.out)
);
sva_case x_sva_case(
.clk (vif.clk),
.in (vif.in ),
.out (vif.out)
);
endmodule
这样也做到了将sva模块与设计模块连接,(其中省略了vif的连接过程)。
连接后,在仿真时,只需要将sva模块加入makefile中进行统一编译即可。
四、SVA语法浅讲
立即断言 | 并发断言 | |
---|---|---|
编写地方 | 过程块 | 过程块,program,interface,module等 |
断言对象 | 布尔表达式 | property |
触发条件 | 过程块中的指定条件 | 与时钟对齐 |
4.1 立即断言
立即断言需要指定在某一特定的时刻进行判断,即立即断言必须放在过程块中,即在特定的程序语句下才能使用。在立即断言中,不能消耗时间。
立即断言的对象是布尔表达式本身,故立即断言只使用assert进行断言判断,并不需要property过程语句。下面有一个简单的立即断言应用例子。
always @(posedge clk) begin //过程块
if(a) // 断言触发条件
assert (a && b); //不带property的断言
end
4.2 并发断言
并发断言是连续运行的模块,与立即断言相反,并发断言必须带时钟,即每一个判断都与时钟对齐。并发断言必须带property。并发断言的语法本质上是property的语法,而在property中可以带sequence。
sequence | property | |
---|---|---|
层次结构 | 低层次:只能例化sequence | 高层次:可以例化sequence和property |
例化 | 例化时直接调用 | 例化需要用assume,cover,assert关键字 |
使用限制 | 不能使用蕴含操作符 | 可以使用蕴含操作符 |
4.2.1 参数
在sequence和property中都可以带参数,可函数的使用是一样的,可以增加断言的可复用性。参数可以定义数据类型,也可以不定义。(下面代码复制于 SV之Assertions 断言)
logic clk = 0;
logic req,gnt;
logic a,b;
//=================================================
// Sequence Layer with args (NO TYPE)
//=================================================
sequence notype_seq (a, b);
(~X & Y) ##1 (~X & ~Y);
endsequence
//=================================================
// Sequence Layer with args (NO TYPE)
//=================================================
sequence withtype_seq (logic X, logic Y);
(~X & Y) ##1 (~X & ~Y);
endsequence
//=================================================
// Property Specification Layer
//=================================================
property req_gnt_notype_prop(M,N);
@ (posedge clk)
req |=> notype_seq(M,N);
endproperty
property a_b_type_prop(logic M, logic N;
@ (posedge clk)
a |=> withtype_seq(M,N);
endproperty
//=================================================
// Assertion Directive Layer
//=================================================
req_gnt_notype_assert : assert property (req_gnt_notype_prop(req,gnt));
a_b_type_assert : assert property (a_b_type_prop(a,b));
4.2.2 SVA的关键字
1.蕴含操作符
蕴含操作符只能在property中使用
关键字 | 描述 |
---|---|
a |-> b | a成立后当前时钟b成立,断言成功(a不成立时b成立,为空成功,不判断失败) |
a |=> b | a成立后下一时钟b成立,断言成功(a成立后下一时钟b不成立,为断言失败) |
//在每一个时钟上升沿判断a是否成立,若a成立,延时两个时钟判断b是否成立
//b成立则断言成功
property pro_a(a,b);
@(posedge clk) a |-> ##2 b;
endsequence
//在每一个时钟上升沿判断a是否成立,若a成立,延时三个时钟判断b是否成立
//b成立则断言成功
property pro_b(a,b);
@(posedge clk) a |=> ##2 b;
endsequence
2.时钟延时关键字
关键字 | 描述 |
---|---|
a ##n b | 当每一个时钟上升沿a成立,且n个时钟周期后b成立,断言成功 |
a ##[n:m] b | 当每一个时钟上升沿a成立,且在后续n-m周期中任何一个周期b成立,断言成功 |
a ##[*] b | 当每一个时钟上升沿a成立,且在当前周期到仿真结束的任何一个周期b成立,断言成功(等价于##[0:$]) |
a ##[+] b | 当每一个时钟上升沿a成立,且在下一周期到仿真结束的任何一个周期b成立,断言成功(等价于##[1:$]) |
//当每一个时钟上升沿a成立,且两个时钟周期后b成立,断言成功
property delay_cycle1(a,b);
@ (posedge clk) a ##2 b;
endproperty
//当每一个时钟上升沿a成立,且在后续2-3周期中任何一个周期b成立,断言成功
property delay_cycle2(a,b);
@ (posedge clk) a ##[2:3] b;
endproperty
//当每一个时钟上升沿a成立,且在当前周期到仿真结束的任何一个周期b成立,断言成功
property delay_cycle3(a,b);
@ (posedge clk) a ##[*] b;
endproperty
//当每一个时钟上升沿a成立,且在下一个周期到仿真结束的任何一个周期b成立,断言成功
property delay_cycle4(a,b);
@ (posedge clk) a ##[+] b;
endproperty
3.时钟连续关键字
关键字 | 描述 |
---|---|
a[*n] | 连续n个时钟a均成立,则断言成功 |
a[->n]b | a成立n次后(不要求连续),在下一个时钟b成立则断言成功 |
a[=n]b | a成立n次后(不要求连续),b成立(后续任意一时钟) |
//连续3个时钟a均成立,则断言成功
property con_cycle1(a,b);
@ (posedge clk) a[*3];
endproperty
//a成立3次后(不要求连续),在下一个时钟b成立则断言成功
property con_cycle2(a,b);
@ (posedge clk) a[->3]b;
endproperty
//a成立3次后(不要求连续),b成立(后续任意一时钟)
property con_cycle3(a,b);
@ (posedge clk) a[=3]b;
endproperty
4.行为关系关键字
关键字 | 描述 |
---|---|
intersect(a,b) | a和b同时成立,且同时结束,则断言成功 |
a and b | a发生时b也发生,则断言成功 |
a or b | a发生或者b发生,断言成功 |
a within b | a发生的时间段内b也发生,则断言成功 |
c throughout(a ##b) | 断定a事件成立到b事件成立的过程中,c事件“一直“成立,则断言成功 |
not a | 当a成立,则断言失败 |
sequence seq1(a,b);
a |-> ##1 b;
endsequence
sequence seq2(a,c);
a |-> ##2 c;
endsequence
sequence seq3(c,d);
c |=> d;
endsequence
property pro_a;
@(posedge clk) seq1 and seq3;
endsequence
property pro_b;
@(posedge clk) seq1 or seq2;
endsequence
property pro_c;
@(posedge clk) intersect(seq1,seq3);
endsequence
property pro_d;
@(posedge clk) seq1 within seq3;
endsequence
property pro_e;
@(posedge clk) not seq1;
endsequence
5.序列采样函数
关键字 | 描述 |
---|---|
$rose | 判断上升沿 |
$fell | 判断下降沿 |
$past | 返回当前时钟周期前一个周期的采样值 |
$stable | 判断信号是否稳定 |
$sampled | 时钟事件最后时刻的采样值 |
6.disable iff的用法
disable iff(expression)
当expression成立的时候,不对该属性进行检查。
//当a=1的时候dis不做检查
property dis(a,b)
disable iff(a==1)
not (@clk) b
endproperty
五、断言中的打印
- $display
- $info
- $warning
- $error
- $fatal
上述打印宏的严重程度由低到高,一般打印写在assert语句内,若不标明,则默认为fatal
aaa: assert property_a
$display("aaaaa");
else
$error("bbbbb");