为什么需要一个测试平台?
主要作用就是将设计和测试平台分开,利用测试平台去测试设计,在sv中利用程序块在逻辑上和时间上分开测试,
1、接口定义
为什么需要接口
主要思想:将各个功能的模块分开,减少代码块的错误和工作量,不然一个信号流经不同的设计层的时候需要一次又一次的声明和连接。将不同的连线捆在一起,就形成了一个接口。
举一个最简单的接口模型
///
//仲裁器的简单接口
interface arb_if(input bit clk) ;
logic [1:0]grant,request ;
logic rst ;
endinterface
/
//使用了简单接口的仲裁器
module arb(arb_if arbif) ;
always@(posedge arbif.clk or posedge arbif.rst)
begin
if(arbif.rst)
arbif.grant <= 2'b00 ;
else if(arbif.request == 2'b01)
arbif.grant <= 2'b11;
else
arbif.grant <= 2'b01;
end
endmodule
/
//使用简单仲裁器接口的测试平台
module test(arb_if arbif) ;
initial begin
@(posedge arbif.clk) ;
arbif.request <= 2'b01 ;//接口信号必须使用非阻塞赋值来驱动
$display("@%0t:Drove req = 01",$time) ;
repeat(2)@(posedge arbif.clk) ;
if(arbif.grant != 2'b01)
$display("@%0t:al:grant!= 2'b01",$time) ;
$finish ;
end
endmodule:test
/
//使用简单仲裁器接口的top模块
module top;
bit clk;
always #5 clk = ~clk ;
arb_if arbif(clk) ;
arb a1 (arbif) ;
test t1(arbif) ;
endmodule:top
/
运行结果
@5000:Drove req = 01
@25000:al:grant!= 2'b01
就是将所需的信号包装在一起,形成了interface
(1)接口的优势
- 将有关信号封装在同一个接口中,对于设计和验证环境便于维护和使用
- interface可以作为SV唯一的硬件和软件环境的媒介交互,
- 接口可以例化,对于多组相同的总线,在例化和使用更加灵活
- 在例化时,添加参数提高其复用性,
- 端口列表中只需要定义时钟、复位等公共信号
(2)激励时序
为了保证测试程序采集到dut的最新值,有几种方法控制通信中时序的问题
(1)使用时钟快控制同步信号的时序
一个接口可以包含多个时钟块,定义了时钟快就可以用@arbiter.cb表达式等待时钟,不需要描述具体的信号和边沿
(2)一般使用logic进行驱动,
(3)为了避免竞争状态,采样需要
2、接口的驱动和采样
竞争问题
大家在跑仿真的过程中,将波形展开时,时钟的上升沿和数据变化的沿,看起来是一样齐的,好像没有延时。而真实电路中,时钟上升沿和驱动信号二者从物理电气特性上来说,延时是必然存在的。
所以默认情况下,对于组合电路的的驱动会添加一个无限小的延迟,只在仿真的有意义,在门级仿真的时候才会表现出来。由于实际情况,clk与被采样数据之间存在若干个延迟,所以同样的时刻,不同的时钟会采到不同的结果,所以如何保证采样的准确性
注释:clk2和clk1在波形上看起来是在相同时间起落的,但是always驱动后,clk2是晚于clk1的一个delta-cycle,d1又晚于一个delta-cycle所以d1和clk2同步。
如何避免采样问题
(1)在驱动时,添加人为延迟
(2)在采样事件前的某段时刻进行采样。
接口中的clocking(时钟块)更好的模拟建立保持时间
在时钟上升沿输入采样,时钟下降沿后输出驱动,这样可以保证数据采样的准确性
时钟上升沿后进行驱动
结论:
为了避免竞争问题,
- 在验证环境的驱动环节就添加固定延迟,在仿真波形中更容易体现出时钟与被驱动信号之间的时序前后关系。
- 把clocking运用到interface中,用来声明各个接口和时钟的采样和关系后,就可以提高数据驱动和采样的准确性。
3、测试的开始和结束
如何进行测试的开始和结束
系统函数$finish()结束仿真,会让仿真退出,将控制权交回给操作系统。
$stop是暂停仿真。