UVM 树形结构:
driver:
// 只有driver的验证平台
module dut(clk,rst_n,rxd,rx_dv,txd,tx_en);
input clk;
input rst_n; // 低电平复位
input[7:0] rxd; // 接收数据
input rx_dv; // 接收的数据有效指示
output [7:0] txd; // 发送数据
output tx_en; // 发送的数据有效指示
reg[7:0] txd;
reg tx_en;
always @(posedge clk)begin
if(!rst_n)begin
txd <= 8'b0;
tx_en <= 1'b0;
end
else begin
txd <= rxd; // 发送数据
tx_en <= rx_dv;
end
end
endmodule
定义类
classs A;
…
endclass
实例化类
A a_inst;
a_inst = new(); // 通过new创造出A的一个实例
my_driver.sv
UVM验证平台中的类driver应该派生自类uvm_driver,一个简单的driver如下例所示:
// 定义my_driver类
class my_driver extends uvm_driver; // my_driver是一个派生自uvm_driver的类,uvm_driver是一个派生自uvm_component的类,要指明两个uvm_component参数:name和parent
// 一个参数是string类型的name,就是这个类的示例的名字;一个是uvm_component类型的parent,以表明这个drive在uvm整个运行树中处于什么位置drive的parent就是input_agent,而input_agent的parent就是env。
function new(string name = "my_driver", uvm_component parent = null);
super.new(name, parent); // 执行父类的new函数
endfunction
extern virtual task main_phase(uvm_phase phase);
endclass // my_driver
// main_phase是uvm_driver中预先定义好的一个任务。因此几乎可以简单地认为,实现一个driver等于实现其main_phase。driver所做的事情几乎都在main_phase中完成
task my_driver::main_phase(uvm_phase phase);
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 // 是向rxd上发送256个随机数据
@(posedge top_tb.clk);
top_tb.rxd <= $urandom_range(0, 255);
top_tb.rx_dv <= 1'b1; // 并将rx_dv信号置为高电平
`uvm_info("my_driver", "data is drived", UVM_LOW) // uvm_info宏。这个宏的功能与Verilog中display语句的功能类似
end
@(posedge top_tb.clk);
top_tb.rx_dv <= 1'b0; // 数据发送完毕后,将rx_dv信号置为低电平
endtask
本节中uvm_info宏打印的结果如下
UVM_INFO my_driver.sv(20)@48500000:drv[my_driver]data is drived
// UVM_INFO关键字:表明这是一个uvm_info宏打印的结果。除了uvm_info宏外,还有uvm_error宏、uvm_warning宏
// my_driver.sv(20):指明此条打印信息的来源,其中括号里的数字表示原始的uvm_info打印语句在my_driver.sv中的行号
// 48500000:表明此条信息的打印时间
// drv:这是driver在UVM树中的路径索引。UVM采用树形结构,对于树中任何一个结点,都有一个与其相应的字符串类型的路径索引。路径索引可以通过get_full_name函数来获取,把下列代码加入任何UVM树的结点中就可以得知当前结点的路径索引:$display("the full name of current component is: %s", get_full_name());
// [my_driver]:方括号中显示的信息即调用uvm_info宏时传递的第一个参数。
// data is drived:表明宏最终打印的信息。
top_tb.sv
对my_driver实例化并且最终搭建的验证平台如下:
// 对my_driver实例化并且最终搭建的验证平台如下:
`timescale 1ns/1ps
`include "uvm_macros.svh" // system verilog includ写法 `include "需要引用的文件"
`include "my_driver.sv"
import uvm_pkg::*; // 导入uvm_pkg库
module top_tb;
reg clk;
reg rst_n;
reg[7:0] rxd;
reg rx_dv;
wire[7:0] txd;
wire tx_en;
dut my_dut(
.clk(clk),
.rst_n(rst_n),
.rx_dv(rx_dv),
.txd(txd),
.tx_en(tx_en)
);
initial begin
my_driver drv; // 定义一个my_driver类,名字是drv
drv = new("drv", null); // 实例化 其传入的名字参数为drv,在真正的验证平台中,parent参数一般不是null,这里暂且使用null。
drv.mian_phase(null); //调用my_driver的main_phase,本节的验证平台还算不上一个完整的UVM验证平台,所以暂且传入null。
$finish(); // Verilog中提供的函数,结束仿真
end
initial begin
clk = 0;
forever begin
#100 clk = ~clk;
end
end
initial begin
#1000;
rst_n = 1'b1;
end
endmodule
运行这个例子,可以看到“data is drived”被输出了256次
加入factory机制 objection机制:
在 top_tb.sv 中显示调用 driver.sv
initial begin
my_driver drv; // 定义一个my_driver类,名字是drv
drv = new("drv", null); // 实例化 其传入的名字参数为drv,在真正的验证平台中,parent参数一般不是null,这里暂且使用null。
drv.main_phase(null); //调用my_driver的main_phase,本节的验证平台还算不上一个完整的UVM验证平台,所以暂且传入null。
$finish(); // Verilog中提供的函数,结束仿真
end
在 top_tb.sv 中隐示调用 driver.sv,需要引入UVM的factory机制。
factory机制的实现被集成在了一个宏中:uvm_component_utils。
在UVM验证平台中,只要一个类使用uvm_component_utils注册且此类被实例化了,那么这个类的main_phase就会自动被调用。
// my_driver.sv
class my_driver extends uvm_driver; /
//factory机制的实现被集成在了一个宏中:uvm_component_utils,要在定义一个新的类时使用这个宏
`uvm_component_utils(my_driver) // 在UVM验证平台中,只要一个类使用uvm_component_utils注册且此类被实例化了,那么这个类的main_phase就会自动被调用。
... ...
endclass
task my_driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
... ...
phase.drop_objection(this); // 将drop_objection语句当成是finish函数的替代者
endtask
// top_tb.sv
module top_tb;
... ...
initial begin
/*
my_driver drv; // 定义一个my_driver类,名字是drv
drv = new("drv", null); // 实例化 其传入的名字参数为drv,在真正的验证平台中,parent参数一般不是null,这里暂且使用null。
drv.main_phase(null); //调用my_driver的main_phase,本节的验证平台还算不上一个完整的UVM验证平台,所以暂且传入null。
$finish(); // Verilog中提供的函数,结束仿真
*/
run_test("my_driver"); //一个run_test语句会创建一个my_driver的实例,并且会自动调用my_driver的main_phase。
end
endmodule
输出
new is called
main_phased is called
加入virtual interface:
避免绝对路径的一个方法是使用宏:
// my_if.sv
// virtual interface
interface my_if(input clk, input rst_n);
logic [7:0] data;
logic valid;
endinterface
UVM引进了config_db机制,把top_tb中的input_if和my_driver中的vif对应起来。
在config_db机制中,分为set和get两步操作。所谓set操作,读者可以简单地理解成是“寄信”,而get则相当于是“收信”。
// top_tb.sv
module top_tb;
... ...
// 实例化接口
my_if input_if(clk, rst_n);
my_if output_if(clk, rst_n);
// 例化DUT
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)
);
// UVM引进了config_db机制,把top_tb中的input_if和my_driver中的vif对应起来
// 在top_tb中执行set操作:set 寄信
initial begin
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input_if);
//uvm_config_db#(virtual 接口module名)::set( ,路径索引, , 例化的接口)?
end
... ...
endmodule
class my_driver extends uvm_driver; // my_driver是一个派生自uvm_driver的类,uvm_driver是一个派生自uvm_component的类,要指明两个uvm_component参数:name和parent
virtual my_if vif; // 声明vif
... ...
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!!!") // 了vm_fatal宏,
endfunction
... ...
endclass
task my_driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
// my_driver中,执行get操作
super.build_phase(phase);
`uvm_info("my_driver", "main_phase is called", UVM_LOW);
/*
top_tb.rxd <= 8'b0;
top_tb.rx_dv <= 1'b0;
*/
vif.data <= 8'b0;
vif.valid <= 1'b0;
while(!vif.rst_n) // while(!top_tb.rst_n)
@(posedge vif.clk); // @(posedge top_tb.clk);
for(int i = 0; i < 256; i++)begin // 是向rxd上发送256个随机数据
@(posedge vif.clk); // @(posedge top_tb.clk);
vif.data <= $urandom_range(0, 255); // top_tb.rxd <=
vif.valid <= 1'b1; // 并将rx_dv信号置为高电平 // top_tb.rx_dv <=
`uvm_info("my_driver", "data is drived", UVM_LOW) // uvm_info宏。这个宏的功能与Verilog中display语句的功能类似
end
@(posedge vif.clk); // @(posedge top_tb.clk);
vif.valid <= 1'b0; // top_tb.rx_dv // 数据发送完毕后,将rx_dv信号置为低电平
phase.drop_objection(this);
endtask