UVM中批量修改寄存器值?set+update?

本文介绍了一种批量修改寄存器值的方法,避免了对整个寄存器模型更新时不必要的写操作,特别是针对那些被设置为易失模式的寄存器。通过定义一个update_regs()方法,可以更精确地控制哪些寄存器需要被更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

如何批量修改寄存器的值?set+update?

在编写测试用例中,我们很多时候都需要通过寄存器做访问,难免需要配置硬件中各个寄存器的值。write( ) 方法是可以直接设置寄存器的真实值(设置硬件中寄存器的值),但是我们总不能一个一个设置寄存器值。一般我们通过对uvm_reg 或者uvm_reg_field 层次调用set() 设置我们所感兴趣的状态,配置寄存器模型中各个域的 desired value。此时,也许你就会直接对rgm(整个uvm_reg_block)调用update()。
也就是说,此时我们通过 set( ) 对某些个reg 的 field 设置了一个我们所感兴趣的期望值,然后通过对整个寄存器模型 update(),更新所有的寄存器 。

PS:update()的作用是当寄存器模型的期望值与寄存器的真实值不同时,则修改寄存器的真实值。

我们进一步查看uvm_reg_block::update()的源码,如下:
在这里插入图片描述
在这里插入图片描述
updata()方法的内部实现:在对uvm_reg_block(寄存器模型)调用update()方法时,就是通过调用每个reg中的update()方法,最后每个 reg 会对其中的各个field做write( )。

但是,因为寄存器模型rgm一般是通过脚本自动生成的,有的寄存器会(比如状态寄存器)被自动设置为volatile模式(易失的)。而采用updata()更新整个reg_block时,会把这些设置为volatile模式的寄存器也做更新。换句话来说,比如状态寄存器,本身我们是不对其进行任何操作的,但你非要对它也进行写操作,这是没完全没有必要的。

PS:什么叫volatile模式?为了防止非预期的修改,表示对应的寄存器域的数值应该由design内部硬件做出更新,即必须要从bus去读取数值后再更新这个寄存器。

那么我们就该避开直接对整个寄存器模型做update,而是调整到对我们所感兴趣的reg进行update。所以首先需要定义一个updata_regs()方法,如下:

//父一级virtual sequence定义方法的调用
//updata_regs()方法的实现
virtual task father_virtual_sequence::update_regs(uvm_reg regs[]);
    uvm_status_e status;
    foreach(regs[i]) regs[i].update(status);//对每个reg调用update
endtask
-------------==================------------------
//子一级virtual sequence调用方法
virtual task son_virtual_sequence::body();
update_regs('{rgm.IC_CON, //注意这里 ’单引号的使用
              rgm.IC_TAR, 
              rgm.IC_FS_SCL_HCNT, 
              rgm.IC_FS_SCL_LCNT, 
              rgm.IC_ENABLE, 
              rgm.IC_DATA_CMD
                });
endtask

总结

  1. 原先是通过set( )设置寄存器域的期望值,再对整个寄存器模型做updata。
  2. 但是发现对整个寄存器模型做update时,会对某些我们不愿做操作的寄存器(如状态寄存器)也做了更新。
  3. 所以通过构建一个方法update_regs(uvm_reg regs[]),将reg数组传递进去,然后对每个reg做updata,而不再对整个rgm直接做update( ) 的方式。
<think>好的,我现在需要回答用户关于UVM如何测试寄存器读写的问题,并且要给出详细的例子和注释。首先,用户之前已经询问过uvm的reg_map,现在他们可能想深入了解具体的实现方法。 我需要先回顾一下UVM寄存器模型的基本概念。UVM寄存器模型(Register Layer)提供了一种抽象的方式来访问DUT中的寄存器,包括前门访问(通过总线)和后门访问(直接HDL路径)。测试寄存器读写通常涉及创建寄存器模型,配置适配器(adapter)和预测器(predictor),以及编写测试序列。 用户需要一个详细的例子,所以我要构造一个简单的场景。例如,假设有一个DUT包含一个控制寄存器CTRL和一个状态寄存器STATUS。CTRL可读写,STATUS只读。需要编写测试用例来验证这两个寄存器的读写功能。 接下来,我需要分步骤说明如何搭建测试环境。首先,定义寄存器模型,包括寄存器寄存器块。然后,在验证环境中集成寄存器模型,连接适配器和预测器。接着,编写测试序列,执行前门和后门读写操作,并检查结果。 在代码部分,要确保注释详细,解释每个部分的作用。例如,在寄存器定义时,说明每个字段的配置;在适配器中,说明如何转换事务;在测试序列中,演示具体的读写操作和结果验证。 还要注意可能的错误点,比如地址映射是否正确,适配器是否与总线协议匹配,预测器如何更新寄存器模型的等。需要确保例子覆盖这些方面,并且注释能够帮助用户理解关键步骤。 最后,总结整个流程,强调UVM寄存器测试的优势,比如可重用性、自动化检查等,并提醒用户在实际应用中需要注意的事项,比如寄存器的访问权限、同步问题等。</think>以下是一个基于UVM寄存器读写测试的详细示例,包含完整注释和代码结构: --- ### **1. 测试环境架构** ```plaintext +------------------+ +------------------+ +------------------+ | Test Sequence | ---> | Register Model | <===> | Adapter | +------------------+ +------------------+ +------------------+ | ^ | | | v v | +------------------+ +-------------+ | Bus Driver | | Predictor | +------------------+ +-------------+ | v +-------------+ | DUT | +-------------+ ``` --- ### **2. 完整代码示例(带详细注释)** #### **步骤1:定义寄存器字段** ```systemverilog // 定义控制寄存器CTRL(32位) class ctrl_reg extends uvm_reg; rand uvm_reg_field en; // 使能位 [0] rand uvm_reg_field mode; // 模式选择 [2:1] rand uvm_reg_field reserved;// 保留字段 [31:3] function new(string name = "ctrl_reg"); super.new(name, 32, UVM_NO_COVERAGE); endfunction virtual function void build(); en = uvm_reg_field::type_id::create("en"); en.configure(this, 1, 0, "RW", 0, 1'b0, 1, 1, 0); mode = uvm_reg_field::type_id::create("mode"); mode.configure(this, 2, 1, "RW", 0, 2'b00, 1, 1, 0); reserved = uvm_reg_field::type_id::create("reserved"); reserved.configure(this, 29, 3, "RW", 0, 29'h0, 1, 0, 0); endfunction endclass ``` #### **步骤2:创建寄存器块** ```systemverilog class my_reg_block extends uvm_reg_block; rand ctrl_reg ctrl; // 控制寄存器 uvm_reg_map main_map; // 主地址映射 function new(string name = "my_reg_block"); super.new(name, UVM_NO_COVERAGE); endfunction virtual function void build(); // 创建寄存器实例 ctrl = ctrl_reg::type_id::create("ctrl"); ctrl.configure(this, null, ""); ctrl.build(); // 创建地址映射:基地址0x0000,总线宽度4字节,小端格式 main_map = create_map("main_map", 'h0000, 4, UVM_LITTLE_ENDIAN); // 将寄存器添加到地址映射:偏移地址0x00 main_map.add_reg(ctrl, 'h00, "RW"); endfunction endclass ``` #### **步骤3:实现总线适配器(Adapter)** ```systemverilog class my_adapter extends uvm_reg_adapter; `uvm_object_utils(my_adapter) function new(string name="my_adapter"); super.new(name); endfunction // 将寄存器事务转换为总线事务 function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw); my_bus_transaction tr = my_bus_transaction::type_id::create("tr"); tr.addr = rw.addr; // 地址直接传递 tr.data = rw.data; // 数据直接传递 tr.rw = (rw.kind == UVM_READ) ? READ : WRITE; return tr; endfunction // 将总线响应转换回寄存器模型 function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw); my_bus_transaction tr; if (!$cast(tr, bus_item)) begin `uvm_fatal("CAST_FAIL", "Bus item类型转换失败") end rw.addr = tr.addr; rw.data = tr.data; rw.status = (tr.error) ? UVM_NOT_OK : UVM_IS_OK; endfunction endclass ``` #### **步骤4:构建测试环境** ```systemverilog class my_env extends uvm_env; my_reg_block reg_model; // 寄存器模型 my_adapter adapter; // 总线适配器 uvm_reg_predictor #(my_bus_transaction) predictor; // 预测器 function void build_phase(uvm_phase phase); reg_model = my_reg_block::type_id::create("reg_model"); reg_model.build(); // 构建寄存器层次 reg_model.lock_model(); // 锁定模型 adapter = my_adapter::type_id::create("adapter"); predictor = new("predictor", this); endfunction function void connect_phase(uvm_phase phase); // 将适配器与寄存器模型关联 reg_model.main_map.set_sequencer(bus_sequencer, adapter); // 连接预测器与总线Monitor predictor.map = reg_model.main_map; predictor.adapter = adapter; bus_monitor.item_collected_port.connect(predictor.bus_in); endfunction endclass ``` #### **步骤5:编写测试序列** ```systemverilog class reg_test_seq extends uvm_sequence; `uvm_object_utils(reg_test_seq) task body(); uvm_status_e status; uvm_reg_data_t rdata; // 1. 前门写入CTRL寄存器 reg_model.ctrl.write(status, 'h3); // 写入0x3(使能位+模式01) if (status != UVM_IS_OK) `uvm_error("WRITE_FAIL", "前门写入失败") // 2. 前门读取并验证 reg_model.ctrl.read(status, rdata); if (rdata !== 'h3) `uvm_error("READ_MISMATCH", $sformatf("读回0x%0h != 期望0x3", rdata)) // 3. 后门直接修改寄存器 reg_model.ctrl.en.set(0); // 通过后门关闭使能位 reg_model.ctrl.update(status); // 同步到DUT(可选) // 4. 后门读取验证 if (reg_model.ctrl.en.get() !== 0) `uvm_error("BACKDOOR_FAIL", "后门修改未生效") endtask endclass ``` --- ### **3. 关键点解释** 1. **寄存器模型初始化** - `build()`:构建寄存器层次结构 - `lock_model()`:防止后续修改地址映射 2. **预测器(Predictor)作用** - 监听总线事务,自动更新寄存器模型,保持模型与DUT同步 3. **前门(Frontdoor) vs 后门(Backdoor)** - 前门:通过总线协议操作(受总线功能约束) - 后门:直接访问HDL信号(立即生效,用于快速检查) 4. **错误检测机制** - `status`返回操作状态(UVM_IS_OK/UVM_NOT_OK) - 显式比较确保数据一致性 --- ### **4. 运行结果示例** ```plaintext [REG_TEST] 前门写入CTRL: 0x00000003 [REG_TEST] 前门读回: 0x00000003 (匹配) [REG_TEST] 后门修改en字段为0 [REG_TEST] 后门读回en: 0 (成功) ``` --- ### **5. 扩展应用** - **覆盖率收集**:添加覆盖组监控寄存器字段组合 - **随机测试**:使用`uvm_reg::randomize()`生成随机激励 - **影子寄存器**:对关键寄存器实现自动跟踪 如需更复杂的场景(如多级地址映射或错误注入),可进一步扩展适配器和测试序列!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小小verifier

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值