函数覆盖及其使用举例

       覆盖是指派生类中如果存在重新定义的函数,其函数名、参数列、返回值类型必须同父类中的相对应被覆盖的函数严格一致。覆盖函数和被覆盖函数只有函数体不同,当派生类对象调用子类中该同名函数时会自动调用子类中的覆盖版本,而不是父类中的被覆盖函数版本。

       覆盖的特征为在不同的范围中(分别位于派生类与基类)的函数,函数名字相同,函数参数相同,基类函数必须有virtual关键字,覆盖的示例代码如下:

#include<iostream>
using namespace std;

//定义基类
class Boy
{
public:
	virtual void g(void)
	{
		cout << "Boy::handsome!" << endl;//函数体
	}
};

//定义派生类
class Girl:public Boy
{
public:
	virtual void g(void)
	{
		cout << "Girl::beautiful!" << endl;//和上面的基类对比,只有函数体不同
	}
};

int main()
{
	Girl d;
	Boy *pb = &d;
	pb->g();
	system("pause");
	return 0;
}

运行结果:

Girl::beautiful!

请按任意键继续. . .

    上面的代码中,虚函数Boy::g(void)被Girl::g(void)覆盖,从而在子类中产生了一个新的函数。

问题:什么时候发生函数覆盖?

   覆盖在继承时派生类对于抽象类或者接口声明的方法的具体实现时使用。

<think> 我们正在讨论UVM寄存器模型(uvm_reg)中的内建读写函数及其使用示例。根据引用内容,我们知道寄存器模型支持前门访问(通过总线UVC)和后门访问(通过UVM DPI)。同时,引用提到寄存器操作中会使用adapter进行桥接(reg2bus和bus2reg)。 接下来,我将详细解释uvm_reg中的内建读写函数,并给出使用示例。 ### 1. uvm_reg的内建读写函数概述 在UVM中,`uvm_reg`类提供了一系列内建函数用于寄存器的读写操作。这些函数可以分为两类: - **前门访问(Frontdoor Access)**:通过总线协议进行读写,需要adapter进行转换[^2][^3]。 - **后门访问(Backdoor Access)**:直接通过HDL路径访问寄存器,使用`uvm_hdl_read`和`uvm_hdl_deposit`[^2]。 #### 主要函数 | 函数原型 | 访问方式 | 描述 | |----------|----------|------| | `virtual task read(output uvm_status_e status, output uvm_reg_data_t value, input uvm_path_e path = UVM_DEFAULT_PATH, ...)` | 前门/后门 | 读取寄存器值 | | `virtual task write(output uvm_status_e status, input uvm_reg_data_t value, input uvm_path_e path = UVM_DEFAULT_PATH, ...)` | 前门/后门 | 写入寄存器值 | | `virtual task mirror(output uvm_status_e status, input uvm_check_e check = UVM_NO_CHECK, input uvm_path_e path = UVM_DEFAULT_PATH, ...)` | 前门/后门 | 读取寄存器值并更新镜像值,可选择是否检查错误 | | `virtual task update(output uvm_status_e status, input uvm_path_e path = UVM_DEFAULT_PATH, ...)` | 前门/后门 | 将期望值写入硬件(如果与镜像值不同) | 其中: - `status`:操作状态(如`UVM_IS_OK`表示成功)。 - `value`:读取或写入的数据。 - `path`:访问路径(`UVM_FRONTDOOR`或`UVM_BACKDOOR`)。 - `check`:在`mirror`任务中,如果设置为`UVM_CHECK`,则读取的值会与镜像值比较。 ### 2. 使用示例 #### 示例1:前门读写操作 假设我们有一个寄存器`ctrl_reg`,我们通过前门访问进行读写。 ```systemverilog // 创建寄存器模型并配置(通常在env中完成) class my_env extends uvm_env; my_reg_block reg_model; uvm_reg_adapter adapter; uvm_reg_predictor #(bus_trans) predictor; virtual function void build_phase(uvm_phase phase); reg_model = my_reg_block::type_id::create("reg_model", this); adapter = my_reg_adapter::type_id::create("adapter", this); predictor = uvm_reg_predictor#(bus_trans)::type_id::create("predictor", this); // 引用[1] // 配置寄存器模型并映射到地址空间 reg_model.configure(null, ""); reg_model.build(); reg_model.lock_model(); // 设置predictor和adapter predictor.map = reg_model.default_map; predictor.adapter = adapter; endfunction endclass // 在测试序列中使用前门读写 class reg_test_seq extends uvm_sequence; my_reg_block reg_model; task body(); uvm_status_e status; uvm_reg_data_t rdata; // 写操作(前门) reg_model.ctrl_reg.write(status, 'h1, .path(UVM_FRONTDOOR)); if (status != UVM_IS_OK) `uvm_error("REG_WRITE", "Write failed") // 读操作(前门) reg_model.ctrl_reg.read(status, rdata, .path(UVM_FRONTDOOR)); if (status != UVM_IS_OK) `uvm_error("REG_READ", "Read failed") else `uvm_info("REG_READ", $sformatf("Read data: 0x%0h", rdata), UVM_LOW) endtask endclass ``` **说明**: - 前门访问需要正确的adapter和predictor配置[^1][^3]。 - 写操作将值`'h1`写入`ctrl_reg`,然后读取该寄存器。 #### 示例2:后门读写操作 后门访问不经过总线,直接操作硬件信号。 ```systemverilog class backdoor_test_seq extends uvm_sequence; my_reg_block reg_model; task body(); uvm_status_e status; uvm_reg_data_t rdata; // 后门写入 reg_model.ctrl_reg.write(status, 'h5, .path(UVM_BACKDOOR)); if (status != UVM_IS_OK) `uvm_error("REG_WRITE", "Backdoor write failed") // 后门读取 reg_model.ctrl_reg.read(status, rdata, .path(UVM_BACKDOOR)); if (status != UVM_IS_OK) `uvm_error("REG_READ", "Backdoor read failed") else `uvm_info("REG_READ", $sformatf("Backdoor read data: 0x%0h", rdata), UVM_LOW) // 使用mirror检查(后门) reg_model.ctrl_reg.mirror(status, UVM_CHECK, UVM_BACKDOOR); // 读取并检查 endtask endclass ``` **说明**: - 后门访问不需要adapter,但需要正确设置寄存器的HDL路径(通常在寄存器定义时指定)。 - `mirror`任务在这里执行读取并与镜像值比较(因为设置了`UVM_CHECK`),如果值不匹配会报错。 #### 示例3:使用mirror和update ```systemverilog class mirror_update_seq extends uvm_sequence; my_reg_block reg_model; task body(); uvm_status_e status; // 设置期望值(不写入硬件) reg_model.ctrl_reg.set('hA); // 将期望值同步到硬件(update会检查期望值与镜像值,不同则写入) reg_model.ctrl_reg.update(status, UVM_FRONTDOOR); if (status != UVM_IS_OK) `uvm_error("UPDATE", "Update failed") // 使用mirror读取硬件寄存器并更新镜像值(不检查) reg_model.ctrl_reg.mirror(status, UVM_NO_CHECK, UVM_FRONTDOOR); endtask endclass ``` **说明**: - `set()`方法只更新寄存器的期望值(desired value),不写入硬件。 - `update()`将期望值写入硬件(如果期望值与镜像值不同)。 - `mirror()`更新镜像值为硬件当前值(可选择是否检查)。 ### 3. 高级应用:内建测试序列 UVM提供了一些内建序列用于寄存器测试,如: - `uvm_reg_hw_reset_seq`:检查复位值[^4]。 - `uvm_reg_bit_bash_seq`:测试寄存器的每个位[^4]。 使用示例: ```systemverilog class hw_reset_test extends uvm_reg_hw_reset_seq; `uvm_object_utils(hw_reset_test) function new(string name=""); super.new(name); endfunction virtual task body(); // 设置寄存器模型(引用[4]) model = reg_model; // reg_model是测试中的寄存器模型实例 super.body(); endtask endclass // 在测试中启动 initial begin hw_reset_test hw_reset_seq = hw_reset_test::type_id::create("hw_reset_seq"); hw_reset_seq.model = reg_model; // 传递寄存器模型 hw_reset_seq.start(null); end ``` ### 4. 错误处理 每次寄存器操作后都应检查`status`变量: - `UVM_IS_OK`:操作成功 - `UVM_HAS_X`:读取值包含X - `UVM_NOT_OK`:操作失败(如总线错误) ### 总结 `uvm_reg`的内建读写函数提供了灵活的前门和后门访问方式,关键点包括: 1. 前门访问需要配置adapter和predictor[^1][^3]。 2. 后门访问需要设置HDL路径[^2]。 3. 使用`mirror`更新镜像值并可选检查,使用`update`同步期望值到硬件。 4. 充分利用内建测试序列(如`uvm_reg_hw_reset_seq`)进行验证[^4]。 §§相关问题§§ 1. 如何为寄存器设置后门路径? 2. 如何自定义寄存器前门访问的adapter? 3. UVM寄存器模型中的镜像值(mirror value)和期望值(desired value)有什么区别? 4. 在内建序列`uvm_reg_bit_bash_seq`中如何排除只读寄存器? </think> ### UVM寄存器模型的内建读写函数详解 #### 1. **核心读写函数** 在UVM寄存器模型中,`uvm_reg`类提供以下关键方法: | 函数原型 | 访问方式 | 描述 | 返回值说明 | |---------|---------|------|-----------| | **`task write(output uvm_status_e status, input uvm_reg_data_t value, input uvm_path_e path = UVM_DEFAULT_PATH)`** | 前门/后门 | 寄存器写入操作 | `status`: 操作状态码 | | **`task read(output uvm_status_e status, output uvm_reg_data_t value, input uvm_path_e path = UVM_DEFAULT_PATH)`** | 前门/后门 | 寄存器读取操作 | `value`: 读取的数据 | | **`task mirror(output uvm_status_e status, input uvm_check_e check = UVM_NO_CHECK, input uvm_path_e path = UVM_DEFAULT_PATH)`** | 前门/后门 | 读取硬件值并更新镜像值 | `check`启用时进行值比对 | | **`task update(output uvm_status_e status, input uvm_path_e path = UVM_DEFAULT_PATH)`** | 前门/后门 | 将期望值写入硬件 | 仅当期望值≠镜像值时写入 | **路径参数说明**: - `UVM_FRONTDOOR`:通过总线物理访问(产生总线事务)[^2] - `UVM_BACKDOOR`:通过DPI直接访问(使用`uvm_hdl_read/deposit`)[^2] #### 2. **使用示例** ##### 示例1:基础读写操作 ```systemverilog // 寄存器声明 class ral_reg_ctrl extends uvm_reg; `uvm_object_utils(ral_reg_ctrl) rand uvm_reg_field en; ... endclass // 读写操作 task test_reg_ops(uvm_reg ctrl_reg); uvm_status_e status; uvm_reg_data_t data; // 写入0xA5(前门访问) ctrl_reg.write(status, 'hA5, UVM_FRONTDOOR); if (status != UVM_IS_OK) `uvm_error("REG", "Write failed") // 读取验证(后门访问) ctrl_reg.read(status, data, UVM_BACKDOOR); `uvm_info("READ", $sformatf("Reg value: 0x%0h", data), UVM_LOW) endtask ``` ##### 示例2:镜像值更新与检查 ```systemverilog // 更新镜像值(不写入硬件) ctrl_reg.set('hFF); // 将期望值同步到硬件 ctrl_reg.update(status); if(status != UVM_IS_OK) `uvm_error("UPDATE", "Sync failed") // 读取硬件值并比对镜像 ctrl_reg.mirror(status, UVM_CHECK); // 启用值检查[^4] ``` ##### 示例3:复位值检查序列 ```systemverilog // 使用内建序列检查复位值 uvm_reg_hw_reset_seq rst_seq = uvm_reg_hw_reset_seq::type_id::create("rst_seq"); rst_seq.model = reg_model; // 绑定寄存器模型[^4] rst_seq.start(null); // 启动序列 ``` #### 3. **前门访问工作原理** 当使用前门访问时,UVM自动完成以下转换: ```mermaid graph LR A[write()/read()] --> B[生成uvm_reg_item] B --> C[adapter.reg2bus] C --> D[总线事务] D --> E[总线驱动] E --> F[物理寄存器] F --> G[返回响应] G --> H[adapter.bus2reg] H --> I[更新镜像值] ``` 关键转换函数: ```systemverilog function uvm_sequence_item adapter::reg2bus(uvm_reg_bus_op rw); bus_trans trans = bus_trans::type_id::create("trans"); trans.addr = rw.addr; trans.data = rw.data; trans.kind = (rw.kind == UVM_READ) ? READ : WRITE; return trans; endfunction ``` #### 4. **高级应用技巧** 1. **批量寄存器操作**: ```systemverilog uvm_reg regs[$]; reg_model.get_registers(regs); foreach(regs[i]) begin regs[i].write(status, 0); // 批量清零 end ``` 2. **条件性后门访问**: ```systemverilog if(reg_model.backdoor_enabled) data_reg.read(status, , UVM_BACKDOOR); else data_reg.read(status, , UVM_FRONTDOOR); ``` 3. **错误注入测试**: ```systemverilog // 强制写入错误值 ctrl_reg.set('hBAD); ctrl_reg.update(status); // 触发异常情况 ``` #### 5. **状态码处理** 关键状态码说明: | 状态码 | 含义 | 典型处理方式 | |-------|------|------------| | `UVM_IS_OK` | 操作成功 | 继续流程 | | `UVM_NOT_OK` | 操作失败 | `uvm_error`记录 | | `UVM_HAS_X` | 读取到X态 | 复位检查或错误注入 | #### 6. **实战建议** 1. **访问路径选择**: - 功能验证优先用前门访问 - 调试/初始化用后门访问 2. **寄存器测试序列**: - 复位检查:`uvm_reg_hw_reset_seq` - 位翻转测试:`uvm_reg_bit_bash_seq`[^4] - 遍历测试:`uvm_reg_access_seq` 3. **覆盖率收集**: ```systemverilog covergroup reg_cg; write: coverpoint status {bins ok = {UVM_IS_OK};} read: coverpoint data {bins vals = {[0:255]};} endgroup ``` --- ### 总结 UVM寄存器模型的内建读写函数提供: 1. **双路径访问**:前门(物理总线)和后门(直接HDL访问)[^2] 2. **值管理**:镜像值自动跟踪硬件状态 3. **事务转换**:通过adapter桥接寄存器操作和总线事务[^3] 4. **内建测试**:标准序列实现复位检查和位翻转测试[^4] 正确使用这些函数可显著提高验证效率和可靠性,建议结合预测器(predictor)实现自动镜像值更新[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值