UVM寄存器模型的本质就是重新定义了验证平台与DUT的寄存器接口,使得验证人员更好地组织及配置寄存器,简化流程、减少工作量。
1.寄存器模型中的基本概念
类名 | 功能 |
---|---|
uvm_reg_field | 寄存器模型中的最小单位 |
uvm_reg | 比uvm_reg_field高一级,但仍是比较小的单位,一个寄存器模型中至少包含一个uvm_reg_field |
uvm_reg_block | 比较大的单位 ,可以添加多个uvm_reg或者uvm_reg_block。一个寄存器模型至少包含一个uvm_reg_block |
uvm_reg_map | 存储寄存器加入寄存器模型时的地址,转换为可以访问的物理地址,reg_block中至少有一个uvm_reg_map |
![]() |
2.配置寄存器模型
uvm_reg_field
是最小的单位,是具体存储寄存器数值的变量,可以直接用这个类。
uvm_reg
则是一个空壳子,是一个纯虚类,不能直接使用,必须由其派生一个新类,类中至少加入一个uvm_reg_field
,才可以使用。
uvm_reg是一个小瓶子, 其中必须装上药丸( uvm_reg_field) 才有意义, 这个装药丸的过程就是定义派生类的过程, 而uvm_reg_block则是一个大箱子, 它中可以放许多小瓶子( uvm_reg) , 也可以放其他稍微小一点的箱子( uvm_reg_block)
1.从uvm_reg派生出一个invert类
class reg_invert extends uvm_reg
rand uvm_reg_field reg_data;
virtual function void build();
reg_data = uvm_reg_field::type_id::create("reg_data");
//parameter: parent, size, lsb_pos, access, volatile, reset value, has_reset, is_rand
reg_data.configure(this, 1, 0, "RW", 1, 0, 1, 1, 0);
endfunction
`uvm_object_utils(reg_invert)
function new(input string name = "reg_invert");
//parameter: name, size, has_coverage
super.new(name, 16, UVM_NO_COVERAGE);
endfunction
endclass
- 每一个派生自
uvm_reg
的类都有一个build
,这个build与uvm_component的build_phase并不一样,它不会自动执行。uvm_reg_field在这里实例化,之后调用data.configure
函数来配置这个字段 - 在new函数中,要将invert寄存器的宽度作为参数传递给
super.new
函数,这个宽度是寄存器的总共位数与系统总线的宽度一致
。super.new中最后一个参数是是否要加入覆盖率的支持,这里选择UVM_NO_COVERAGE
,即不支持
2. 定义好此寄存器后,需要在一个由reg_block派生的类中将其实例化
class reg_model extends uvm_reg_block;
rand reg_invert invert;
virtual function void build();
default_map = create_map("default_map", 0, 2, UVM_BIG_ENDIAN, 0);
invert = reg_invert::type_id::create("invert", , get_full_name());
invert.configure(this, null, "");
invert.build();
default_map.add_reg(invert, 'h9, "RW");
endfunction
`uvm_object_utils(reg_model)
function new(input string name = "reg_model");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
- 在
build函数
中实现所有寄存器的实例化 一个uvm_reg_block中一定要对应一个uvm_reg_map,系统中已有声明好的default_map,只需要在build函数中实例化。通过调用uvm_reg_block的create_map
来实现- 实例化invert并调用
invert.configure
函数,这个函数主要功能是指定寄存器进行后门访问操作时的路径。 - 最后
将寄存器加入default_map中
,将实例化的寄存器加入default_map
中,否则无法进行前门访问操作。add_reg函数
第一个参数是要加入的寄存器,第二个参数是寄存器的地址,第三个是寄存器的读取方式
至此,一个简单的寄存器模型已完成
3.将寄存器模型集成到验证平台中
寄存器模型的前门访问方式:
无论读写,寄存器模型都会通过sequence产生一个uvm_reg_bus_op的变量,此变量的信息经过转换器(adapter)转换后交给bus_sequencer,随后交给bus_driver,由bus_driver实现最终的前门访问读写操作。
1.定义一个转换器
class my_adapter extends uvm_reg_adapter;
string tID = get_type_name();
`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);
...
endfunction : reg2bus
function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
...
endfunction : bus2reg
endclass
一个转换器要定义好两个函数:
reg2bus
,其作用为将寄存器模型通过sequence发出的uvm_reg_bus_op型的变量转换成bus_sequence能够接受的形式bus2reg
,其作用为当监测到总线上有操作时,将收集来的transaction转换成寄存器模型能够接受的形式
2.在bus_test中加入寄存器模型
将一个寄存器模型集成到base_test中,至少需要在base_test中定义两个成员变量,reg_model和reg_sqr_adapter
class base_test extends uvm_test;
my_env env;
my_vsqr v_sqr;
reg_model rm;
my_adapter reg_sqr_adapter;
endclass
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
env = my_env::type_id::create("env", this);
v_sqr = my_vsqr::type_id::create("v_sqr", this);
rm = reg_model::type_id::create("rm", this);
rm.configure(null, "");
rm.build();
rm.reset();
reg_sqr_adapter = new("reg_sqr_adapter");
env.p_rm = this.rm;
endfunction
function void base_test::connect_phase(uvm_phase phase);
super.connect_phase(phase);
v_sqr.p_my_sqr = env.i_agt.sqr;
v_sqr.p_bus_sqr = env.bus_agt.sqr;
v_sqr.p_rm = this.rm;
rm.default_map.set_sequencer(env.bus_agt.sqr, reg_sqr_adapter);
rm.default_map.set_auto_predict(1);
endfunction
- 所有用到的类在
build_phase
中实例化,在实例化reg_model还要做四件事,第一是调用configure函数
;第二调用build函数
,将所有寄存器实例化;第三调用lock_model函数
,调用后,reg_model中不能再加入新的寄存器了;第四调用reset函数
,将寄存器的值从0变为设置的复位值。 - 在
connect_phase
中,将转换器和bus_sequencer通过set_sequence
函数告知reg_model的default_map,并将default_map设置为自动预测状态