目录
三、UVM常用类
1.1uvm_component和uvm_object
图1 UVM常用类的继承关系
UVM中两大基本概念就是component和object,uvm_object是UVM中最基本的类,几乎所有的类都继承于uvm_object,其中包括uvm_component。因此uvm_component拥有uvm_object的基本特性,但又有自己独特的特性:
1.uvm_component通过在new时指定parent参数来形成一种树形组织结构,这是uvm_object所没有的,因此只有uvm_component及其派生类可以成为树形结构的结点;
2.uvm_component拥有phase自动执行的特点,整个树形结构按照phase机制自动执行结点的各个phase;
1.2常用的uvm_object类
uvm_sequence_item:所有的transaction要从uvm_sequence_item派生,事实上uvm_sequence_item派生自uvm_transaction,但其增加了很多实用的成员变量和方法,因此transaction从uvm_sequence_item派生,可以直接使用这些新增加的变量和方法;
//transaction派生自uvm_sequence_item
class my_transaction extends uvm_sequence_item;
//定义数据包
typedef enum{WR,RD} opt_e;
rand bit[7:0] data;
rand opt_e opt;
//注册factory
`uvm_object_utils_begin(my_transaction)
//加入field_automatic
`uvm_field_int(data,UVM_ALL_ON)
`uvm_field_enum(opt_e,opt,UVM_ALL_ON)
`uvm_object_utils_end
//new函数
function new(string name="my_transaction");
super.new(name);
endfunction
function my_transaction my_copy(my_transaction tr);
this.data=tr.data;
this.opt=tr.opt;
endfunction
function void my_print(my_transaction tr);
$write("data=%0d",tr.data);
$write("opt=%s",tr.opt.name());
endfunction
//重写pre_randomize和post_randomize函数
extern function void pre_randomize();
extern function void post_randomize();
endclass
uvm_sequence:所有sequence要从uvm_sequence派生,sequence就是sequence_item的组合,在task body中通过`uvm_do(my_trans)自动创建transaction的实例my_trans并将其随机化,最终发送给sequencer;
//sequence派生自uvm_sequencer
class my_sequence extends uvm_sequence #(my_transaction);
//声明transaction句柄
my_transaction tr;
//new函数
function new(string name="my_sequence");
super.new(name);
endfunction
//在task body中产生随机transaction
virtual task body();
//使用starting_phase提起和撤销objection
if(starting_phase!=null)
starting_phase.raise_objection(this);
repeat(10) begin
`uvm_do(tr)
end
#100;
if(starting_phase!=null)
starting_phase.drop_objection(this);
endtask
//注册factory
`uvm_object_utils(my_sequence)
endclass
config:config一般直接从uvm_object派生,它的作用是将所有的参数放在一个object(对象)中,然后通过config_db的方式设置给所有需要这些参数的component;
uvm_reg_item:派生自uvm_sequence_item,用于register model当中;
uvm_phase:派生自uvm_object,用于控制component的行为方式,使component按照规范执行各个不同的phase;
1.3常用的uvm_component类
uvm_driver:所有的driver都要派生自uvm_driver。driver的功能主要是向sequencer索要sequence_item(transaction),通过seq_item_port.get_next_item(req)向sequencer索要transaction,并驱动到DUT端口,完成从事务级到端口级的转换。
//driver派生自uvm_driver
class my_driver extends uvm_driver #(my_transaction);
//声明virtual interface
virtual my_if vif;
//new函数
function new(string name="my_driver",uvm_component parent=null);
super.new(name,parent);
endfunction
//加入factory
`uvm_component_param_utils(my_driver)
//build_phase中进行接口连接和创建实例
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
uvm_config_db#(virtual my_if)::get(this,"","vif",vif);//接口连接
endfunction
//task main_phase中向sequencer请求transaction并驱动到DUT
task main_phase(uvm_phase phase);
super.main_phase(phase);
vif.data<='b0;
vif.winc<=1'b0;
while(!vif.wrst_n)
@(posedge vif.wclk);
while(1) begin//使用while(1)循环,只要有transaction即驱动
seq_item_port.get_next_item(req);//try_next_item为非阻塞
drive_one_pkt(req);
seq_item_port.item_done();
end
endtask
//驱动方式需要满足总线协议
extern virtual task drive_one_pkt(my_transaction tr);
endclass
uvm_monitor:所有monitor都要派生自uvm_monitor。monitor主要功能是从DUT端口接收数据,并将接收到的数据转换为transaction级的sequence_item,最终发送给scoreboard进行比较;
//monitor继承自uvm_monitor
class my_monitor extends uvm_monitor;
//声明虚接口
virtual my_if vif;
//声明发信端口将采样的数据发送给scoreboard
uvm_analysis_port#(my_transaction) ap;
//注册工厂
`uvm_component_utils(my_monitor)
//new函数
function new(string name="my_monitor",uvm_component parent=null);
super.new(name,parent);
endfunction
//接口连接和创建实例
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
ap=new("ap",this);//例化通信端口
uvm_config_db#(virtual my_if)::get(this,"","vif",vif);//接口连接
endfunction
//main_phase中实现DUT数据采样
task main_phase(uvm_phase phase);
super.main_phase(phase);
my_transaction tr;
while(1) begin
tr=new("tr");
collect_one_pkt(tr);
ap.write(tr);//通过端口发送至scoreboard
end
endtask
//采样方式满足总线协议
extern virtual task collect_one_pkt(my_transaction tr);
endclass
uvm_sequencer:所有sequencer都要派生自uvm_sequencer。sequencer的功能是组织管理sequence,当driver请求数据时,它就把sequence发送的sequence_item转发给driver;
//sequencer继承自uvm_sequencer
class my_sequencer extends uvm_sequencer #(my_transaction);
//注册工厂
`uvm_component_utils(my_sequencer)
//new函数
function new(string name="my_sequencer",uvm_component parent=null);
super.new(name,parent);
endfunction
endclass
uvm_scoreboard:一般scoreboard派生自uvm_scoreboard。scoreboard的功能是接收reference model和monitor发送来的数据,并进行比较,根据比较结果判断DUT功能是否正常;
//scoreboard继承自uvm_scoreboard
class my_scoreboard extends uvm_scoreboard;
//声明队列存放期望值
my_transaction expect_queue[$];
//声明收信端口
uvm_blocking_get_port #(my_transaction) exp_port;
uvm_blocking_get_port #(my_transaction) act_port;
//注册工厂
`uvm_component_utils(my_scoreboard)
//new函数
function new(string name="my_scoreboard",uvm_component parent=null);
super.new(name,parent);
endfunction
//build_phase中创建实例
function void build_phase(uvm_phase phase);
super.build_phase(phase);
exp_port=new("exp_port",this);
act_port=new("act_port",this);
endfunction
//mian_phase中实现期望和实际结果的比较,并产生比较结果
task main_phase(uvm_phase phase);
super.main_phase(phase);
my_transaction expect,actual,tmp;//定义期望、实际、中转事务
bit result;//定义对比结果
fork//期望结果存储与对比同时进行
while(1) begin
exp_port.get(expect);//接收reference model产生的期望结果
expect_queue.push_back(expect);//存放期望结果
end
while(1) begin
act_port.get(actual);//从monitor接收DUT输出结果
if(expect_queue.size()>0) begin
tmp=expect_queue.pop_front();//取出期望数据
result=actual.compare(tmp);//比较期望和实际结果
if(result) begin
`uvm_info("my_scoreboard","compare successful",UVM_LOW)
end
else begin
`uvm_error("my_scoreboard","compare failed")
$display("the expect pkt is");
tmp.my_print();
$display("this actual pkt is");
actual.my_print();
end
end
else begin
`uvm_error("my_scoreboard","received from DUT,while expect_queue is empty")
end
end
join
endtask
endclass
reference model:reference_model一般直接派生自uvm_component。通过高级语言如SV实现和DUT相同的功能,并发送输出结果供scoreboard进行比较。也通过DPI接口进行调用C或其他高级语言model;
//reference model继承自uvm_component
class my_model extends uvm_component;
//声明通信端口句柄
uvm_blocking_get_port #(my_transaction) port;
uvm_analysis_port #(my_transaction) ap;
//注册工厂
`uvm_component_utils(my_model)
//new函数
function new(string name="my_model",uvm_component parent=null);
super.new(name,parent);
endfunction
//创建实例
function void build_phase(uvm_phase phase);
super.build_phase(phase);
port=new("port",this);
ap=new("ap",this);
endfunction
//main_phase接收DUT输入数据并实现DUT功能,产生期望结果发送给scoreboard
virtual task main_phase(uvm_phase phase);
super.main_phase(phase);
my_transaction tr;
my_transaction tr_new;
while(1) begin
port.get(tr);
tr_new=new("tr_new");
tr_new.my_copy(tr);
tr_new.my_print();
ap.write(tr_new);
end
endtask
endclass
uvm_agent:所有的agent派生自uvm_agent。它的作用是将driver和monitor封装在一起,其引入了枚举变量is_active,用于控制实际例化的对象;
//agent继承自uvm_agent
class my_agent extends uvm_agent;
//声明driver、monitor、sequencer句柄
my_driver drv;
my_monitor mon;
my_sequencer sqr;
//声明通信端口
uvm_analysis_port #(my_transaction) ap;
//注册工厂
`uvm_component_utils(my_agent)
//new函数
function new(string name="my_agent",uvm_component parent=null);
super.new(name,parent);
endfunction
//build_phase中创建实例,agent中内置成员变量is_active
function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(is_active==UVM_ACTIVE) begin
sqr=my_sequencer::type_id::create("sqr",this);
drv=my_driver::type_id::create("drv",this);
end
mon=my_monitor::type_id::create("mon",this);
endfunction
//端口连接
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
if(is_active==UVM_ACTIVE) begin
drv.seq_item_port.connect(sqr.seq_item_export);//driver与sequencer通信端口连接
end
ap=mon.ap;//agent发信端口指向monitor发信端口
endfunction
endclass
uvm_env:所有的env都派生自uvm_env。env作用是将验证平台固定不变的component封装在一起,提高验证平台的复用性。当要运行不同测试用例时,只需例化env即可生成相同的验证组件;
//env继承自uvm_env
class my_env extends uvm_env;
//声明in/out agent、reference model及scoreboard句柄
my_agent i_agt,o_agt;
my_model mdl;
my_scoreboard scb;
//fifo用于缓存发向reference model的输入采样数据
uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
//注册工厂
`uvm_component_utils(my_env)
//new函数
function new(string name="my_env",uvm_component parent=null);
super.new(name,parent);
endfunction
//build_phase创建实例并指定agent.is_active值
function void build_phase(uvm_phase phase);
super.build_phase(phase);
i_agt=my_agent::type_id::create("i_agt",this);
o_agt=my_agent::type_id::create("o_agt",this);
mdl =my_model::type_id::create("mdl",this);
scb =my_scoreboard::type_id::create("scb",this);
i_agt.is_active=UVM_ACTIVE;
o_agt.is_active=UVM_PASSIVE;
endfunction
//connect_phase中进行component的通信端口连接
function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
//model通过fifo接收来自monitor的输入采样数据
i_agt.ap.connect(agt_mdl_fifo.analysis_export);
mdl.port.connect(agt_mdl_fifo.blocking_get_export);
//mdl产生的期望结果发送给scoreboard的exp_port
mdl.ap.connect(scb.exp_port);
//monitor采样的输出数据发送给scoreboard的act_port
o_agt.ap.connect(scb.act_port);
endfunction
enclass
uvm_test:所有的testcase都要派生自uvm_test或其派生类。通常会从uvm_test派生出base_test,例化env并设置sequencer的default_sequence,并进行验证平台的基础设置或参数设置,实际的test_case派生自base_test,然后根据测试点进行针对性修改。可以通过命令行参数+UVM_TESTNAME=<case_name>指定运行的case;
//base_test继承自uvm_test
class base_test extends uvm_test;
//声明env句柄
my_env env;
//new函数
function new(string name="base_test",uvm_component parent=null);
super.new(name,parent);
endfunction
//build_phase中创建实例并启动sequence
function void build_phase(uvm_phase phase);
super.build_phase(phase);
env=my_env::type_id::create("env",this);
//启动default_sequence
uvm_config_db#(uvm_object_wrapper)::set(this,
"env.i_agt.sqr.main_phase",
"default_sequence",
my_sequence::type_id::get());
endfunction
//report_phase进行仿真后报告
function void report_phase(uvm_phase);
super.report_phase(phase);
uvm_report_server server;
int err_num;
server=get_report_server();
err_num=server.get_severity_count(UVM_ERROR);//获取UVM_ERROR数量
if(err_num!=0) begin
$display("TEST CASE IS ERROR");
end
else begin
$diaplay("TEST CASE IS PASSED");
end
endfunction
endclass
top_tb:验证的顶层模块,进行接口发信、DUT例化以及case运行;
`timescale 1ns/1ps
`include "uvm_macros.svh"
import uvm_pkg::*;
`include "dut.v"
`include "my_if.sv"
import tb.pkg::*;
//tb顶层模块
module top_tb;
//定义公共信号clk、rst_n等
reg clk,rst_n;
//声明接口
my_if.INPUT input_if;
my_if.OUTPUT output_if;
my_if vif(clk,rst_n);
//driver、monitor的接口发信
initial begin
uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.i_agt.drv","vif",input_if);
uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.i_agt.mon","vif",input_if);
uvm_config_db#(virtual my_if)::set(null,"uvm_test_top.o_agt.mon","vif",outpur_if);
end
//clk
initial begin
clk=0;
forever #10 clk=~clk;
end
//rst_n
initial begin
rst_n=1'b0;
#100;
rst_n=1'b1;
end
//运行case
initial begin
run_test();
end
//例化DUT
dut inst_dut(vif);
endmodule
1.4UVM树形结构
UVM采样树形的组织结构管理验证平台的各个component。所有的component类如sequencer、driver、monitor、agent、model、scoreboard、env、case都是树的一个结点。它们在new时都需要指定一个uvm_component类型的变量parent,及它们的上一层级(父结点),通常都在component的上一层级创建实例,因此parent参数为this。
图2 UVM树形结构
如果component例化时指定其父结点parent为null,那么这个component的parent将会被系统设置为系统中唯一的uvm_root的实例uvm_top。uvm_root的存在确保了整个验证平台只有一颗UVM树,所有的结点都是uvm_top的子结点。使用uvm_top.print_topology()函数可以打印整个UVM树的拓扑结构。通过comp.get_full_name()可以返回任意结点的路径索引。
uvm_top.print_topology();//打印拓扑结构
`uvm_info("drv_full_name",get_full_name(),UVM_LOW)//打印路径uvm_test_top.env.i_agt.drv
使用comp.get_parent()返回component的parent类,使用comp.get_child(string name)返回指定的child类,comp.get_num_children()函数用于返回当前component所有child的数量。
//UVM源代码
extern virtual function uvm_component get_parent();
extern function uvm_component get_child(string name);
extern function int get_num_children();