【IC验证】UVM实验lab05

1. 寄存器建模的要点和顺序

1.1 寄存器创建

定义单个uvm_reg,各个域的确定,并利用configure函数来配置属性;

class ctrl_reg extends uvm_reg;
	'uvm_object_utils(ctrl_reg)/*uvm_reg;uvm_mem;uvm_reg_map;uvm_reg_block都继承于uvm_object类*/
	uvm_reg_field reserved;
	rand uvm_reg_field pkt_len;
	rand uvm_reg_field prio_level;
	rand uvm_reg_field chnl_en;
	
	function new(string name = "ctrl_reg");
		super.name(name, 32, UVM_NO_COVERAGE);
	endfunction 
	
	virtual function void build();
		reserved   = uvm_reg_field::type_id::create("reserved"  );
		pkt_len    = uvm_reg_field::type_id::create("pkt_len   ");
		prio_level = uvm_reg_field::type_id::create("prio_level");
		chnl_en    = uvm_reg_field::type_id::create("chnl_en   ");
												
		reserved  .configure(this, 26, 6, "RO", 0, 26'h0, 1, 0, 0);
		pkt_len   .configure(this, 3 , 2, "RW", 0, 3'h0,  1, 1, 0);
		prio_level.configure(this, 2 , 1, "RW", 0, 2'h3,  1, 1, 0);
		chnl_en   .configure(this, 1 , 0, "RW", 0, 1'h1,  1, 1, 0);
		
	endfunction
endclass
  • 在new()函数中,第二个参数是寄存器的宽度,一般为32位,第三个参数用于指定是否需要进行覆盖率收集。不需要则写为UVM_NO_COVERAGE, 需要则写为UVM_CVR_ALL
  • uvm_reg;uvm_mem;uvm_reg_map;uvm_reg_block都继承于uvm_object类
  • uvm_reg类中的build()函数,不同于UVM_component的bulid_phase,不会自动执行,需要手动调用
  • configure配置所需要的参数如下

field_name.configure( .parent          ( this ),          // 参数一是此域的父辈,也就是此域位于哪个寄存器中,即是this;
                                   .size              ( 1    ),          // 参数二是此域的宽度;
                                   .lsb_position ( 0    ),          // 参数三是此域的最低位在整个寄存器的位置,从0开始计数;
                                   .access        ( "RW" ),       // 参数四表示此域段的存取方式(属性);
                                   .volatile        ( 0    ),          // 参数五表示是否是易失的(volatile),这个参数一般不会使用;
                                  .reset            ( 0    ),          // 参数六表示此域上电复位后的默认值;
                                 .has_reset     ( 1    ),          // 参数七表示此域可复位;
                                 .is_randomize   ( 1 ),          // 参数八表示这个域是否可以随机化;
                                 .individually_accessible( 0    )      // 参数九表示这个域是否可以单独存取。

);

1.2 寄存器映射

定义uvm_reg_block,在里面例化定义的uvm_reg,使用configure做配置;通过add_reg()函数添加各个uvm_reg对应的偏移地址和访问属性。

class mcdf_rgm extends uvm_reg_block;
	'uvm_object_utils(uvm_rgm)
	ctrl_reg chnl0_ctrl_reg;
	ctrl_reg chnl1_ctrl_reg;	
	ctrl_reg chnl2_ctrl_reg;	
	
	function new(string name = "mcdf_rgm");
		super.new(name, UVM_NO_COVERAGE);
	endfunction
	
	virtual function void build();
		chnl0_ctrl_reg = ctrl_reg::type_id::create("chnl0_ctrl_reg");
		chnl0_ctrl_reg.configure(this);
		chnl0_ctrl_reg.build();
		
		chnl1_ctrl_reg = ctrl_reg::type_id::create("chnl1_ctrl_reg");
		chnl1_ctrl_reg.configure(this);
		chnl1_ctrl_reg.build();		
		
		chnl2_ctrl_reg = ctrl_reg::type_id::create("chnl2_ctrl_reg");
		chnl2_ctrl_reg.configure(this);
		chnl2_ctrl_reg.build();		
		
		map = create_map("map", 'h0, 4, UVM_LITTLE_ENDING);//给基地址
		
		map.add_reg(chnl0_ctrl_reg, 32'h0000 0000, "RW");//给偏移地址
		map.add_reg(chnl1_ctrl_reg, 32'h0000 0000, "RW");		
		map.add_reg(chnl2_ctrl_reg, 32'h0000 0000, "RW");		
		
		chnl0_ctrl_reg.add_hdl_path_slice($sformatf("mem[%0d]", 'SLV0_RW_REG), 0, 32);
		chnl1_ctrl_reg.add_hdl_path_slice($sformatf("mem[%0d]", 'SLV0_RW_REG), 0, 32);		
		chnl2_ctrl_reg.add_hdl_path_slice($sformatf("mem[%0d]", 'SLV0_RW_REG), 0, 32);		
		
		add_hdl_path("tb.dut.ctrl_regs_inst");
		
		lock_model();
	
	endfunction
endclass

在build函数中需要做以下几步,

  • 创建各个寄存器,配置寄存器实例的属性
  • 并调用寄存器的build()函数,实现对各个域的配置
  • 创建map,create_map第一个参数是名字,第二个参数是该reg block的基地址,第三个参数是寄存器所映射到的总线宽度(单位是byte),第四个参数是大小端,用于指定字节排序,一般有大端序(UVM_BIG_ENDING,高位字节存储在低地址处,低位字节存储在高地址处)和小端序(UVM_LITTLE_ENDING,低位字节存储在低地址处,高位字节存储在高地址处 )第五个参数指定连续地址是否相隔1个字节 (TRUE; 默认值) 或n_bytes (FALSE)。,一般为默认值1。
  • 通过add_reg来添加各个reg的偏移地址和访问属性。第一个参数是要添加的寄存器名,第二个是地址,第三个是寄存器的读写属性。
  • 关联寄存器模型和HDL硬件,用于将寄存器字段的一部分(即切片)与HDL路径绑定。从而使得UVM寄存器模型能够访问和操纵实际的硬件寄存器。第一个参数是hdl的地址,第二个参数是寄存器字段中切片的起始位,第三个参数是寄存器字段切片的大小。
  • lock_model()锁定该寄存器模型,不能进行诸如添加寄存器或存储器之类的结构改变。因此必须确保在调用lock_model之前已经创建了所有子块、映射和寄存器。

1.3 Adapter实现

adapter具有桥接功能,实现uvm_reg_bus_op和transaction之间的转换,实现前门访问和DUT返回值对寄存器模型的修改.

class reg2mcdf_adapter extends uvm_reg_adapter;
	'uvm_object_utils(reg2mcdf_adapter)
	
	function new(string name = "reg2mcdf_adapter");
		super.new(name);
		provides_response = 1;
	endfunction
	
	function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
		reg_trans t = reg_trans::type_id::create("t");
		t.cmd  = (rw.kind == UVM_WRITE)? UVM_WRITE: UVM_READ;
		t.addr = rw.addr;
		t.data = rw.data;
		return t;
	endfunction
	
	function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
		reg_trans t;
		if(!$cast(t,bus_item))begin
			'uvm_fatal("CASTFALL","provide bus_item is not of the correct type")
			return;
		end
		rw.kind = (t.cmd == 'WRITE)? UVM_WRITE: UVM_READ;
		rw.addr= t.addr;
		rw.data = t.data;
		rw.status = UVM_IS_OK;
	endfunction
endclass

通过reg2bus和bus2reg函数来实现数据类型的转换。

bus2reg()函数将收集到的transaction转换成寄存器模型使用的uvm_reg_bus_op类型变量,用以更新寄存器模型中相应寄存器的值;

virtual function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
virtual function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);

1.4 顶层集成block, adapter和predictor

1.4.1 例化并创建

class mcdf_env extends uvm_env;
	chnl_agent chnl_agts[3];
	reg_agent reg_agt;
	fmt_agent fmt_agt;
	mcdf_checker chker;
	mcdf_coverage cvrg;
	mcdf_virtual_sequencer virt_sqr;
	
	//例化adapter,predictor和register model句柄
	mcdf_rgm rgm;
	reg2mcdf_adapter adapter;
	uvm_reg_predictor #(reg_trans) predictor;
	
	'uvm_component_ultis(mcdf_env)
	function new(string name = "mcdf_env", uvm_component parent);
		super.new(name);
	endfunction	
	
	function void build_phase(uvm_phase phase)
		...
		//创建adapter,predictor和register model句柄
		rgm = mcdf_rgm::type_id::create("rgm", this);
		rgm.build();
		adapter = reg2mcdf_adapter::type_id::create("adapter", this);
		predictor = uvm_reg_predictor#(reg_trans)::type_id::create("predictor", this);
	endfunction
	
	...
	
endclass

1.4.2 句柄连接

	/*将 reg_agt.sequencer 关联到 rgm.map,并使用 adapter 进行适配*/
	rgm.map.set_sequencer(reg_agt.sequencer, adapter);
	/*将monitor的analysis_port连接到predictor的输入端口,使predictor能够接收monitor捕获的总线事务。*/
	reg_agt.monitor.mon_ana_port.connect(predictor.bus_in);
	predictor.map = rgm.map;
	predictor.adapter = adapter;
	virt_sqr.rgm = rgm;

1.4.2 在test层运行

top_seq.start(env.virt_sqr);

2. 对寄存器的某些域进行修改

对寄存器进行操作会对整个寄存器进行修改

rgm.chnl0_ctrl_reg.write(status, wr_val);

但对整个寄存器进行读写的机会不多,更多的是修改或者读取某个特定的域,因此需要以下的方式。这种方式不会对已有的其他寄存器激励产生影响

rgm.chnl0_ctrl_reg.filed.set(data);

rgm.chnl0_ctrl_reg.update(status);//注意修改的是desired value

或许寄存器某些域的值

rgm.chnl0_ctrl_reg.mirror(status);//进行前门的读,更新mirror与硬件DUT保持一致

rgm.chnl0_ctrl_reg.filed.get(data);

  • 8
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值