为了不与《UVM实战》一书的DUT例子完全重合,自己找了一个DUT:
module router(input logic clk, reset_n, router_io.dut io);
同时该DUT自己也定义了虚接口:
`ifndef ROUTER_IO__SV
`define ROUTER_IO__SV
interface router_io(input bit clk);
logic [15:0] frame_n ;
logic [15:0] valid_n ;
logic [15:0] din ;
logic [15:0] dout ;
logic [15:0] busy_n ;
logic [15:0] valido_n ;
logic [15:0] frameo_n ;
clocking drvClk @(posedge clk);
output frame_n;
output valid_n;
output din;
input busy_n;
endclocking: drvClk
clocking iMonClk @(posedge clk);
input frame_n;
input valid_n;
input din;
input busy_n;
endclocking: iMonClk
clocking oMonClk @(posedge clk);
input dout;
input valido_n;
input frameo_n;
endclocking: oMonClk
modport dut(input clk, frame_n, valid_n, din, output dout, busy_n, valido_n, frameo_n);
endinterface: router_io
`endif
因此,我们在my_driver中需要使用虚接口,同时编写完成对DUT的驱动的task:
class my_driver extends uvm_driver;
virtual router_io vif_data;
virtual reset_io vif_reset;
function new (string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task my_driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("my_driver", "main_phase is called", UVM_LOW);
vif_data.din <= 'd0;
vif_data.valid_n <= 'd0;
vif_data.frame_n <= 'd0;
while (!vif_reset.reset_n)
@(vif_data.clk);
for (int i = 0; i < 256; i++) begin
@(vif_data.clk);
vif_data.din <= $urandom_range(0,65535);
vif_data.valid_n <= $urandom_range(0,1);
vif_data.frame_n <= 1;
end
@(vif_data.clk);
vif_data.din <= 'd0;
vif_data.valid_n <= 'd0;
vif_data.frame_n <= 'd0;
phase.drop_objection(this);
endtask
reset是个单独的接口,得同样操作。那么问题是test bench的顶层(DUT的if所在之处)如何和现在这个driver里的接口关联起来?因为我有答案了,所以可以向培训老师一样对自己说:“我们想象一下,在tb 顶层,需要调用这个接口,并且与DUT关联起来,与driver也关联起来。第一个简单直接例化DUT时用这个接口就行,第二个是UVM精髓之一,这是一种脱离RTL模块层次设计的隐藏的另一种拓扑结构,这个接结构(树)里有一种config_db机制。”9点又到了,感觉没写啥东西,时间过的真快,一个小时完全不够呢。明天继续tb top 和 DUT,driver之间的关系。
本文暂时没有评论,来添加一个吧(●'◡'●)