UVM实战 卷I学习笔记10——UVM中的寄存器模型(5)


寄存器模型对DUT的模拟

期望值与镜像值

由于DUT中寄存器的值可能是实时变更的,寄存器模型不能实时知道变更,因此寄存器模型中寄存器的值有时与DUT中相关寄存器的值并不一致。对于任意一个寄存器,寄存器模型中都会有一个专门的变量用于最大可能地与DUT保持同步,这个变量在寄存器模型中称为DUT的镜像值(mirrored value)。

寄存器模型中还有期望值(desired value)。如目前DUT中invert的值为’h0,寄存器模型中的镜像值也为’h0,但希望向此寄存器中写入一个’h1,一种方法是直接调用write任务将’h1写入,期望值与镜像值都更新为’h1;另外一种方法是通过set函数将期望值设置为’h1(此时镜像值依然为0),之后调用update任务,update任务会检查期望值和镜像值是否一致,如果不一致,那么将会把期望值写入DUT中并更新镜像值

class case0_cfg_vseq extends uvm_sequence;
	…
	virtual task body();
		…
		p_sequencer.p_rm.invert.set(16'h1);
		value = p_sequencer.p_rm.invert.get();
		`uvm_info("case0_cfg_vseq", $sformatf("invert's desired value is %0h ", 
												value), UVM_LOW)
		value = p_sequencer.p_rm.invert.get_mirrored_value();
		`uvm_info("case0_cfg_vseq", $sformatf("invert's mirrored value is %0h ", 
												value), UVM_LOW)
		p_sequencer.p_rm.invert.update(status, UVM_FRONTDOOR);
		value = p_sequencer.p_rm.invert.get();
		`uvm_info("case0_cfg_vseq", $sformatf("invert's desired value is %0h ",
												value), UVM_LOW)
		value = p_sequencer.p_rm.invert.get_mirrored_value();
		`uvm_info("case0_cfg_vseq", $sformatf("invert's mirrored value is %0h ", 
												value), UVM_LOW)
		p_sequencer.p_rm.invert.peek(status, value);
		`uvm_info("case0_cfg_vseq", $sformatf("invert's actual value is %0h", 
												value), UVM_LOW)
		if(starting_phase != null)
			starting_phase.drop_objection(this);
	endtask

通过get函数可以得到寄存器的期望值,通过get_mirrored_value可以得到镜像值

存储器并不存在期望值和镜像值寄存器模型不对存储器进行任何模拟。若要得到存储器中某个存储单元的值,只能使用read、write、peek、poke四种操作。

常用操作及其对期望值和镜像值的影响

  • read&write操作:无论通过后门访问还是前门访问从DUT中读取或写入寄存器的值,操作完成后寄存器模型都会根据读写的结果更新期望值和镜像值(二者相等)
  • peek&poke操作:操作完成后寄存器模型会根据操作的结果更新期望值和镜像值(二者相等)
  • get&set操作:set操作会更新期望值,但镜像值不会改变。get操作会返回寄存器模型中当前寄存器的期望值
  • update操作:这个操作会检查寄存器的期望值和镜像值是否一致不一致就会将期望值写入DUT中并且更新镜像值,使其与期望值一致每个由uvm_reg/uvm_reg_block派生来的类都会有update操作,从uvm_reg_block派生的类会递归调用所有加入此reg_block的寄存器的update任务。
  • randomize操作:寄存器模型提供randomize接口。randomize后期望值会变为随机的数值,镜像值不会改变。并非寄存器模型中所有寄存器都支持此函数,如果不支持则randomize调用后其期望值不变。要关闭随机化功能,如在reg_invert的build中调用reg_data.configure时将其第八个参数设置为0即可。
  • 一般randomize和update一起使用。如在DUT上电复位后需要配置一些寄存器的值。这些寄存器的值通过randomize获得,并使用update任务配置到DUT中。

寄存器模型中一些内建的sequence

*检查后门访问中hdl路径的sequence

UVM提供一系列sequence用于检查寄存器模型及DUT中的寄存器。其中uvm_reg_mem_hdl_paths_seq用于检查hdl路径的正确性。这个sequence的原型为:

class uvm_reg_mem_hdl_paths_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item))

这个sequence的运行依赖于在基类uvm_sequence中定义的一个变量:uvm_reg_block model

启动此sequence时必须给model赋值。在任意sequence中可以启动此sequence:

class case0_cfg_vseq extends uvm_sequence;
	…
	virtual task body();
		..
		uvm_reg_mem_hdl_paths_seq ckseq;
		…
		ckseq = new("ckseq");
		ckseq.model = p_sequencer.p_rm;
		ckseq.start(null);
		…
	endtask
endclass

调用该sequence的start任务时,传入的sequencer参数为null。因为它正常工作不依赖于这个sequencer,而依赖于model变量。这个sequence会试图读取hdl所指向的寄存器,如果无法读取则给出错误提示。由这个sequence的名字也可以看出,它除了检查寄存器外,还检查存储器。如果某个寄存器/存储器在加入寄存器模型时没有指定其hdl路径,那么此sequence在检查时会跳过这个寄存器/存储器

*检查默认值的sequence

uvm_reg_hw_reset_seq用于检查上电复位后寄存器模型与DUT中寄存器的默认值是否相同,它的原型为:

class uvm_reg_hw_reset_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_item));

对于DUT来说在复位完成后其值就是默认值。但对于寄存器模型来说,如果只是将它集成在验证平台上而不做任何处理,那么它所有寄存器的值为0,此时需要调用reset函数来使其内寄存器的值变为默认值(复位值):

function void base_test::build_phase(uvm_phase phase);
	…
	rm = reg_model::type_id::create("rm", this);
	…
	rm.reset();
	…
endfunction

这个sequence在其检查前会调用model的reset函数,所以即使在集成到验证平台时没有调用reset函数,这个sequence也能正常工作。除了复位(reset)外,这个sequence所做的事情就是使用前门访问的方式读取所有寄存器的值,并将其与寄存器模型中的值比较。这个sequence在启动时也需要指定其model变量

如果想跳过某个寄存器的检查,可以在启动此sequence前使用resource_db设置不检查此寄存器。resource_db机制与config_db机制的底层实现是一样的,uvm_config_db类就是从uvm_resource_db类派生而来的。由于在寄存器模型的sequence中get操作是通过resource_db来进行的,所以这里使用resource_db来进行设置:

function void my_case0::build_phase(uvm_phase phase);
	…
	uvm_resource_db#(bit)::set({"REG::",rm.invert.get_full_name(),".*"},
								"NO_REG_TESTS", 1, this);
	…
endfunction

或者使用:

function void my_case0::build_phase(uvm_phase phase);
	…
	uvm_resource_db#(bit)::set({"REG::",rm.invert.get_full_name(),".*"},
								"NO_REG_HW_RESET_TEST", 1, this);
endfunction

*检查读写功能的sequence

UVM提供两个sequence分别用于检查寄存器和存储器的读写功能:uvm_reg_access_seq用于检查寄存器的读写,它的原型为:

class uvm_reg_access_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_it em))

使用此sequence也需要指定其model变量

此sequence使用前门访问的方式向所有寄存器写数据,然后使用后门访问的方式读回并比较结果。最后反过来使用后门访问的方式写入数据,再用前门访问读回。这个sequence要正常工作必须为所有寄存器设置好hdl路径

如果要跳过某个寄存器的读写检查,可以在启动sequence前使用如下的两种方式之一进行设置:

function void my_case0::build_phase(uvm_phase phase);//set for reg access sequence
	uvm_resource_db#(bit)::set({"REG::",rm.invert.get_full_name(),".*"},
								"NO_REG_TESTS", 1, this);
	uvm_resource_db#(bit)::set({"REG::",rm.invert.get_full_name(),".*"},
								"NO_REG_ACCESS_TEST", 1, this);
	…
endfunction

uvm_mem_access_seq用于检查存储器的读写,它的原型为:

class uvm_mem_access_seq extends uvm_reg_sequence #(uvm_sequence #(uvm_reg_it em)

启动此sequence同样需要指定其model变量。这个sequence会通过使用前门访问的方式向所有存储器写数据,然后使用后门访问的方式读回并比较结果。最后反过来使用后门访问的方式写入数据,再用前门访问读回。这个sequence要正常工作必须为所有的存储器设置好HDL路径

如果要跳过某块存储器的检查,可使用如下的三种方式之一进行设置:

function void my_case0::build_phase(uvm_phase phase);//set for mem access sequence
	uvm_resource_db#(bit)::set({"REG::",rm.get_full_name(),".*"},
								"NO_REG_TESTS", 1, this);
	uvm_resource_db#(bit)::set({"REG::",rm.get_full_name(),".*"},
								"NO_MEM_TESTS", 1, this);
	uvm_resource_db#(bit)::set({"REG::",rm.invert.get_full_name(),".*"},
								"NO_MEM_ACCESS_TEST", 1, this);
endfunction
  • 3
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值