1. 寄存器模型基本概念
UVM读取寄存器的方式包括不使用寄存器模型和使用寄存器模型两种方式。如下图所示:
(1)不使用寄存器模型
(2)使用寄存器模型
有了寄存器模型后,scoreboard只需与寄存器模型打交道,可以在任何耗费时间的phase中使用寄存器模型以前门访问或者后门访问的方式来读取寄存器的值,还可以在某些不耗费时间的phase中使用后门访问的方式来读取寄存器的值。
前门访问:通过模拟cpu在总线上发出读指令,进行读写操作。该过程是消耗仿真时间的。
后门访问:不通过总线进行读写操作,而是直接通过层次化的引用来读写寄存器的值。
uvm_reg_field:如下图所示:
reg_field | reg_field | |
---|---|---|
2 | 1 | 0 |
reserved | fifo_full | fifo_empty |
uvm_reg
fifo_full、fifo_empty两个域对应寄存器模型中的uvm_reg_field。“reserved”并不是一个域。
uvm_reg:一个寄存器中至少包含一个uvm_reg_field。
uvm_reg_block:在其中可以加入许多的uvm_reg,也可以加入其他的uvm_reg_block。一个寄存器模型中应至少包含一个uvm_reg_block.
uvm_reg_map:存储寄存器对应的地址,并将其转换为可访问的物理地址。在每个reg_block内部,至少包含一个uvm_reg_map。
2. 简单的寄存器模型
加入DUT有一个寄存器fifo,为其建立寄存器模型,首先需从uvm_reg派生一个fifo类:
class reg_fifo extends uvm_reg;
rand uvm_reg_field fifo_full;
rand uvm_reg_field fifo_empty;
virtual function void build();
fifo_full = uvm_reg_field::type_id::create("fifo_full");
fifo_empty = uvm_reg_field::type_id::create("fifo_empty");
fifo_full.configure(this,1,1,"RW",1,0,1,1,0);
fifo_empty.configure(this,1,0,"RW",1,0,1,1,0);
endfunction
`uvm_object_utils(reg_fifo);
function new(input string name = "reg_fifo");
super.new(name,8,UVM_NO_COVERAGE);
endfunction
endclass
每一个派生自uvm_reg的类都有一个build函数,但是它不会自动执行,需要手动调用。所有的uvm_reg_field都在此处实例化。fifo_full和fifo_empty实例化后,需要调用configure函数来配置该字段。
configure函数详解:
(1)此域的父辈
(2)此域的宽度
(3)此域的最低位在整个寄存器中的位置
(4)此字段的存取方式,UVM支持25种存取方式,如"RO"、"RW"等
(5)此域是否是易失的
(6)此域上电复位后的默认值
(7)此域是否有复位
(8)此域是否可以随机化
(9)此域是否可以单独存取
寄存器被定义好后,在由reg_block派生的类中将其实例化:
class reg_model extends uvm_reg_block;
rand reg_fifo fifo;
virtual function void build();
default_map = create_map("default_map",0,1,UVM_BIG_ENDIAN,0);//名字,基地址,系统总线宽度,大小端,是否按照byte寻址
fifo = reg_fifo::type_id::create("fifo",,get_full_name());
fifo.configure(this,null,"");//指定寄存器进行后门访问操作时的路径 寄存器所在uvm_reg_block的指针,reg_file的指针,后门访问路径
fifo.build();
default_map.add_reg(fifo,`h6,"RW");//将寄存器添加到default_map中 待加入的寄存器,寄存器的地址,寄存器的存取方式
endfunction
`uvm_object_utils(reg_model);
function new(input string name="reg_model");
super.new(name, UVM_NO_COVERAGE);
endfunction
endclass
3. 将寄存器模型集成到验证平台中
寄存器模型前门访问方式流程如下图所示,左图为读操作,右图为写操作。
在实现读写操作时,寄存器模型会通过sequence产生一个uvm_reg_bus_op的变量,变量中存储着操作类型(读/写)、操作的地址和写入的数据。此变量中的信息需要经过一个转换器(adapter)转换后交给bus_sequencer,然后再交给bus_driver,由bus_driver实现最终的前门访问的读写操作。转换器定义如下:
class adapter extends uvm_reg_adapter;
string tid = get_type_name();
`uvm_object_utils(adapter);
function new(string name="adapter");
super.new(name);
endfunction
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
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",);
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
endclass
reg2bus:将寄存器模型通过sequence发出的uvm_reg_bus_op型的变量转换成bus_sequencer可以接受的形式。
bus2reg:将transaction转换成寄存器模型可以接受的形式。
在base_test中加入寄存器模型代码如下所示:
class base_test extends uvm_test;
env m_env;
vsqr m_sqr;
reg_model m_reg_model;
adapter m_adapter;
endclass
function void base_test::build_phase(uvm_phase phase);
super.build_phase(phase);
m_env = env::type_id::create("m_env",this);
m_sqr = vsqr::type_id::create("m_sqr",this);
m_reg_model = reg_model::type_id::create("m_reg_model",this);
m_reg_model.configure(null,""); //父辈,后门访问路径
m_reg_model.build();
m_reg_model.lock_mode();//m_reg_model中不能再加入新的寄存器
m_reg_model.reset();
m_adapter = new("m_adapter");
m_env.p_reg_model = this.m_reg_model;
endfunction
function void base_test::connect_phase(uvm_phase phase);
super.connect_phase(phase);
m_sqr.p_sqr = m_env.i_agt.sqr;
m_sqr.p_bus_sqr = m_env.bus_agt.sqr;
m_sqr.p_reg_model = this.m_reg_model;
m_reg_model.default_map.set_sequencer(m_env.bus_agt.sqr,m_adapter);//将转换器和bus_sequencer通过set_sequencer函数告知reg_model的default_map
m_reg_model.default_map.set_auto_predict(1);//default_map设置为自动预测状态
endfunction