SVA学习之路(1)--- SystemVerilog Assertion简介与实战
一、前言
在数字电路设计验证中,传统的验证方法往往需要编写大量的测试向量和复杂的测试环境。然而,随着设计复杂度的增加,这些方法变得越来越低效。SystemVerilog Assertion(SVA)作为一种声明式验证方法,正成为硬件验证工程师的必备技能。
SVA允许你在设计中直接嵌入属性描述,实时监控设计行为,及早发现错误,大大提高验证效率和设计质量。
SVA是SystemVerilog语言的一部分,专门用于描述设计行为的时序属性。它不改变设计功能,而是作为"监控器"嵌入到设计中,检查设计是否按照预期行为运行。
二、立即断言(Immediate Assertions)
立即断言在当前仿真时刻立即检查条件是否成立,不依赖时钟,类似于if语句中的条件检查,语法简单,适合检查组合逻辑或单周期信号。
编写代码文件tb.sv如下所示
systemverilog
module tb;
reg a,b;
initial begin
a = 0; b = 0;
#10ns; a = 1;
assert(a&b);
#10ns; b = 1;
assert(a&b);
#10ns; a = 0;
assert(a&b);
end
initial begin
$timeformat(-9, 2, "ns", 10);
$fsdbDumpfile("test.fsdb");
$fsdbDumpvars(0, tb);
$fsdbDumpSVA(0, tb);
#100ns;
$finish(2);
end
endmodule
编写运行makefile文件如下所示:
bash
all: comp sim
comp:
vcs -full64 -kdb -sverilog -debug_access+all -timescale=1ns/1ps tb.sv
sim:
./simv
verdi:
verdi tb.sv -ssf test.fsdb &
运行日志如下,可以看到打印了两次失败

使用verdi查看如下所示,在波形上可以看到两次失败的示意箭头

三、并发断言(Concurrent Assertions)
并发断言是基于时钟边沿检测时序逻辑的行为。它在仿真过程中持续检测监控信号的时序关系。
区别即时断言和并发断言的关键词是"property",编写代码文件tb.sv如下所示
systemverilog
module tb;
reg a,b;
reg clk;
assert property(
@(posedge clk) a&b==1'b1
);
initial begin
clk <= 1'b0;
forever #2ns clk <= ~clk;
end
initial begin
a = 0; b = 0;
#10ns; a = 1;
#10ns; b = 1;
#10ns; a = 0;
end
initial begin
$timeformat(-9, 2, "ns", 10);
$fsdbDumpfile("test.fsdb");
$fsdbDumpvars(0, tb);
$fsdbDumpSVA(0, tb);
#100ns;
$finish(2);
end
endmodule
运行日志如下,可以看到打印了多次失败

使用verdi查看如下所示,在波形上可以看到多次失败的示意箭头

如果想要查看成功的示意箭头,添加运行时选项如下所示:
powershell
./simv +fsdb+sva_success
重新查看波形如下所示,可以看到成功的示意箭头

四、时钟检测
编写如下代码检测时钟信号clk,频率为10MHz,占空比为50%。
verilog
module tb;
reg clk;
reg rst_n;
// Clock genrate
initial begin
clk = 1'b0;
forever begin
#50 clk = 1'b1;
#50 clk = 1'b0;
end
end
// Reset genrate
initial begin
rst_n <= 1'b0;
repeat(10) @(posedge clk);
rst_n <= 1'b1;
end
// ==================== SVA ====================
// Check 10MHz, period for 100ns
property clk_period_check;
realtime rise_time, current_period;
@(posedge clk)
disable iff (!rst_n)
(1, rise_time = $realtime)
##1 (clk === 1'b0, current_period = $realtime - rise_time)
##0 (current_period) inside {[99900ps:100100ps]};
endproperty
// Check 50% duty
property clk_duty_cycle_check;
realtime rise_time, duty_time;
@(posedge clk)
disable iff (!rst_n)
(1, rise_time = $realtime) |->
@(negedge clk) (clk === 1'b1, duty_time = $realtime - rise_time)
##0 (duty_time) inside {[49900ps:50100ps]};
endproperty
check_clk_period : assert property (clk_period_check)
else $error("Clock period error! Expected 100ns");
check_clk_duty_cycle : assert property (clk_duty_cycle_check)
else $error("High level duty cycle error! Expected 50ns");
initial begin
$timeformat(-9, 2, "ns", 10);
$fsdbDumpfile("test.fsdb");
$fsdbDumpvars(0, tb);
$fsdbDumpSVA(0, tb);
#100us;
$finish(2);
end
endmodule