一个DMA DUT中有如下的寄存器:
register | address | field |
---|---|---|
INTR | h400 | MASK(31:16), SATUS(15:0) |
CTRL | h404 | RESVD(31:10),IO__MEM(9),W_COUNT(8:1),START_DMA(0) |
IO_ADDR | h408 | ADDRESS(31:0) |
MEM_ADDR | h40C | ADDRESS(31:0) |
下面是一个简化的验证平台架构,主要考虑寄存器模型的使用,忽略了scoreboard。
寄存器模型的创建,主要有以下步骤:
- 编写寄存器类(reg,block)
- 编写adapter
- 实例化寄存器模型,并配置
- 对寄存器模型进行读写操作
寄存器类
首先编写寄存器类,按照已知的寄存器结构,依次定义field,reg,block。
/*---INTR---*/
class intr extends uvm_reg;
`uvm_object_utils(intr)
/*定义field*/
rand uvm_reg_field status;
rand uvm_reg_field mask;
function new(string name="intr");
/*寄存器宽度32bit*/
super.new(name,32,UVM_NO_COVERAGE);
endfunction
function void build();
status = uvm_reg_field::type_id::create("status");
mask = uvm_reg_field::type_id::create("mask");
/*对filed进行配置*/
status.configure(.parent(this),
.size(16),
.lsb_pos(0),
.access("RW"),
.volatile(0),
.reset(0),
.has_reset(1),
.is_rand(1),
.individually_accessible(0));
mask.configure(.parent(this),
.size(16),
.lsb_pos(16),
.access("RW"),
.volatile(0),
.reset(0),
.has_reset(1),
.is_rand(1),
.individually_accessible(0));
endfunction
endclass
/*---CTRL---*/
class ctrl extends uvm_reg;
`uvm_object_utils(ctrl)
rand uvm_reg_field start_dma;
rand uvm_reg_field w_count;
rand uvm_reg_field io_mem;
rand uvm_reg_field resvd;
function new(string name="ctrl");
super.new(name,32,UVM_NO_COVERAGE);
endfunction
function void build();
start_dma = uvm_reg_field::type_id::create("start_dma");
w_count = uvm_reg_field::type_id::create("w_count");
io_mem = uvm_reg_field::type_id::create("io_mem");
resvd = uvm_reg_field::type_id::create("resvd");
start_dma.configure(.parent(this),
.size(1),
.lsb_pos(0),
.access("RW"),
.volatile(0),
.reset(0),
.has_reset(1),
.is_rand(1),
.individually_accessible(0));
w_count.configure(.parent(this),
.size(8),
.lsb_pos(1),
.access("RW"),
.volatile(0),
.reset(0),
.has_reset(1),
.is_rand(1),
.individually_accessible(0));
io_mem.configure(.parent(this),
.size(1),
.lsb_pos(9),
.access("RW"),
.volatile(0),
.reset(0),
.has_reset(1),
.is_rand(1),
.individually_accessible(0));
resvd.configure(.parent(this),
.size(22),
.lsb_pos(10),
.access("RW"),
.volatile(0),
.reset(0),
.has_reset(1),
.is_rand(1),
.individually_accessible(0));
endfunction
endclass
/*---IO_ADDR---*/
class io_addr extends uvm_reg;
`uvm_object_utils(io_addr)
rand uvm_reg_field address;
function new(string name="io_addr");
super.new(name,32,UVM_NO_COVERAGE);
endfunction
function void build();
address = uvm_reg_field::type_id::create("address");
address.configure(.parent(this),
.size(32),
.lsb_pos(0),
.access("RW"),
.volatile(0),
.reset(0),
.has_reset(1),
.is_rand(1),
.individually_accessible(0));
endfunction
endclass
/*---MEM_ADDR---*/
class mem_addr extends uvm_reg;
`uvm_object_utils(mem_addr)
rand uvm_reg_field address;
function new(string name="mem_addr");
super.new(name,32,UVM_NO_COVERAGE);
endfunction
function void build();
address = uvm_reg_field::type_id::create("address");
address.configure(.parent(this),
.size(32),
.lsb_pos(0),
.access("RW"),
.volatile(0),
.reset(0),
.has_reset(1),
.is_rand(1),
.individually_accessible(0));
endfunction
endclass
/*---regmodel---*/
class dma_reg_model extends uvm_reg_block;
`uvm_object_utils(dma_reg_model)
/*在block中创建定义好的reg*/
rand intr reg_intr;
rand ctrl reg_ctrl;
rand io_addr reg_io_addr;
rand mem_addr reg_mem_addr;
function new(string name="dma_reg_model");
super.new(name,UVM_NO_COVERAGE);
endfunction
function void build();
/*依次实例化reg,并配置*/
reg_intr = intr::type_id::create("reg_intr");
reg_intr.build();
reg_intr.configure(this);
reg_ctrl = ctrl::type_id::create("reg_ctrl");
reg_ctrl.build();
reg_ctrl.configure(this);
reg_io_addr = io_addr::type_id::create("reg_io_addr");
reg_io_addr.build();
reg_io_addr.configure(this);
reg_mem_addr = mem_addr::type_id::create("reg_mem_addr");
reg_mem_addr.build();
reg_mem_addr.configure(this);
/*设置map,在map中添加reg,并设置相应的地址*/
default_map = create_map("my_map",0,4,UVM_LITTLE_ENDIAN);
default_map.add_reg(reg_intr,'h0,"RW");
default_map.add_reg(reg_ctrl,'h4,"RW");
default_map.add_reg(reg_io_addr,'h8,"RW");
default_map.add_reg(reg_mem_addr,'hC,"RW");
lock_model();
endfunction
endclass
adapter类
编写adapter类,主要是编写reg2bus以及bus2reg,旨在将寄存器模型自动生成的uvm_reg_bus_op与自定义的dma_seq_item进行转化。
class dma_adapter extends uvm_reg_adapter;
`uvm_object_utils(dma_adapter)
function new(string name="dma_adapter");
super.new(name);
endfunction
function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
dma_seq_item tx;
tx = dma_seq_item::type_id::create("tx");
tx.wr_en = (rw.kind==UVM_WRITE);
tx.addr = rw.addr;
if (tx.wr_en) tx.wdata = rw.data;
return tx;
endfunction
function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
dma_seq_item tx;
assert($cast(tx,bus_item))
else `uvm_fatal("", "A bad thing has just happened in my_adapter")
rw.kind = tx.wr_en?UVM_WRITE:UVM_READ;
rw.addr = tx.addr;
rw.data = tx.rdata;
rw.status = UVM_IS_OK;
endfunction
endclass
实例化并配置寄存器模型
在env中实例化寄存器模型以及adapter,给寄存器模型配置sequencer以及adapter,
class dma_model_env extends uvm_env;
`uvm_component_utils(dma_model_env)
dma_agent dma_agnt;
dma_reg_model regmodel;
dma_adapter m_adapter;
function new(string name,uvm_component parent);
super.new(name,parent);
endfunction
function void build_phase(uvm_phase phase);
super.build_phase(phase);
regmodel = dma_reg_model::type_id::create("regmodel",this);
m_adapter = dma_adapter::type_id::create("m_adapter",this);
dma_agnt = dma_agent::type_id::create("dma_agnt",this);
regmodel.build();
endfunction
function void connect_phase(uvm_phase phase);
regmodel.default_map.set_sequencer(dma_agnt.sequencer, m_adapter);
regmodel.default_map.set_base_addr('h400);
endfunction
endclass
使用寄存器模型
在sequence类中使用寄存器的读写操作,在test类中将env的寄存器模型赋予sequence。
/*sequence*/
class dma_reg_seq extends uvm_sequence;
`uvm_object_utils(dma_reg_seq)
dma_reg_model regmodel;
function new(string name="");
super.new(name);
endfunction
task body();
uvm_status_e status;
uvm_reg_data_t incoming;
bit[31:0] rdata;
if (starting_phase!=null) starting_phase.raise_objection(this);
regmodel.reg_intr.write(status,32'h1234_1234);
regmodel.reg_ctrl.write(status,32'h1234_5678);
regmodel.reg_io_addr.write(status,32'h1234_9ABC);
regmodel.reg_mem_addr.write(status,32'h1234_DEF0);
regmodel.reg_intr.read(status,rdata);
regmodel.reg_ctrl.read(status,rdata);
regmodel.reg_io_addr.read(status,rdata);
regmodel.reg_mem_addr.read(status,rdata);
if (starting_phase!=null) starting_phase.drop_objection(this)
endtask
endclass
/*test类*/
class dma_reg_test extends dma_model_base_test;
`uvm_component_utils(dma_reg_test)
dma_reg_seq reg_seq;
function new(string name="dma_model_test",uvm_component parent);
super.new(name,parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
reg_seq = dma_reg_seq::type_id::create("reg_seq");
endfunction
task run_phase(uvm_phase phase);
phase.raise_objection(this);
if(!reg_seq.randomize())
`uvm_error("","Randomize Failed!");
reg_seq.regmodel = env.regmodel;
reg_seq.starting_phase=phase;
reg_seq.start(env.dma_agnt.sequencer);
phase.drop_objection(this);
phase.phase_done.set_drain_time(this,50);
endtask
endclass