寄存器模型的应用场景

本文探讨了寄存器模型在硬件设计中的应用,包括寄存器检查、硬件驱动中的影子寄存器、功能覆盖率测量和自定义covergroup。介绍了如何利用寄存器模型进行数据比对和DUT功能验证,以及如何通过自动化方式收集覆盖率数据并配合scoreboard进行测试和验证。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、概述

  • 通过寄存器模型的常规方法,可以用来检查寄存器,以及协助检查硬件设计逻辑进和比对数据。
  • 在软件实现硬件驱动和固件层时,也会实现类似寄存器模型镜像值的方法,即在寄存器配置的底层函数中,同时也声明一些全局的影子寄存器。这些影子寄存器的功能就是暂存当时写入寄存器的值,而在后期使用时,如果这些寄存器是非易失的,那么就可以省略读取寄存器的步骤,转而使用影子寄存器的值。这么做的好处在于响应更迅速,而不再通过若干个时钟周期的总线发起请求和等待响应,但另外一方面这么做的前提同测试寄存器模型的目的是一样的,即寄存器的写入值可以准确地反映到硬件中的寄存器。
  • 利用寄存器模型的另一个场景是,在对硬件数据通路做数据比对时,需要及时地知道当时的硬件配置状况,而利用寄存器模型的镜像值可以实现实时读取,而不需要从前门访问。后门访问也可以在零时刻内完成,只是这么做会省略检查寄存器的步骤,即假设寄存器模型的镜像值同硬件中的寄存器真实值保持一致,而这一假设存在验证风险。所以只有这么做,才能为后期软件开发时使用影子寄存器扫清可能的硬件缺陷。
  • 寄存器模型不但可以用来检查硬件寄存器,也可以用来配合scoreboard实时检查DUT的功能。

二、寄存器检查

用来检查寄存器时,有以下几种可行的方式:

  • 从前门写,并且从前门读。这种方式最为常见,但是无法检查地址是否正确映射,而前门与后门混合操作的方式可以保证地址的映射检查。
  • 从前门写,再从后门读。
  • 从后门写,再从前门读。
  • 对于一些状态寄存器(硬件自身信号会驱动更新其实际值),先用peek()来获取(并且会调用predict()方法来更新镜像值),再调用mirror()方法来从前门访问并且与之前更新的镜像值比较。

上面的这些方法,在寄存器模型的内建序列中都已经实现。与内建序列相比,自建序列可以更灵活,更贴近需求,而内建序列使用简单,是全自动化的方式。

在配合scoreboard实施检查DUT的功能时,需要注意:

  • 无论是将寄存器模型通过config_db进行层次化配置,还是间接通过封装在配置对象(configuration object)中的寄存器模型句柄,都需要scoreboard可以索引到寄存器模型。
  • 在读取寄存器或者寄存器域的值时,需要加以区分。uvm_reg类中没有类似value的成语来表征其对应硬件寄存器的值。
  • uvm_reg并不是寄存器模型的最小切分单元,uvm_reg_field才是。uvm_reg可以理解为uvm_reg_field的容器,一个uvm_reg可以包含多个顺序排列的uvm_reg_field。在取值时,可以使用uvm_reg_field的成员value直接访问,最好使用uvm_reg类和uvm_reg_field类都具备的接口函数get_mirrored_value()

三、功能覆盖率概述

在测试寄存器以及设计的某些功能配置模式时,也需要统计测试过的配置情况。就MCDF寄存器模型来看,除了测试寄存器本身,还需要考虑在不同的配置下,设计的数据处理、仲裁等功能是否正确,所以需要放置功能覆盖率在寄存器模型中。由于寄存器描述文件的结构化,可以通过扩充寄存器模型生成器的功能,使得生成的寄存器模型也可以自动包含各个寄存器域的功能覆盖率。UVM的寄存器模型已经内置了一些方法用来使能对应的covergroup,同时在调用write()或者read()方法时,会自动调用covergroup::sample()来完成功能覆盖率收集。

四、覆盖率自动收集模式

class ctrl_reg extends uvm_reg;
	`uvm_object_utils(ctrl_reg)
	uvm_reg_field reserved;
	rand uvm_reg_field pkt_len;
	rand uvm_reg_field prio_level;
	rand uvm_reg_field chnl_en;
	covergroup value_cg;
		option.per_instance = 1;
		reserved: coverpoint reserved.value[25:0];
		pkt_len: coverpoint pkt_len.value[2:0];
		prio_level: coverpoint prio_level.value[1:0];
		chnl_en: coverpoint chnl_en.value[0:0];
	endgroup
	function new(string name="ctrl_reg");
		super.new(name, 32, UVM_CVR_ALL);
		set_coverage(UVM_CVR_FIELD_VALS);
		if(has_coverage(UVM_CVR_FIELD_VALS)) begin
			value_cg = new();
		end
	endfunction
	
	virtual function 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, 3, "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'h0, 1, 1, 0);
	endfunction
	
	function void sample(
		uvm_reg_data_t data,
		uvm_reg_data_t byte_en,
		bit is_read,
		uvm_reg_map map
	);
		super.sample(data, byte_en, is_read, map);
		sample_values();
	endfunction
	
	function void sample_values();
		super.sample_values();
		if(get_coverage(UVM_CVR_FIELD_VALS)) begin
			value_cg.sample();
		end
	endfunction
endclass

sample()可以理解为read()write()方法的回调函数,需要填充该方法,使得可以保证自动采样数据。

sample_values()是提供外部调用的方法,在一些特定事件触发时,例如中断、复位等场景,可以在外部通过监听具体事件来调用该方法。在sample_values()方法中,可以通过调用get_coverage()方法来判断是否允许进行覆盖率采样。

五、覆盖率外部事件触发收集

更贴合实际的、可作为覆盖率验收标准的covergroup定义还当采取自定义的形式,一方面来限定感兴趣的域和值,一方面来指定感兴趣的采样事件,即使用合适的事件来触发采样,通过这种方式,最后可以完成寄存器功能覆盖率的验证完备性标准。

class mcdf_coverage extends uvm_subscriber #(mcdf_bus_trans);
	mcdf_rgm rgm;
	`uvm_component_utils(mcdf_coverage)
	covergroup reg_value_cg;
		option.per_instance = 1;
		CH0LEN: coverpoint rgm.chnl0_ctrl_reg.pkt_len.value[2:0] {bins len[] = {0, 1, 2, 3, [4:7]};}
		CH0PRI: coverpoint rgm.chnl0_ctrl_reg.prio_level.value[1:0];
		CH0CEN: coverpoint rgm.chnl0_ctrl_reg.chnl_en.value[0:0];
		CH1LEN: coverpoint rgm.chnl1_ctrl_reg.pkt_len.value[2:0] {bins len[] = {0, 1, 2, 3, [4:7]};}
		CH1PRI: coverpoint rgm.chnl1_ctrl_reg.prio_level.value[1:0];
		CH1CEN: coverpoint rgm.chnl1_ctrl_reg.chnl_en.value[0:0];
		CH2LEN: coverpoint rgm.chnl2_ctrl_reg.pkt_len.value[2:0] {bins len[] = {0, 1, 2, 3, [4:7]};}
		CH2PRI: coverpoint rgm.chnl2_ctrl_reg.prio_level.value[1:0];
		CH2CEN: coverpoint rgm.chnl2_ctrl_reg.chnl_en.value[0:0];
		CH0AVL: coverpoint rgm.chnl0_stat_reg.fifo_avail.value[7:0] {bins avail[] = {0, 1, [2:7], [8:55], [56:61], 62, 63};}
		CH1AVL: coverpoint rgm.chnl0_stat_reg.fifo_avail.value[7:0] {bins avail[] = {0, 1, [2:7], [8:55], [56:61], 62, 63};}
		CH2AVL: coverpoint rgm.chnl0_stat_reg.fifo_avail.value[7:0] {bins avail[] = {0, 1, [2:7], [8:55], [56:61], 62, 63};}
		LEN_COMB: cross CH0LEN, CH1LEN, CH2LEN;
		PRI_COMB: cross CH0PRI, CH1PRI, CH2PRI;
		CEN_COMB: cross CH0CEN, CH1CEN, CH2CEN;
		AVL_COMB: cross CH0AVL, CH1AVL, CH2AVL;
	endgroup
	function new(string name, uvm_component parent);
		super.new(name, parent);
		reg_value_cg = new();
	endfunction
	function void build_phase(uvm_phase phase);
		if(!uvm_config_db#(mcdf_rgm)::get(this, "", "rgm", rgm)) begin
			`uvm_info("GETRGM", "no top-down RGM handle is assigned", UVM_LOW)
		end
	function
	function void write(T t);
		reg_value_cg.sample();
	endfunction
endclass
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值