SystemVerilog 第4章 连接设计和测试平台
4.1 将测试平台和设计分开
- SV引入程序块(program block),从逻辑和时间上将TB和DUT分开
4.1.1 测试平台和DUT通信
- 验证分为:生成激励→获取输出响应→决定对错→衡量进度
4.1.2 端口通信
- 使用端口进行通信的arbiter
//RTL
module arb_port(
output logic [1:0] grant,
input logic [1:0] request,
input logic rst,
input logic clk
);
...
always @(posedge clk or posedge rst) begin
...
end
endmodule
//TB
module test(
input logic [1:0] grant,
output logic [1:0] request,
output logic clk,
output logic rst
);
initial begin
@(posedge clk) request <= 2'b10;
...
end
endmodule
//TOP
module top;
logic [1:0] grant, request;
bit clk, rst;
always #5 clk = ~clk;
arb_port a1(grant,request,rst,clk);
test t1(grant,request,rst,clk);
endmodule
4.2 接口
- 接口像模块一样例化,可以用于模块间以及RTL与TB间的信号连接
4.2.1 使用接口简化连接
- 在RTL, TB, TOP模块外要提前声明interface
使用接口进行通信的arbiter
//interface
interface arb_if(
input bit clk
);
logic [1:0] grant, request;
logic rst;
endinterface: arbif
//RTL
module arb(
arb_if arbif
);
...
always @(posedge arbif.clk or posedge arbif.rst) begin
...
end
endmodule: arbiter
//TB
module test(
arb_if arbif
);
...
initial begin
@(posedge arbif.clk) arbif.request <= 2'b01;
...
end
endmodule: test
//TOP
module top;
bit clk;
always #5 clk = ~clk;
arb_if arbif(clk);
arb a1(arbif);
test t1(arbif);
endmodule: top
4.2.2 连接接口和端口
- 可以使用 .port(ifc.signal) 的形式将 端口 连接到 接口 上
arb_port a1(
.grant(arbif.grant),
.request(arbif.request),
.rst(arbif.rst),
.clk(arbif.clk)
);
4.2.3 modport将信号分组
- modport可以将信号分组指明其方向
//interface
interface arb_if(
input bit clk
);
logic [1:0] grant, request;
logic rst;
modport DUT(
input request,clk,rst,
output grant
);
modport TEST(
output request,rst,
input grant,clk
);
modport MONITOR(
input request,grant,clk,rst
);
endinterface: arbif
//RTL
module arb(
arb_if.DUT arbif
);
...
endmodule: arbiter
//TB
module test(
arb_if.TEST arbif
);
...
endmodule: test
//TOP没有变动
module top;
bit clk;
always #5 clk = ~clk;
arb_if arbif(clk);
arb a1(arbif);
test t1(arbif);
endmodule: top
- 使用modport名的方式有两种
①在TB和RTL的portlist中声明,TOP不声明(更具物理意义)
②在TOP中声明,在TB和RTL中申明(适用于多次例化同一模块)
4.2.4 总线设计modport
- 由于不是每个信号都必须连接,所以总线接口中要为主/从设备、仲裁器和监视器定义不同的modport
4.2.5 监视器模块
module monitor(arb_if.MONITOR arbif);
...
endmodule
4.2.6 接口优缺点
- 优点
①便于设计重用
②避免连错
③便于增减信号
④modport可用于信号方向检测 - 缺点
①点对点连接冗长
②同时使用 接口名.信号名
③专用接口复用率低,工作量大
④不同接口之间连接困难
4.3 激励时序
4.3.1 时钟块控制同步信号时序
- 接口块中使用时钟块来指定需要同步的信号相对于时钟的时序,时钟中的任何信号都将同步地驱动和采样
- 可以使用default语句指定一个时钟偏移,但默认输入信号尽在设计执行前被采样,且设计的输出信号在当前时间片又被驱动回当前设计
- clocking块默认在clk到来前#1step延时前采样输入信号(前一个time slot的postponed区域),#0后驱动输出信号
clocking cb @(posedge clk);
default input #1step output #0step;
input ...;
output ...;
endclocking
- 一个接口可以包含多个时钟块
- 定义时钟块之后,测试平台可以使用@arbif.cb表达式等待时钟,不用描述确切地时钟信号名和边沿
- 时钟块地声明和调用
//interface
interface arb_if(input bit clk);
logic [1:0] request, grant;
bit rst;
clocking cb @(posedge clk);
output request;
input grant;
endclocking
modport TEST(
clocking cb,
output rst
);
modport DUT(
input request,clk,rst,
output grant
);
endinterface
//测试平台
module test(
arb_if.TEST arbif
);
initial begin
arbif.cb.request <= 0;
@arbif.cb;
$display("@%0t: Grant = %b",$time,arbif.cb.grant);
end
endmodule
4.3.2 接口中的logic和wire
- logic可以用于接口中的过程块(过程赋值语句),但不能用于多驱动源(会报错)
- wire只能用于连续赋值语句,但interface中的wire型clockvar可以直接在过程块中赋值
4.3.3 verilog时序问题
- 测试平台应该在逻辑和时序方面都独立于设计
- 测试平台需要模仿测试仪的行为:当在有效时钟边沿或边沿之后驱动待测设计,然后再有效时钟边沿到来之前,尽可能晚地采样(满足协议前提下)
- 如果测试平台和DUT仅由verilog模块构成,无法实现合理地时序实现
4.3.4 测试平台/设计之间的竞争
- 如果仿真放在module中,那么DUT和TB执行是同时进行的,有可能发生clk拉高激活DUT但对应的共有信号data在clk拉高同时还未update,产生竞争
- 解决办法即将仿真放在program block中
- 什么是竞争和冒险?如何避免?
竞争:在组合电路中,信号经由不同的途径达到某一会合点的时间有先有后,这种现象称为竞争.
冒险:由于竞争而引起电路输出发生瞬间错误现象称为冒险.表现为输出端出现了原设计中没有的窄脉冲,常称其为毛刺.
竞争与冒险的关系
消除方法:增加冗余项;输出端并联电容;引入选通信号
4.3.5 程序块(program block)和时序区域(timing region)
- SV中将测试代码放在程序块中实现测试和设计的事件(event)分开调度
- program的语法和module十分类似(可以在module中例化),但program中不能有任何的层次级别(module实例,interface以及其他program)
- 采用program的testbench
program test(
arb_if.TEST arbif
);
initial begin
arbif.cb.request <= 0;
repeat(2) @arbif.cb;
$display("@%0t: Grant = %b",$time,arbif.cb.grant);
end
endprogram: test
- program使用注意事项
①测试代码应当包含在一个单个的program中
②不能有层次级别,但可以使用OOP来创建动态、分层的testbench
③可以采用多个程序块将多个测试代码结合在一起 - program的时间片划分(timing region)
– active: RTL, 门级代码, clock generator
– observed: assertion
– reactive: testbench
_ postponed: 所有活动结束后的只读时间段采样信号
4.3.6 仿真结束
- verilog:只有遇到$finish才结束仿真
- system verilog:
①$finish同样适用
②每个program被看作一个测试,当所有program中所有initial都结束时即结束仿真
③$exit提前中断program
4.3.7 指定DUT和TB之间的延时
- clocking模块会同步输出信号(即软件延迟为0),将输出信号直接送到DUT中,在reactive区域运行的program块在同一个time slot内再触发一次active区域
4.4 接口的驱动和采样
4.4.1 接口同步
- 测试平台才能使用带有clocking block的interface同步信号的驱动和采样
- clocking block中的信号将得到同步,clocking block之外的异步信号在通过interface时没有任何延迟
- 使用clocking block中的信号时候要加上前缀 arbif.cb.request/.grant
- 使用@和wait同步信号
@等待电平信号发生跳变;wait等待电平信号为真
4.4.2 信号采样
- clocking block中input采样是在clk到来之前进行(postponed区域)
- 如果DUT在clk到来时信号发生变化,采样任然保持clk前值,不会立即更新(因为DUT是硬件,信号改变有延时,需要setup time)
4.4.3 信号驱动
- 如果program在clk到来时驱动信号,那么信号会立即驱动到DUT(因为program是软件,没有延时,且clocking block的驱动output skew默认为#0step)
4.4.4 时钟块信号驱动&延时
- 时钟块驱动应采用“同步驱动(synchronous drive)”,即“<=”来驱动信号
- 时钟块接口信号驱动时的延时
① ##2 arbif.cb.request <= 0;
② repeat(2) @arbif.cb; arbif.cb.request <= 0;
4.4.5 接口中的双向信号
- 在接口中声明的inout信号驱动方式有二
①连续赋值语句
②虚接口(传入program的interface默认即为虚接口) - 在接口中的clocking block中列出的inout、input或ouput信号,在SV中可以直接采用过程赋值语句驱动,因为clocking block中的信号是clockvar,SV通过将clockvar关联到接口信号的方式间接进行驱动
4.4.6 program中不能使用always
- program是软件块,语句顺序一次执行,不符合always的多次执行
- program在initial执行完即仿真结束(相当于$finish),program中的always需要额外添加$exit才能强制结束
- program中可以使用initial forever来实现always的功能
4.4.7 时钟发生器
- 时钟产生应该放在module中,使其拥有一定物理延迟(在program中产生的话,clk会和驱动信号同时到达DUT,在DUT中会产生竞争)
- 阻塞赋值“=”避免了0时刻产生有效clk沿;非阻塞赋值“<=”设初值后可以在0时刻产生有效clk沿
4.5 模块连接
- 使用top模块例化interface,DUT,TB从而实现模块连接
- 隐式端口连接(.*)的使用条件
①端口名字相同
②端口位宽相同
③数据类型兼容
4.6 顶层作用域
- verilog中只有宏定义可以跨越模块边界创建全局变量
- SV中引入了编译单元(compilation unit),是和源文件一起编译的一个组合
- 顶层作用域:即块外的作用域,等同于编译单元作用域(也称$unit)。顶层作用域的成员是全局成员
- SV可以采用$root或$unit来引用块外成员
- 顶层常数定义方法有二
①定义顶层参数: parameter TIMEOUT = 1000;
②const声明: const string time_out_msg = “error: time out”
4.7 程序模块交互
- program不能例化module,但可以读写访问module中所有信号;module可以例化program,但不能访问program中的信号
- program中不能有任何层次实例,但是可以调用module中的例程执行不同的动作
4.8 SV断言
绿皮书上这一块写的太简略了,这里转发两位前辈的博客
SV Assertions 断言
SVA 断言语法
4.9 四端口ATM路由器
ATM(asynchronous) router
4.10 ref端口方向
- ref参数是对变量的引用(net除外)
4.11 仿真结束
- initial块结束时,隐性调用$exit标志program结束
- 当所有program块结束时,隐形调用$finish结束仿真
- module或program中可以定义finial块指定仿真器退出前需要执行的代码,但不能含事件调度以及任何延时
final command;
4.12 LC3 取指模块定向测试
- little computer 3(LC3)