UVM 基础入门
一、框架
框架一
构成环境的组件都从uvm_component类继承而来,这是因为它们都从uvm_component类继承了phase机制,都会经历各个phase阶段。常见组件如下:
-
uvm_sequencer
:所有的sequencer都要派生自uvm_sequencer。
sequencer功能:组织管理sequence,driver申请数据时,它就把sequence生成的sequence_item发给driver。 -
uvm_driver
:所有的driver都要派生自uvm_driver。
driver功能:向sequencer申请sequence_item(数据包),并将包里的信息按照总线协议规定驱动到DUT的端口上。//uvm_driver定义 class uvm_driver #(type REQ=uvm_sequence_item,type RSP=REQ) extends uvm_component;
-
uvm_monitor
:所有的的monitor都要派生自uvm_monitor。
monitor功能:为了检测接口数据,从DUT接收数据,并将其转成transaction级别的sequence_item,发送给scoreboard,供其比较。 -
uvm_agent
:所有的agent要派生自uvm_agent。
agent功能:将sequencer、driver和monitor封装在一起,agent模块的使用提高了代码的可重用性。
(UVM_ACTIVE/UVM_PASSIVE、两种模式) -
uvm_scoreboard
:一般的scoreboard都要派生自uvm_scoreboard。
scoreboard功能:比较reference model和monitor分别发送来的数据,根据比较结果判断DUT是否正确工作。 -
uvm_env
:所有的env要派生自uvm_env。
env功能:将平台上的component组件封装在一起,实现一个环境多个用例。运行不同用例时,在其中实例化env即可。可以看做一个结构化的容器。 -
uvm_test
:所有的测试用例要派生自uvm_test或其派生类
。
任何一个派生的测试用例中,都要实例化env。这样,当用例运行时,才能把数据发给DUT,并接收DUT的数据。 -
reference model
:UVM中并没有针对reference model定义一个类。通常,reference model直接派生自uvm_component。
作用就是模仿DUT,完成与DUT相同的功能。DUT是用Verilog写成的时序电路,而reference model则可以直接使用SystemVerilog高级语言的特性,同时还可以通过DPI等接口调用其他语言来完成与DUT相同的功能。
框架二
框架三
二、各组建关系
component与object是UVM中最基本的两个概念。
uvm_object是UVM中最基本的类,几乎全部的类都是由uvm_object类派生出来,其中包含uvm_component。
uvm_object 提供的核心方法主要提供与数据操作的相关服务,Copy
、Clone
、Compara
、Print
、pack/unpack
uvm_component有两大特性是uvm_object所不具备的。
- **一
是通过在new的时候必须要指定parent参数来产生一种树形的组织结构
; -
二
是具有phase的自动执行特点
。所有的UVM树的结点都是由uvm_component构成的,只有基于uvm_component派生出的类才有可能是UVM树的结点。UVM中一些常见类的继承关系如图3所示。
三、uvm_object类
类的预定义
预定义uvm_object的相关扩展类,以便在uvm_object的相关方法中调用:
typedef class uvm_report_object;
typedef class uvm_object_wrapper;
typedef class uvm_objection; // uvm_object类中未使用
typedef class uvm_component; // uvm_object类中未使用
typedef class uvm_status_container;
类的方法
类的声明
virtual class uvm_object extends uvm_void;
endclass
变量声明
// 静态变量,决定是否使能UVM中的播种机制,影响全局的reseed()方法;
static bit use_uvm_seeding = 1;
// 局部变量,通过new函数将类的实例化名传递给该变量,变量对子类及外部不可见;
local string m_leaf_name;
// 局部变量,使用new()函数实例化类时,m_inst_count自动加1并传递给该局部变量,用于返回实例化id,变量对子类及外部不可见;
local int m_inst_id;
// 静态局部变量,用于返回实例化次数,该局部变量对子类可见,对外部不可见;
static protected int m_inst_count;
static uvm_status_container __m_uvm_status_container = new;
// 查找表,用于copy()函数的实现;
local static uvm_object uvm_global_copy_map[uvm_object];
方法声明
extern function new (string name="");
extern function void copy (uvm_object rhs);
extern virtual function void do_pack (uvm_packer packer);
extern function int unpack (ref bit bitstream[],
input uvm_packer packer=null);
//其他见UVM参考手册
方法的实现
- new()函数:通过给定的字符串名称创造该类的实例化对象,如果名字没有给定,那么该对象无名。字符串名传递给局部变量m_leaf_name,实例化 m_inst_id 通过静态局部变量m_inst_count计数的方式指定。
function uvm_object::new (string name="");
m_inst_id = m_inst_count++;
m_leaf_name = name;
endfunction
-
clone ()函数:虚方法,创建并返回待clone对象的副本。
function uvm_object uvm_object::clone(); uvm_object tmp; tmp = this.create(get_name()); // 调用create()方法,创建实例 if(tmp == null) uvm_report_warning("CRFLD", $sformatf("The create method failed for %s, object cannot be cloned", get_name()), UVM_NONE); else tmp.copy(this); // 调用uvm_object中的copy函数 return(tmp); endfunction 123456789
-
print ()函数:方法以给定的 printer 参数控制的格式和方式深度打印该对象的属性。如果参数没有指定,那么使用全局的<uvm_default_printer>所定义的输出格式。
function void uvm_object::print(uvm_printer printer=null); if (printer==null) // 检查是否给定uvm_printer参数,c如果没有指定参数,使用默认的uvm_default_printer; printer = uvm_default_printer; if (printer == null) // 检查uvm_default_printer是否为空; `uvm_error("NULLPRINTER","uvm_default_printer is null") // 其中printer.knobs.mcd=UVM_STDOUT=1 $fwrite(printer.knobs.mcd,sprint(printer)); // 使用$fwrite调用sprint函数将类对象的属性打印至标准输出。 endfunction 12345678
-
sprint ()函数: 与 print() 函数类似,但 sprint() 函数返回字符串而不是显示到标准输出。因此该函数被print()函数调用。
非虚函数,不可被重载,继承子类必须重写do_print()函数,并且使用提供的printer策略来格式化输出。
function string uvm_object::sprint(uvm_printer printer=null); bit p; if(printer==null) printer = uvm_default_printer; // 同样的,调用该方法时如果没有指定参数,那么使用默认的uvm_default_printer; // not at top-level, must be recursing into sub-object if(!printer.istop()) begin __m_uvm_status_container.printer = printer; __m_uvm_field_automation(null, UVM_PRINT, ""); do_print(printer); return ""; end printer.print_object(get_name(), this); // 如果printer对象中的m_string字符串非空,那么返回该非空字符串,不再调用emit()方法; if (printer.m_string != "") return printer.m_string; return printer.emit(); endfunction
输出如下表格形式字符串:
--------------------------------------------------- Name Type Size Value --------------------------------------------------- c1 container - @1013 d1 mydata - @1022 v1 integral 32 'hcb8f1c97 e1 enum 32 THREE str string 2 hi value integral 12 'h2d ---------------------------------------------------
-
copy ()函数:拷贝一份备份到指定对象,用于实例的复制,使用B.copy(A)的方式将A的实例拷贝到B的实例中,此时B的实例已经分配好了内存空间。该函数没有使用关键字virtual,不可被子类覆盖(override)。
function void uvm_object::copy (uvm_object rhs); // For cycle checking static int depth; // 如果copy对象非空且已经存在于局部变量uvm_global_copy_map中,此时已经保存了该对象的备份,那么直接返回; if((rhs !=null) && uvm_global_copy_map.exists(rhs)) begin return; end // 如果copy对象为空,那么给出警告并返回; if(rhs==null) begin uvm_report_warning("NULLCP", "A null object was supplied to copy; copy is ignored", UVM_NONE); return; end // 如果对象非空且没有被保存,那么将该类对象保存到uvm_global_copy_map且深度+1 uvm_global_copy_map[rhs]= this; ++depth; __m_uvm_field_automation(rhs, UVM_COPY, ""); // 调用虚函数,没有被重写时无返回值; do_copy(rhs); // 调用虚函数,没有被重写时无返回值; --depth; // 调用完毕后深度计数递减并删除copy至uvm_global_copy_map的对象; if(depth==0) begin uvm_global_copy_map.delete(); end endfunction
-
do_copy ()函数:无任何返回值,由于copy()函数不可被override(没有用virtual关键字),因此do_copy()函数被用来copy一个子类域。
function void uvm_object::do_copy (uvm_object rhs); return; endfunction
实现方式如下:子类必须调用父类的do_copy()函数,且必须在copy之前使用$cast进行类型转换。
class mytype extends uvm_object; ... int f1; virtual function void do_copy (uvm_object rhs); mytype rhs_; super.do_copy(rhs); $cast(rhs_,rhs); field_1 = rhs_.field_1; endfunction
-
compare ()函数:深度比较此数据对象的成员与rhs(right hand side)中提供的对象的成员。匹配返回1,否则返回0。
function bit uvm_object::compare (uvm_object rhs, uvm_comparer comparer=null); ... if(!done) begin comparer.compare_map[rhs]= this; __m_uvm_field_automation(rhs, UVM_COMPARE, ""); dc = do_compare(rhs, comparer); // 返回dc显示调用do_compare()方法; end ... return (comparer.result == 0 && dc == 1); endfunction
-
pack ()函数:将对象的属性按位串接在bit数组中;
function int uvm_object::pack (ref bit bitstream [], input uvm_packer packer =null ); m_pack(packer); packer.get_bits(bitstream); return packer.get_packed_size(); endfunction
-
pack_bytes ()函数:将对象的属性按位串接在byte数组中;
-
pack_ints ()函数:将对象的属性按位串接在int数组中;
-
unpack ()函数:从bit数组中提取对象的属性值。
function int uvm_object::unpack (ref bit bitstream [], input uvm_packer packer=null); m_unpack_pre(packer); packer.put_bits(bitstream); m_unpack_post(packer); packer.set_packed_size(); return packer.get_packed_size(); endfunction
-
unpack_bytes ()函数:从byte数组中提取对象的属性值。
-
unpack_ints ()函数:从int数组中提取对象的属性值。
-
m_pack ()函数:uvm_object类内部方法,不能直接使用。
function void uvm_object::m_pack (inout uvm_packer packer); // 参数如果是null,那么使用默认uvm_default_packer,这与uvm_printer使用类似; if(packer!=null) __m_uvm_status_container.packer = packer; else __m_uvm_status_container.packer = uvm_default_packer; packer = __m_uvm_status_container.packer; packer.reset(); // 初始化变量 packer.scope.down(get_name()); // 将实例化对象的字符串名称push到packer的局部变量m_stack中; __m_uvm_field_automation(null, UVM_PACK, ""); do_pack(packer); // 调用do_pack()方法; packer.set_packed_size(); // 设置包的大小 packer.scope.up(); // 将m_stack队列出栈并进行其他操作; endfunction
参考: