文章目录
前言
在 UVM(Universal Verification Methodology)中,uvm_object 是一个非常基础且重要的类。它为验证项目提供了灵活且强大的功能,通过从 uvm_object 派生,可以创建各种用于验证的类和对象,如事务类、配置类、寄存器模型等。以下通过示例代码详细讲解如何将 uvm_object 应用到实际的验证项目中。
一、定义事务类
在验证项目中,事务(transaction)是验证平台与设计之间交互的基本单元。以下是一个从 uvm_object 派生定义的事务类示例:
class my_transaction extends uvm_object;
// 定义事务的属性
rand bit [7:0] data; // 数据字段
rand bit [3:0] addr; // 地址字段
bit valid; // 有效位
// 注册类到 UVM 工厂
`uvm_object_utils(my_transaction)
// 构造函数
function new(string name = "my_transaction");
super.new(name);
endfunction
// 实现 copy 方法
virtual function void do_copy(uvm_object rhs);
my_transaction tx;
if (!$cast(tx, rhs)) begin
`uvm_fatal("COPY", "Copy failed: wrong type")
end
this.data = tx.data;
this.addr = tx.addr;
this.valid = tx.valid;
endfunction
// 实现 compare 方法
virtual function bit do_compare(uvm_object rhs, uvm_comparer comparer);
my_transaction tx;
if (!$cast(tx, rhs)) return 0;
return (this.data == tx.data) && (this.addr == tx.addr) && (this.valid == tx.valid);
endfunction
// 实现 print 方法
virtual function void do_print(uvm_printer printer);
printer.print_field("data", this.data, 8);
printer.print_field("addr", this.addr, 4);
printer.print_field("valid", this.valid, 1);
endfunction
endclass
- 属性定义
data 和 addr 是随机化的寄存器类型,它们的值可以通过随机化生成。
valid 用来表示数据的有效性。 - uvm_object_utils 宏
这个宏的作用是将类注册到 UVM 工厂,使得可以通过工厂创建对象,并且可以支持对象的复制、比较和打印。 - 构造函数
构造函数调用了父类的构造函数,并传递了对象的名称。 - do_copy 方法
这个方法用于对象的复制。它首先将输入对象强制转换为 my_transaction 类型,然后将属性值复制到当前对象。 - do_compare 方法
这个方法用于对象的比较。它首先将输入对象强制转换为 my_transaction 类型,然后比较各个属性的值。 - do_print 方法
这个方法用于打印对象的属性。它通过 uvm_printer 打印字段的名称和值。
二、使用事务类
在测试平台中,可以使用事务类来创建、随机化、复制和比较对象:
module testbench;
initial begin
my_transaction tx1, tx2;
// 创建事务对象
tx1 = my_transaction::type_id::create("tx1");
tx2 = my_transaction::type_id::create("tx2");
// 随机化事务对象
if (!tx1.randomize()) `uvm_error("RAND", "Randomization failed")
if (!tx2.randomize()) `uvm_error("RAND", "Randomization failed")
// 打印事务对象
tx1.print();
tx2.print();
// 复制事务对象
tx2.copy(tx1);
// 比较事务对象
if (tx2.compare(tx1)) begin
`uvm_info("COMPARE", "Transaction matched", UVM_LOW)
end else begin
`uvm_error("COMPARE", "Transaction mismatch")
end
end
endmodule
- 创建事务对象
使用 type_id::create 方法通过工厂创建对象,传递对象的名称。 - 随机化事务对象
调用对象的 randomize 方法,为随机化的寄存器属性赋值。 - 打印事务对象
调用对象的 print 方法,打印对象的属性。 - 复制事务对象
调用对象的 copy 方法,将一个对象的值复制到另一个对象。 - 比较事务对象
调用对象的 compare 方法,比较两个对象的值。
三、配置验证平台
uvm_object 还可以用于定义配置类,用于规范验证平台的行为方式:
class my_config extends uvm_object;
// 定义配置参数
bit [3:0] bus_width;
bit clk_freq;
// 注册类到 UVM 工厂
`uvm_object_utils(my_config)
// 构造函数
function new(string name = "my_config");
super.new(name);
endfunction
// 实现 copy 方法
virtual function void do_copy(uvm_object rhs);
my_config cfg;
if (!$cast(cfg, rhs)) begin
`uvm_fatal("COPY", "Copy failed: wrong type")
end
this.bus_width = cfg.bus_width;
this.clk_freq = cfg.clk_freq;
endfunction
// 实现 compare 方法
virtual function bit do_compare(uvm_object rhs, uvm_comparer comparer);
my_config cfg;
if (!$cast(cfg, rhs)) return 0;
return (this.bus_width == cfg.bus_width) && (this.clk_freq == cfg.clk_freq);
endfunction
// 实现 print 方法
virtual function void do_print(uvm_printer printer);
printer.print_field("bus_width", this.bus_width, 4);
printer.print_field("clk_freq", this.clk_freq, 1);
endfunction
endclass
- 配置参数
bus_width 和 clk_freq 是配置参数,用于描述验证平台的行为。 - 使用配置类
在验证平台中,可以通过 config_db 将配置类设置给各个组件:
initial begin
my_config cfg = new("cfg");
cfg.bus_width = 8;
cfg.clk_freq = 1;
// 设置配置类
uvm_config_db#(my_config)::set(null, "*", "my_config", cfg);
end
四、寄存器模型
uvm_object 还可以用于定义寄存器模型中的类,如 uvm_reg:
class my_reg extends uvm_reg;
// 定义寄存器的属性
rand bit [7:0] data;
// 注册类到 UVM 工厂
`uvm_object_utils(my_reg)
// 构造函数
function new(string name = "my_reg");
super.new(name, 8);
endfunction
endclass
- 定义寄存器
my_reg 是从 uvm_reg 派生的类,用于描述寄存器的行为和结构。 - 使用寄存器模型
在验证平台中,可以创建寄存器对象并对其进行操作:
initial begin
my_reg reg = my_reg::type_id::create("reg");
// 随机化寄存器对象
if (!reg.randomize()) `uvm_error("RAND", "Randomization failed")
// 打印寄存器对象
reg.print();
end
五、工厂模式
uvm_object 还可以用于实现工厂模式,动态创建对象:
class my_factory extends uvm_object;
// 注册类到 UVM 工厂
`uvm_object_utils(my_factory)
// 构造函数
function new(string name = "my_factory");
super.new(name);
endfunction
endclass
initial begin
uvm_factory factory = uvm_factory::get();
factory.register_type_by_name("my_factory", my_factory::get_type());
end
- 工厂模式
通过工厂模式,可以动态选择创建对象的类型。
六、派生类
1. 定义派生自 uvm_object
的类
在验证项目中,我们常常需要自定义一些类来封装数据或配置信息,这些类可以派生自 uvm_object
。
示例代码:定义 my_transaction
类
class my_transaction extends uvm_sequence_item;
// 定义成员变量,封装事务信息
rand bit [47:0] dst_mac;
rand bit [47:0] src_mac;
rand bit [15:0] frame_type;
rand byte data[];
bit [31:0] fcs;
// 工厂注册,方便 UVM 工厂管理对象创建
`uvm_object_utils_begin(my_transaction)
`uvm_field_int(dst_mac, UVM_ALL_ON)
`uvm_field_int(src_mac, UVM_ALL_ON)
`uvm_field_int(frame_type, UVM_ALL_ON)
`uvm_field_array_int(data, UVM_ALL_ON)
`uvm_field_int(fcs, UVM_ALL_ON)
`uvm_object_utils_end
// 构造函数
function new(string name = "my_transaction");
super.new(name);
endfunction
endclass
解释:
my_transaction
类派生自uvm_sequence_item
,而uvm_sequence_item
又派生自uvm_object
,用于封装一个 MAC 帧的相关信息。uvm_object_utils_begin
和uvm_object_utils_end
是 UVM 提供的宏,用于将类注册到 UVM 工厂,方便后续创建对象和实现多态。uvm_field_*
宏用于指定类的成员变量参与 UVM 的自动化操作,如打印、复制、比较等。
2. 使用 uvm_object
进行数据传输
在验证平台中,uvm_object
常用于在不同组件之间传输数据。例如,sequence
生成 sequence_item
(派生自 uvm_object
),并通过 sequencer
传递给 driver
。
示例代码:定义 my_sequence
类
class my_sequence extends uvm_sequence #(my_transaction);
// 工厂注册
`uvm_object_utils(my_sequence)
// 构造函数
function new(string name = "my_sequence");
super.new(name);
endfunction
// body 任务,生成事务并发送
virtual task body();
my_transaction tr;
repeat (10) begin
// 创建事务对象
tr = my_transaction::type_id::create("tr");
// 随机化事务
assert(tr.randomize());
// 发送事务
start_item(tr);
finish_item(tr);
end
endtask
endclass
解释:
my_sequence
类派生自uvm_sequence
,并指定了事务类型为my_transaction
。- 在
body
任务中,通过create
方法创建my_transaction
对象,然后对其进行随机化,最后使用start_item
和finish_item
方法将事务发送给sequencer
。
3. 利用 uvm_object
的自动化功能
uvm_object
提供了许多自动化功能,如打印、复制、比较等,可以方便我们进行调试和验证。
示例代码:使用自动化功能
module test;
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_transaction.sv"
`include "my_sequence.sv"
initial begin
my_transaction tr1, tr2;
// 创建事务对象
tr1 = my_transaction::type_id::create("tr1");
// 随机化事务
assert(tr1.randomize());
// 打印事务信息
$display("Transaction 1:");
tr1.print();
// 复制事务
tr2 = tr1.clone();
$display("Transaction 2 (cloned from tr1):");
tr2.print();
// 比较事务
if (tr1.compare(tr2)) begin
$display("Transactions are equal.");
end else begin
$display("Transactions are not equal.");
end
end
endmodule
解释:
print
方法用于打印事务的详细信息,方便调试。clone
方法用于复制一个事务对象,创建一个新的对象并复制其成员变量的值。compare
方法用于比较两个事务对象的成员变量是否相等。
4. 定义配置类(派生自 uvm_object
)
在验证项目中,我们常常需要定义一些配置类来管理验证平台的参数和行为。这些配置类可以派生自 uvm_object
,并通过 config_db
进行配置传递。
示例代码:定义 my_config
类
class my_config extends uvm_object;
// 定义配置参数
bit [7:0] addr_width;
bit [7:0] data_width;
// 工厂注册
`uvm_object_utils_begin(my_config)
`uvm_field_int(addr_width, UVM_ALL_ON)
`uvm_field_int(data_width, UVM_ALL_ON)
`uvm_object_utils_end
// 构造函数
function new(string name = "my_config");
super.new(name);
endfunction
endclass
示例代码:使用 config_db
进行配置传递
module test;
import uvm_pkg::*;
`include "uvm_macros.svh"
`include "my_config.sv"
initial begin
my_config cfg;
// 创建配置对象
cfg = my_config::type_id::create("cfg");
// 设置配置参数
cfg.addr_width = 8;
cfg.data_width = 32;
// 将配置对象存储到 config_db 中
uvm_config_db#(my_config)::set(null, "*", "my_config", cfg);
// 从 config_db 中获取配置对象
my_config get_cfg;
if (!uvm_config_db#(my_config)::get(null, "*", "my_config", get_cfg)) begin
`uvm_fatal("CFG_GET", "Failed to get configuration object.")
end
// 打印配置信息
$display("Address width: %0d", get_cfg.addr_width);
$display("Data width: %0d", get_cfg.data_width);
end
endmodule
解释:
my_config
类派生自uvm_object
,用于封装验证平台的配置参数。uvm_config_db
是 UVM 提供的一个全局数据库,用于在不同组件之间传递配置信息。通过set
方法将配置对象存储到数据库中,通过get
方法从数据库中获取配置对象。
通过以上步骤,我们可以将 uvm_object
应用到实际的验证项目中,实现数据封装、传输、自动化操作和配置管理等功能。