Get格雅UVM实战指南

合集下载
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

UVM实战指南
Callback——最简单的callback
2021-01-21 22:06:01| 分类:SystemVerilog|字号订阅
在RVM、VMM、OVM/UVM中,常常提到callback这个概念。

指在事先设置好的地方留下一个接口,通过向这个接口添加一些函数对象,来到达不改变代码结构而动态修改代码行为。

下面用systemverilog来举一个简单的callback 例子:
1 class callback;
2
3 virtual task cb_pre_run();
4 $display("base callback run");
5 endtask:cb_pre_run
6
7 endclass:callback
8
9 class widget;
10
11 callback cb_queue[$];
12
13 function void add_cb(callback cb);
14 cb_queue.push_back(cb);
15 endfunction:add_cb
16
17 task run();
18 // add callback here
19 foreach(cb_queue[i])begin
20 cb_queue[i].cb_pre_run();
21 end
22 $display("widget run....");
23 endtask:run
24 endclass:widget
25
26 module top;
27
28 class ext_callback extends callback;
29 task cb_pre_run();
30 $display("ext callback run");
31 endtask:cb_pre_run
32 endclass:ext_callback
33 widget w;
34 callback cb0;
35 ext_callback cb_ext;
36
37
38 initial begin
39 w =new;
40 cb0 =new;
41 cb_ext =new;
42 w.run;
43 w.add_cb(cb0);
44 $display("=========== After Add base Callback");
45 w.run;
46 w.add_cb(cb_ext);
47 $display("=========== After Add extention Callback");
48 w.run;
49 end
50
51 endmodule;// top
在sysverilog中,没有函数指针的概念,因此必须将函数包装成为一个对象,就是上面例子
中的callback class. 而为了在widget的对象中使用这个函数对象,必须事先在设计好的调用点对callback对象中的函数进行逐个调用〔代码19-21行〕。

widget的对象事先并不知道有多少callback 对象,而是将所有的callback对象放到自己的一个callback对象队列中(cb_queue)。

然后逐个对这个队列中的所有对象进行函数调用。

上面的程序编译执行后,结果如下:
widget run....
=========== After Add base Callback
base callback run
widget run....
=========== After Add extention Callback
base callback run
ext callback run
widget run....
可以看出,在执行的过程中,可以对widget 对象进行动态的添加callback,从而动态的改变widget对象的动作。

上面的例子非常简单,仅仅是输出一些讯息而已,有一些局限:
1. 这个callback结构并不能够真的改变widget对象的内部成员,以及处理的数据内容,仅仅能够输出一些讯息。

2. 对每一个widget的对象,都需要单独添加相关callback对象,假设程序中又创立了一个新的widget对象,那么这个对象的callback queue初始是空的,也就是没有callback。

必须再次添加才能让这个新的widget 调用相应的callback功能。

3. c allback只有一个地方,可以扩展到多个地方。

另外也可以使用function,而不仅仅是task.
工厂模式的简单理解
首先,如果一个客户要用到一款,一般的做法是客户去创立一款,然后拿来用:
这时,客户需要知道怎么去创立一款,客户和就紧密耦合在一起了.为了降低耦合,就出现了工厂类,把创立的操作放到了工厂里面去,客户直接使用工厂的创立方法,传入想要的型号就行了,而不必去知道创立的细节.
随着种类越来越多,简单工厂模式出现了弊端,每次新参加品种,工厂类都要参加新的创立逻辑.这时我们就可以把工厂类定义成了接口,而每增加一种新的,就增加该对应工厂类的实现,这样工厂的设计就可以扩展了,而不必去修改原来的代码:
随着工厂规模的继续扩大,工厂开始生产充电器了.这时候工厂有二个系列的产品: 和充电器.而必须使用对应的充电器才能使用.这时候分别使用一个工厂和一个充电器工厂都不能满足我们的需求,我们必须确认跟充电器的对应关系.我们把工厂改造一下,把工厂跟充电器工厂联系在一起:
抽象工厂模式工厂方法模式简单工厂模式
UVM实战指南——第1局部
2021-10-31 21:54:15| 分类:SystemVerilog | 标签:uvm |字号订阅
验证的复杂度已经明显超过设计。

为了处理复杂度,面向对象,设计模式等软件设计的思想逐渐渗透到了硬件设计领域。

UVM的适时提出,有望能够尽早一统江湖,不再有各门各派军阀割据。

最近,出了一本UVM新书,工作之余,我抽空进行翻译,供大家参考。

英文来源:
UVM实战指南——第1局部
这篇文章是新书?A Practical Guide to Adopting the Universal Verification Methodology?的第四章UVM library根底的节选。

这一章的后续会在之后4周的周一连续发表。

如果等不及,可以到amazon 网站购置。

Sharon Rosenberg and Kathleen A. Meade, Cadence Design Systems
UVM是功能验证的第一个最正确实践和方法学。

如之前提到,UVM实现了成熟的高级验证方法。

尽管其类库可以任意使用,我们强烈建议
按照后续章节描述的方式来使用,因为这些方法源自于成功经验。

这一章讲解库的结构以及根本功能,重点放在大多数验证环境所需要的根本特征上。

注意:为了简化,例子并没有完全遵循UVM建议的架构和方法学。

这一章主要包括:
•使用UVM库
•根本类
•TLM端口
•工厂模式
•消息和汇报
•配置机制
4.1 使用UVM库
为了使用UVM库,用户需要:

•在所需要的地方导入uvm_pkg
•包含UVM宏
Hello World例子
下面的例子功能是在屏幕上显示消息:"Hello World!〞
1 // Compile the UVM package
2 `include “uvm_pkg.sv〞
3 module hello_world_example;
4 // Import the UVM library and include the UVM macros
5 import uvm_pkg::*;
6 `include “uvm_macros.svh〞
7 initial begin
8 `uvm _info (“info1”,“Hello World!〞, UVM _LOW)
9 end
10 endmodule: hello_world_example
第1-2行:注释用来提醒需要编译UVM库。

uvm_pkg.sv是UVM顶层文件,此文件将所有的UVM文件放在一个systemverilog文件包中。

注意:SystemVerilog包需要在模块以外进行编译。

建议用命令行方式进行编译。

由于包常被多个单元使用,不要将包的编译绑定到某个指定的单元。

第5行:当库已经编译,用户可以在任意编译单元导入此systemverilog包
第6行:必须分别包含UVM宏,因为宏属于编译指令,不会在多个编译步骤中保持存在。

为了防止重复编译整个库,宏被分开包含。

〔每个需要用的宏的文件都必须包含〕
第8行:`uvm_info宏属于UVM消息打印功能的一局部。

UVM消息打印允许格式化以及控制屏幕显示。

在这里,我们仅仅打印消息“Hello World!〞
UVM库使用指南
•为了防止命名冲突,防止在全局环境中导入uvm_pkg. 其他的package也同样适用此原那么〔防止全局环境中导入〕
•顶层文件一般如下格式
`ifndef <FILE _NAME>_SVH
`define <FILE _NAME>_SVH
... 文件内容
`endif
这种方式允许UVM库被多个地方引用而防止重复声明,一起编译的时候仅编译一次。

建议用户在自己的UVM文件中也使用此方式。

•在Cadence的Incisive Enterprise
Simulator(IES)仿真器中运行仿真器自带的
UVM库:
% irun -uvm
•使用非仿真器自带的UVM库,使用命令行:% irun -uvmhome $UVM _HOME
4.2 根本类
图4-1 类图描绘了UVM库中的一些类。

这些类提供了一些自动机制,可以用来衍生出用户自定义的类以及类的行为。

除了自动机制,这些基类还提供了支持统一和重用的API。

注意:此图没有显示uvm_transaction类。

尽管这个类仍然属于库,Accellera TSC并不建议使用它。

〔建议使用uvm_sequence_item〕后续章节讨论uvm_object和
uvm_component抽象类
4.3 uvm_object类
抽象类uvm_object是所有UVM数据类型的基类。

他的主要功能是定义完成了一系列通用的操作方式,比方创立, 复制, 打包/解包
(pack/unpack), 比拟, 打印, 以及记录(record). 从uvm_object衍生出来的类必须实现类似create()和get_type_name()这样的纯虚函数。

以下代码演示了一个没有使用UVM object类的AMBA APB传输的例子
实例4–1: 非UVM类定义
1 typedef enum bit { APB _READ, APB_WRITE} apb _direction _enum;
2 class apb_transfer;
3 rand bit [ 31:0] addr;
4 rand bit [ 31:0] data;
5 rand apb _direction _enum direction;
6 function void print();
7 $display("%s transfer: addr=%h data=%h", (), addr, data);
8 endfunction : print
9 endclass : apb_transfer
上面简单的例子包含了一个几乎所有交易(transaction)都有的print()方法。

大局部数据项都需要打印,复制,比拟,打包,解包以及其他功能。

如果让开发者去自已去定义这些功能,将不方便复用,环境整合者必须学习从多个地方得到的不同类的使用规那么以及行为特征。

UVM库通过引入uvm_object基类解决了这个
问题。

基类定义了一些通用的效劳。

测试向量中所有的类都必须间接或直接由uvm_object派生出来。

UVM类库也实现了一些宏,这些宏能够自动实现打印,复制,克隆,打包(pack),解包(unpack)方法等等。

实例4–2:从uvm_object继承而来的APB 传输类
1 typedef enum bit { APB _READ, APB_WRITE} apb_direction_enum;
2 class apb_transfer extends uvm_object;
3 rand bit [ 31:0] addr;
4 rand bit [ 31:0] data;
5 rand apb_direction_enum direction;
6 // Control field - does not translate into signal data
7 rand int unsigned transmit_delay; //delay between transfers
8 //UVM automation macros for data items
9 `uvm_object_utils_begin (apb_transfer)
10 `uvm _field _int (addr, UVM _DEFAULT)
11 `uvm _field _int (data, UVM _DEFAULT)
12 `uvm _field _enum(apb _direction _enum, direction, UVM _DEFAULT)
13 `uvm _field _int(transmit _delay, UVM _DEFAULT | UVM_NOCOMPARE)
14 `uvm_obj ect_utils_end
15 // Constructor - required UVM syntax
16 function new (string name="apb _transfer");
17 super.new(name);
18 endfunction : new
19 endclass : apb_transfer
第9-14行: UVM自动宏
第16-18行:在数据对象中构造函数并不是必须的。

如果使用构造函数,它的所有的参数都必须有缺省值. 〔针对uvm_object如此,但是uvm_component并不是这样,其parent参数并没有缺省值,因为不可能在类中知道其父组件)
UVM自动域
使用`uvm_object_utils_begin(TYPE)和
`uvm_object_utils_end宏来完成UVM对象的根本操作声明。

•实现get_type_name()函数,此函数返回内容为这个对象类型名字的字符串
•实现create()函数,此函数通过调用指定类型的构造函数来创立一个对象
•在工厂中注册此类型,以便在测试向量中的其他地方能够被重载
•实现一个静态方法get_type(),此方法在工厂操作中需要被用到
`uvm_field_*宏将自动实现指定域的一些对象方法:print(), copy(), clone(), pack(),
unpack(), compare(),以及record()。

支持嵌套对象以及用户自定义。

宏语法如下:
语法:`uvm_field_*(field_name,flag) “field_name〞必须是类的某个属性的标识符;“flag〞用来定制此域的自动化。

“flag〞是一数字,可以用或“|〞或者“+〞进行组合。

此语法适合:对象、字符串、事件(event)、实数、队列、以及不同类型的数组。

域自动有多种形式,请参考UVM参考手册。

uvm_object定义指南
•建议从uvm_sequence_item继承对象,此方式能够给对象增加一些额外的域,同时允
许对象成为uvm_sequence随机的一局部。

•对`uvm_field_*宏使用UVM_DEFAULT作为“flag〞参数〔而不是UVM_ALL_ON)。

目的是为了让UVM架构增加一些缺省下并不
翻开的自动功能。

其他一些标志是用来去掉
一些自动化功能的。

•在构造函数中的参数中将类名字设置为缺
省值
•不要忘记调用super.new(name),作为构造函数的第一个语句
•另一个不同的宏包含了类型参
数:`uvm_object_param_utils*
•`uvm_field_object缺省是深度操作。

如果你使用另外一个对象,请使用
UVM_REFERENCE标志来禁止深度操作。

实例4-3演示了域自动宏的更多用法,包括类组合和动态数组。

此例子中的yapp_packet类包含一个pkt_header域。

实例4–3:uvm_object自动域
1 class packet_header extends uvm_object; // Packet Header class
2 rand bit [ 5:0] length;
3 rand bit [ 1:0] addr;
4 `uvm_object_utils_begin (packet_header)
5 `uvm_field_int (length, UVM _DEFAULT)
6 `uvm _field_int(addr, UVM _DEFAULT)
7 `uvm_object_utils_end
8 endclass : packet_header
9 typedef enum bit { BAD _PARITY, GOOD _PARITY } parity_e;
10 class yapp_packet extends uvm_object;
11 // Physical Data
12 rand packet_header header; // pkt_header class contains: addr, length
13 rand bit [7:0] payload [] ; // dynamic array in the range [1:63]
14 bit [7:0] parity; // calculated in post _randomize()
15 // Control Knob
16 rand parity_e parity_type; // randomized to determine parity type
17 rand int packet_delay;
18 // UVM macros for built-in automation - These declarations enable automation
19 // of the data_item fields and implement create() and get _type _name()
20 `uvm_object_utils_begin (yapp_packet)
21 `uvm _field _object (header, UVM _DEFAULT)
22 `uvm _field _array _int (payload, UVM _DEFAULT)
23 `uvm _field _int (parity, UVM _DEFAULT)
24 `uvm_field_enum(parity_e, parity_type, UVM _DEFAULT)
25 `uvm _field _int(packet _delay, UVM _DEFAULT | UVM_DEC |
UVM_NOCOMPARE)
26 `uvm_object_utils_end
27 // Constructor - required syntax for UVM automation and utilities
28 function new (string name = "yapp_packet");
29 super.new(name);
30 header = packet_header::type_id::create("header"); // allocation using the
factory
31 endfunction : new
32 endclass : yapp_packet
第1行:header_packet由uvm_object继承而来
第21行:如果这是一个参考类,那么我们需要使用UVM_REFERENCE标志来防止深度操作。

payload是一个动态数组,使用一个特殊的宏可以打印数组的所有成员。

第24行:枚举类型语法有一个额外参数:
`uvm_field_enum(<enum_type>,<field_nam e>,<flags>)
第30行:不用“header=new(“header〞)〞,我们使用工厂模式。

用`uvm_object_utils实现的create()方法允许在另外工程和测试环境中扩充交易(transaction)。

请参考第62页的“UVM 工厂〞了解更多信息。

UVM对象自动用法实例
下面的例子演示了UVM库内建的自动特性。

假定实例4-2中的apb_transfer已经定义。

实例4–4:UVM对象的自动特性的用法
1 module automation_example;
2 // Import the UVM library and include the UVM macros
3 import uvm_pkg::*;
4 `include “uvm_macros.svh〞
5 // Include the APB transfer class
6 `include “apb_transfer.sv〞
7 apb_transfer my_xfer, tx1, tx2, tx3;
8 initial begin
9 my_xfer = apb_transfer::type _id::create("my_xfer");
10 if (!my_xfer.randomize())
11 `uvm_fa tal(“RANDFAIL〞,〞can not randomize my_xfer〞)
12 tx1 = my_xfer; // tx1 and my_xfer share the same memory
13 // Create a new apb_transfer
14 tx2 = apb_transfer::type _id::create("tx2");
15 tx2.copy(tx1); // Copies fields from tx1 to tx2
16 $cast(tx3, tx1.clone()); // Creates a new apb_transfer and copy all
17 // specified fields from tx1 to tx3
18 if(!tx3 pare(tx2))
19 `uvm_error(“CompareFailed〞, “The comparison failed〞)
20 my_xfer.print(); // Prints my_xfer in a table format
21 my_xfer.print(uvm_default_tree_printer); // Prints in “tree〞format
22 end
23 endmodule: automation_example
为了使用UVM库,必须导入UVM包,并且包含文件"uvm_macros.svh〞.此例子创立、随机化、复制、克隆、并且打印apb_transfer. 使用宏自动实现一个列表以及树形格式化的打印方法print()。

缺省的表格格式如下:
Name Type Size Value
my_xfer apb_transfer - @560
addr integral 32 'hb2cbb864
data integral 32 'heba598d7
direction apb_direction_enum 1 APB _WRITE
transmit_delay integral 32 'h6
缺省的树形格式如下:
my_xfer: (apb_transfer@560) {
addr: 'hb2cbb864 data: 'heba598d7 direction: APB _WRITE
transmit_delay: 'h6
}
下面的输出显示了第40页实例4-3中随机的yapp_packet。

此封包包括一个头对象,一个具
有动态数组类型的payload。

下面表格中数值很
容易格式化并打印如下表格:
Type Size Value Name
my_packet yapp_packet - @570 header pkt_header - @596 length integral 6 'h30 addr integral 2 'h2 payload da(integral) 48 - [0] integral 8 'h7b

integral 8 'he7

integral 8 'h8c ... ... ... ...

integral 8 'hf 3

integral 8 'h78 [47] integral 8 'he1 parity integral 8 'h74 parity_type parity_e 1 GOOD _PARITY packet_delay integral 32 'd8 自动域宏将实现一个字符串打印方法:sprint(),此方法将返回一个可以用来格式化显示的字符串。

打印格式是可以配置的,详细请参考UVM 参考手册。

注意:表格打印使用的是最复杂的格式化功能,
因此运行代价最高。

如果你的仿真器要打印大批
量的交易(transaction), 最好使用树形格式。

使用UVM自动域
使用UVM 自动域是可选的。

尽管用户可以自己实现这局部功能,我们还是建议使用自动宏来实现。

使用宏有以下优点:
•高效率:如果自己实现,会需要很多时间来实现这些子例程以及丰富的可选项
•可扩展性:在嵌套的对象中增加一个域需要了解UVM的原始实现方式,这常常需要很
强的技巧。

而用自动宏只需要一行语句即可•实现的一致性对复用很重要——例如,如果每个开发者都是用自行开发的print()方法,那么使用多个包的整合人员将不得不分析各不一样的日志文件,包括域头,值,基,以及结构,这些都属于开发者的各自喜好。

•可维护性——维护少数行数的代码简化了
UVM开发者的工作
•代码正确性——对于大量的数据对象,很难确保所有的例程都是正确的,BUG会出现在不恰当的时间
请注意,这些宏是属于库的内部实现,已经很成熟,不需要用户来调试。

对缺省的对象函数print()
进行调试就好似对verilog仿真器中的$display 函数进行调试。

总之,我们见过一些用户最初比拟担忧宏的使用,后来一旦他们尝试使用宏,就变得十分喜欢它,成为宏的爱好者,并感慨道:“他们真好用!〞
UVM实战指南——第2局部
2021-12-24 20:54:22| 分类:SystemVerilog | 标签:uvm |字号订阅
英文来源:
译者序:UVM的核心组件,以及UVM仿真阶段的控制是整个验证环境的根本零部件,必须掌握。

UVM的参数config机制对验证增加了许多灵活性和复用性,在参数配置和SystemVerilog Interface信号传递上起到了关键作用。

4.4 uvm_component类
所有的UVM验证环境的组件,包括环境和测试项都是直接或者间接由uvm_component类继承而来。

此类是一个准静态类,只能在仿真开始的时候创立(构建[build]阶段). 从此类继承的用户自定义类继承了许多内建的自动化功能。

注意:一般来说,通过从某个方法学构建的一系列类继承来设计自己的类,这系列类本身都是由uvm_component继承而来。

由于提供各种功能的类都是由此类派生,理解uvm_component 类是非常重要的。

下面章节描述uvm_component类的一些功能,以及如何使用。

关键功能是:
•Phasing and execution control 阶段(phase)执行控制
•Hierarchy information functions 层次信息功能
•Configuration methods 配置方法
•Factory convenience methods 工厂方法
•Hierarchical reporting control 层次化汇报控制
仿真阶段控制方法
在Verilog/VHDL类似的硬件描述语言中,在仿真开始之前静态构建各个实例的层次结构,保证了在跑仿真之前,所有的实例都连接存在且正确连接。

在Systemverilog中,类对象是在运行时被创立,这就引出一些问题:什么时候进行传输交易产生和执行比拟平安?什么时候能够确保所有的UVM组件都已经被创立?可以连接什么TLM端口?
尽管每个开发者都能够设计各自的同步方式,SystemVerilog UVM类库提供了一系列的阶段(phase)方法进行环境同步,而不需要事先设计方案。

这些阶段给用户提供了一些钩子,实现在关键的时间点执行一些逻辑。

比方,如果需要在仿真结束的时候执行检查逻辑,可以扩展check()阶段方法,在其中嵌入执行代码。

那么这些代码在仿真过程中将会在想要的时间点被执行。

除了run阶段方法之外,剩下所有的内建阶段方法的执行是在零时间内完成。

请参考UVM类参考手册中的uvm_phase文档获得更多的信息。

从高层次来看,已有的阶段方法是〔以仿真执行的顺序〕:
new() —尽管他不是一个UVM阶段函数,但是组件的构造函数是组件被创立生成时第一个执行的函数。

uvm_component构造函数包含两个参数:一个字符串name参数代表组件的名称,以及一个对组件父亲的引用。

建议不要使用构造函数参数的缺省值,这样可以保证用户提供有意义的名字和父类。

构造函数格式如下:
class street extends uvm_component;
`uvm_component_utils(street)
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction : new
endclass : street
技巧:关于构造函数的使用指南
•所有的组件类型的构造函数必须使用名称参数和父类参数。

因为工厂需要提供名称参数和父类参数来构建组件。

•不要使用缺省参数,以确保用户必须给这两个参数提供有意义的值
•使用简洁,意思清晰的名称
•对于组件数组,名字使用索引:agent[index], 比方env.agent[0].monitor, env.agent[1].monitor,等等
build()—第一个UVM阶段方法是build()方法,对每个组件会自顶向下自动调用。

此方法可以对子组件进行配置,然后创立子组件。

由于构造函数不是完全多态的,因此我们使用此方法来构建在构造函数中没有被创立的子组件。

因为build()方法是自顶向下调用,所以父类的构造配置将在子类build()方法之前完成。

一些关于build()使用指南:
•每个build()的实现必须先调用super.build(), 除非此组件明确关闭了自动配置功能。

调用super.build()会通过apply_config_settings方法调用来完成更新组件的配置域。

尽管不建议使用,父组件可以显式的在parent.build()中调用子组件的build(). 调用
super.build()确保了build()不被调用两次。

•在build()中分配子组件,而不是在构造函数中分配子组件。

这样可以允许组件在分配层次结构之前可以配置。

由于构造函数不是多态的,所以除非在build()函数中来创立结构,否那么一个继承类不可能改变结构。

•如果组件在build()阶段之后被分配,UVM将会报错。

•子组件的配置需要在build()方法中,子组件被创立之前完成。

•如果通过重载build方法来改变组件的结构,此时不能够调用super.build(),因为这会导致父类的build被执行。

如果你仍然想要uvm_component的build()方法的行为,可以通过调用uvm_component::build()来完成,这个方式会直接跳过父类的build方法。

.
class my_derived_comp extends mycomp;
function new(string name, uvm_component parent);
super.new(name,parent);
endfunction
function void build();
uvm_component::build(); //不调用super.build()
...//做其他事情
endfunction
endclass
connect()—此阶段在build()之后执行。

由于所有的子组件在build()阶段自顶向下都被创立,用户可以认定在connect()被调用的时候,所有组件的拓扑结构都已经成功创立. 此方法用来做事物级建模(TLM)的连接, 指针引用的赋值,以及虚拟接口(virtual interface)的赋值。

end_of_elaboration()— end_of_elaboration()阶段确保所有的连接,所有的引用都在connect阶段完成。

在end_of_elaboration阶段执行之前,UVM完成了所有的TLM绑定,因此在此阶段,所有的端口绑定都已经建立,可以被访问使用。

start_of_simulation()—此阶段提供了一个机制来防止仿真0时刻各个组件之间的互相依赖。

组件在此阶段初始化,确保在run阶段开始的时候,一切都已经正确初始化。

run()—run()阶段是唯一的一个耗时间的阶段,它定义了组件运行时的主要功能。

由于它被定义为任务(task)类型,所以可以消耗仿真时间。

当组件从run任务返回时,并不代表run阶段结束。

有可能其内部创立的进程还在继续执行。

run阶段是通过global_stop_request()任务来完成。

此任务在发生超时,或者在对象机制使用的时候调用进程终止方法。

extract()—此阶段用来在仿真结束,,在检查校验阶段之前,提取仿真的结果。

此阶段可以用来收集断言错误的个数,覆盖率,以及DUT内部讯号和存放器的值,组件内部变量值,组件的统计信息及其他信息。

此阶段执行不耗时间(消耗0时间),是一个函数调用。

调用方式是自底向上。

(译者补充:从叶子(leaf)组件的extract函数开始调用,深度优先)
check()—在前阶段(extract阶段)得到了重要的仿真结果,check阶段对结果进行分析判断,以此决定整个仿真的最终结论。

此阶段也是一个0执行时间的函数调用,自底向上调用。

report()—此阶段最后执行,用来将结果输出到文件或者屏幕。

此阶段是一个0执行时间的函数,自底向上调用。

尽管所有这些钩子方法在uvm_component以及他的子类中定义,只需要实现用到的相关〔阶段〕函数/任务〔不需要全部实现〕. 组件不需要对子组件使用`uvm_field_object宏,组件在被创立的时候总是被提供其父组件,从而构建了所有组件的层次结构。

仿真阶段和层次构造的例子在后面给出:
层次信息函数
在仿真时,组件树是静态的。

组件对层次信息非常注意,可以查询其父类,子类,以及他们在层次结构中的位置。

相关的uvm_component成员函数如下:
•get_parent()—返回父组件句柄,如果没有,返回null
•get_full_name()—返回此对象的完整层次结构名字.缺省实现是将层次结构中的父类和其叶子对象相拼接。

•get_child(string_name)—根据参数名字返回子组件的句柄
以下例子演示了UVM仿真的阶段控制,以及组件的层次信息方法的使用。

包含了street, city, state的组合。

并演示了get_child(), get_parent(), 以及get_full_name()的功能。

不建议使用new()来创立对象。

请参考62页的"UVM工厂"章节。

实例4-5: uvm_component仿真阶段控制以及层次信息相关方法
此例子演示了组件层次,包括州,城市,街道等对象实例。

注意构造函数以及build(),run(), end_of_elaboration()阶段函数/任务的使用。

用户通过阶段钩子函数/任务来完成想要的功能。

从run_test()出发,各阶段开始按步骤自动开始执行。

此例子也展示了get_child(), get_parent()和get_full_name()函数得到层次信息的功能
1. module test;
2. import uvm_pkg::*;
3. `include "uvm_macros.svh"
4. class street extends uvm_component;
5. `uvm_component_utils(street)
6. function new(string name, uvm_component parent);
7. super.new(name, parent);
8. endfunction : new
9. task run ();
10. `uvm_info("INFO1", "I vacationed here in 2021", UVM_LOW)
11. endtask : run
12. endclass : street
13. class city extends uvm_component;
14. street Main_St;
15. `uvm_component_utils(city)
16. function new(string name, uvm_component parent);
17. super.new(name, parent);
18. endfunction : new
19. function void build(); // note that we allocate in the build()
20. super.build();
21. Main_St = street::type_id::create("Main_St", this);
22. endfunction : build
23. task run();
24. uvm_component child, parent;
25. string pr, ch;
26. child = get_child("Main_St");
27. parent = get_parent();
28. pr = parent.get_full_name();
29. ch = child.get_name();
30. `uvm_info(get_type_name(), $psprintf("Parent:%s Child:%s", pr, ch),
UVM_LOW)
31. endtask : run
32. endclass : city
33. class state extends uvm_component;
34. city Capital_city;
35. `uvm_component_utils(state)
36. function new(string name, uvm_component parent);
37. super.new(name, parent);
38. endfunction : new
39. function void build();
40. super.build();
41. Capital_city = city::type_id::create("Capital_city", this);
42. endfunction : build
43. function void end_of_elaboration();
44. this.print();
45. endfunction : end_of_elaboration
46. endclass : state
47. state Florida = state::type_id::create("Florida", null);
48. state New_York = state::type_id::create("New_York", null);
49. // Start UVM Phases
50. initial run_test();
51. initial #100 global_stop_request();
52. endmodule : test
第4行:street类定义。

由于street没有子组件,所以不需要build()方法。

new()和build()方法仅仅调用super.new()和super.build().【补充说明:如果没有定义build()方法,将会调用父类中的build方法,也就是super.build()方法。

这是虚函数的特点:如果有就用自己的,如果没有,就用父类的】
第9行:run()方法打印一些信息
第13行:city组件中包含一个street子组件
第19行:street在build()阶段通过工厂模式被创立
第26行:在run()方法中,city调用get_child(),得到street子组件的信息。

第27行:get_parent()函数返回city的父组件,即state.
第28行:get_full_name()函数返回关于父组件的完整的层次结构
第47-48行:实例化创立两个state对象,也通过工厂模式创立
第50行:调用run_test()开始UVM的各阶段仿真。

结果如下:
--------------------------------------------------------------
--------
Name Type Size Value
----------------------------------------------------------------------
Florida state - @580
Capital_City city - @711
Main_St street - @712
----------------------------------------------------------------------
----------------------------------------------------------------------
Name Type Size Value
----------------------------------------------------------------------
New_York state - @638
Capital_City city -
@776
Main_St street -
@769
----------------------------------------------------------------------
UVM_INFO @0: New_York.Capital_City [city] Parent:New_York Child:Main_St
UVM_INFO @0:
New_York.Capital_City.Main_St [INFO1] I vacationed here in 2021
UVM_INFO @0: Florida.Capital_City [city] Parent:Florida Child:Main_St
UVM_INFO @0:
Florida.Capital_City.Main_St [INFO1] I vacationed here in 2021
Simulation complete via $finish(1) at time 100 NS + 0
./uvm_st_ci_st.sv:75 initial #100 $finish;
组件uvm_top
有一个特殊的UVM组件叫做"uvm_top"。

此组件是uvm_root类的单一实例,也是由
uvm_component继承而来。

uvm_top组件负责对所有的组件进行阶段控制,并且提供了查找
功能〔uvm_top.find()和uvm_top.find_all()),方便定位一些特定的组件。

uvm_top组件还提
供了一个存储全局配置的地方。

调用set_config_*()函数相当于调用uvm_top.set_config_*().
对消息打印函数也如此,调用uvm_report_*(或者`uvm_info/warning/error/fatal)等价于调用uvm_top.uvm_report*函数。

4.5 UVM配置机制
通用的验证组件复用的一个重要方面是其具备可以配置为期望的工作模式的能力。

UVM提供了一个灵活的配置机制,允许在运行时配置属性和组件拓扑结构,而不需要派生和使用工厂模式。

组件属性的配置通过set_config* 应用接口界面(API)来实现,包括以下3个API:virtual function void set_config_int (string inst_name,string field_name,
uvm_bitstream_t value)
virtual function
void set_config_string (string
inst_name,string field_name, string value ) virtual function
void set_config_object (string inst_name, string field_name,uvm_object value, bit clone = 1 )
参数描述:
•inst_name: 组件的层次路径,可以使用通用字符“*〞
•field_name:需要配置的字段名字,此字段在对象实例中有定义.
•value: 字段配置值. 根据set_config的调用不同,可以是uvm_bitsream, string或者uvm_object类型的值
•clone: 仅对set_config object有效,表示提供一个不同的拷贝,或者仅仅传递value 对象的引用
通过umv_field_*宏注册的UVM属性字段将会自动的通过组件的super.build()方法进行更新。

这些属性可以用来控制组件build()方法的执行。

同时,也可以通过get_config_*方法在build()阶段之前手动的获得配置值。

注意: 如果要影响测试向量的拓扑结构,配置设定必须在build阶段之前完成。

下面的实例演示了配置机制的使用,我们通过使用配置机制来设置state的名字和每个state的投票数。

实例4-6: 配置机制
1. module test;
2. class state extends uvm_component;
3. string state_name;
4. int unsigned num_votes;
5. // Configurable fields must use the field declaration macros. Use the
6. // UVM_READONLY flag if the property should not be configurable.
7. `uvm_component_utils_begin(state)
8. `uvm_field_string(state_name, UVM_DEFAULT)
9. `uvm_field_int(num_votes, UVM_DEFAULT | UVM_DEC)
10. `uvm_component_utils_end
11. function new(string name, uvm_component parent);
12. super.new(name, parent);
13. endfunction : new
14. function void end_of_elaboration();
15. this.print();
16. endfunction : end_of_elaboration
17. endclass : state
18. state my_state1, my_state2;
19. initial begin
20. // configure BEFORE create
21. set_config_string("my_state1", "state_name", "GEORGIA");
22. set_config_string("my_state2", "state_name", "CONNECTICUT");
23. set_config_int("*", "num_votes", 7);
24. // create
25. my_state1 = state::type_id::create("my_state1", null);
26. my_state2 = state::type_id::create("my_state2", null);
27. fork
28. run_test();
29. #100 global_stop_request();
30. join
31. end
32. endmodule : test
第23行:我们使用“*〞来配置num_votes字段。

所有的state都将使用此配置值。

下面的信息显示了在end_of_elaboration阶段之后组件的配置信息。

---------------------------------------------------------------
Name Type Size Value。

相关文档
最新文档