举例说明:在tc_test和rm之间传递一个cfg句柄
在 UVM 中,test(测试用例)与rm(Register Model,寄存器模型)之间通过uvm_config_db传递cfg句柄(配置对象),核心是利用uvm_config_db的 “路径 + 名称” 索引机制,将test中配置好的cfg对象写入数据库,再在rm的构建阶段读取该句柄。以下是标准流程 + 完整代码示例,覆盖关键步骤与避坑要点:
一、前提定义:先明确cfg类与rm类
首先定义通用的配置类(xxx_cfg)和寄存器模型类(xxx_rm),作为传递的基础:
systemverilog
// 1. 定义配置类(含寄存器模型所需的参数:基地址、时钟周期、访问模式等)
class axi_cfg extends uvm_object;
`uvm_object_utils(axi_cfg)
// 寄存器模型核心配置参数
bit [31:0] reg_base_addr = 32'h1000_0000; // 寄存器基地址
int clk_period = 10; // 时钟周期(ns)
bit reg_coverage = 1; // 是否开启寄存器覆盖率
function new(string name = "axi_cfg");
super.new(name);
endfunction
endclass
// 2. 定义寄存器模型类(需接收cfg句柄)
class axi_rm extends uvm_reg_block;
`uvm_object_utils(axi_rm)
axi_cfg cfg; // 本地存储cfg句柄
uvm_reg status_reg; // 示例:状态寄存器
uvm_reg ctrl_reg; // 示例:控制寄存器
function new(string name = "axi_rm");
super.new(name, build_coverage(UVM_NO_COVERAGE));
endfunction
// 构建阶段:读取cfg句柄 + 初始化寄存器
virtual function void build();
super.build();
// 步骤1:从uvm_config_db读取cfg句柄(核心)
if (!uvm_config_db#(axi_cfg)::get(this, "", "axi_cfg_h", cfg)) begin
`uvm_fatal("RM_GET_CFG_ERR", "寄存器模型未读取到cfg句柄!")
end
// 步骤2:使用cfg参数初始化寄存器(示例)
status_reg = uvm_reg::type_id::create("status_reg");
status_reg.configure(this, 32, 0); // 32位,非可配置
ctrl_reg = uvm_reg::type_id::create("ctrl_reg");
ctrl_reg.configure(this, 32, 0);
// 步骤3:基于cfg的基地址创建寄存器映射
default_map = create_map("default_map", cfg.reg_base_addr, 4, UVM_LITTLE_ENDIAN);
default_map.add_reg(status_reg, 32'h00, "RW"); // 偏移0x00
default_map.add_reg(ctrl_reg, 32'h04, "RW"); // 偏移0x04
`uvm_info("RM_CFG_LOAD", $sformatf(
"寄存器模型加载cfg成功:基地址=0x%0h, 时钟周期=%0dns",
cfg.reg_base_addr, cfg.clk_period), UVM_MEDIUM)
endfunction
endclass
二、核心流程:Test 中 Set → RM 中 Get
步骤 1:Test 中创建并配置 cfg,写入 uvm_config_db
test是验证环境的顶层控制者,需先实例化cfg并配置参数,再通过uvm_config_db传递给rm:
systemverilog
class axi_base_test extends uvm_test;
`uvm_component_utils(axi_base_test)
axi_cfg cfg; // 配置对象
axi_rm rm; // 寄存器模型
uvm_env env; // 验证环境(可选)
function new(string name = "axi_base_test", uvm_component parent = null);
super.new(name, parent);
endfunction
// 构建阶段:创建cfg → 配置参数 → set到uvm_config_db → 创建rm
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
// 步骤1:实例化并配置cfg
cfg = axi_cfg::type_id::create("cfg");
cfg.reg_base_addr = 32'h2000_0000; // 自定义基地址(覆盖默认值)
cfg.clk_period = 20; // 自定义时钟周期
cfg.reg_coverage = 1; // 开启覆盖率
// 步骤2:将cfg句柄写入uvm_config_db(核心)
// 关键:路径指向rm的实例路径,确保rm能读取到
uvm_config_db#(axi_cfg)::set(
this, // 上下文:当前test组件
"rm", // 目标路径:test下的rm实例(相对路径)
"axi_cfg_h", // 配置项名称(需与rm中get的名称一致)
cfg // 要传递的cfg句柄
);
// 步骤3:创建寄存器模型实例(自动触发rm的build_phase)
rm = axi_rm::type_id::create("rm", this);
rm.build(); // 显式调用build(或由UVM自动调用)
endfunction
// 运行阶段:验证cfg传递结果
virtual task run_phase(uvm_phase phase);
phase.raise_objection(this);
`uvm_info("TEST_CFG_CHECK", $sformatf(
"Test中cfg参数:基地址=0x%0h, RM中cfg基地址=0x%0h",
cfg.reg_base_addr, rm.cfg.reg_base_addr), UVM_MEDIUM)
phase.drop_objection(this);
endtask
endclass
步骤 2:RM 中读取 cfg 句柄(已嵌入上述axi_rm的build函数)
核心代码回顾:
systemverilog
// 在axi_rm的build函数中
if (!uvm_config_db#(axi_cfg)::get(this, "", "axi_cfg_h", cfg)) begin
`uvm_fatal("RM_GET_CFG_ERR", "寄存器模型未读取到cfg句柄!")
end
三、关键细节与避坑指南
1. 路径匹配原则(最易出错)
set的inst_name需指向rm的实例路径:- 若
rm在test下实例化为rm,则inst_name = "rm"; - 若
rm在env.agent下,需写inst_name = "env.agent.rm"; - 若用绝对路径,可写
"/uvm_top/axi_base_test/rm"(不推荐,降低可重用性)。
- 若
get的inst_name填空字符串"":表示 “从当前组件(rm)的路径下读取”。
2. 类型严格一致
set和get的模板参数必须完全匹配(如axi_cfg不能写成uvm_object):
systemverilog
// 错误示例:类型不匹配(set是axi_cfg,get是uvm_object)
uvm_config_db#(uvm_object)::get(this, "", "axi_cfg_h", cfg); // 编译/运行错误
3. 执行顺序:先 Set 后 Get
set必须在rm的build_phase之前执行(test的build_phase先于rm的build_phase,符合 UVM 的 phase 执行顺序);- 禁止在
test的connect_phase才set(此时rm的build_phase已执行,读取失败)。
4. 全局传递(可选:跨层级传递)
若rm不在test直接子层级(如在env内部),可通过通配符*批量传递:
systemverilog
// Test中:给所有路径下的rm传递cfg
uvm_config_db#(axi_cfg)::set(this, "*rm", "axi_cfg_h", cfg);
四、扩展场景:RM 在 Env 中时的传递
若rm实例化在env中,而非直接在test中,只需调整set的路径:
systemverilog
// Test的build_phase
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
cfg = axi_cfg::type_id::create("cfg");
// 路径改为:test → env → rm
uvm_config_db#(axi_cfg)::set(this, "env.rm", "axi_cfg_h", cfg);
env = axi_env::type_id::create("env", this); // 创建env(内部包含rm)
endfunction
// Env的build_phase
class axi_env extends uvm_env;
`uvm_component_utils(axi_env)
axi_rm rm;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
rm = axi_rm::type_id::create("rm", this); // rm在env中实例化
endfunction
endclass
五、验证传递成功的方法
- 日志打印:在
rm的build函数中打印cfg的参数(如上述示例中的RM_CFG_LOAD日志); - 断言检查:在
rm中对cfg的关键参数做断言,确保未使用默认值:systemverilog
assert(cfg.reg_base_addr == 32'h2000_0000) else begin `uvm_error("RM_CFG_ERR", "cfg参数未正确传递!") end - UVM 打印层级:通过
+UVM_VERBOSITY=UVM_HIGH查看uvm_config_db的读写日志。
总结
test与rm之间传递cfg句柄的核心步骤:
- 定义:创建包含 RM 所需参数的
cfg类; - Set:在
test的build_phase中实例化并配置cfg,通过uvm_config_db#(cfg类型)::set写入(路径指向 RM 实例); - Get:在
rm的build函数中通过uvm_config_db#(cfg类型)::get读取,做容错处理; - 使用:基于
cfg参数初始化 RM 的寄存器、映射等。
关键原则:路径匹配、类型一致、执行顺序(先 Set 后 Get),这是确保传递成功的核心。
5496

被折叠的 条评论
为什么被折叠?



