验证的环境可以分为软件和硬件两部分,分别对应动态和静态。接口更加偏向于硬件的module,在硬件世界中实例化。
虚接口的必要性
在验证环境中,硬件部分(DUT和Interface)属于静态范畴,意味着在编译的时候就要被分配内存进行初始化;软件部分(class)属于动态范畴,在仿真运行时才会被分配空间初始化。二者之间的关系类似动态变量和静态变量。
由此会产生的问题是:当在class中调用接口与DUT链接时时,如果不加virtual关键字,则意味着在编译的时候就要为接口开辟内存初始化,而编译的时候,作为软件部分的class是不会被分配空间的。因此导致矛盾的出现(通俗说法:妈妈还没出生,孩子先出生了)
因此,为了解决矛盾,需要将class中的接口声明为virtual,将物理的接口替换为句柄,编译时不占用内存空间,在仿真跑起来的时候再传入接口实例,从而解决冲突。
就像绿皮书中说的:“虚接口是唯一链接动态对象和静态模块、接口的一种机制”。
虚接口的作用
为了方便的将软件世界的tb产生的激励输送给硬件module,需要一个媒介,即虚拟接口,本质上是一个指向硬件接口的指针,即是物理接口的句柄
仿真代码:
class transaction;//用rand来生成随机化激励
rand logic load_valid;
rand logic [3:0] load_value;
endclass
class driver;
virtual counter_if vif;//生成一个virtual interface句柄
function new(input virtual counter_if vif);//该new函数用来使虚接口句柄指向interface
this.vif=vif;
endfunction
transaction tr;
task run(int n);//产生n次激励
for(int i=0;i<n;i++)begin
tr=new;
assert(tr.randomize());//
$display("tr.load_valid=%0d,tr.load_value=%0d",tr.load_valid,tr.load_value);
@(posedge vif.clk)begin//在clk上升沿,通过虚接口将transaction中的随机激励传送到interface上
vif.load_valid<=tr.load_valid;
vif.load_value<=tr.load_value;
end
end
endtask
endclass
module tb_top;
logic clk,rstn;
logic[3:0]q;
counter_if dutif(clk);//实例化interface
driver my_driver;//声明句柄
counter u_counter(.resetn(rstn),
.clk(clk),
.load_valid(dutif.load_valid),
.load_value(dutif.load_value),
.q(out));//实例化DUT
initial begin
my_driver=new(dutif);//实例化驱动,使虚接口句柄指向interface
repeat(2) @(posedge clk);
@(posedge rstn);
repeat(5) @(posedge clk);
my_driver.run(20);//产生20次随机激励
end
initial begin//生成T=10的时钟
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
endmodule
interface counter_if(input logic clk);
logic load_valid;
logic [3:0] load_value;
endinterface
module counter(input clk,resetn,
input [3:0] load_value,
input load_valid,
output reg [3:0] q);
always@(posedge clk or negedge resetn)begin
if(!resetn)
q<=4d'0;
else if(load_valid)
q<=load_value;
else
q<=q+4'd1;
end
endmodule
仿真详细流程:
在Linux终端中依次输入以下命令启动VCS:
vcs -full64 -kb -debug_access+all -sverilog 文件名
/.simv -gui
在VCS的dve中依次输入以下命令启动仿真:
dump -deltaCycle on
run
仿真输出如下:
仿真波形如下:
我对于counter计数到6的这个时钟上升沿有疑问,不确定在这个posedge clk边沿,counter会执行q<=q+4'd1,还是q<=load_value。因此,调用Delta Cycle查看
结果如下:
可见,在该时钟上升沿,q的+1操作先于load_value的变化,因此counter会在当前clk周期变为6,在下一个clk上升沿变为12(load_value的值)。