参考绿皮书第十章:高级接口
为什么要引入虚接口?
接口(interface)代表了实际的物理信号,是静态的,而验证平台需要动态的连接设计,比如一个driver类要连接DUT的不同接口,但你肯定不希望每个接口都写一个对应的driver类和DUT接口连接,而是通过例化传参的方式连接DUT。因此system verilog提出了一种虚接口(virtual interface)的概念,虚接口是物理接口的句柄,在仿真平台运行时,再决定要传入哪一个物理接口的句柄,并产生相应激励。因此,虚接口将物理DUT和仿真验证平台分开,将硬件和软件分开,把软件平台的激励送给了硬件。(虚接口是唯一一个桥接动态对象和静态模块、接口的一种机制:引自绿皮书)
示例:一个最简单的验证平台激励DUT的例子
DUT:
module dut(
input clk,
input rst_n,
input [3 : 0] load_value,
input load_vld,
output reg [3 : 0] q
);
always@(posedge clk or negedge rst_n)begin
if(!rst_n) q <= 'd0;
else if(load_vld) q <= load_value;
end
endmodule
TB:
interface dut_if(input logic clk);
logic [3 : 0] load_value;
logic load_vld;
endinterface
class transaction;
randc logic [3 : 0] load_value;
randc logic load_vld;
endclass
class driver;
virtual dut_if vif;
function new (input virtual dut_if vif);
this.vif = vif;
endfunction
transaction tr;
task run(input int n);
for (int i = 0; i < n ; i++)begin
tr = new();
tr.randomize();
$display("tr.load_value=%0d, tr.load_vld=%0d", tr.load_value, tr.load_vld);
@(posedge vif.clk)begin
vif.load_value <= tr.load_value;
vif.load_vld <= tr.load_vld;
end
end
endtask
endclass
module tb_dut();
logic clk, rst_n;
logic [3 : 0] q;
//==================parameter
parameter PERIOD = 10;
//==================definaiton
dut_if dutif(clk);
driver my_driver;
//==================initial
initial begin
clk = 0;
forever #(PERIOD / 2) clk = ~clk;
end
initial begin
rst_n = 0;repeat(5) @(posedge clk);
rst_n = 1;repeat(50) @(posedge clk);
$finish;
end
initial begin
my_driver = new(dutif);
@(rst_n);
repeat(5) @(posedge clk);
my_driver.run(20);
end
//===================dut
dut u_dut(
.clk(clk),
.rst_n(rst_n),
.load_value(dutif.load_value),
.load_vld(dutif.load_vld),
.q(q)
);
`ifdef FSDB
initial begin
$fsdbDumpfile("dut.fsdb");
$fsdbDumpvars;
$fsdbDumpMDA();
end
`endif
endmodule
最终贴出仿真结果: