一、虚接口:
interface 简化了模块之间的连接,但是无法很好地适用于基于OOP的测试平台,无法在program 、class 中进行实例化,所以引入了虚接口,virtual interface的本质是指针,是指向interface的指针,即virtual interface是可以在class中实例化的数据类型,interface将测试平台与DUT分开,
virtual interface可以在TB的不同位置操纵一组虚拟信号,而不是直接操纵实际的信号。
//1.create DUT file counter.sv:
module counter(
input logic resetn ,
input logic clk ,
input logic [3:0] load_value,
input logic load_valid,
output logic [3:0] q
);
always_ff @(posedge clk or negedge resetn)
begin
if(!resetn)
q <= 4'd0;
else if (load_valid)
q <= load_value;
else
q <= q+1;
end
endmodule
//2.create interface file counter_if.sv:
interface counter_if (input logic clk);
logic load_valid;
logic [3:0] load_value;
endinterface
//2.create transaction.sv:
class transaction;
rand logic load_valid;
rand logic [3:0] load_value;
endclass
//4.create file driver.sv:
class driver;
virtual counter_if vif;//声明虚接口的句柄
transaction tr;
function new(input virtual counter_if vif);//形参也是虚接口
this.vif=vif;
endfunction
task run (int n = 10);
for(int i=0;i<n;i++)
begin
tr=new();
assert(tr.randomize());
$display("tr.load_valid=%d,tr.load_value=%d", tr.load_valid, tr.load_value);
@(posedge vif.clk)
begin
vif.load_valid <= tr.load_valid;
vif.load_value <= tr.load_value;
end
end
endtask
endclass
//5.create tb_top.sv:
module tb_top;
logic clk;
logic rstn;
logic [3:0] out;
counter_if dutif(clk);//实例化接口
driver my_driver; //声明my_driver对象的句柄
initial begin
my_driver=new(dutif);//使虚接口指向实例化的接口dutif
repeat(2)@(posedge clk);
@(posedge rstn);
repeat(5)@(posedge clk);
my_driver.run(20);
end
initial begin
clk=1'b0;
forever #5 clk=~clk;
end
initial begin
rstn=1; repeat(2) @(posedge clk);
rstn=0; repeat(5) @(posedge clk);
rstn=1; repeat(50) @(posedge clk);
$finish;
end
counter u_counter (
.resetn (rstn ),
.clk (clk ),
.load_valid(dutif.load_valid),
.load_value(dutif.load_value),
.q (out )
);
endmodule
打印的随机结果:
tr.load_valid=0,tr.load_value= 4
tr.load_valid=0,tr.load_value=14
tr.load_valid=1,tr.load_value= 4
tr.load_valid=1,tr.load_value= 5
tr.load_valid=1,tr.load_value=11
tr.load_valid=1,tr.load_value= 4
tr.load_valid=0,tr.load_value=15
tr.load_valid=1,tr.load_value=12
tr.load_valid=1,tr.load_value=13
tr.load_valid=0,tr.load_value=12
tr.load_valid=1,tr.load_value= 6
tr.load_valid=0,tr.load_value=14
tr.load_valid=0,tr.load_value=12
tr.load_valid=1,tr.load_value=15
tr.load_valid=0,tr.load_value=14
tr.load_valid=1,tr.load_value= 6
tr.load_valid=0,tr.load_value= 9
tr.load_valid=1,tr.load_value= 7
tr.load_valid=1,tr.load_value= 5
tr.load_valid=1,tr.load_value= 7
说明:
- 实例化的接口必须正确连接到DUT
- 必须在类中用关键词virtual声明虚接口的句柄,并且有相应驱动
- 必须将virtual interface指向实例化的interface
二、包(package)
一、包
SV提供了一种在多个module、interface和program中共享parameter、data、type、task、function、class等的方法,包就是把相关的方法封装起来,也可以对类封装,将相关的类封装到一起,每个包分配单独的命名空间,这样对于不同的验证模块,有相同名字的类(这个类的内容和功能是不同的)也可以通过包来区别,sv中包不支持包含module、interface(会报错)。
包的定义:(关键词:package.....endpackage)
package regs_pkg;
`include "stimulator.sv"
`include "monitor.sv"
`include "checker.sv"
`include "env.sv"
endpackage
package arb_pkg;
`include "stimulator.sv"
`include "monitor.sv"
`include "checker.sv"
`include "env.sv"
endpackage
module arbiter_tb;
regs_pkg::monitor mon1 = new();
arb_pkg::monitor mon2 = new();
endmodule
说明:
- 上示代码,定义了regs_pkg、arb_pkg两个包,但其中包含的类的文件名和类的名字相同,当调用时需要用包的名字来指明调用的是哪个包的类。
- 包的命名一般要求要独一无二,便于区分命名空间。
package regs_pkg;
`include "regs_stm.sv"
`include "regs_mon.sv"
`include "regs_chk.sv"
`include "regs_env.sv“
endpackage
package arb_pkg;
`include "arb_stm.sv"
`include "arb_mon.sv"
`include "arb_chk.sv"
`include "arb_env.sv"
endpackage
module test_tb;
import regs_pkg::*;///regs_pkg中的所有元素都可以被当前文件直接调用
import arb_pkg::*;
regs_mon mon1= new();
arb_mon mon2 = new();
endmodule
说明:
- 在上示代码中,包的名字和类的文件名和名类的名字都不同时(代码风格文件名和类名一般相同),可以"import pkg_name::*"来导入pkg_name中包所有的类,因为类名不同,所以也可以直接引用。
- 包中可以定义类、静态方法和静态变量。
- 在包中include的文件的路径是默认路径,若包含的文件不在默认路径时,在编译时需要把文件相对路径包含进去,可以在Makefile中包含。在使用`include的关键词完成类在包中的封装,要注意编译的前后顺序来放置各个`include的类文件。
另:
- generator产生这个激励数据 ,driver把激励加载到接口去驱动DUT,stimulator=generator+driver