1.前面的例子使用的是绝对路径,移植性差,因此用interface是极好的:
`ifndef MY_IF__SV
`define MY_IF__SV
interface my_if(input clk, input rst_n);
logic [7:0] data;
logic valid;
endinterface
`endif
2.例化DUT是可以直接连到端口上(systemverilog):
my_if input_if(clk, rst_n);
my_if output_if(clk, rst_n);
dut my_dut(.clk(clk),
.rst_n(rst_n),
.rxd(input_if.data),
.rx_dv(input_if.valid),
.txd(output_if.data),
.tx_en(output_if.valid));
3.想在driver中使用,想当然的是在里面声明同样类型的变量,再赋值:
class my_driver extends uvm_driver;
my_if drv_vif;
..........................
endclass
1)但这样是报错的,在class中是不能声明一个interface的实参的,只有top_tb这种module可以。
2)即:在class中不能声明 一个占大面积内存的interface;最多声明一个virtual interface,这种同样类型的virtual变量,类似C语言的一个指针,把它的值赋为module中实参interface的地址。
3)UVM这么做的感觉和硬件的表现出奇一致。
3.1)硬件只有一个interface,想要接到这些信号的module一定要连上去才行;
3.2) UVM也一样,要想接到这个interface的class也只是声明个同样类型的virtual。把实参地址赋给它,最终还是有一个interface。
3.3)如果class中可以声明实参interface,再赋值,感觉也可以。但是整个UVM会有两个interface,与硬件不对应,不优雅;而且还浪费内存。幸好UVM会报错,根本不让这么做。
4所以要想下面这么做:
class my_driver extends uvm_driver;
virtual my_if vif;
5. main_phase中赋值如下:
task my_driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("my_driver", "main_phase is called", UVM_LOW);
vif.data <= 8'b0;
vif.valid <= 1'b0;
while(!vif.rst_n)
@(posedge vif.clk);
for(int i = 0; i < 256; i++)begin
@(posedge vif.clk);
vif.data <= $urandom_range(0, 255);
vif.valid <= 1'b1;
`uvm_info("my_driver", "data is drived", UVM_LOW);
end
@(posedge vif.clk);
vif.valid <= 1'b0;
phase.drop_objection(this);
endtask
6.现在的问题是怎么把 top_tb 中的 input_if和my_driver中的vif连起来;或者说把实参interface的地址赋给virtual这个像pointer上。因为UVM建立了一个新的层次结构所以top_tb.my_driver.xxx是不行的。要用config_db,分为set 和get。
top_tb如下:
initial begin
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
end
my_driver如下:
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
`uvm_info("my_driver", "build_phase is called", UVM_LOW);
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_driver", "virtual interface must be set for vif!!!")
endfunction
1)build_phase 和 main_phase一样,也是UVM内建的phase。
2)UVM启动后,自动执行build_phase。
3)build_phase在new之后,main_phase之前执行。
4)build_phase做一些准备工作,主要通过config_db的set和get 传递变量,把整个UVM 的参数配置好,再让main_phase运行。
5)需要加入super.build_phase,因为其父类的build_phase执行一些必要操作,需显示调用。那是先执行父类还是本身呢?因该是父类把,super在最前。
6)build_phase是函数phase,不消耗仿真时间,(运行之前,配置参数用);main_phase是任务phase,总在仿真时间为0时执行。($time 可以看到,之前没有objection也可以证明)。和verilog中function 不消耗时间,task 消耗时间的概念相同。
7)config_db的set 和 get 第三个参数必须完全一致(synopsys也是这么说的)。set 的第二个参数表示路径索引。
8)set 和 get使用双冒号因为两个都是静态函数,uvm_config_db #() 传参是类型,即寄信种类。
9)get的时候以uvm_test_top的名字到数据库去找。
7.问题:
set 的第三个参数 “vif”,是放到数据库的标志,可以随便起;还是因为 my_driver 中也定义了vif,所以一定要一样呢?验证一下:
top_tb.sv改成如下:
为避免my_driver报错,把所有 vif 关于代码都去掉:
编译,仿真通过。打印所有信息。可见 set 的时候是不管是否存在真正的UVM索引。就像是直接写到了第三方的数据库。
8.my_driver中 get的第三个参数改成testvif应该可以吧。
编译仿真通过,信息打印,数据发出。没有问题。
9.get怎么写能通过 bbb.aaa 找到interface 呢?
10.如果是ccccc 的方法呢?
编译报错:
1)bit[39:0] 什么错误??。。看不懂
2)如果把ccccc 换成 ccc 。就是bit[23:0]什么错误? 看不懂。。。