一般来说,在数字IC验证中,编写testbench文件会采用verilog,但随着设计越来越复杂,为了更方便例化模块,面向对象编程的SystemVerilog(以下简称SV)越来越流行。
下文部分图片和观点摘自《芯片验证漫游指南》刘斌著
1.SV基本语法见链接:https://blog.csdn.net/qq_39815222/article/details/106538103
2.面向对象编程有关面向对象编程的基础概念参考 C/C++——基础语法学习 篇第4大节。
链接:https://blog.csdn.net/qq_39815222/article/details/80529000
为提高调用效率,免于模块重用,面对对象编程可创建复杂的数据类型并把他们封装在一起使其共同工作。
与C++中不同的是,在SV中,若对象没有指针指向它,它会自动释放掉其物理空间
在新旧项目版本交替过程中,利用继承可在就testbench文件基础上添加新功能。
类内部声明和定义必须是task或function,不得使用过程块always或initial
newnew用来给创建的对象分配空间
new函数不会显示地返回数值,因为系统会固定例化的对象句柄本身;此外new函数是function,不能包含延时语句,即立即执行返回。
在继承时,子类在定义new函数时,首先调用父类的new函数即super.new()。如果父类的new函数没有参数,子类也可以省略该调用,而系统会在编译是自动添加super.new()。
this当使用一个变量名的时候,systemverilog将先在当前作用域内寻找,接着在上一级作用域内寻找,直到找到该变量为止。
virtual interface在类定义中不能声明interface,而应使用virtual interface。
constraint通常constraint约束块会和rand一同声明在类中
示例class class1; //声明
bit [31:0] A; //properties
function void f1(); //methods
endfunction: f1
endclass: example
class class2 extends class1; //继承
function void f1();
super.f1(); //调用父类的成员并改写
endfunction
endclass
class1 example1; //调用
example1 = new(); //构造函数:分配空间,初始化类里面的变量,返回该空间的地址给example1
example2 = example1; //把example1的地址空间给example2
example1.A = 32'h48; //赋值
example1.f1();
使用一个关键词表明一个意图,通过 property 表现出来,通常验证时会根据spec文档,验证信号间的时序情况,若不符合spec,则需要debug
gtdlsd的用法参考链接:https://blog.csdn.net/moon9999/article/details/106035582
示例//模块化编码
property getSignal;
@(posedge clk) not( countValue < 9 );
endproperty: getSignal
agetSignal: assert property( getSignal ) statement1; //若满足assertion执行statement1,否则执行statement2
else statement2;
//非模块化编码
P1: assert property( @(posedge clk) $rose sys_set |-> ##[0:6] $rose rec_readyH); //判断在sys_set上升沿开始后,持续6个时钟上升沿,后拉高rec_readyH
检测验证目标完成的百分比,
covergroup可以包含多个覆盖采样点,EDA会产生gtdls来保存采样数据,并通过分析工具来统计
根据spec人为描述功能,检查哪些功能覆盖到,需要做的主要是定义覆盖集(采样点)和采样时间
提高功能覆盖率的方法增加发包数以及尝试新的随机种子。code coverage
包括语句,翻转,条件,状态机,路径覆盖率(path),表达式覆盖率等
VCS命令可参照:https://blog.csdn.net/qq_39815222/article/details/106826194assertion coverage
检测信号间承接的情况示例
class1 tr;
covergroup conPort; //声明功能覆盖率
coverpioint tr.port; //定义采样点,计算覆盖率
endgroup
conPort ck = new; //实例化
initial begin
repeat(100) begin
tr.randmize;
ck.sample(); //采样,收集覆盖率
@(clk);
end
end
当事件发生时,才会采样断言触发
5. SV的仿真调度机制
充分理解SV的不同结构体在仿真中执行的先后顺序,有利于理解testbench中对DUT的驱动和采样的顺序,进而避免不合理的驱动和采样方式。
时间片(time-slot)是仿真时间中的一个抽象单位Ts,该单位内所有的线程(always,initial,assertion等)和数据对象的赋值被赋予相应的优先级,依次被执行。这种优先级即如下图所示的调度区域。observerd和reactive专门用于SV验证,其余区域均继承于verilog调度区域。
从preponed region进入active region后,将执行所有处于该阶段的线程。其中与非阻塞赋值有关的操纵执行完成后,对应的线程进入NBA region,而带有“#0”延时操作的线程直接进入inactive区。inactive
所有被零延时操作的线程在inactive中被激活,在被执行之前迁往active区,所以#0会延缓线程执行的时间NBA
该区域是在所有active和inactive均没有其他线程后所到达的调度区,到达该区域后,之前在active的非阻塞赋值生效,如果这些非阻塞触发了别的线程,那么被触发的线程要被迁移至activeObserve
但之前active/inactive/NBA区域全部执行完毕后,即表示设计部分的线程执行完毕,则进入为assertion准备的observed区。由于断言属性中需要监测设计中的变量,且必须等到所有数据对象被赋予最终的数值,所以该区域处在设计区域之后。
这样做的好处是,避免因采集的变量不稳定导致属性检查错误。该区域同样适用interface和program中的采样操作,使得采样的数据就是该Ts内的最终值。reactive
数据采样后,assertion须进行属性判断,同时如果该区域对设计区域中的线网和变量赋值,则使被激活的线程再次迁移到active区;经过信号采样后,处于testbench中的线程也在该区域执行。postpone
在分别经历了与设计、testbench有关的区域后,当前Ts进入与postpone。该区域内的值保持稳定,且与下一个Tsprepone的值一致。同时该区域也作为SV PLI/DPI的回调函数点,使得在SV外部的调用语言(如C)在使用SV变量使仍然可以用到最新的数值。