上一节只给了一个driver,使用UVM搭建的验证平台,严格来说都不算一个UVM验证平台,这些东西几乎没用用到UVM的特性。这一节要引入一个UVM的factory机制,它可以带给我们一个功能:自动创建一个类并调用其中的任务task和函数function。
代码如下:
class my_driver extends uvm_driver;
`uvm_component_utils(my_driver)
function new(string name = "my_driver",uvm_component parent = null);
super.new(name,parent);
`uvm_info("my_driver","new is called",UVM_LOW);
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass
task my_driver::main_phase(uvm_phase phase);
`uvm_info("my_driver","main_phase is called",UVM_LOW);
top_tb.rxd <= 8'b0;
top_tb.rx_dv <= 1'b0;
while(!top_tb.rst_n)
@(posedge top_tb.clk);
for(int i = 0; i < 256; i++) begin
@(posedge top_tb.clk);
top_tb.rxd <= $urandom_range(0,255);
top_tb.rx_dv <= 1'b1;
`uvm_info("my_driver","data is drived",UVM_LOW);
end
@(posedge top_tb.clk);
top_tb.rx_dv <= 1'b0;
endtask
factory机制的实现被集成在了一个宏中:uvm_component_utils。这个宏可以把my_driver这个我们已经定义好的类注册到UVM的一张表中。
在给driver加入factory机制后,我们的top_tb.sv也需要做出一些改动:
`timescale 1ns/1ps
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "my_driver.sv"
module top_tb;
...
initial begin
run_test("my_driver");
end
...
endmodule
这里主要是用run_test()这个语句来代替了之前代码中第一个块中的语句,也就是my_driver的实例化和main_phase的显示调用。运行这个新平台,会出现如下语句:
new is called
main_phase is called
一个run_test语句会创建一个my_driver实例,并且会自动调用my_driver中的main_phase。这里的run_test传递了一个“my_driver”的字符串,UVM根据这个字符串建立了其对应类的一个实例。
所以这就是uvm_component_utils宏给我们带来的效果:根据类名来创建一个类的实例。只有在使用了`uvm_component_utils这个宏把my_driver这个类注册进来,我们在使用run_test这个语句时my_driver才能被相应的实例化和调用其中的任务。所以要记住:所用派生自uvm_component及其派生类的类都应该通过`uvm_component_utils这个宏来注册。
除了根据一个字符串创建类的实例化以外,上面的代码中还有一个神奇之处,就是my_driver类中main_phase这个任务被自动调用了。在UVM验证平台中,只要一个类使用了uvm_component_utils注册且类被实例化了,那么该类的main_phase就会被自动调用。这也就是上一节为什么强调实现driver就是实现其main_phase的原因。
上面的例子中,只输出到了"main_phase is called",但并没有输出“data is drived”,而按照预期,他应该被输出256次,关于这个问题,涉及到UVM的objection机制。