在 UVM(或 SystemVerilog)中,子类cfg(配置类)修改父类声明的变量,核心是利用继承的可见性规则(父类变量需为protected/public),通过 “直接赋值、方法重载、构造函数初始化” 等方式实现,以下是分场景的标准做法(含完整代码示例):
一、前提:父类变量的可见性(关键)
父类中变量的访问权限决定子类能否直接修改,UVM 中推荐父类变量声明为protected(子类可访问,外部不可改)或public(全范围可访问),禁止用local(子类不可访问)。
父类 cfg 定义(示例):
systemverilog
// 父类配置类:base_cfg
class base_cfg extends uvm_object;
`uvm_object_utils(base_cfg)
// 声明可被子类修改的变量(protected:子类可访问,外部不可改)
protected int burst_len = 8; // 默认突发长度
protected bit en_coverage = 1; // 默认开启覆盖率
protected bit [31:0] base_addr = 32'h1000_0000; // 默认基地址
// 可选:父类提供修改变量的方法(推荐,封装逻辑)
virtual function void set_burst_len(int len);
if (len > 0 && len <= 64) begin // 合法性检查
burst_len = len;
end else begin
`uvm_error("BASE_CFG_ERR", $sformatf("burst_len=%0d 超出范围", len))
end
endfunction
function new(string name = "base_cfg");
super.new(name);
endfunction
endclass
二、场景 1:子类 cfg 中直接修改父类变量(最简单)
子类继承父类后,可直接访问protected/public变量并赋值(推荐在new函数或post_randomize中修改)。
子类 cfg 定义:
systemverilog
// 子类配置类:axi_cfg(继承base_cfg)
class axi_cfg extends base_cfg;
`uvm_object_utils(axi_cfg)
// 新增子类独有的变量
bit en_debug = 0;
// 场景1.1:在构造函数(new)中修改父类变量
function new(string name = "axi_cfg");
super.new(name);
// 直接修改父类protected变量
burst_len = 16; // 覆盖父类默认值8
base_addr = 32'h2000_0000; // 覆盖默认基地址
en_coverage = 0; // 关闭覆盖率
endfunction
// 场景1.2:在自定义方法中修改父类变量
virtual function void cfg_init();
burst_len = 32; // 动态修改
`uvm_info("AXI_CFG_MOD", $sformatf("父类burst_len修改为:%0d", burst_len), UVM_MEDIUM)
endfunction
// 场景1.3:随机化时修改(post_randomize)
virtual function void post_randomize();
super.post_randomize(); // 先执行父类的post_randomize
burst_len = burst_len * 2; // 随机化后调整父类变量
endfunction
endclass
三、场景 2:通过父类方法修改(推荐,封装性更好)
父类提供set_*方法修改变量(含合法性检查),子类调用该方法修改,避免直接赋值导致的非法值。
子类中调用父类方法:
systemverilog
class axi_cfg extends base_cfg;
`uvm_object_utils(axi_cfg)
function new(string name = "axi_cfg");
super.new(name);
// 调用父类方法修改(自动做合法性检查)
super.set_burst_len(20); // 合法值:修改为20
super.set_burst_len(100); // 非法值:父类会报错,变量不修改
endfunction
endclass
四、场景 3:重载父类方法修改(适配特殊逻辑)
若父类方法的逻辑不满足子类需求,可重载父类的set_*方法,在子类中定制修改变量的逻辑。
子类重载父类方法:
systemverilog
class axi_cfg extends base_cfg;
`uvm_object_utils(axi_cfg)
// 重载父类的set_burst_len方法
virtual function void set_burst_len(int len);
// 子类定制逻辑:允许更大的突发长度
if (len > 0 && len <= 128) begin
burst_len = len;
`uvm_info("AXI_CFG_SET", $sformatf("子类修改burst_len为:%0d", len), UVM_MEDIUM)
end else begin
`uvm_error("AXI_CFG_ERR", $sformatf("burst_len=%0d 超出子类范围(0~128)", len))
end
endfunction
function new(string name = "axi_cfg");
super.new(name);
set_burst_len(64); // 调用子类重载后的方法
endfunction
endclass
五、场景 4:通过外部组件(如 test/sequence)间接修改
若子类cfg已实例化,可通过句柄直接修改父类继承的变量(需变量为protected/public)。
Test 中修改子类 cfg 的父类变量:
systemverilog
class axi_test extends uvm_test;
`uvm_component_utils(axi_test)
axi_cfg cfg;
function void build_phase(uvm_phase phase);
super.build_phase(phase);
// 实例化子类cfg
cfg = axi_cfg::type_id::create("cfg");
// 直接修改父类继承的变量
cfg.burst_len = 4; // 子类句柄可直接访问父类protected变量
// 调用父类方法修改
cfg.set_burst_len(8);
endfunction
endclass
六、关键注意事项(避坑指南)
-
访问权限:
- 父类变量若声明为
local,子类无法访问(编译报错),需改为protected/public; - UVM 中推荐父类变量用
protected(封装性更好),仅通过方法对外暴露修改接口。
- 父类变量若声明为
-
初始化顺序:
- 子类
new函数中,需先调用super.new()(执行父类构造),再修改变量; - 若父类
new中对变量做了初始化,子类new中修改会覆盖父类值。
- 子类
-
随机化兼容:
- 若父类变量需要随机化,需在父类中用
rand声明:rand int burst_len;; - 子类可通过
constraint约束父类随机变量:systemverilog
class axi_cfg extends base_cfg; `uvm_object_utils(axi_cfg) constraint burst_len_c { burst_len inside {[16:32]}; } // 约束父类变量 endclass
- 若父类变量需要随机化,需在父类中用
-
UVM 工厂兼容:
- 父类和子类都需加
uvm_object_utils宏,确保工厂创建实例时变量初始化生效; - 若通过
uvm_config_db传递 cfg 句柄,修改后需确保所有使用该 cfg 的组件同步更新。
- 父类和子类都需加
七、总结
子类 cfg 修改父类变量的核心方法:
| 方法 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| 直接赋值 | 简单修改、无合法性检查 | 简洁高效 | 无校验,易赋非法值 |
| 调用父类 set 方法 | 需合法性检查、复用父类逻辑 | 安全、符合封装原则 | 需父类提前定义方法 |
| 重载父类 set 方法 | 子类需定制修改逻辑 | 灵活、适配子类需求 | 代码量稍多 |
| post_randomize 修改 | 随机化后调整变量 | 兼容随机化流程 | 仅随机化场景生效 |
核心原则:父类尽量封装修改变量的方法(而非直接暴露变量),子类通过方法修改,既保证合法性,又提升代码可维护性。
1132

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



