目录如下:
第2章 一个简单的UVM验证平台
2.1 验证平台的组成
2.2 只有driver的验证平台
2.2.1 最简单的验证平台
2.2.2 加入factory机制
2.2.3 加入objection机制
2.2.4 加入virtual interface
2.3 为验证平台加入各个组件
2.3.1 加入transaction
2.3.2 加入env
2.3.3 加入monitor
未完待续
正文在下面
2.3.2 加入env
代码又多了一个my_env.sv。
在之前的例子中,我们只在top_tb中实例化了组件driver(通过run_test(“my_driver”)),之后还要加入reference model、scoreboard等等。那么问题就来了,假如这些组件都已经定义好了,我们应该在验证平台的什么位置去实例化他们?① 在top_tb中?在top_tb中用run_test进行实例化不可行,因为它只能实例化一个类;② 用2.2.1的方式实例化?不可行,因为run_test相当于在top_tb的结构层次之外建立一个新的结构层次,而2.2.1节的方式是基于top_tb的层次结构,如果基于此进行实例化,那么run_test的引用就没有什么意义了。
上面问题的答案是引入一个容器类,在这个容器类中实例化driver、monitor、reference model和scoreboard,再调用run_test时,传递的参数不再是my_driver或者别的某一个组件的类名称,而是这个容器类,既让UVM自动创建这个容器类的实例。在UVM中,这个容器类称为uvm_env,下面将会详细介绍。
一、my_env
容器env——my_env.sv代码如下:
`ifndef MY_ENV__SV
`define MY_ENV__SV
class my_env extends uvm_env;
my_driver drv;
function new(string name = "my_env", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
drv = my_driver::type_id::create("drv", this);
endfunction
`uvm_component_utils(my_env)
endclass
`endif
代码分析如下:
① 第4行,所有的env应该派生自uvm_env;
② 第6行,在容器类my_env中例化my_driver,产生句柄drv;
③ 第14行,是drv的实例化,没有直接调用my_driver的new函数,而是用了一种古怪的方式,这种方式是factory机制注册过的类才能使用的实例化方式;只有使用这种方式实例化的实例,才能使用后文要讲述的factory机制中最为强大的重载功能。验证平台中的组件在实例化时都应该使用type_name::type_id::create的方式;
④ 第17行,因为容器类在仿真中也是一直存在的,所以用该使用uvm_component_utils来注册。
让我们再来看一下第14行实例化语句,传递了两个参数,一个是实例化名字drv,另一个是parent,在这里是this指针,表示my_env。之前说了后面介绍的parent:由于my_driver是在uvm_env中实例化的,所以my_driver的父节点(parent)就是my_env。通过parent,UVM建立起了树形的组织结构。在树型结构中,由run_test创建的实例是树根(这里是my_env),并且树根的名字是固定的,是uvm_test_top;树根下面会长出树叶(现在只有my_driver),长树叶的过程要在my_env的build_phase中用type_name::type_id::create的方式手动实现。树根和树叶都是由uvm_component或者其派生类继承而来的(在my_env.sv中看到new函数由name和parent两个参数,就应该能看出来),现在UVM树的结构如下图所示:

二、top_tb
现在验证平台存在两个build_phase(my_env和my_driver),他们的执行顺序是从根到叶。 my_driver在验证平台中的层次结构从根变为了叶,所以top_tb中用config_db机制寄interface时,要改变相应的路径,当然run_test的参数也要变化,代码如下:
`timescale 1ns/1ps
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "my_if.sv"
`include "my_transaction.sv"
`include "my_driver.sv"
`include "my_env.sv"
module top_tb;
reg clk;
reg rst_n;
reg[7:0] rxd;
reg rx_dv;
wire[7:0] txd;
wire tx_en;
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));
initial begin
clk = 0;
forever begin
#100 clk = ~clk;
end
end
initial begin
rst_n = 1'b0;
#1000;
rst_n = 1'b1;
end
initial begin
run_test("my_env");
end
initial begin
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top.drv", "vif", input_if);
end
endmodule
和之前的top_tb对比一下,区别如下:
① 第8行,用`include将新写的my_env包含进来;
② 第43行(之前的42行),传递给run_test的参数不再是my_driver了,而是my_env,因为此时UVM树的根变为了my_env;
③ 第47行(之前的48行),参数化的类uvm_config_db调用静态成员set,第二个参数变味了uvm_test_top.drv,这是因为set的第二个参数时路径索引,可以理解为要把这个接口寄给谁,因为是要寄给drv,所以这样写。注意在my_env的build_phase中,用type_name::type_id::create的方式实例化类my_driver时,传递给name的参数时drv。
my_driver.sv、my_transaction.sv、my_if.sv没有变化。
三、小结
- 容器env实例化UVM组件,top_tb中只需要例化env;
- 容器env实例化组件的方式是type_name::type_id::create;
- UVM树的概念,在代码中如何体现;
- build_phase的执行顺序。