为什么要进行寄存器验证?
1 验证过程中不但要对DUT功能进行验证
2 还要对DUT中的所包含的存储单元(寄存器和存储器)进行验证
寄存器:存储配置信息、状态信息
存储器:芯片处理时需要的中间数据
最简单验证寄存器的方法?
1 要往寄存器中写入和读取数据,可以启动用于读写数据的sequence来完成
2 sequence产生的读写事务(sequence item)会通过sequencer传递到driver中
3 driver将对应的信号驱动给DUT
4 DUT根据驱动信号作出相应的动作(改变寄存器的值?返回寄存器的值?)
5 monitor从接口捕获相应信息(判断driver操作的是哪个寄存器以及其对应的值),然后送入scoreboard进行对比
寄存器模型(RAL)的必要性?
建立寄存器模型,实例化在env当中,作为平台组件的一部分,这样scoreboard可以通过寄存器模型提供的API间接访问DUT的寄存器和存储器模型,这样scoreboard完全与sequence分开,访问的结果也由寄存器模型返回,可以使用read、write读取或者向寄存器中写入值。
事务转换器:寄存器模型要想访问DUT中的寄存器还需要事务转换器Adapter,因为寄存器模型产生的类型与sequencer能接收的类型可能不同,这个转换器Adapter需要自己完成。将寄存器模型与sequencer连接起来,寄存器模型就可以与DUT进行通讯。
前门访问和后门访问?
frontdoor:寄存器模型通过产生事务,并借助agent来访问DUT中的存储器,这种模式称为frontdoor模式
backdoor模式:通过DUT的设计层次路径直接访问寄存器
寄存器模型的构成?
1 register model
UVM中用类为寄存器建立模型,这个模型在env中实例化,其他组件通过在内部建立句柄进行引用
内部构成:1个block块中有多个reg、多个block、几个memory以及map,1个reg中包含多个field
寄存器模型是寄存器的存储器和寄存器的镜像
2 adapter
从基类扩展,需要用到两个方法:
reg2bus:寄存器模型产生的uvm_reg_bus_op类型转换为自定义类,转换后的自动发往sequencer
bus2reg:自定义类转换为uvm_reg_bus_op类型的事务对象,转换后的事务对象被寄存器模型分析,更新寄存器模型中相关寄存器的值
3 寄存器模型、adapter与sequencer连接
借助寄存器模型中的map
- env中的adapter对象赋值给句柄
- agent中的sequencer对象赋值给句柄
- 其他任务和函数
寄存器模型嵌入测试平台?
假设测试平台的其他部分已经创建完成
1 为DUT创建寄存器模型
假设DUT包含3个寄存器:config_reg、mode_reg、data_mem
1.1 寄存器config_reg
class config_reg_c extends uvm_reg; //config_reg寄存器创建的类命名congif_reg_c ,从uvm_reg扩展
rand uvm_reg_field f1; //为寄存器添加第一个域f1 , 域的类型为uvm_reg_field (UVM内建类)
rand uvm_reg_field f2; //域2
rand uvm_reg_field f3; //域3
rand uvm_reg_field f4; //域4
virtual function void build(); //build函数中实例化4个域
f1 = uvm_reg_field::type_id::creat("f1");
f2 = uvm_reg_field::type_id::creat("f2");
f3 = uvm_reg_field::type_id::creat("f3");
f4 = uvm_reg_field::type_id::creat("f4");
f1.configure(this,1,0,"RW",0,h0,1,1,1); //调用域函数config,配置域在寄存器中的属性
f2.configure(this,1,1,"RO",0,h0,1,1,1); //位于哪个寄存器、位宽、初始位、属性
f3.configure(this,5,2,"RW",0,h0,1,1,1); //复位值
f4.configure(this,1,7,"RW",0,h0,1,1,1); //是否可以被复位、随机化、单独存取
endfunction
'uvm_object_untils(config_reg_c)
function new(string name = "config_reg_c");
super.new(new,8,UVM_NO_CONVERAGE); //super.new函数:对象名、寄存器总位宽、寄存器是否支持覆盖率统计
end
endclass
1.2 寄存器mode_reg
class mode_reg_c extends uvm_reg;
rand uvm_reg_field data;
virtual function void build();
data = uvm_reg_field::type_id::creat("data");
fi.configure(this,8,0,"RW",0,'h0,1,1,1);
endfunction
'uvm_obhect_utils(mode_reg_c)
function new(string name = "mode_reg_c");
super.new(name,8,UVM_NO_CONVERAGE);
end
endclass
1.3 存储器模型data_mem
仅需要在构造函数指定大小和位宽
class data_mem_c extends uvm_mem; //寄存器继承于uvm_mem类
'uvm_object_untils(data_mem_c)
function new(string name = "data_mem_c");
super.new(name,512,16);
end
endclass
1.4 封装起来整个寄存器模型
uvm_reg_block基类扩展:包含两个寄存器和一个存储器,并进行了映射,这些地址为以后的操作提供了信息
class reg_model_c extends uvm_reg_block;
rand config_reg_c config;
rand mode_reg_c mode_reg;
data_mem_c data_mem;
virtual function void build();
config_reg = config_reg_c::type_id::creat("config_reg", ,get_full_name());
1.5 为实现前门操作创建转换器Adapter
前门访问工作流程
文件: src/ch7/section7.2/my_adapter.sv
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 : new
function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
bus_transaction tr;
tr = new("tr");
tr.addr = rw.addr;
tr.bus_op = (rw.kind == UVM_READ) ? BUS_RD: BUS_WR;
if (tr.bus_op == BUS_WR)
tr.wr_data = rw.data;
return tr;
endfunction : reg2bus
function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
bus_transaction tr;
if(!$cast(tr, bus_item)) begin
`uvm_fatal(tID,
"Provided bus_item is not of the correct type. Expecting bus_transaction")
return;
end
rw.kind = (tr.bus_op == BUS_RD) ? UVM_READ : UVM_WRITE;
rw.addr = tr.addr;
rw.byte_en = 'h3;
rw.data = (tr.bus_op == BUS_RD) ? tr.rd_data : tr.wr_data;
rw.status = UVM_IS_OK;
endfunction : bus2reg
endclass : my_adapter
过程:
从基类uvm_reg_adapter类扩展
文件: src/ch7/section7.2/my_adapter.sv
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 : new
需要对此类中的两个函数进行重载:
一 reg2bus
将寄存器模型(通过sequence发送的)uvm_reg_bus_op转换为bus_sequencer能接受的事务对象bus transaction
function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw); //返回uvm_sequence_item类型句柄
bus_transaction tr; //创建bus_transaction对象
tr = new("tr");
tr.addr = rw.addr; //将rw地址赋值给tr的addr成员
tr.bus_op = (rw.kind == UVM_READ) ? BUS_RD: BUS_WR; //根据 rw 的操作类型设置 tr 的 bus_op 成员
if (tr.bus_op == BUS_WR) //如果 tr 是写操作(BUS_WR),则将 rw 的数据赋值给 tr 的 wr_data 成员
tr.wr_data = rw.data;
return tr;
endfunction : reg2bus
返回uvm_sequence_item类型的句柄,该句柄指向转换之后的事务对象
输入参数为需要进行转换的事务对象uvm_reg_bus_op:结构类型为uvm内建的结构体
二 bus2reg
当检测到总线有操作,将收集的transaction转换为寄存器模型能接受的形式,以便寄存器模型能更新寄存器的值
function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
bus_transaction tr;
//使用 $cast 函数尝试将 bus_item 转换为 bus_transaction 类型
if(!$cast(tr, bus_item)) begin
`uvm_fatal(tID,
"Provided bus_item is not of the correct type. Expecting bus_transaction")
return;
end
rw.kind = (tr.bus_op == BUS_RD) ? UVM_READ : UVM_WRITE; //如果转换成功,将 tr.bus_op 的值用于设置 rw.kind
rw.addr = tr.addr;
rw.byte_en = 'h3;
rw.data = (tr.bus_op == BUS_RD) ? tr.rd_data : tr.wr_data; //根据 tr.bus_op 的值,将 tr.rd_data 或 tr.wr_data 的值赋给 rw.data
rw.status = UVM_IS_OK;
endfunction : bus2reg
1.6 在测试平台中例化寄存器模型、转换器
文件: src/ch7/section7.2/base_test.sv
class base_test extends uvm_test;
my_env env;
my_vsqr v_sqr;
reg_model rm;
my_adapter reg_sqr_adapter;
function new(string name = "base_test", uvm_component parent = null);
super.new(name,parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);
extern virtual function void report_phase(uvm_phase phase);
`uvm_component_utils(base_test)
endclass
function void base_test::build_phase(uvm_phase phase);
//调用父类(通常是 uvm_test)的 build_phase 方法
super.build_phase(phase);
//实例化环境(env)、虚拟序列生成器(v_sqr)和寄存器模型(rm):
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);
//实例化reg_model后的几个函数:congigure函数、build函数、lock_model函数、reset函数
rm.configure(null, "");
rm.build();
rm.lock_model();
rm.reset();
//创建一个寄存器适配器(reg_sqr_adapter)的实例
reg_sqr_adapter = new("reg_sqr_adapter");
//将寄存器模型(rm)的实例赋值给环境(env)的 p_rm 成员。这允许环境访问寄存器模型。
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
function void base_test::report_phase(uvm_phase phase);
uvm_report_server server;
int err_num;
super.report_phase(phase);
server = get_report_server();
err_num = server.get_severity_count(UVM_ERROR);
if (err_num != 0) begin
$display("TEST CASE FAILED");
end
else begin
$display("TEST CASE PASSED");
end
endfunction
1.7 在验证平台中使用寄存器模型