目录
前文总体介绍的时候说明了为什么要用ral,今天简单说明ral的基本原理和使用。
RAL的基本原理
UVM关于RAL的定义:UVM中的RAL(Register Abstraction Layer)是一种用于抽象寄存器访问的机制。RAL通过将寄存器和寄存器字段抽象为对象,使得验证工程师可以在更高的层次上进行寄存器操作,而不需要直接与硬件信号交互。
在不使用ral的情况下,对寄存器进行读写等操作都是需要先找到寄存器的地址,加入ral之后,验证环境隐藏了这一底层的操作。带来的优点是,即使寄存器地址发生变化(这在项目中是常有的事情),也不用频繁修改环境。而且在验证环境中显示使用名字进行操作,具有更高的可读性。
RAL的核心思想是将寄存器的读写操作封装为方法,并通过这些方法进行寄存器的配置和状态检查。
RAL在代码中的集成
在UVM中,RAL的集成通常包括以下几个步骤:
- 定义寄存器模型:首先需要定义寄存器模型,包括寄存器、寄存器字段、寄存器块等。这些定义通常使用SystemVerilog的
uvm_reg
、uvm_reg_field
、uvm_reg_block
等类来实现。寄存器模型的来源是设计人员编写的寄存器说明文档,基本上每个公司都有脚本来根据寄存器说明文档来生成寄存器模型。如果寄存器发生变更,只需要用脚本重新生成寄存器模型即可。
class my_reg extends uvm_reg;
`uvm_object_utils(my_reg)
uvm_reg_field field1;
uvm_reg_field field2;
function new(string name = "my_reg");
super.new(name, 32, UVM_NO_COVERAGE);
endfunction
virtual function void build();
field1 = uvm_reg_field::type_id::create("field1");
field1.configure(this, 8, 0, "RW", 0, 8'h00, 1, 1, 1);
field2 = uvm_reg_field::type_id::create("field2");
field2.configure(this, 8, 8, "RW", 0, 8'h00, 1, 1, 1);
endfunction
endclass
- 创建寄存器块:寄存器块是寄存器的容器,通常包含多个寄存器。寄存器块可以通过继承
uvm_reg_block
类来创建。
class my_reg_block extends uvm_reg_block;
`uvm_object_utils(my_reg_block)
my_reg reg1;
my_reg reg2;
function new(string name = "my_reg_block");
super.new(name, UVM_NO_COVERAGE);
endfunction
virtual function void build();
reg1 = my_reg::type_id::create("reg1");
reg1.configure(this);
reg1.build();
reg2 = my_reg::type_id::create("reg2");
reg2.configure(this);
reg2.build();
default_map = create_map("default_map", 0, 4, UVM_LITTLE_ENDIAN);
default_map.add_reg(reg1, 'h0);
default_map.add_reg(reg2, 'h4);
endfunction
endclass
- 连接寄存器模型到硬件:寄存器模型需要与实际的硬件接口进行连接。这通常通过
uvm_reg_adapter
和uvm_reg_predictor
来实现。uvm_reg_adapter
负责将寄存器操作转换为总线事务,而uvm_reg_predictor
则用于预测寄存器的状态。
class my_adapter extends uvm_reg_adapter;
`uvm_object_utils(my_adapter)
function new(string name = "my_adapter");
super.new(name);
endfunction
virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
my_bus_transaction trans = my_bus_transaction::type_id::create("trans");
trans.addr = rw.addr;
trans.data = rw.data;
trans.kind = rw.kind;
return trans;
endfunction
virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
my_bus_transaction trans;
if (!$cast(trans, bus_item)) begin
`uvm_fatal("CAST", "Failed to cast bus_item to my_bus_transaction")
end
rw.addr = trans.addr;
rw.data = trans.data;
rw.kind = trans.kind;
endfunction
endclass
- 在测试环境中集成寄存器模型:最后,将寄存器模型集成到测试环境中,并通过
uvm_reg_sequence
进行寄存器操作。
class my_test extends uvm_test;
`uvm_component_utils(my_test)
my_reg_block reg_block;
my_adapter adapter;
uvm_reg_predictor predictor;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
reg_block = my_reg_block::type_id::create("reg_block");
reg_block.build();
reg_block.lock_model();
adapter = my_adapter::type_id::create("adapter");
predictor = uvm_reg_predictor::type_id::create("predictor");
predictor.adapter = adapter;
predictor.map = reg_block.default_map;
endfunction
virtual task run_phase(uvm_phase phase);
uvm_reg_sequence seq = uvm_reg_sequence::type_id::create("seq");
seq.model = reg_block;
seq.start(null);
endtask
endclass
RAL提供的寄存器操作方法
列举常用的方法
configure() 配置寄存器属性
add_field() 添加字段到寄存器
set() 设置寄存器值
get() 获取寄存器值
read() 发起读操作
write() 发起写操作
mirror() 镜像寄存器值
update() 更新硬件寄存器
predict() 预测字段值
操作举例
uvm_reg::read() / write() 读写单个寄存器
uvm_reg_block::read_reg() / write_reg() 通过层次路径访问寄存器
uvm_reg::peek() / poke() 直接访问寄存器(绕过总线)
批量操作
uvm_reg_block::mirror() 镜像多个寄存器
uvm_reg_block::update() 更新多个寄存器
uvm_reg_block::reset() 重置寄存器模型
预测与检查方法
uvm_reg::predict() 预测寄存器值
uvm_reg::get_mirrored_value() 获取镜像值
uvm_reg::needs_update() 检查是否需要更新
uvm_reg::is_known() 检查值是否已知
代码举例
这些方法共同构成了UVM RAL的强大功能,使得寄存器建模、访问和验证变得更加高效和可靠。