2.1 验证平台的组成
2.2 只有driver的验证平台
2.2.1 最简单的验证平台
class my_driver extends uvm_driver;
function new(string name = "my_driver", uvm_component parent = null);
super.new(name, parent);
endfunction
extern virtual task main_phase(uvm_phase phase)
endclass
task my driver::main_phase(uvm_phase phase);
......
`uvm_info("my_driver", "data is drived", UVM_LOW)
......
endtask
uvm_driver是一个派生字uvm_component的类,每一个派生自uvm_component或其派生类的类在其new函数种要指明两个参数:name 和 parent。
uvm_info宏有三个参数,第一个参数是字符串,把打印的信息归类,第二个参数是具体要打印的信息,第三个参数是冗余级别(UVM_LOW, UVM_MEDIUM, UVM_HIGH)。
get_full_name() 获取路径索引。
2.2.1 加入factory机制
"my_driver.sv"
class my_driver extends uvm_driver;
`uvm_component_utils(my_driver)
......
endclass
"top_tb.sv"
module top_tb;
initial begin
run_test("my_driver");
end
endmodule
`uvm_component_utils(my_driver) 其功能之一是将my_driver登记在UVM内部的一张表中,这张表是factory功能实现的基础。
一个run_test语句会创建一个my_driver的实例,并会自动调用my_driver的main_phase。只有在类的定义时声明了`uvm_component_utils(my_driver)才能使用这个功能。所有派生自uvm_component及其派生类的类都应该使用宏注册。
2.2.3 加入objection机制
task my_driver::main_phase(uvm_phase phase);
phase.raise_objection(this);
......
phase.drop_objection(this);
endtask
raise_objection语句必须在main_phase种第一个消耗仿真时间的语句之前,如$display语句可以放在raise_objection之前。
2.2.4 加入virtual interface
"top_tb.sv"
module top_tb;
initial begin
run_test("my_driver");
end
initial begin
uvm_config_db#(virtual my_if)::set(null, "uvm_test_top", "vif", input);
end
endmodule
"my_driver.sv"
virtual function void build_phase(uvm_phase phase);
......
if(!uvm_config_db#(virtual my_if)::get(this, " ", "vif", vif)
......
endfunction
UVM通过run_test语句实例化了一个脱离了top_tb层次结构的实例,建立了一个新的层次结构。
对于这种脱离了top_tb层次结构,同时又期望在top_tb中对其进行某些操作的实例,UVM引进了config_db机制。
在build_phase中主要通过config_db的 set 和 get 操作来传递一些数据,以及实例化成员变量等。build_phase是一个函数phase,不消耗仿真时间,总是在仿真时间为0时执行,而main_phase是一个任务phase。
config_db的 set 和 get 函数都有四个参数,这两个函数的第三个参数必须完全一致。set函数的第四个参数表示要将哪个interface通过config_db传递给my_driver,get函数的第四个参数表示把得到的interface传递给哪个my_driver的成员变量。set函数的第二个参数表示的是路径索引,无论传递给run_test的参数是什么,创建的实例的名字都为uvm_test_top,因为set操作的目标是my_driver,所以set函数的第二个参数就是uvm_test_top。
2.3 为验证平台加入各个组件
2.3.1 加入transaction
验证平台的组件之间,信息的传递是基于transaction的。
class my_transaction extends uvm_sequence_item;
rand bit [47:0] dmac;
......
constraint ...{}
function bit [31:0] calc_crc();
return 32'h0;
endfunction
`uvm_object_utils(my_transaction)
function new(...);
......
endclass
在UVM中,所有transaction都要从uvm_sequence_item派生。
my_transaction有生命周期,这种类都是派生自uvm_object或者uvm_object的派生类,uvm_sequence_item的祖先就是uvm_object,UVM中具有这种特征的类都要使用uvm_object_utils宏来实现。
2.3.2 加入env
run_test只能实例化一个实例。引入一个容器类,在这个容器类中实例化driver, monitor, reference model 和 scoreboard等,在调用run_test时,传递参数是这个容器类,uvm_env。
class my_env extends uvm_env;
my_driver drv;
......
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
factory机制注册过的类的实例化: type_name::type_id::create()。
2.3.3 加入monitor
class my_monitor extends uvm_monitor;
virtual my_if vif;
`uvm_component_utils(my_monitor)
function new(...)
......
virtual function void build_phase(uvm_phase phase);
......
endclass
2.3.4 封装成agent
driver和monitor二者处理的是同一种协议,agent将driver和monitor封装在一起,不同agent代表了不同的协议。
class my_agent extends uvm_agent;
my_driver drv;
my_monitor mon;
......
`uvm_component_utils(my_agent)
endclass
function void my_agent::build_phase(uvm_phase phase);
super.build_phase(phase);
if(is.active == UVM_ACTIVE) begin
drv = my_driver::type_id::create("drv", this);
end
mon = my_monitor::type_id::create("mon", this);
endfunction
function void my_agent::connect_phase(uvm_phase phase);
super.connect_phase(phase);
endfunction
is_avtive 是uvm_agent的一个成员变量:
uvm_active_passive_enum is_active = UVM_ACTIVE;
typedef enum bit {UVM_PASSIVE=0, UVM_ACTIVE=1} uvm_active_passive_enum;
2.3.5 加入reference model
class my_model extends uvm_component;
uvm_blocking_get_port #(my_transaction) port;
uvm_analysis_port #(my_transaction) ap;
在UVM中,通常使用TLM(Transaction Level Modeling)实现compone之间transaction级别的通信。
数据的一种发送方式:uvm_analysis_port
数据的一种接收方式:uvm_blocking_get_port
my_monitor在main_phase收集完一个transaction,将其写入ap中:
task my_monitor::main_phase(uvm_phase phase);
......
ap.write(tr);
my_env中使用fifo将 ap 和 port 端口联系在一起:
uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
......
agt_mdl_fifo = new("agt_mdl_fifo", this);
my_env在connect_phase中将fifo分别与my_monitor中的analysis_port和my_model中的blocking_get_port相连:
function void my_env::connect_phase(uvm_phase phase);
super.connect_phase(phase);
i_agt.ap.connect(agt_mdl_fifo.analysis_export);
mdl.port.connect(agt_mdl_fifo.blocking_get_export);
endfunction
my_agent在connect_phase中:ap = mon.ap。
2.3.6 加入scoreboard
class my_scoreboard extends uvm_scoreboard;
2.3.7 加入field_automation机制
class my_transaction extends uvm_sequence_item;
rand bit[47:0] dmac;
rand byte pload[];
......
`uvm_object_utils_begin(my_transaction)
`uvm_field_int(dmac, UVM_ALL_ON)
`uvm_field_array_int(pload, UVM_ALL_ON)
`uvm_object_utils_end
endclass
使用uvm_object_utils_begin和uvm_object_utils_end来实现my_factory的factory注册,在这两个宏中间,使用uvm_field宏注册所有字段,就可以直接调用copy, compare, print等函数。
data_size = tr.pack_bytes(data_q)/8
pack_bytes将 tr 中的所有字段变成byte流放入data_q中,字段按照uvm_field系列宏书写的顺序排列。
data_size = tr.unpack_bytes(data_array)/8
unpack_bytes将data_q中的byte流转换成 tr 中的各个字段,其输入参数必须是一个动态数组。
2.4 UVM的终极大作:Sequence
2.4.1 在验证平台中加入sequencer
在规范化的UVM验证平台中,driver只负责驱动transaction,而不负责产生transaction。
class my_sequencer extends uvm_sequencer #(my_transaction);
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
`uvm_component_utils(my_sequencer)
endclass
class my_driver extends uvm_driver #(my_transaction);
2.4.2 sequence机制
class my_sequence extends uvm_sequence #(my_transaction);
my_transaction m_trans;
function new(string name = "my_sequence");
super.new(name);
endfunction
virtual task body();
repeat (10) begin
`uvm_do(m_trans)
end
#1000;
endtask
`uvm_object_utils(my_sequence)
endclass
当一个sequence启动之后,会自动执行body中的代码。
`uvm_do: (1)创建一个my_transaction的实例m_trans; (2)将其随机化; (3)最终将其送给sequencer。
uvm_driver中有成员变量seq_item_port,uvm_sequencer中有成员变量seq_item_export,这两者之间可以建立一个通道,通道中传递transaction。在my_agent中,使用connect函数将两者联系在一起:
drv.seq_item_port.connect(sqr.seq_item_export);
2.4.3 default_sequence的使用
"my_env.sv"
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
......
uvm_config_db#(uvm_object_wrapper)::set(this, "i_agt.sqr.main_phase", "default_sequence", my_sequence::type_id::get());
2.5 建造测试用例
2.5.1 加入base_test
class base_test extends uvm_test;
my_env env;
function new(...)
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void report_phase(uvm_phase phase);
`uvm_component_utils(base_test)
endclass
2.5.2 UVM中测试用例的启动
class case0_sequence extends uvm_sequence #(my_transaction);
......
endclass
class my_case0 extends base_test
......
uvm_config_db#(uvm_object_wrapper)::set(this, "env.i_agt.sqr.main_phase", "default_sequence", case0_sequence::type_id::get());
class case1_sequence extends uvm_sequence #(my_transaction);
......
endclass
class my_case1 extends base_test
......
endclass