验证一个设计需要经过几个步骤:生成输入激励,捕获输出响应,决定对错和衡量进度。测试平台包裹着设计,发送激励并且捕获设计的输出,核心概念是除了待测设计DUT的行为之外,测试平台仿真了其他的所有行为。测试平台需要一种更高层次的方法来跟设计建立通信,同时需要一种可靠的描述时序的方法,可以在正确的时间点驱动和采样同步信号,避免Verilog模型中常见的竞争状态。
一、将测试平台和设计分开
测试平台的代码独立与设计的代码,在传统的Verilog中,使用模块来保存测试平台经常会引起驱动和采样时的时序问题,所以SystemVerilog引入了程序块(program block),从逻辑上和时间上来分开测试平台。SystemVerilog实际上是使用接口来解决上述两个问题,接口是一种代表一捆连线的结构,它是具有智能同步和连接功能的代码。一个接口可以像模块那样例化,也可以像信号一样连接到端口。
1、测试平台和DUT之间的通信
这是一个没有使用接口的仲裁器的简单测试平台
2、与端口的通信
//使用端口的仲裁器模型
module arb_port (output logic[1:0] grant,
input logic[1:0] request,
input logic rst,
input logic clk);
...
always @(postedge clk or posedge rst) begin
if(rst)
grant<=2'b00;
else
...
end
endmodule
测试平台通过端口与设计连接
module test (output logic[1:0] grant,
input logic[1:0] request,
input logic rst,
input logic clk);
initial begin
@ (postedge clk) request <= 2'b01;
$display(“@%0t:Drove req=01”, $time);
repeat (2) @(postedge clk);
if (grant != 2'01)
$display(“@%0t:al: grant != 2'b01”, $time);
...
$finish;
endmodule
顶层网单连接了测试平台和DUT,并且含有一个简单的时钟发生器(clock generator)
module top;
logic[1:0] grant,request;
bit clk,rst;
always # 5 clk = ~clk;
arb_port al (grant, request, rst, clk)
test t1(grant,request,rst,clk);
endmodule
二、接口
逻辑设计变得越来越复杂,块之间的通信也必须分割成独立的实体。使用接口为块之间的通信建模,接口可以看作一捆智能的连线,它包含了连接、同步、甚至两个或者多个块之间的通信功能,它们连接了设计块和测试平台。
1、使用接口来简化连接
注意到使用接口扩展到了这两个块中,包括了测试平台和DUT的驱动和接受功能模块。时钟可以是接口的一部分或者是一个独立的端口。
仲裁器的简单接口
interface arb_if(input bit clk);
logic[1:0] grant, request;
logic rst;
endinterface
使用了简单接口的仲裁器
module arb(arb_if arbif);
...
always @(postedge arbif.clk or postedge arbif.rst)
begin
if(arbif..rst)
arbif.grant <= 2'b00;
else
arbif.grant <= next_grant;
...
end
endmodule
使用了简单仲裁器接口的测试平台
module test(arb_if arbif);
...
initial begin
// 此处省略了复位代码
@(postedge arbif.clk)
arbif.request <= 2'b01; //接口信号必须使用非阻塞赋值来驱动
$display(“@%0t:Drove req=01”, $time);
repeat (2) @(postedge arbif.clk);
if(arbif.grant != 2'b01)
$display(“@%0t:al: grant != 2'b01”, $time);
$finish;
end
endmodule : test
使用简单仲裁器接口的top模块,所有这些块都在top模块中例化和连接。
module top;
bit clk;
always # 5 clk = ~ clk;
arb_if arbif(clk);
arb al(arbif);
test t1(arbif);
endmodule : top
2、连接接口和端口
还可以将接口的信号直接连接到每个端口上。
module top;
bit clk;
always # 5 clk = ~ clk;
arb_if arbif(clk);
arb_port al(.grant (arbif.grant),
.request (arbif.request),
.rst(arbif.rst),
.clk(arbif,clk));
test t1(arbif);
endmodule : top
3、使用modport将接口中的信号分组
在接口中使用modport结构能够将信号分组并指定方向。
interface arb_if(input bir clk);
logic[1:0] grant, request;
logic rst;
modport TEST(output request,rst,
input grant,clk);
modport DUT(input request,rst,clk
output grant);
modport MONITOR(input request,rst,grant,clk);
endinterface
在端口连接表中使用modport时需要将modport名放在接口名后面。
接口中使用modport的仲裁器模型
module arb(arb_if.DUT arbif);
...
endmodule
接口中使用modport的测试平台
module test(arb_if.TEST arbif);
...
endmodule
4、接口的优缺点
在接口中不能例化模块,但是可以例化其他接口。
优点:
- 接口便与设计重用,当两个块之间有两个以上的信号连接,并且使用特定的协议通信的时候,应当考虑使用接口。
- 接口可以用来替代原来需要在模块或者程序中反复声明并且位于代码内部的一系列信号,减少了连接错误的可能性。
- 要增加一个新的信号时,在接口中只需要声明一次,不需要再更高层的模块层声明。
- modport允许一个模块很方便地将接口中的一系列信号捆绑在一起,也可以为信号指定方向。
缺点:
- 对于点对点的连接,使用modport的接口描述跟使用信号列表的端口一样的冗长。
- 必须同时使用信号名和接口名。
- 如果要连接的两个模块使用的是一个不会被重用的专用协议,使用接口需要做比端口连线更多的工作。
- 连接两个不用的接口很困难
三、激励时序
测试平台和设计之间的时序必须密切配合。
1、使用时钟块控制同步信号的时序
接口块可以使用时钟块来指定同步信号相对于时钟的时序。时钟块中的任何信号都将同步地驱动或采样,这就保证了测试平台在正确的时间点与信号交互。一个接口可以包含多个时钟块,因为每个块中都只有一个时钟表达式,所以每一个对应一个时钟域。@(postedge clk)定义了单时钟沿,@(clk)定义了DDR时钟(双数据率)。你也可以在时钟块中使用default语句指定一个时钟偏移,但是默认情况下输入信号仅在设计执行前被采样,并且设计的输出信号在当前时间片又被驱动回当前设计。
带时钟块的接口
interface arb_if(input bit clk);
logic[1:0] grant, request;
logic rst;
clocking cb @(postedge clk);
output request;
intput grant;
endclocking
modport TEST(clocking cb,
output rst);
modport DUT(input request,rst,output,grant);
endinterface
2、接口中的logic和wire对比
测试平台在接口中使用过程赋值语句驱动一个异步信号,那么该信号必须是logic类型的。wire类型变量只能被连续赋值语句驱动。时钟块中的信号始终是同步地可以定义为logic或者wire。接口中的信号使用logic类型的另一个原因是如果你无意中使用了多个元件的驱动源,编译器回自动报错。
驱动接口中的logic和wire信号
interface asynch_if();
logic l;
wire w;
endinterface
module test(asynch_if ifc);
logic local_wire;
assign ifc.w = local_wire;
initial begin
ifc.l <= 0; //直接驱动异步logic信号
local_wire <= 1; //但是只能用assign驱动wire信号
endmodule
3、Verilog的时序问题
测试平台应该不仅在逻辑上而且在时序方面独立于设计。测试平台应当在有效时钟边沿或者边沿之后驱动待测设计,然后在有效时钟边沿到达之前,在满足协议时序的前提下尽可能晚地采样。
4、测试平台和设计之间的竞争状态
竞争状态出现在测试平台先产生start信号,然后再产生其他信号的时候。内存被start信号唤醒的时候,write、addr、data信号却仍然保留着原来的值。
5、程序块和时序区域
时序问题和竞争状态的根源在于设计和测试平台的事件(event)混在同一个时间片(time slot)内,即使在纯RTL程序中也会发生同样的问题。
SystemVerilog把测试平台的事件和设计分开调度,在SystemVerilog中,测试平台的代码在一个程序块中,这跟模块非常类似,模块含有代码和变量,可以在其他模块中例化。而程序块不能有任何层次级别,例如模块的实例、接口或者其他程序。
SystemVerilog时间步长内的主要区域:
Verilog中大多数的事件在有效区域active region执行,对于非阻塞赋值和PLI等来讲还存在一些其他的执行区域。
在一个时间片内首先执行的是Active区域,在这个区域中运行设计事件,包括RTL、门级代码和时钟发生器。第二个区域是Observed区域,执行断言。接下来执行的是测试平台的Reactive区域。最后就是Postponed区域,它将在时间片的最后,所有设计活动都结束后的只读时间段采样信号。
6、仿真的结束
在Verilog中,仿真在调度事件存在的时候会继续执行,直到遇到$finish。
SystemVerilog把任何一个程序块都将视为含有一个测试,如果仅有一个程序块,那么当完成所有initial块中的最后一个语句时,仿真就结束了,因为编译器认为这就是测试的结尾,及时还有模块或者程序块的线程在运行,仿真也会结束。如果存在多个程序块,仿真在最后一个程序块结束时结束。也可以执行 $exit提前中断任何一个程序块,或者仍然可以使用 $finish来结束仿真。
7、指定设计和测试平台之间的延时
时钟块的默认时序实在# lstep延时之后采样输入信号,在# 0延时之后驱动输出信号。lstep延时规定了信号在前一个时间片Postponed区域,在设计有任何新的动作之前捕获输出值。