前端学习 10-1 :验证中的UVM
目录
1、类库地图
2、各种类介绍
3、工厂的意义
工厂的创建
类继承
类的实例化
对象的操作
类中使用virtual:
4、UVM 关键组件
uvm_componet
uvm_transaction
uvm_config_db#
uvm_phase
run_test
function new();
5、::
6、 消息管理
消息处理:
1、类库地图
一个类定义了一组大体上相似的对象。这组对象的共同特征加以抽象,并存储再一个类中,可以建立丰富的类库,是衡量一个面向对象程序设计语言成熟与否的重要标志。
一个子类可以继承父类的属性和方法,继承后,在子类中不必定义,子类还可以定义自己的属性和方法。
2、各种类介绍
·核心基类;
·工厂(factory)类;
·事务(transaction)和序列(sequence)类;
·结构创建(structure creation)类;
·环境组件(environment component)类;
·通信管道(channel)类;
·信息报告(message report)类;
·寄存器模型(register model)类;
·线程同步(thread synchronization)类;
·事务接口(transaction interface)类。
首先,核心基类提供最底层的支持,包括一些基本的方法,如复制、创建、比较和打印。在核心类之上发展了支持UVM特性的各个相关的类群。
工厂类提供注册环境组件、创建组件和覆盖组件类型的方法。
事务类和序列类用来规定在TLM传输管道中的数据类型和数据生成方式。
环境组件类则是构成验证结构的主要部分,组件之间的嵌套关系通过层层例化和连接形成结构层次关系。
事务接口类和通信管道类则共同实现组件之间的通信和存储。
线程同步类则比SV自身的同步方法更方便,发生同步时可传递的信息更多。
信息报告类使得从UVM环境报告的信息一致规范化,便于整体的控制和过滤。
最后,寄存器模型类用来完成对寄存器和存储的建模、访问和验证。
UVM可以帮助验证师更快搭建出结构良好、便于复用的验证环境。层次化的组件通过phase机制可以有序完成环境的创建、连接和运行,同样,层次化的sequence通过test可以完成不同的测试场景。
评价:工厂机制是UVM 的魅力所在。
3、工厂的意义
//////// 先说设计模式, “ 每一个模式描述了我们周围不断重复发生的问题,以及该问题的解决方案的核心,这样你就能一次又一次的使用该方案,而不必做重复劳动 " 。设计模式的核心在于,提供相关问题的解决方案。-----好像一个个公司一样
创建型设计模式,抽象了实例化过程,帮助一个系统独立于 如何创建、组合和表示它的哪些对象。此模式包括面向类和面向对象两种,一个类创建型模式使用继承改变被实例化的类。
factory method , 定义一个用于创建对象的接口,让子类决定实例化哪一个类。 //////
UVM 工厂的存在就是为了更方便地替换验证环境中的实例或已注册的类型,同时工厂的注册机制带来配置的灵活性。这里的实例或类型替代在 UVM中称为覆盖(override),用来替换的对象或类型应满足注册(registration)和多态(polymorphism)的要求,我们下面具体谈到如何编写这些要求的代码。
UVM的验证环境构成分为两部分,一部分构成环境的层次,这部分代码通过uvm_component 类完成,另一部分构成环境的属性(例如配置)和数据传输,这一部分通过uvm_object类完成。这两种类的集成关系从UVM类库地图可以看到,uvm_component 类继承于uvm_object类,而这两种类也是进出工厂的主要模具和生产对象。
工厂生产模具,通过注册,可以利用工厂完成对象创建;工厂生产,有可灵活替代的好处,这使得在不修改原有验证环境层次和验证包的同时,实现对环境内部组件类型或对象的覆盖。
工厂的创建
定义
例如:class comp1 extend uvm_component;
//类继承。派生类为 comp1 从uvm_component 继承。派生类自动得到基类的所有成员和函数。这些在派生类中都可以直接访问,从而实现代码的复用。
注册
`uvm_componet_utlis(comp1)
在工厂注册的类一般分为两大类型,即 uvm_componet 和 uvm_object
UVM提供了多个工厂机制的注册宏,下面给出一种常用的注册宏。
`uvm_{component,object}_utils(class_type_name),
构建函数
component 构建函数
function new (string name = " name", uvm_component parent = null)
创建对象comp_type::type_id::create(string name,uvm_component parent);
object 构建函数 function new (string name = “name”)
创建对象object_type::type_id::create(string name);
例1 uvm_object类型:
class obj extends uvm_object; //定义
`uvm_object_utlis(obj) //注册
function new(string name='obj'); //构建函数
super.new(name);
$display($sfortmatf("%s is ceated",name));
endfunction:new
例2 uvm_componet类型:
class comp extends uvm_componet; //定义
`uvm_componet_utlis(obj) //注册
function new(string name='comp',uvm_componet parent=null); //构建函数
$display($sfortmatf("%s is ceated",name));
endfunction:new
注:以上两个代码的注册宏和new()函数是范式,结构和参数都不可更改。
类继承
例如:class comp1 extend uvm_component
派生类名为 comp1 从uvm_component 继承。
派生类自动得到基类的所有成员和函数。这些在派生类中都可以直接访问,从而实现代码的复用。
派生类的对象生成时,要调用构造函数进行初始化。构造函数的名字和类名相同,它没有返回值和返回类型。
对构造函数的调用 是对象创建过程的一部分,对象创建之后就不能再调用构造函数了。
类的实例化
通过new 创建的对象,是动态对象。仿真器接到new 的指令后,会在内存中划分一块空间。
对于大部分类来说,只创建类,而不实例化,是没有意义的。
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);.....
initial begin
my_driver drv;
drv = new("drv",null);
drv.main_phase(null);
$finish();
end
对象的操作
对于创建的对象,需要通过调用对象类中定义的成员函数 来对它进行操作,格式为:
对象名 . 成员函数(实参表)。
类中使用virtual:
在 UVM 中,virtual 主要用于 实现多态(一个接口,多种方法),允许子类重写(override)父类的方法,并在运行时 才决定所调用的函数。虚函数的定义是在原函数前加一个 virtual.
对虚函数的 限制是:只有类的成员函数才可以是虚函数。
virtual interface
在sv 中,使用interface 来连接 验证平台与DUT 的端口。module 里直接使用 interface, 在class 中要使用virtual interface。
类的引用
在 UVM 中,任何类(包括 driver
、monitor
、sequence
等)都可以合法引用其他类,但需遵循正确的跨文件管理规则。以下是具体场景分析:
2. 各类组件的引用权限
组件类型 | 能否引用其他类? | 典型应用场景 | 注意事项 |
---|---|---|---|
uvm_env | ✅ 是 | 集成多个子组件(driver/monitor) | 需在 |
uvm_driver | ✅ 是 | 引用 transaction 或 interface | 避免直接引用 monitor |
uvm_monitor | ✅ 是 | 引用 scoreboard 或 coverage | 建议通过 analysis port 通信 |
uvm_sequence | ✅ 是 | 引用 transaction 或 sequencer | 避免直接引用 driver |
uvm_scoreboard | ✅ 是 | 引用 monitor 或 reference model | 通过 TLM 通信而非直接引用 |
uvm_test | ✅ 是 | 引用 env 或配置类 | 顶层通常只引用 env |
4、UVM 关键组件
贴一个链接:UVM基础-组件(driver、monitor、agent...) - 哔哩哔哩
uvm_componet
uvm_driver (激励事务转为 pin 信号驱动DUT)
uvm_monitor (收集DUT 输出给scoreboard)
uvm_agent (将 sequence 、driver、monitor 封装起来,链接 sequence 和 monitor)
uvm_env (包含多个agent、reference model、scoreboard model)
一个容器,实例化driver, monitor , reference model, scoreboard model
uvm_test (用来实例化 testcase)
uvm_scoreboard (计分板,判断DUT 行为是否符合预期)
uvm_transaction
uvm_sequence (启动sequence 产生事务,通过TLM端口送至driver一侧),
1、所有的transaction 都要从 uvm_sequence_item 派生(其祖先是uvm_object)。
2、使用uvm_object_utils 宏实现factory 机制, 而不是 uvm_component_utils。
3、 在整个仿真期间,my_driver 一直存在,而transaction 有生命周期。它在仿真的某一时间产生,经过 driver 驱动,再经过 reference model处理,最终由scoreboard 比较完成后,其生命周期就结束了。
一般来说,这种有生命周期的类都是派生自uvm_object 或者 uvm_object的派生类,这些类都使用uvm_object_utils 宏来实现。
uvm_config_db#
因为run_test 实例化了一个脱离 top_tb 的层次,所以使用config_db (实现不同组件(component)间数据共享)。
config 机制,在仿真时进行动态的变量设置。uvm_config_db 有3种 使用方式:
1、将virtual interface 传递到环境中;
2、设置单一变量值,例如int 、string;
3、传递配置对象(config object)到环境
uvm_config_db#(T)::set(第一个参数, "第二个参数", "第三个参数", 第四个参数);
T可以为普通变量、对象(类的实例化)、虚接口(virtual interface)。是“寄信“的类型。
第一个参数必须是一个uvm_component实例的指针
第二个参数是相对此实例的路径。一般的,如果第一个参数被设置为this,那么第二个参数可以是一个空的字符串。
第一个和第二个参数联合起来组成目标路径,与此路径符合的目标才能收信。
第三个参数就是set函数中的第三个参数,这两个参数必须严格匹配
第四个参数则是要设置的变量。
uvm_config_db#(T)::get(第一个参数, "第二个参数", "第三个参数", 第四个参数);
第一参数,一般为this,当第一个参数为this时,第二个参数为空,第三个参数和set的第三个参数一致,第四个参数,是T类型的本地变量。
config_object
整合每个组件变量放在 uvm_object 中,
uvm_phase
UVM用phase 管理验证平台的运行,例如给DUT 施加激励、监测DUT 输出,在这些phase 中完成。phase 下都以统一的命名 xxx_phase,且都有一个类型为uvm_phase,名为phase 的参数。
build_phase:
主要负责构建测试平台的组件层次结构。在此阶段,验证工程师可以实例化类对象并设置关键参数,或者从 uvm_config_db
获取配置参数,完成组件的工厂注册和类型覆盖, 但不会进行任何功能仿真。
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase); // 必须首先调用父类
// 1. 获取配置参数
if (!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("NOVIF", "No virtual interface provided")
// 2. 创建子组件
agent = my_agent::type_id::create("agent", this);
monitor = my_monitor::type_id::create("monitor", this);
// 3. 设置默认配置
uvm_config_db#(int)::set(this, "agent", "data_width", 64);
endfunction
# drive 、monitor 都是agent 的变量,所以他们的实例化都在agent 的build_phase 中执行。
需要加入super.build_phase? UVM 组件的生命周期依赖于父类(如 uvm_component
)在 build_phase
中执行的关键初始化操作。父类 build_phase
的关键行为一般有 3 种:父类会通过 uvm_config_db
自动获取配置参数;如果父类定义了子组件(如 uvm_env
包含 uvm_agent
),会触发子组件的创建;初始化报告机制(uvm_report_handler
),注册组件到工厂。
build_phase 执行后,会执行 connect_phase, connect_phase 用来做连接工作。
main_phase
uvm_driver 所做的事情几乎都在main_phase 中完成。所以main_phase :
执行核心测试和验证活动,
生成并发送事务到 DUT,
监控 DUT 响应并收集数据。
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);.....
initial begin
my_driver drv;
drv = new("drv",null);
drv.main_phase(null);
$finish();
end
run_test
run_test 语句会替换掉 my_driver 的实例化及 main_phase的显示调用。
它会自己创建my_driver的实例,并自动调用 main_phase。 -------根据传入的字符串“my_driver”
initial begin
run_test("my_driver");
end
在uvm 平台中,只要一个类使用uvm_componet_utlis 注册,且该类被实例化了,那么它的main_phase 就会被自动调用。
function new();
function new()
是 SystemVerilog/UVM 中的 构造函数,用于:初始化对象成员变量、分配内存资源、建立对象间的层次关系。
在 UVM 中,所有组件(如 uvm_driver
、uvm_env
)和对象(如 uvm_sequence_item
)都必须通过 new()
进行实例化。
// 推荐:仅做简单初始化
function new(string name, uvm_component parent);
super.new(name, parent); //// 必须调用父类构造函数
this.data = 0; // 初始化自定义成员
this.enable = 1;
endfunction// 不推荐:在构造函数中执行复杂操作
function new(string name, uvm_component parent);
super.new(name, parent);
this.config = read_config_file(); // 应放在build_phase
endfunction
工厂创建流程,当调用 type_id::create()
时,工厂调用类的 new()
方法,返回实例化对象。
my_agent agent = my_agent::type_id::create("agent", this);
5、::
::
是 SystemVerilog 中的 作用域解析运算符(Scope Resolution Operator),主要用于:
- 访问类中的静态成员(静态变量、静态方法)。
- 调用父类中的方法(尤其是被重写的方法)。
- 引用命名空间或包中的内容。
关键应用场景
(1) 访问静态成员
// 定义静态变量和方法
class my_config extends uvm_object;
static int default_verbosity = UVM_MEDIUM;
static function void set_default(int verb);
default_verbosity = verb;
endfunction
endclass
// 通过 :: 访问静态成员
int verb = my_config::default_verbosity;
my_config::set_default(UVM_HIGH);
(2) 调用父类方法
class my_driver extends uvm_driver;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase); // 调用 uvm_driver::build_phase
`uvm_info("DRV", "Custom build_phase", UVM_LOW)
endfunction
endclass
(3) UVM 工厂注册
class my_transaction extends uvm_sequence_item;
`uvm_object_utils(my_transaction) // 宏展开后使用 ::
endclass
typedef uvm_object_registry#(my_transaction) type_id;
static function type_id get_type();
return type_id::get();
endfunction
(4) 访问包中的内容
import uvm_pkg::*; // 导入整个包
// 通过 :: 显式访问特定类
uvm_config_db#(int)::set(null, "*", "mode", 1);
3. UVM 中的特殊用法
(1) uvm_config_db
操作,uvm_config_db#(T)
是一个参数化的类,::set
和 ::get
是其静态方法
uvm_config_db#(virtual my_if)::set(this, "env.agent.*", "vif", vif);
(2) 工厂创建对象
my_transaction tx = my_transaction::type_id::create("tx");
(3) 调用被重写的父类方法
class child_class extends parent_class;
virtual function void print();
parent_class::print(); // 显式调用父类方法
$display("Child extension");
endfunction
endclass
6、 消息管理
在UVM 环境中或环境外,只要有引入 uvm_pkg,均可以通过下面的方法来按照消息的严重级别和冗余度来打印消息。
function void uvm_report_info(string id, string message, int verbosity = UVM_MEDIUM, string filename = "", int line = 0) ;
例子:`uvm_info("TRACE", $sformatf("%m"), UVM_HIGH);
上面的消息函数有若干共同的信息,它们是严重级别(severity)、冗余度(verbosity)、
消息ID、消息、文件名和行号:
• 严重级别:从函数名本身也可以得出,这4个严重级别分别是UVM_INFO、UVM_WARNING、UVM_ERROR、UVM_FATAL。不同的严重级别在打印的消息中有不同的指示,同时,仿真器对不同严重级别消息的处理方式不同。例如,对于UVM_FATAL的消息,默认情况下仿真会停止。
• 消息ID:该ID 可以是任意的字符串,用来标记该消息。这个标记与消息本身一起打印出来,同时不同的标记也可以用来进行消息处理。(把消息归类)
• 消息:即消息文本的主体。
• 冗余度:冗余度与消息处理中的过滤直接相关。冗余度的设置如果低于过滤的开关,那么该消息会打印出来,否则不会被打印出来。重要的信息设 UVM_LOW, 可有可无的信息设 UVM_HIGH。UVM 默认只打印UVM_MEDIUM 或 UVM_LOW信息。
消息处理:
通常情况下,消息处理的方式是同消息的严重级别对应的。如果用户有额外的需求,也可以修改对各个严重级别的消息处理方式。我们首先来看有哪些消息处理方式,如表10.5所示。
表10.5 消息处理方式列表
NO_ACTION 不做任何处理
UVM_DISPLAY 将消息输出到标准输出端口
UVM_LOG 将消息写入到文件中
UVM_COUNT 增加退出计数变量 quit_ count。当quit_count 达到一定数值时,停止仿真
UVM EXIT 立刻退出仿真
UVM_CALL_HOOK 调用对应的回调函数
UVM_STOP 停止仿真
各严重级别默认的消息处理方式
严重级别 默认处理方式
UVM_INFO UVM_DISPLAY
UVM_WARNING UVM_DISPLAY
UVM_ERROR UVM_DISPLAYIUVM_COUNT
UVM_FATAL UVM_DISPLAY |UVM_EXIT
还没完,继续更新中