1. 单例类Singleton Class
单例类是一种设计模式,用于确保一个类只有一个实例,并提供一个全局访问点来获取该实例。在UVM库中,单例模式被广泛应用于一些管理器类(manager classes),例如uvm_root(UVM的根类)、uvm_factory(工厂类)、uvm_report_handler(报告处理器)等。
1.1 单例类的定义方法
- 将构造函数声明为protected,以防止外部通过new直接创建实例。
- 定义一个静态(static)成员变量来保存类的唯一实例。
- 提供一个公共的静态方法(通常名为get)来获取该唯一实例。在这个方法中,如果实例尚未创建,则创建实例;否则返回现有实例。
class singleton_example extends uvm_object;
// 静态实例变量
protected static singleton_example m_inst;
// ↑访问权限 ↑存储类别 ↑数据类型 ↑变量名
// 私有构造函数 - 关键所在!
protected function new(string name = "singleton_example");
super.new(name);
`uvm_info("SINGLETON", "Constructor called", UVM_LOW)
endfunction
// 获取实例的静态方法
static function singleton_example get();
if (m_inst == null) begin
m_inst = new(); // 只有这里能调用构造函数
end
return m_inst;
endfunction
endclass
创建单例类实例时,调用get方法即可:
singleton_example obj = singleton_example::get(); //调用get方法创建对象
singleton_example obj1 = singleton_example::get(); //第二次调用,返回同一对象
1.2 单例类的优点
- 全局访问:提供统一的全局访问点
- 资源节约:避免重复创建相同功能的实例
- 状态共享:便于在多个组件间共享状态信息
- 配置集中:集中管理全局配置信息
2. 工厂机制概述与设计理念
2.1 什么是UVM工厂机制
UVM工厂机制是一种基于类型重载的面向对象设计模式,它允许在运行时动态替换对象和组件的类型,而无需修改原始代码。这是UVM框架中实现可配置性和可重用性的核心特性。
2.2 工厂机制的设计目标
- 灵活性:在不修改测试平台代码的情况下改变组件或对象类型
- 可重用性:相同的测试环境可以配置不同的组件实现
- 可维护性:通过配置而非代码修改来适应变化
- 可扩展性:轻松添加新的组件类型而不影响现有代码
2.3 工厂机制的基本原理
在上一章节中介绍了单例类的特性,UVM中工厂机制就是一个单例类的典型应用,如下图中为uvm factory类的部分源码,与单例类的定义一致,uvm_factory用了更严格的local属性。
单例类保证了在整个验证环境中只创建一个工厂,以确保统一管理。
// 传统方式 - 硬编码类型
my_driver drv = new("drv");
// 工厂方式 - 动态类型创建
my_driver drv = my_driver::type_id::create("drv");
工厂内部建立了一张类型注册表,通过维护一个类型注册表,在运行时根据注册信息创建对象实例,并支持用派生类型替换基类型。
3. 工厂注册机制与类型声明
工厂内部建立的类型注册表通过关联数组实现,数组的索引是注册的类型,值是 uvm_object_wrapper 类型的对象。
3.1 组件和对象的注册宏
UVM提供了不同的注册宏用于组件和对象:
// 对于uvm_component派生类
`uvm_component_utils(T) // 非参数化组件
`uvm_component_param_utils(T) // 参数化组件
// 对于uvm_object派生类
`uvm_object_utils(T) // 非参数化对象
`uvm_object_param_utils(T) // 参数化对象
3.2 注册宏的使用示例
// 组件注册示例
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver) // 组件注册
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass
// 对象注册示例
class my_transaction extends uvm_sequence_item;
`uvm_object_utils(my_transaction) // 对象注册
rand bit [31:0] addr;
rand bit [31:0] data;
function new(string name = "my_transaction");
super.new(name);
endfunction
endclass
// 参数化类注册示例
class my_sequencer #(type T = my_transaction) extends uvm_sequencer #(T);
`uvm_component_param_utils(my_sequencer #(T)) // 参数化组件注册
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
endclass
3.3 注册宏的内部机制
在uvm源码中,注册宏展开如下:
//先将uvm_component_utils(T) 宏展开为两个宏
`define uvm_component_utils(T) \
`m_uvm_component_registry_internal(T,T) \
`m_uvm_get_type_name_func(T) \
//m_uvm_component_registry_internal(T,S) 展开如下
//`define m_uvm_component_registry_internal(T,S)
typedef uvm_component_registry #(T,`"S`") type_id;
static function type_id get_type();
return type_id::get();
endfunction
virtual function uvm_object_wrapper get_object_type();
return type_id::get();
endfunction
//m_uvm_get_type_name_func(T) 展开如下
//`define m_uvm_get_type_name_func(T)
const static string type_name = `"T`";
virtual function string get_type_name ();
return type_name;
endfunction
4. 对象创建与工厂方法
4.1 工厂创建方法
使用工厂机制创建对象和组件:
class my_env extends uvm_env;
`uvm_component_utils(my_env) //注册到工厂
my_driver drv;
my_monitor mon;
my_sequence seq;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// 使用工厂创建组件
drv = my_driver::type_id::create("drv", this);
mon = my_monitor::type_id::create("mon", this);
// 使用工厂创建对象(在运行时)
seq = my_sequence::type_id::create("seq");
endfunction
endclass
注册后创建实例时,my_driver为用户自定义的类,type_id为uvm_component_registry的别名,create是uvm_component_registry类预定义的方法
4.2 创建方法的参数说明
// 组件创建:需要名称和父组件指针
comp = component_type::type_id::create(string name, uvm_component parent);
// 对象创建:只需要名称
obj = object_type::type_id::create(string name);
4.3 工厂创建的优势
class base_test extends uvm_test;
virtual function void create_components();
// 传统方式 - 编译时绑定
// my_driver drv = new("drv", this);
// 工厂方式 - 运行时绑定,支持重载
my_driver drv = my_driver::type_id::create("drv", this);
endfunction
endclass
5. 类型重载机制与方法
重载(Override)是UVM工厂机制中最重要的功能之一,它提供了运行时类型替换的强大能力,在工厂中建立了重载的队列,当需要重载时,在队列中查找重载。
5.1 常用的重载类型
- 类型重载:类型重载影响所有该类型实例的创建。
//---------------------------------按类型设置重载---------------------------
// 方法原型
function void set_type_override_by_type (uvm_object_wrapper original_type,
uvm_object_wrapper override_type,
bit replace=1);
// 使用示例
factory.set_type_override_by_type(
base_driver::get_type(), // 原始类型
enhanced_driver::get_type(), // 重载类型
1 // 替换已存在的重载
);
//---------------------------------按类型名设置重载---------------------------
// 方法原型
function void set_type_override_by_name (string original_type_name,
string override_type_name,
bit replace=1);
// 使用示例
factory.set_type_override_by_name(
"base_driver", // 原始类型名
"enhanced_driver", // 重载类型名
1 // 替换已存在的重载
);
- 实例重载:实例重载只影响特定路径的实例创建。
//---------------------------------按类型设置实例重载---------------------------
// 方法原型
function void set_inst_override_by_type (string relative_inst_path,
uvm_object_wrapper original_type,
uvm_object_wrapper override_type);
// 使用示例
factory.set_inst_override_by_type(
"env.agent.driver0", // 实例路径
base_driver::get_type(), // 原始类型
debug_driver::get_type() // 重载类型
);
//---------------------------------按类型名设置实例重载---------------------------
// 方法原型
function void set_inst_override_by_name (string relative_inst_path,
string original_type_name,
string override_type_name);
// 使用示例
factory.set_inst_override_by_name(
"env.agent.monitor", // 实例路径
"base_monitor", // 原始类型名
"coverage_monitor" // 重载类型名
);
5.2 重载的应用场景
// 基础驱动类
class my_driver extends uvm_driver #(my_transaction);
`uvm_component_utils(my_driver)
// 基础功能实现
endclass
// 增强驱动类
class enhanced_driver extends my_driver;
`uvm_component_utils(enhanced_driver)
// 添加性能监控、错误检测等增强功能
endclass
// 错误注入驱动类
class error_injection_driver extends my_driver;
`uvm_component_utils(error_injection_driver)
// 专门用于错误注入测试
endclass
// 在测试中应用重载
class error_injection_test extends uvm_test;
virtual function void build_phase(uvm_phase phase);
// 对特定驱动实例应用错误注入
set_inst_override_by_type("env.agent*.drv",
my_driver::get_type(),
error_injection_driver::get_type());
endfunction
endclass
6. 实际应用与最佳实践
6.1 完整的工厂应用示例
// 基础事务类
class base_transaction extends uvm_sequence_item;
`uvm_object_utils(base_transaction)
rand bit [31:0] addr;
rand bit [31:0] data;
endclass
// 扩展事务类
class extended_transaction extends base_transaction;
`uvm_object_utils(extended_transaction)
rand bit [7:0] user_id;
rand bit priority;
endclass
// 基础序列类
class base_sequence extends uvm_sequence #(base_transaction);
`uvm_object_utils(base_sequence)
virtual task body();
base_transaction tr;
tr = base_transaction::type_id::create("tr");
// 序列逻辑
endtask
endclass
// 基础驱动类
class base_driver extends uvm_driver #(base_transaction);
`uvm_component_utils(base_driver)
virtual task run_phase(uvm_phase phase);
forever begin
seq_item_port.get_next_item(req);
// 驱动逻辑
seq_item_port.item_done();
end
endtask
endclass
// 测试类 - 演示工厂重载
class factory_test extends uvm_test;
`uvm_component_utils(factory_test)
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// 在测试开始时设置重载
// 1. 全局类型重载 - 所有base_transaction被extended_transaction替换
set_type_override_by_type(base_transaction::get_type(),
extended_transaction::get_type());
// 2. 特定组件重载 - 只有这个测试使用增强驱动
set_type_override_by_type(base_driver::get_type(),
enhanced_driver::get_type());
endfunction
endclass
6.2 调试和报告方法
class factory_debug extends uvm_test;
virtual function void report_phase(uvm_phase phase);
uvm_factory factory = uvm_factory::get();
`uvm_info("FACTORY", "Factory debug information:", UVM_LOW)
// 打印所有已注册的类型
factory.print();
// 打印所有已设置的重载
factory.print_overrides();
// 检查特定类型是否被重载
if (factory.find_override_by_type(base_driver::get_type(),
this.get_full_name()) != null) begin
`uvm_info("FACTORY", "base_driver is overridden in this test", UVM_MEDIUM)
end
endfunction
endclass
6. 总结
UVM工厂机制通过类型注册、动态创建和类型重载,为验证环境提供了极大的灵活性和可扩展性。掌握工厂机制的使用方法和最佳实践,对于构建可重用、可配置的验证平台至关重要。通过合理运用工厂机制,可以实现测试用例的高度定制化,而无需修改底层验证组件代码。、