概述
- 测试平台(testbench)是整个验证系统的总称。
- 测试平台包括验证结构中的各个组件(层次)、组件之间的连接关系(通信)、测试平台的配置和控制。
- 从更系统的意义来讲,它还包括编译仿真的流程、结果分析报告和覆盖率检查等。
- 从狭义上讲,我们主要关注验证平台的结构和组件部分,他们可以产生设计所需要的各种输入,也会在此基础上进行设计功能的检查。
测试环境结构图
- 首先编译:RTL(DUT)、TB
- 各个组件之间是相互独立的、
- 验证组件与设计之间需要连接(接口连接)
- 验证组件之间也需要进行通信(信箱)
- 验证环境也需要时钟和复位信号的驱动
验证语言应用趋势
- 近年来SystemVerilog的使用比例明显占据主导地位。
- SystemC和C/C++在验证部分也有其应用空间。
- 借助于语言的统一趋势,方法学(UVM)的统一也已经完成。验证初学者赶上了一个好时代,一个再纷乱繁杂的统一时代。
验证环境组件
激励发生器
- Stimulator(激励发生器)是验证环境的重要部件,在一些场合中,它也被称为driver(驱动器)、BFM(bus function model,总线功能模型),behavioral(行为模型)或者generator(发生器)。
- Stimulator的主要职责是模拟与DUT相邻设计的接口协议,只需要关注于如何模拟接口信号,使其能够以真实的接口协议来发送激励给DUT。
- Stimulator不应该违反协议,但不拘束于真实的硬件行为,还可以给出更多丰富的只要协议允许的激励场景。
- 比真实硬件行为更丰富的激励,会使得在模块级的验证更加充分,因为它不但验证过了硬件普通的接口协议情景,还模拟出更多复杂的、在更高系统级别无法产生出来的场景。
- Stimulator的接口主要是向DUT之间连接,此外,也应该有时钟和复位的输入,确保生成的数据同DUT的接口一侧是同步的关较精细的stimulator还可以有其它的配置接口用来控制接口的数据生成。
- Stimulator也可以有存储接口数据生成历史的功能,这可以用来在仿真运行时或者结束后查看接口数据,方便统计或者调试。
- 从stimulator同DUT的连接关系来看,我们可以将其进一步分为两种:initiator(发起器)和responder(响应器)。
- 就我们要验证的MCDF来看,与下行通道从端(channel slave)的连接或寄存器接口的连接,这两部分的stimulator都属于initiator,它们的功能是主动发起接口数据传输。
- 与MCDF formatter接口的连接,该stimulator则属于responder,它的职责是对接口的数据发送请求做出响应,而它本身不会主动发送数据。
监测器
- Monitor(监测器)的主要功能是用来观察DUT的边界或者内部信号,并且经过打包整理传送给其它验证平台的组件,例如checker(比较器)。
- 从监测信号的层次来划分monitor的功能,它们可以分为观察DUT边界信号和观察DUT内部信号。
- 观察DUT边界信号。对于系统信号如时钟,可以监测其频率变化;对于总线信号,可以监测总线的传输类型和数据丙容,以及检查总线时序是否符合协议。
- 观察DUT内部信号。从灰盒验证的手段来看,往往需要探视DUT内部信号,用来指导stimulator的激励发送,或者完成覆盖率收集,又或者完成内部功能的检查。
- 如果没有特殊的需要,我们应采取灰盒验证的策略(而非白盒)。观察的内部信号应当尽量少,且应当是表示状态的信号。不建议采集中间变量信号的原因在于,这些信号的时序、逻辑甚至留存性都不稳定,这种不稳定对于验证环境的收敛是有害的。
- 可以通过接口信息计算的,就尽量少去监测内部信号,因为这种方式也有悖于假定设计有缺陷的验证思想。我们观测到的内部信号在被环境采纳之前也有必要确认它们的逻辑正确性,这一要求可以通过动态检查或者断言触发的方式来实现。
比较器
- checker肩负了模拟设计行为(reference model)和功能检查的任务将DUT输入接口侧的数据汇聚给内置的reference model(参考模型),reference model在这里扮演了模拟硬件功能的角色通过数据比较的方法,检查实际收集到的DUT输出端接口数据是否同reference model产生的期望数据一致
- 线上比较(online check):在仿真时收集数据和在线比较,并且实时报告
- 线下比较(offline check):将仿真时收集到的数据记录在文件中,在仿真结束后,通过脚本或者其他手段进行数据比较
- reference model也会内置一些缓存,分别存放从DUT输入端观察到的数据,以及经过功能转换的数据,同时Checker也有其它缓存来存放从输出端采集到的数据。
任务和函数
区别
- fuction不会消耗仿真时间,而task则可能会消耗仿真时间
- function无法调用task(因为task可能会消耗仿真时间),而task可以调用function
- 一个可以返回数据的function只能返回一个单一数值,而任务或者void function不会返回数值
- 一个可以返回数据的function可以作为一个表达式中的操作数(赋值),而该操作数的值即function的
函数function
- 必须提供返回值
- void函数不会返回具体数值,但是返回空
- 函数的参数列表方向也可以声明为input、output、inout和ref
- 对于函数返回的方式,也同C一致,有两种方法,既可以使用return直接返回值,也可以将值赋给与函数同名的变量
- 只是return会立即返回,而赋值给函数同名变量后将继续执行后续代码
- 如果调⽤具有返回值的函数,但是⼜不适⽤该返回值时,我们建议为其添加
- void’(some_fuction())进⾏强制转换,不要返回值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28function [15:0] myfuncl(input [7:0] x,y);
myfuncl = x*y-1;
endfunction
function [15:0] myfunc2(input [7:0]x,y);
return x*y-1;
endfunction
function [15:0] myfunc3(input [7:0]x
,input [7:0]y);
myfunc2 = x*y-1;
endfunction
function void myfunc4(input [7:0] x,
input [7:0] y,
output [15:0] z );
z = x*y-1;
endfunction
initial begin
byte unsigned a=3;
byte unsigned b=4;
byte unsigned c1,c2;
myfunc4(a,b,c1);
$display("c1=%0d",c1);
c2=myfunc3(a,b);
$display("c2=%0d",c2);
end
任务task
- 任务的定义可以指定参数,input、output、inout及ref 皆可(使用ref时实参形参类型必须高度一致)
- 任务可以消耗仿真时间
- 任务可以调用其他任务或者函数
- return能提前结束任务(类似于break用法,直接退出后面不执行)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23`timecale 1ns/1ps
task mytask3(input [7:0] x,
input [7:0] y,
output [15:0] z); //输出类型使用ref,可以实时传参,不需要task执行完毕
#5ns;
z = x*y-1;
return; //强制退出,后面的不执行
#5ns;
endtask
byte unsigned a = 3;
byte unsigned b = 4;
logic [15:0] c1, c2;
initial begin mytask3(a,b,c1);
$display("c1=%0d", c1);
end
initial begin
repeat(12) begin
#1ns;
$display("etime stc1=80d", $time, cl);
end
参数默认值
- SV允许方法声明参数的默认值(默认input),参数的方向可以时input、inout、output和ref
- 带有参数默认值的方法被调用时,如果这些参数没有被传递值,那么编译器将会为这些参数传入对应的默认值
- SV允许类似于模块例化一样,可由参数位置顺序在调用方法时传递参数,也可以由参数名字调用方式时绑定参数
- 参数未声明类型默认1-bit的logic类型
1
2
3
4
5
6
7
8
9
10
11task read(int j=0,int k,int data=1); // j默认值为0 data默认值为1
...
endtask
read(n,5); // is equivalent to read(0,5,1);
read(2,5); // is ecquivalent to read(2,5,1);
read(,5,); // is equivalent to read(0,5,1);
read(,5,7); // is ecquivalent to read(0,5,7);
read(1,5,2); // is equivalent to read(1,5,2);
read(); // error;k has no default value
read(1,,7); // error;k has no default value
fuction和task的参数方向
- input采样只在函数或者任务的入口,其他时候不影响内部或者外部的数值。
- output驱动只在函数或者任务的出口,其他时候不影响内部或者外部的数值。
- ref为引用本质上为一个值,可以实时传递,既检测也修改。
- const ref可以时刻检测外部数据,但是不对其进行修改。
静态函数和动态函数
- 如果变量被声明为automatic,那么进入该方法后,就会自动创建,离开该方法后,就会被销毁;
- 而static则是在仿真开始时就会被创建,直到仿真结束,可以被多个方法/进程共享。
通过几个例子看其区别:
1 | function automatic int auto_cnt(input a); //定义为automatic后,cnt默认为automatic |
思考问题
- 激励器(stimulator)对于它所生成的激励,有什么办法将其所有激励数据保存下来?
- 对于实验中的DUT(router)设计,验证环境组件checker比较器数据正确的逻辑是什么?如何利用从monitor in(监测输入接口)和monitor out(监测输出接口)采取到的数据呢?
- 为什么大多数的功能都应该在模块级验证完成呢?
- function和task的比较?
- 参数方向有哪些?差别在哪里?