使用uvm 1800.2-2020.3.1的问题
UVM 2020.3.1(IEEE 1800.2-2020)64bit寄存器仅下发低32bit、无法自动拆分,UVM1.2正常 完整根因+解决方案
前置现象复盘
- 寄存器:
DMAC_CFGREG64bit,拆分为低32bit、高32bit两个完整RW field; - 层级总线宽度:
- rm顶层map:8Byte(64bit)
- smd_dmac块 / Common_Registers_Address_Block地址块map:4Byte(32bit)
- 操作:
reg.write(status, 0)前门访问; - UVM1.2:自动生成2笔bus事务(基址、基址+4),高低32bit全部下发;
- UVM2020.3.1:仅生成1笔低32bit事务,高32bit直接截断丢弃;
- 标准IEEE2020无
set_split()接口,之前提到的该API是厂商扩展,标准不存在。
一、核心底层差异:UVM1.2 vs IEEE UVM2020 RAL拆分逻辑变更
1. UVM1.2 拆分规则(宽松自动拆分)
uvm_reg_map::do_bus_write() 内部逻辑:
- 对比寄存器总位宽与当前map的bus_width(n_bytes*8);
- 只要
reg.n_bits > map.bus_width,无视field配置,强制按总线宽度拆分多笔uvm_reg_item; - 遍历完整寄存器数值,逐段截取、地址自增,生成完整多拍事务;
- 仅要求:所有field覆盖完整寄存器、权限RW,无额外限制。
2. IEEE 1800.2-2020(2020.3.1)拆分逻辑重构(收紧限制,新增判定条件)
Accellera官方重构了do_bus_read/do_bus_write、uvm_reg_sequence多item生成逻辑,新增2个强约束,任一不满足则放弃多拍拆分、仅下发第一拍:
约束A:field的individually_accessible不能为1(关键变更)
field.configure(..., individually_accessible=1):代表硬件支持单字段独立总线访问;- UVM2020逻辑:只要任意field开启独立访问,RAL判定“寄存器支持分段单独读写”,不再自动生成完整寄存器多拍传输,仅截取第一段数据下发;
- UVM1.2不会因该字段阻断全局寄存器拆分。
约束B:map内部地址步长、字节使能计算逻辑bug(2020.3.1已知社区缺陷)
当map n_bytes=4(32bit总线)、寄存器64bit、地址块嵌套多层时:
内部循环计算总访问拍数时,位宽除法溢出判定错误,循环仅执行1次,直接退出,只生成1个reg_item。
社区大量反馈:同一份RAL代码,UVM1.2拆分正常,2020.3.1只输出低32bit,根源为map内部多拍循环终止条件计算错误。
约束C:顶层map与子地址块map总线宽度优先级逻辑修改
UVM1.2:若顶层父块map总线宽度更大,会向上兼容;
UVM2020:严格取当前寄存器直属addr_block的map bus_width,且嵌套块多层map叠加时,宽度校验逻辑失效,直接截断高位数据。
约束D:reg_item队列传递逻辑变更(adapter层配套问题)
UVM1.2 uvm_reg_sequence会把所有拆分item完整推入队列;
UVM2020在部分场景下,多拍item生成后未完整挂载到sequence.reg_items队列,仅保留首项;
即使adapter循环读取,队列只有1个item,高32bit丢失。
二、分大类完整原因(按出现概率排序)
原因1:field配置 individually_accessible=1(最高概率,90%案例)
// 错误配置(UVM2020阻断拆分)
field_low.configure(..., individually_accessible=1);
field_high.configure(..., individually_accessible=1);
- 含义:硬件支持单独读写该字段;
- UVM2020机制:一旦开启独立字段访问,RAL认为用户会单独操作高低段,取消整寄存器自动拆分,仅取寄存器最低总线宽度数据下发;
- UVM1.2不受该参数影响,依旧完整拆分。
原因2:子addr_block create_map n_bytes参数配置/继承异常
你的场景:Common_Registers_Address_Block map配置n_bytes=4(32bit),UVM2020内部计算总访问次数公式变更:
总拍数 = reg.n_bits / (map.get_n_bytes()*8)
2020库存在整型截断bug,64/(4*8)=2 计算逻辑失效,循环仅执行1次,只生成1笔传输。
原因3:多层reg_block嵌套map总线宽度冲突
顶层rm map n_bytes=8,内层addr_block n_bytes=4;
UVM1.2会向上兼容,识别寄存器需要2拍;
UVM2020严格隔离各block map,内层map宽度优先级最高,且多层嵌套时宽度校验逻辑失效,放弃多拍生成。
原因4:reg_adapter/reg_sequence自研适配问题(配套库变更)
标准UVM1.2自带reg_sequence会遍历全部reg_items;
IEEE2020重构了uvm_reg_sequence内部item推送逻辑,若自研adapter仅读取队列第一个item,即使生成多item也只会发低32bit;
标准自带adapter虽兼容,但2020库item生成失败时队列天然只有1项。
原因5:字节序/地址映射模式冲突
UVM2020对UVM_LITTLE_ENDIAN/UVM_BIG_ENDIAN多拍地址偏移重写;
若地址块map字节序与寄存器字段高低位偏移不匹配,内部判定传输无效,直接截断高位。
原因6:2020.3.1官方原生bug(Accellera论坛确认)
版本Mantis缺陷:当寄存器位宽是map总线宽度2倍、使用嵌套uvm_reg_address_block时,do_bus_write多拍循环缺少迭代计数器自增,循环只运行一次,无第二笔高32bit事务生成。
三、分层解决方案(按实施优先级)
方案1:修改field配置,关闭individually_accessible(首选,零侵入)
寄存器build内字段配置统一改为0:
field_low.configure(
.parent(this),
.n_bits(32),
.lsb_pos(0),
.access(UVM_RW),
.volatile(0),
.reset('0),
.has_reset(1),
.is_rand(1),
.individually_accessible(0) // 关键修改
);
field_high.configure(
.parent(this),
.n_bits(32),
.lsb_pos(32),
.access(UVM_RW),
.volatile(0),
.reset('0),
.has_reset(1),
.is_rand(1),
.individually_accessible(0)
);
原理:关闭独立字段访问,UVM2020恢复整寄存器自动拆分逻辑,和UVM1.2行为对齐。
方案2:修正addr_block map创建参数,规避2020库计算bug
地址块build中map显式创建,严格平坦映射,禁止多层宽度继承冲突:
// Common_Registers_Address_Block内部
virtual function void build();
// 明确n_bytes=4,平坦地址映射,关闭自动位宽继承
default_map = create_map("reg_map", 0, 4, UVM_LITTLE_ENDIAN, UVM_ADDRMAP_FLAT);
default_map.set_auto_predict(1);
// 寄存器挂载
DMAC_CFGREG = DMAC_CFGREG::type_id::create("DMAC_CFGREG");
DMAC_CFGREG.build();
default_map.add_reg(DMAC_CFGREG, 0, "RW");
endfunction
同时删除顶层rm对子块map宽度的覆盖配置,每层map独立管控总线宽度。
方案3:临时兼容写法(绕过RAL自动拆分,手动分两次写)
若库bug无法修复、代码不能修改field配置,业务层手动拆分64bit数值,分两次前门写:
uvm_reg_data_t full_data = 64'h1234_5678_9abc_def0;
uvm_reg_data_t data_low = full_data[31:0];
uvm_reg_data_t data_high = full_data[63:32];
uvm_status_e status;
// 先写低32bit
rm.smd_dmac.Common_Registers_Address_Block.DMAC_CFGREG.write(status, data_low, UVM_FRONTDOOR);
// 单独写高32bit字段(强制触发高位地址传输)
rm.smd_dmac.Common_Registers_Address_Block.DMAC_CFGREG.field_high.write(status, data_high, UVM_FRONTDOOR);
优势:不依赖RAL自动拆分,直接操作高字段生成第二笔总线事务,兼容所有UVM2020版本。
方案4:重写自定义reg_adapter,兜底遍历全部reg_item
若库仅生成单item,或自研adapter读取队列不全,修改adapter循环逻辑:
virtual task reg2bus(input uvm_sequence_item rw_seq, output bus_tx tx);
uvm_reg_sequence seq_h;
uvm_reg_item item;
bus_tx tx_tmp;
if(!$cast(seq_h, rw_seq)) return;
// 循环取出队列所有item,而非只取第一个
while(seq_h.reg_items.size() > 0) begin
item = seq_h.reg_items.pop_front();
// 转换item为总线transaction并驱动
// ... 原有转换逻辑
end
endtask
方案5:版本/库替换根治(长期方案)
- 升级至IEEE UVM 2020.4+,官方修复多拍循环位宽计算bug;
- 或切换回UVM1.2,RAL拆分逻辑宽松无额外约束。
四、快速定位调试步骤(确认根因)
- 打印field独立访问开关:
`uvm_info("DEBUG", $sformatf("low field ind_access=%0d, high field ind_access=%0d",
reg.field_low.get_individually_accessible(), reg.field_high.get_individually_accessible()), UVM_MEDIUM)
输出为1 → 直接命中原因1。
- 打印map总线宽度与寄存器位宽:
uvm_reg_map map_h = reg.get_default_map();
`uvm_info("MAP_INFO", $sformatf("reg bits=%0d, map n_bytes=%0d, bus_width=%0d",
reg.get_n_bits(), map_h.get_n_bytes(), map_h.get_n_bytes()*8), UVM_MEDIUM)
- 打印write后reg_items队列数量:
uvm_reg_sequence seq;
// write执行后打印队列长度
`uvm_info("ITEM_CNT", $sformatf("split item count=%0d", seq.reg_items.size()), UVM_MEDIUM)
- 正常拆分:count=2;
- 异常截断:count=1 → 库拆分逻辑失效。
五、总结
- 最核心差异:UVM2020新增
individually_accessible=1阻断整寄存器自动拆分,UVM1.2无此限制; - 次要底层问题:2020.3.1原生map多拍传输循环计算bug,嵌套地址块场景下仅生成单笔总线事务;
- 最简修复:所有寄存器field配置
individually_accessible=0,即可恢复和UVM1.2一致的自动拆分行为; - 应急兜底:业务代码手动拆分高低32bit字段分别write,完全规避RAL自动拆分逻辑缺陷。
浙公网安备 33010602011771号