寄存器模型实例 | UVM笔记10

Posted by Kion on February 25, 2020

一个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。

8w7iy6.png

寄存器模型的创建,主要有以下步骤:

  • 编写寄存器类(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

参考