寄存器模型概览

一、寄存器模型概览

  • 寄存器是模块之间互相交谈的窗口,一方面可以通过读出寄存器的状态,获取硬件当前的状况,另外一方面也可以通过配置寄存器,使得寄存器工作在一定的模式下。在验证的过程中,寄存器的验证也排在了验证清单的前列,只为只有首先保证寄存器的功能正确,才会使得硬件与硬件之间的交谈是“语义一致”的。如果寄存器配置结果与寄存器配置内容不同,那么硬件无法工作在想要的模式下,同时寄存器也可能无法正确反映硬件的状态。
  • 硬件中的各个功能模块可以由处理器来配置功能以及访问状态,而与处理器的对话即是通过寄存器的读写来实现的。
  • 寄存器的硬件实现是通过触发器,而每一个比特位的触发器都对应着寄存器的功能描述。一个寄存器一般由32个比特位构成,将单个寄存器拆分之后,又可以分为多个域(field),不同的域往往代表着某一项独立的功能。单个的域可能有多个比特位构成,也可能由单一比特位构成,这取决于该域的功能模式可配置的数量。而不同的域,对于外部的读写而言,又大致可以分为WO(只写),RO(只读)和RW(读写),除了这些常见的操作属性以外,还有一些特殊行为(quirky)的寄存器,例如读后擦除模式(clean-on-read,RC),只写一次模式(write-one-to-set,W1S)。

对于MCDF的寄存器模块描述,将0x00功能寄存器和0x10状态寄存器位用图来表示。
在这里插入图片描述
通常来讲,一个寄存器有32位宽,寄存器按照地址索引的关系是按字对齐的,上图中的寄存器有多个域,每个域的属性也可以不相同,reserved域表示的是该域所包含的比特位暂时保留以作日后功能的扩展使用,而对保留域的读写不起任何作用,即无法写入而且读出值也是它的复位值。上面的这些寄存器按照地址排列,即可构成寄存器列表,称之为寄存器块,实际上,寄存器块除了包含寄存器,也可以包含存储器,因为它们的属性都近乎读写功能,以及表示为同外界通信的接口。

如果将这些寄存器有机的组合在一起,MCDF的寄存器功能模块即可由这样一个register block来表示:
在这里插入图片描述
一个寄存器可以由多个域构成,而单个域可以包含多个比特位,一个功能模块中的多个寄存器可以组团构成一个寄存器模型。上面的图中除了包含了DUT的寄存器模块(由硬件实现),还有属于验证环境的寄存器模型。这两个模块包含的寄存器信息是高度一致的,属于验证环境的寄存器模型也可以抽象出一个层次化的寄存器列表,该列表所包含的地址、域、属性等信息都与硬件一侧的寄存器内容一致。对于功能验证而言,可以将总线访问寄存器的方式抽象为寄存器模型访问的方式,这种方式使得寄存器后期的地址修改(例如基地址更改)或者域的添加都不会对已有的激励构成影响,从而提高已有测试序列的复用性。

二、中心化管理方式

在这里插入图片描述

通过软件建立寄存器模型的方法要保证与硬件寄存器的内容属性保持一致,这离不开一份中心化管理的寄存器描述文件。寄存器描述文档使用了结构化的文档描述方式,这也是为什么可以通过XML或者Excel(CSV)等数据结构化的方式来实现寄存器的功能描述。

通过数据结构化的存储方式,可以在硬件和软件开发过程中以不同方式来使用寄存器描述文档:

  • 系统工程师会撰写并维护寄存器描述文档,而后归置到中心化存储路径供其他工程师开发使用。
  • 硬件工程师会利用寄存器描述文件生成寄存器硬件模块(包含各个寄存器的硬件实现和总线访问模块)。
  • 验证工程师会利用寄存器描述文件来生成UVM寄存器模型,以供验证过程中的激励使用、寄存器测试和功能覆盖率收集。
  • 软件工程师会利用该文件生成用于软件开发的寄存器配置的头文件,从而提高软件开发的可维护性。

三、uvm_reg相关概念

在构建UVM寄存器模型的过程中,需要用到如下与模型构建相关的类和它们的功能:

在这里插入图片描述

四、MCDF寄存器模型

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;
	function new(string name="ctrl_reg");
		super.new(name, 32, UVM_NO_COVERAGE);
	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
endclass

class stat_reg extends uvm_reg;
	`uvm_object_utils(stat_reg)
	uvm_reg_field reserved;
	rand uvm_reg_field fifo_avail;
	function new(string name="stat_reg");
		super.new(name, 32, UVM_NO_COVERAGE);
	endfunction
	virtual function build();
		reserved = uvm_reg_field::type_id::create("reserved");
		fifo_avail = uvm_reg_field::type_id::create("fifo_avail");
		reserved.configure(this, 24, 8, "RO", 0, 24'h0, 1, 0, 0);
		fifo_avail.configure(this, 8, 0, "RO", 0, 8'h0, 1, 1, 0);
	endfunction
endclass

class mcdf_rgm extends uvm_reg_block;
	`uvm_object_utils(mcdf_rgm)
	rand ctrl_reg chnl0_ctrl_reg;
	rand ctrl_reg chnl1_ctrl_reg;
	rand ctrl_reg chnl2_ctrl_reg;
	rand ctrl_reg chnl0_stat_reg;
	rand ctrl_reg chnl1_stat_reg;
	rand ctrl_reg chnl2_stat_reg;
	uvm_reg_map map;
	function new(string name="mcdf_rgm");
		super.new(name, UVM_NO_COVERAGE);
	endfunction
	virtual function 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();
		chnl0_stat_reg = ctrl_reg::type_id::create("chnl0_stat_reg");
		chnl0_stat_reg.configure(this);
		chnl0_stat_reg.build();
		chnl1_stat_reg = ctrl_reg::type_id::create("chnl1_stat_reg");
		chnl1_stat_reg.configure(this);
		chnl1_stat_reg.build();
		chnl2_stat_reg = ctrl_reg::type_id::create("chnl2_stat_reg");
		chnl2_stat_reg.configure(this);
		chnl2_stat_reg.build();
		//map name, offset, number of bytes, endianess
		map = create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);
		map.add_reg(chnl0_ctrl_reg, 32'h00000000, "RW");
		map.add_reg(chnl1_ctrl_reg, 32'h00000004, "RW");
		map.add_reg(chnl2_ctrl_reg, 32'h00000008, "RW");
		map.add_reg(chnl0_stat_reg, 32'h00000010, "RO");
		map.add_reg(chnl1_stat_reg, 32'h00000014, "RO");
		map.add_reg(chnl2_stat_reg, 32'h00000018, "RO");
		lock_model();
	endfunction	
endclass

五、寄存器建模

关于寄存器建模的基本要点和顺序:

  • 在定义单个寄存器时,需要将寄存器的各个域整理出来,在创建之后还应当通过uvm_reg_field::configure()函数来进一步配置各自属性。
  • 在定义uvm_reg_block时,需要注意reg_blockuvm_memuvm_reg以及uvm_reg_map的包含关系。首先uvm_reguvm_mem分别对应着硬件中独立的寄存器或者存储,而一个uvm_reg_block可以用来模拟一个功能模块的寄存器模型,其中可以容纳多个uvm_reguvm_mem实例,其次map的作用一方面用来表示寄存器和存储对应的偏移地址,同时由于一个reg_block可以包含多个map,各个map可以分别对应不同总线或者不同地址段。在reg_block中创建了各个uvm_reg之后,需要调用uvm_reg::configure()去配置各个uvm_reg实例的属性。
  • 因为uvm_reg_map也会在uvm_reg_block中例化,在例化之后需要通过uvm_reg_map::add_reg()函数来添加各个uvm_reg对应的偏移地址和访问属性等。只有规定了这些属性,才可以在稍后的前门访问(frontdoor)中给出正确的地址。
  • uvm_reg_block可以对更大的系统做寄存器建模,这意味着uvm_reg_block之间也可以存在层次关系,上层uvm_reg_blockuvm_reg_map可以添加子一级uvm_reg_blockuvm_reg_map,用来构建更全局的“版图”,继而通过uvm_reg_blockuvm_reg_map之间的层次关系来构建更系统的寄存器模型。

六、模型使用流程

在这里插入图片描述

  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值