寄存器模型概览

一、寄存器模型概览

  • 寄存器是模块之间互相交谈的窗口,一方面可以通过读出寄存器的状态,另外一方面也可以通过配置寄存器,使得寄存器工作在一定的模式下。在验证的过程中,寄存器的验证也排在了验证清单的前列,只为只有首先保证寄存器的功能正确。
  • 硬件中的各个功能模块可以由处理器来配置功能以及访问状态,而与处理器的对话即是通过寄存器的读写来实现的。
  • 寄存器的硬件实现是通过触发器,而每一个比特位的触发器都对应着寄存器的功能描述。一个寄存器一般由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等数据结构化的方式来实现寄存器的功能描述。

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

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

三、uvm_reg相关概念(重要)

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

四、MCDF寄存器模型

:寄存器模型可以通过脚本自动生成,第三部分会讲解

class ctrl_reg extends uvm_reg;    //寄存器1
	`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();      //build函数:对filed进行创建;对filed进行配置
		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);  //利用configure函数域内bit定义
		pkt_len.configure(this, 3, 3, "RW", 0, 3'h0, 1, 1, 0);     //域内bit定义
		prio_level.configure(this, 2, 1, "RW", 0, 2'h3, 1, 1, 0);  //域内bit定义
		chnl_en.configure(this, 1, 0, "RW", 0, 1'h0, 1, 1, 0);     //域内bit定义
                       //(this,位数,起点位,属性,0,reset值
	endfunction
endclass

class stat_reg extends uvm_reg;   //寄存器2
	`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");//例化一个寄存器10
		chnl0_ctrl_reg.configure(this);  //将reg和reg_block关联起来
		chnl0_ctrl_reg.build();          //定义reg中的各个filed。

		chnl1_ctrl_reg = ctrl_reg::type_id::create("chnl1_ctrl_reg");//例化一个寄存器11
		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", 基地址, 总线访问位宽, 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之间的层次关系来构建更系统的寄存器模型。

对于验证过程中的不同角色,他们会参与上述的部分流程:

  • 系统工程师需要提供寄存器描述文件。
  • 模块验证人员需要生成寄存器模型。
  • VIP开发人员需要提供总线适配器。
  • TB构建人员(与模块验证人员有时候不是同一个人)需要集成寄存器模型。
  • 模块验证人员还需艳完成后续的寄存器模型检查和功能覆盖率收集。

猜你喜欢

转载自blog.csdn.net/Arvin_ing/article/details/128237760
今日推荐