寄存器模型的集成

news2024/11/19 2:42:42

前言:当拿到寄存器模型总线后,就要实现总线适配器,这就是集成的过程。

接下来需要考虑选择与DUT寄存器接口一致的总线UVC, 该UVC会提供硬件级别的访问方式。 要完成一次硬件级别的总线传输, 往往需要考虑给出地址数据队列访问方式等, 而寄存器模型可以使得硬件级别的抽象级上升到寄存器级别。由此带来最直观的好处在于, 以往由具体地址来指定寄存器的方式, 将由寄存器名称来替代, 同时寄存器模型封装的一些函数使得可以对域做直接操作,这一升级使得转变后的测试序列更易读。 而伴随着项目变化, 无论寄存器基地址如何变化, 寄存器级别实现的配置序列都要比硬件级别的序列具备更好的维护性。

激励的流向来看, 寄存器序列(而不是总线序列)将带有目标寄存器的相关信息存放到uvm_reg_item实例中, 送往adapter

adapter在接收到uvm_reg_item之后, 从中抽取出总线UVC所需的信息 , 同时生成总线UVC需要的bus_seq_item类型在完成了数据内容抽取和二次写入之后,bus_seq_item由adapter送往总线UVC

总线UVC从bus_seq _item获取地址、 数据、 操作模式等信息之后发起总线的读写访问 。如果总线上有反馈信号标示访问是否成功,则该标示应当由总线sequencer按照response item的路径返回至adapter,adapter也应对该反馈信号做出处理。这一反馈路径在读访问时也会将总线读回的数据返同至adapter, 并最终作为返回值交回到与寄存器操作有关的方法。

 

总线UVC的实现

MCDF访问寄存器的总线接口时序较为简单。控制寄存器接口首先需要在每一个时钟解析cmd。

  • cmd写指令时,即需要把数据cmd_data_in写入到cmd_addr对应的寄存器中。
  • cmd读指令时,即需要从cmd_addr对应的寄存器中读取数据,在下一个周期,cmd_addr对应的寄存器数据被输送至cmd_data_out接口。

总线UVC示例

下面是一段8位地址线,32位数据线的总线UVC实现代码。

class mcdf_bus_trans extends uvm_sequence_item; 
	rand bit[1:0]cmd; 
	rand bit[7:0] addr; 
	rand bit[31:0] wdata; 
	bit[31:0]rdata; 
	`uvm_object_utils_begin(mcdf_bus_trans)
		...
	`uvm_object_utils_end 
endclass 
class mcdf_bus_sequencer extends uvm_sequencer; 
	virtual mcdf_if vif; 
	`uvm_component_utils(mcdf_bus_sequencer)
		...
	function void build_phase(uvm_phase phase); 
		if(!uvm_config_db#(virtual_mcdf_if)::get(this,"","vif",vif))begin; 
			`uvm_error("GETVIE","no virtual interface is assigned")
		end 
	endfunction 
endclass
class mcdf_bus_monitor extends uvm_monitor; 
	virtual mcdf_if vif; 
	uvm_analysis_port#(mcdf_bus_trans) ap;
	`uvm_component_utils(mcdf_bus_monitor)
	...
	function void build_phase(uvm_phase phase); 
		if(!uvm_config_db#(virtual mcdf_if)::get(this,"","vif",vif))begin
			`uvm_error("GETVIF","no virtual interface is assigned")
		end 
		ap=new("ap",this); 
	endfunction 
	task run_phase(uvm_phase phase); 
		forever begin 
			mon_trans(); 
		end 
	endtask
	task mon_trans(); 
		mcdf_bus_trans t; 
		@(posedge vif.clk); 
			if(vif.cmd==WRITE) begin 
				t=new(); 
				t.cmd=WRITE; 
				t.addr=vif.addr; 
				t.wdata=vif.wdata; 
				ap.write(t);
			end 
			else if(vif.cmd==READ) begin 
				t=new(); 
				t.cmd=READ; 
				t.addr=vif.addr; 
				fork 
					begin 
						@(posedge vif.clk);
						#10ps; 
						t.rdata=vif.rdata; 
						ap.write(t); 
					end 
				join_none 
			end 
	endtask 
endclass: mcdf_bus_monitor
class mcdf_bus_driver extends uvm_driver; 
	virtual mcdf_if vif;
	`uvm_component_utils(mcdf_bus_driver)
	...
	function void build_phase(uvm_phase phase); 
		if(!uvm_config_db#(virtual mcdf_if)::get(this,"","vif", vif)) begin 
			`uvm_error("GETVIF","no virtual interface is assigned")
		end 
	endfunction 
	task run_phase(uvm_phase phase); 
		REQ tmp; 
		mcdf_bus_trans req, rsp; 
		reset_listener(); 
		forever begin 
			seq_item_port.get_next_item(tmp); 
			void'($cast(req, tmp)); 
			`uvm_info("DRV",$sformatf("got a item \n %s", req. sprint()), UVM_LOW)
			void'($cast(rsp, req. clone())); 
			rsp.set_sequence_id(req.get_sequence_id()); 
			rsp.set_transaction_id(req.get_transaction_id()); 
			drive_bus(rsp); 
			seq_item_port.item_done(rsp);
			`uvm_info("DRV",$sformatf("sent a item \n %s", rsp.sprint()), UVM_LOW)
		end 
	endtask
	task reset_listener(); 
		fork 
			forever begin 
				@(negedge vif.rstn) drive_idle(); 
			end 
		join_none 
	endtask
	task drive_bus(mcdf_bus_trans t); 
		case(t.cmd) 
			`WRITE: drive_write(t); 
			`READ: drive_read(t); 
			`IDLE: drive_idle(1); 
			default:`uvm_error("DRIVE","invalid mcdf command type received!")
		endcase
	endtask 
	task drive_write(mcdf_bus_trans t); 
		@(posedge vif.clk); 
			vif.cmd<=t.cmd; 
			vif.addr <=t.addr; 
			vif.wdata<=t.wdata; 
	endtask
	task drive_read(mcdf_bus_trans t);
		@(posedge vif.clk); 
			vif.cmd<=t.cmd; 
			vif.addr<=t.addr; 
		@(posedge vif.clk);
			#10ps; 
			t.rdata=vif.rdata; 
	endtask 
	task drive_idle(bit is_sync=0);
		if(is_sync)@(posedge vif.clk); 
		vif.cmd <='h0; 
		vif.addr <='h0; 
		vif.wdata<='h0; 
	endtask
endclass

总线UVC的解析

上面给出的代码襄括了 mcdf_bus_ agent 的所有组件: sequence itemsequencerdrivermonitoragent 。我们对这些代码的部分实现给出解释:

  • mcdf_bus_trans包括了可随机化的数据成员cmd、addr、wdata和不可随机化的rdata。rdata之所以没有声明为rand类型,是因为它应从总线读出或者观察,不应随机化。
  • mcdf_bus_monitor会观测总线,其后通过analysis_port写出到目标analysis组件,稍后它将连接到uvm_reg_predictor。
  • mcdf_bus_driver主要实现了总线驱动和复位功能,通过模块化的方法reset_listener()、drive_bus()、drive_write()、drive_read()和drive_idle()可以解析三种命令模式IDLE、WRITE和READ,并且在READ模式下,将读回的数据通过item_done(rsp)写回到sequencer和sequence一侧。建议通过clone()命令创建RSP对象后,通过set_sequence_id()和set_transaction_id()两个函数保证REQ和RSP的中保留的ID信息一致。

MCDF寄存器设计代码

param_def.v

`define  ADDR_WIDTH 8
`define  CMD_DATA_WIDTH 32

`define  WRITE 2'b10          //Register operation command
`define  READ  2'b01
`define  IDLE  2'b00

`define SLV0_RW_ADDR 8'h00    //Register address 
`define SLV1_RW_ADDR 8'h04
`define SLV2_RW_ADDR 8'h08
`define SLV0_R_ADDR  8'h10
`define SLV1_R_ADDR  8'h14
`define SLV2_R_ADDR  8'h18

`define SLV0_RW_REG 0
`define SLV1_RW_REG 1
`define SLV2_RW_REG 2
`define SLV0_R_REG  3
`define SLV1_R_REG  4
`define SLV2_R_REG  5

`define FIFO_MARGIN_WIDTH 8

`define PRIO_WIDTH 2
`define PRIO_HIGH 2
`define PRIO_LOW  1

`define PAC_LEN_WIDTH 3
`define PAC_LEN_HIGH 5
`define PAC_LEN_LOW  3    

reg.v

   module ctrl_regs(	clk_i,
						rstn_i,
						cmd_i,
						cmd_addr_i,
						cmd_data_i,
						cmd_data_o,
			      		slv0_pkglen_o,
						slv1_pkglen_o,
						slv2_pkglen_o,
			     		slv0_prio_o,
						slv1_prio_o,
						slv2_prio_o,		
						slv0_margin_i,
						slv1_margin_i,
						slv2_margin_i,
					 	slv0_en_o,
						slv1_en_o,
						slv2_en_o);
						
input clk_i;
input rstn_i;
input [1:0] cmd_i;
input [`ADDR_WIDTH-1:0]  cmd_addr_i; 
input [`CMD_DATA_WIDTH-1:0]  cmd_data_i;
input [`FIFO_MARGIN_WIDTH-1:0] slv0_margin_i;
input [`FIFO_MARGIN_WIDTH-1:0] slv1_margin_i;
input [`FIFO_MARGIN_WIDTH-1:0] slv2_margin_i;

reg [`CMD_DATA_WIDTH-1:0] mem [5:0];    //5个reg
reg [`CMD_DATA_WIDTH-1:0] cmd_data_reg; //1个reg

output  [`CMD_DATA_WIDTH-1:0] cmd_data_o;
output  [`PAC_LEN_WIDTH-1:0]  slv0_pkglen_o;    //送给formator
output  [`PAC_LEN_WIDTH-1:0]  slv1_pkglen_o;    //送给formator
output  [`PAC_LEN_WIDTH-1:0]  slv2_pkglen_o;    //送给formator
output  [`PRIO_WIDTH-1:0]  slv0_prio_o;     //优先级送给arbiter
output  [`PRIO_WIDTH-1:0]  slv1_prio_o;     //优先级送给arbiter
output  [`PRIO_WIDTH-1:0]  slv2_prio_o;     //优先级送给arbiter
output   slv0_en_o;   //送给chnal
output   slv1_en_o;   //送给chnal
output   slv2_en_o;   //送给chnal

always @ (posedge clk_i or negedge rstn_i) //Trace fifo's margin
begin 
  if (!rstn_i)
    begin
      mem [`SLV0_R_REG] <= 32'h00000020;   //FIFO's depth is 32
      mem [`SLV1_R_REG] <= 32'h00000020;
      mem [`SLV2_R_REG] <= 32'h00000020;
    end
    else 
    begin
      mem [`SLV0_R_REG] <= {24'b0,slv0_margin_i};
      mem [`SLV1_R_REG] <= {24'b0,slv1_margin_i};
      mem [`SLV2_R_REG] <= {24'b0,slv2_margin_i};
    end
end

always @ (posedge clk_i or negedge rstn_i)begin //write R&W register
	if (!rstn_i)begin 
    	mem [`SLV0_RW_REG] = 32'h00000007;
    	mem [`SLV1_RW_REG] = 32'h00000007;
   	 	mem [`SLV2_RW_REG] = 32'h00000007;
  	end
 	else if (cmd_i== `WRITE) begin
		case(cmd_addr_i)
			`SLV0_RW_ADDR: mem[`SLV0_RW_REG]<= {26'b0,cmd_data_i[`PAC_LEN_HIGH:0]};				
			`SLV1_RW_ADDR: mem[`SLV1_RW_REG]<= {26'b0,cmd_data_i[`PAC_LEN_HIGH:0]};			
			`SLV2_RW_ADDR: mem[`SLV2_RW_REG]<= {26'b0,cmd_data_i[`PAC_LEN_HIGH:0]};   
		endcase 
	end	
end 

always@ (posedge clk_i or negedge rstn_i) // read R&W, R register
  	if(!rstn_i)
  	  	cmd_data_reg <= 32'b0;
	else if(cmd_i == `READ)begin       
		case(cmd_addr_i)
			`SLV0_RW_ADDR:		cmd_data_reg  <= mem[`SLV0_RW_REG];
			`SLV1_RW_ADDR:		cmd_data_reg  <= mem[`SLV1_RW_REG];
			`SLV2_RW_ADDR:	  	cmd_data_reg  <= mem[`SLV2_RW_REG];					
			`SLV0_R_ADDR: 		cmd_data_reg  <= mem[`SLV0_R_REG];
			`SLV1_R_ADDR: 		cmd_data_reg  <= mem[`SLV1_R_REG];
			`SLV2_R_ADDR: 		cmd_data_reg  <= mem[`SLV2_R_REG];
		endcase
     end
assign  cmd_data_o  	= cmd_data_reg;
assign  slv0_pkglen_o  	= mem[`SLV0_RW_REG][`PAC_LEN_HIGH:`PAC_LEN_LOW];
assign  slv1_pkglen_o  	= mem[`SLV1_RW_REG][`PAC_LEN_HIGH:`PAC_LEN_LOW];
assign  slv2_pkglen_o 	= mem[`SLV2_RW_REG][`PAC_LEN_HIGH:`PAC_LEN_LOW];

assign  slv0_prio_o  = mem[`SLV0_RW_REG][`PRIO_HIGH:`PRIO_LOW];
assign  slv1_prio_o  = mem[`SLV1_RW_REG][`PRIO_HIGH:`PRIO_LOW];
assign  slv2_prio_o  = mem[`SLV2_RW_REG][`PRIO_HIGH:`PRIO_LOW];
  
assign  slv0_en_o = mem[`SLV0_RW_REG][0];
assign  slv1_en_o = mem[`SLV1_RW_REG][0];
assign  slv2_en_o = mem[`SLV2_RW_REG][0];

endmodule

Adapter的实现

 在具备了MCDF总线UVC之后,需要实现adapter。每一个总线对应的adapter所完成的桥接功能即是在uvm_reg_bus_op和总线transaction之间的转换。在开发一个总线adapter是,需要事先下面几点:

- uvm_reg_bus_op与总线transaction中各自的数据映射。
- 实现reg2bus()bus2reg()两个函数,这两个函数即实现了两种transaction的数据映射。
- 如果总线支持byte访问,可以使能supports_byte_enable;如果总线UVC要返回response数据,则应当使能provides_response。在本例中,mcdf_bus_driver在读数时会将读回的数据填入到RSP并返回至sequencer,因此需要在adapter中使能provides_responses。由此使得bus2reg()函数调用时得到的数据是总线返回时的transaction,但读者需要注意如果总线UVC不支持返回RSP(没有调用put_response(RSP)或者item_done(RSP)),那么不应该置此位,否则adapter将会使得验证环境挂起。默认情况下,上述的两个成员的复位值都是0。
 

//实例
class reg2mcdf_adapter extends uvm_reg_adapter;
	`uvm_object_utils(reg2mcdf_adapter)
	function new(string name = "mcdf_bus_trans");
		super.new(name);
		provides_response = 1;
	endfunction
	function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
		mcdf_bus_trans t = mcdf_bus_trans:;tpe_id::create("t");
		t.cmd = (rw.kind == UVM_WRITE) ? `WRITE : `READ;
		t.addr = rw.addr;
		t.wdata = rw.data;
		return t;
	endfucntion
	function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
		mcdf_bus_trans t;
		if(!$cast(t, bus_item)) begin
			`uvm_fatal("NOT_MCDF_BUS_TYPE", "Provided 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.cmd == `WRITE) ? t.wdata : t.rdata;
		rw.status = UVM_IS_OK;
	endfunction
endclass

Adapter的解析

该类在构建函数中使能了provide_response,

这是因为mcdf_bus_driver在发起总线访问之后会将RSP一并返回到sequencer。reg2bus()完成的桥接场景是,如果用户在寄存器级别做了操作,那么寄存器级别操作的信息uvm_reg_bus_op会被记录,同时调用uvm_reg_adapter::reg2bus()函数。在完成了将uvm_reg_bus_op的信息映射到mcdf_bus_trans之后,函数将mcdf_bus_trans实例返回。而在返回mcdf_bus_trans之后,该实例将通过mcdf_bus_seqeuncer传入到mcdf_bus_driver。这里的transaction传输是后台隐式调用的,不需要主动发起。
寄存器无论读写,都应当知道总线操作后的状态返回,对于读操作时,也需要知道总线返回的读数据,因此uvm_reg_adapter::bus2reg()即是从mcdf_bus_driver()将数据写回至mcdf_bus_sequencer,而一直保持监听的reg2mcdf_adapter一旦从sequence获取了RSP(mcdf_bus_trans)之后就将自动调用bus2reg()函数。
bus2reg()函数的功能与reg2bus()相反,完成了从mcdf_bus_trans到uvm_reg_bus_op的内容映射。在完成映射之后,更新的uvm_reg_bus_op数据最终返回至寄存器操作场景层。
对于寄存器操作,无论读操作还是写操作,都需要经历调用reg2bus(),继而发起总线事务,而完成事务发回反馈之后,又需要调用bus2reg(),将总线的数据返回至寄存器操作层面。
 

Adapter的集成

//实例
class mcdf_bus_env extends uvm_env;
	mcdf_bus_agent agent;
	mcdf_rgm rgm;
	reg2mcdf_adapter reg2mcdf;
	`uvm_component_utils(mcdf_bus_env)
	...
	function void build_phase(uvm_phase phase);
		agent = mcdf_bus_agent::type_id::create("agent", this);
		if(!uvm_config_db#(mcdf_rgm)::get(this, "", "rgm", rgm)) begin
			`uvm_info(GETRGM", "no top-down RGM handle is assigned", UVM_LOW)
			rgm = mcdf_rgm::type_id::create("rgm", this);
			`uvm_info(NEWRGM", "created rgm instance locally", UVM_LOW)
		end
		rgm.build();
		rgm.map.set_auto_predict();
		reg2mcdf = reg2mcdf_adapter::type_id::create("reg2mcdf");
	endfunction
	function void connect_phase(uvm_phase phase);
		rgm.map.set_sequencer(agent.sequencer, reg2mcdf);
	endfunction
endclass

class test1 extends uvm_test;
	mcdf_rgm rgm;
	mcdf_bus,env env;
	`uvm_component_utils(test1)
	...
	function void build phase(uvm phase phase);
		rgm = mcdf_rgm::type_id::create("rgm" , this);
		uvm_config_db#(mcdf_rgm)::set(this, "env*" , "rgm" , rgm);
		env = mcdf_bus_env::type_id::create("env" , this);
	endfunction
	task run_phase(uvm_phase phase);
		...
	endtask
endclass

在具备了寄存器模型mcdf_rgm、总线UVC mcdf_bus_agent和桥接reg2mcdf_adapter之后,就需要考虑如何将adapter集成到验证环境中去:
- 对于mcdf_rgm的集成,倾向于顶层传递的方式,即最终从test层传入寄存器模型句柄。这种方式有利于验证环境mcdf_bus_env的闭合性,在后期不同test如果要对rgm做不同的配置,都可以在顶层例化,而后通过uvm_config_db来传递。
- 寄存器模型在创建之后,还需要显式调用build()函数。需要注意uvm_reg_block时uvm_object类型,因此预定义的build()函数并不会自动执行,还需要单独调用。
- 在还未集成predictor之前,采用auto prediction的方式,因此调用了函数set_auto_predict()。
- 在顶层环境的connect阶段中,需要将寄存器模型的map组件与bus sequencer和adapter连接。这么做的必要性在于将map(寄存器信息)、sequencer(总线侧激励驱动)和adapter(寄存器级别和硬件总线级别的桥接)关联在一起,只有通过这一步,adapter的桥接功能才可以工作。
 

访问方式

利用寄存器模型,可以更加方便地对寄存器做操作,将访问方式分为两种,前门访问(front-door)和后门访问(back-door)。前门访问,是在寄存器模型上做的读写操作,最终会通过总线UVC来实现总线上的物理时序访问,因此是真实的物理操作。后门访问,是利用UVM DPI(uvm_hdl_read()、uvm_hdl_deposit()),将寄存器的操作直接作用到DUT内的寄存器变量,而不通过物理总线访问。
 

前门访问

前门访问实例中的sequence继承于uvm_reg_sequenceuvm_reg_sequence除了具备一般uvm_sequence的预定义方法外,还具有跟寄存器操作相关的方法。在下例中,有两种操作方式:
- 第一种即uvm_reg::read()/write()。在传递时,需要注意将参数path指定为UVM_FRONTDOORuvm_reg::read()/write()方法可传入的参数比较多,除了statusvalue参数需要传入,其他参数可采用默认值。
- 第二种即uvm_reg_sequence::read_reg()/write_reg()。在使用时,需要注意将参数path指定为UVM_FRONTDOOR
 

//实例
class mcdf_example_seq extends uvm_reg_sequence;
	mcdf_rgm rgm;
	'uvm_declare _p_sequencer (mcdf_bus_sequencer)
	...
	task body();
		uvm_status_e status;
		uvm_reg_data_t data;
		if(!uvm_config_db#(mcdf_rgm)::get(null, get_full_name(), "rgm", rgm)) begin
			uvm_error("GETRGM", "no top-down RGM handle is assigned")
		end
		//register model access write()/read()
		rgm.chn10_ctrl_reg.read (status, data,UVM_FRONTDOOR, .parent(this));
		rgm.chn10_ctrl_reg.write(status, 'h11, UVM_FRONTDOOR, .parent(this));
		rgm.chn10_ctrl_reg.read (status, data, UVM_FRONPDOOR, .parent(this));
		//pre-defined methods access
		read_reg (rgm.chnl1_ctrl_reg, status, data, UVM_FRONTDOOR);
		write_reg(rgm.chnl1_ctrl_reg, status, 'h22, UVM_PRONTDOOR);
		read_reg (rgm.chnl1_ctrl_reg, status, data, UVM_PRONTDOOR);
	endtask
endclass

后门访问

在进行后门访问时,首先需要确保寄存器模型在建立时,将各个寄存器映射到了DUT一侧的HDL路径。下面的代码实现了寄存器模型与DUT各个寄存器的路径映射:

class mcdf_rgm extends uvm_reg_block ;
	... 	//寄存器成员和map声明
	virtual function build();
		...		//寄存器成员和map创建
		//关联寄存器模型和HDL
		add_hdl_path("reg_backdoor_access.dut");
		chnl0_ctrl_reg.add_hdl_path_slice($sformatf("regs[%0d]", `SLVO_RW_REG), 0, 32);
		chnl1_ctrl_reg.add_hdl_path_slice($sformatf("regs[%0d]", `SLV1_RW_REG), 0, 32);
		chn12_ctrl_reg.add_hdl_path_slice($sformatf("regs[%0d]", `SLV2_Rw_REG), 0, 32);
		chn10_stat_reg.add_hdl_path_slice($sformatf("regs[%0d]", `SLV0_R_REG ), 0, 32);
		chnl1_stat_reg.add_hdl_path_slice($sformatf("regs[%0d]", `SLV1_R_REG ), 0, 32);
		chn12_stat_reg.add_hdl_path_slice($sformatf("regs[%0d]", `SLV2_R_REG ), 0, 32);
		lock_model() ;
	endfunction
endclass

上例中通过uvm_reg_block::add_hdl_path(),将寄存器模型关联到了DUT一侧,而通过uvm_reg::add_hdl_path_slice()完成了将寄存器模型的各个寄存器成员与HDL一侧的地址映射。另外,寄存器模型build()函数最后以lock_model()结尾,该函数的功能是结束地址映射关系,并且保证模型不会被其他用户修改。
在寄存器模型完成了HDL路径映射后,次啊可以利用uvm_reg或uvm_reg_sequence自带的方法进行后门访问,后门访问有几类方法提供:
- uvm_reg::read()/write(),在调用该方法时需要注明UVM_BACKDOOR的访问方式。
- uvm_reg_sequence::read_reg()/write_reg(),在调用该方法时需要注明UVM_BACKDOOR的访问方式。
- 另外,uvm_reg::peek()/poke()两个方法,也分别对应了读取寄存器(peek)和修改寄存器(pook)两种操作,而用户无需指定访问方式尾的UVM_BACKDOOR,因为这两种方法本来就只针对于后门访问。
 

//实例
class mcdf_example_seq extends uvm_reg_sequence;
	mcdf_rgm rgm;
	`uvm_object_utils(mcdf_example_seq)
	'uvm_declare_p_sequencer(mcdf_bus_sequencer)
	...
	task body();
		uvm_status_e status;
		uvm_reg_data_t data;
		if(!uvm_config_db#(mcdf_rgm)::get(null, get_full_name(), "gm", rgm)) begin
			'uvm_error("GETRGM", "no top-down RGM handle is assigned")
		end
		//register model access write()/read()
		rgm.chnl0_ctrl_reg.read (status, data, UVM_BACKDOOR, .parent(this));
		rgm.chnl0_ctrl_reg.write(status, 'h11, UVM_BACKDOOR, .parent(this));
		rgm.chnl0_ctrl_reg.read (status, data, UVM_BACKDOOR, .parent(this));
		//register model access poke ( )/peed ()
		rgm.chnl1_ctrl_reg.peek(status, data, .parent(this));
		rgm.chnl1_ctrl_reg.poke(status, 'h22, .parent(this));
		rgm.chnl1_ctrl_reg.peek(status, data, .parent(this));
		//pre-defined methods read_reg () / write_reg()
		read_reg (rgm.chnl2_ctrl_reg, status, data, UVM_BACKDOOR);
		write_reg(rgm.chn12_ctrl_reg, status, 'h22, UVM_BACKDOOR);
		read_reg (rgm.chn12_ctrl_reg, status, data, UVM_BACKDOOR);
		//pre-defined methods peek_reg()/poke_reg()
		peek_reg(rgm.chnl2_ctrl_reg, status, data);
		poke_reg(rgm.chnl2_ctrl_reg, status, 'h33);
		peek_reg(rgm.chnl2_ctrl_reg, status, data);
	endtask
endclass

前门与后门的比较

从上面的差别可以看出,后门访问较前门访问更便捷更快一些,但不能单纯依赖后门访问。因此利用寄存器模型的前门访问和后门访问混合方式,对寄存器验证的完备性更有帮助。 

前门后门的混合应用

实际应用场景:
- 通过前门访问的方式,先验证寄存器访问的物理通路工作正常,并且有专门的寄存器测试的前门访问用例,来遍历所有的寄存器。在前门访问被验证充分的前提下,可以在后续测试中使用后门访问来节省访问多个寄存器的时间。
- 如果DUT实现了一些特殊寄存器,例如只能写一次的寄存器等,我们建议用物理方式去访问以确保反映真实的硬件行为。
- 寄存器随机设置的精髓不在于随机可设置的域值,而是为了考虑日常不可预期的场景,先通过后门访问随机化整个寄存器列表(在一定的随机限制下),随后再通过前门访问来配置寄存器。这么做的好处在于,不再只是通过设置复位之后的寄存器这种更有确定性的场景,而是通过让测试序列一开始的寄存器值都随机化来模拟无法预期的硬件配置前场景,而在稍后设置了必要的寄存器之后,再来看是否会有意想不到的边界情况发生。
- 有的时候,即便通过先写再读的方式来测试一个寄存器,也可能存在地址不匹配的情况。譬如寄存器A地址本应该0x10,寄存器B地址本应该为0x20;而在硬件实现中,寄存器A对应的地址为0x20,寄存器B对应的地址为0x10。像这种错误,即便通过先写再读的方式也无法有效测试出来,那么不妨在通过前门配置寄存器A之后,再通过后门访问来判断HDL地址映射的寄存器A变量值是否改变,最后通过前门访问来读取寄存器A的值。上述的方式是在前门测试的基础之上又加入了中途的后门访问和数值比较,可以发现地址映射到错误寄存器的问题。
- 对于一些状态寄存器,在一些时候外界的激励条件修改会依赖这些状态寄存器,并且在时序上的要求也可能很严格。例如,上面MCDF的寄存器中有一组状态寄存器表示各个channel中FIFO的余量,而channel中FIFO的余量对于激励驱动的行为也很重要。无论是前门访问还是后门访问,都可能无法第一时间反映FIFO当前时刻的余量。因此对于要求更严格的测试场景,除了需要前门和后门来访问寄存器,也需要映射一些重要的信号来反映更即时的信息。
 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/125054.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网鼎杯2020青龙组——filejava通关思路

目录 1、启动靶场&#xff0c;访问页面 2、BP抓包 &#xff08;三&#xff09;代码审计 1&#xff0e;XMLReader 2&#xff0e;SAXBuilder 3&#xff0e;SAXReader 4&#xff0e;SAXParserFactory 5&#xff0e;Digester 6&#xff0e;DocumentBuilderFactory 漏洞利用 0x0…

C++--数据结构--图解B树--B+树--B*树--0718 19

1、常见的搜索结构 种类 数据格式时间复杂度顺序查找无要求O(N)二分查找有序O(log_2 N)二叉搜索树无要求O(log_2 N)二叉平衡树无要求O(log_2 N)哈希无要求O(1) 如果数据量很大&#xff0c;比如有100G数据&#xff0c;无法一次放进内存中&#xff0c;那就只能放在磁盘上了…

互联网+时代的到来,让一站式婚庆管理系统成为潮流

自20世纪90年代初中国第一家婚庆公司成立至今&#xff0c;婚庆市场是越做越大。作为新兴产业的婚庆行业蕴藏着巨大的商机&#xff0c;婚庆市场空间日趋扩大&#xff0c;婚庆产业逐渐成为前景看好的朝阳产业。因此&#xff0c;市面上的婚庆企业也越来越多。但是想要在众多同行中…

Codeforces Round #841 (Div. 2) and Divide by Zero 2022 A-D

等System test的时候顺便水一篇吧233&#xff0c;感觉题目挺好的&#xff0c;但是我C、D都快要调完了&#xff0c;还是难受。 应该是我参加的今年最后一场比赛了。 Codeforces Round #841 (Div. 2) and Divide by Zero 2022 A. Joey Takes Money #include<bits/stdc.h&g…

数字射线检测图像质量

对比度 物体对比度 ΔI/I−μΔT/(1n)\Delta I/I -\mu \Delta T /(1n)ΔI/I−μΔT/(1n) 屏幕亮度 LkILkILkI 人眼感觉到的亮度 BKlnLBKln LBKlnL 人眼感觉到的亮度对比度 ΔBKln((LΔL)/L)\Delta B K ln((L\Delta L)/L)ΔBKln((LΔL)/L) 其中&#xff0c;III为射线强度 ΔB…

杭州市 智慧城市物联网支撑平台 功能规范 附下载地址

智慧城市物联网介绍 智慧城市是一个有机结合的大系统&#xff0c;涵盖了更透切的感知、更全面的互连&#xff0c;更深入的智能。物联网是智慧城市中非常重要的元素&#xff0c;它侧重于底层感知信息的采集与传输&#xff0c;城市范围内泛在网方面的建设。 通过智慧城市物联网支…

curl升级到7.87(centos7和TencentOS2.4 tk)

centos7升级curl到7.8.7,按照之前写过的一篇文章,大致按描述操作即可。只不过需要做一点点修正... CentOS 7升级curl_乐大师的博客-CSDN博客_centos7 curl升级 更新操作中会报错安装失败,提示如下: 针对这个问题只需要增加一个参数即可解决。 yum -y update curl --ena…

Linux系统安装Redis(实现远程登录)

目录 &#xff08;一&#xff09;下载和安装 &#xff08;二&#xff09;配置redis.con配置文件 1&#xff0c;开启远程连接有以下三步&#xff1a; 2&#xff0c;配置登录密码 ​&#xff08;三&#xff09;开放端口 1&#xff0c;开放Linux系统上的3306端口 2&#xff0…

记录一次Tomcat靶机渗透

Apache Tomcat&#xff0c;是世界上最广泛使用的Java Web服务器之一。带有默认配置的Tomcat服务器非常容易发现。发现暴露Web应用管理器的服务器也非常容易&#xff0c;它是一个应用&#xff0c;允许管理员启动、停止、添加和删除服务器中的应用。 信息搜集 第一步&#xff1a…

频谱分析误差主要表现在三个方面

频谱分析仪是研究电信号频谱结构的仪器&#xff0c;用于信号失真度、调制度、谱纯度、频率稳定度和交调失真等信号参数的测量&#xff0c;可用以测量放大器和滤波器等电路系统的某些参数&#xff0c;是一种多用途的电子测量仪器。它又可称为频域示波器、跟踪示波器、分析示波器…

基于python多光谱遥感数据处理、图像分类、定量评估及机器学习方法

普通数码相机记录了红、绿、蓝三种波长的光&#xff0c;多光谱成像技术除了记录这三种波长光之外&#xff0c;还可以记录其他波长&#xff08;例如&#xff1a;近红外、热红外等&#xff09;光的信息。与昂贵、不易获取的高光谱、高空间分辨率卫星数据相比&#xff0c;中等分辨…

【蚂蚁】Alluxio在蚂蚁集团大规模训练中的应用

本期内容我们邀请到了来自蚂蚁集团的开发工程师陈传迎老师&#xff0c;给大家分享Alluxio在蚂蚁集团是如何支持大规模模型训练的。 首先是关于引入Alluxio的背景&#xff1a; 为什么要引入Alluxio&#xff1f;Alluxio到底解决了什么问题&#xff1f; 带着这些问题&#xff0…

USDP使用笔记(八)Flink配置及简单测试

Flink配置Flink配置及简单测试 上一篇&#xff1a;https://lizhiyong.blog.csdn.net/article/details/123560865 将USDP2.0自带的Flink更换为Flink1.14后&#xff0c;还没有来得及改配置。不改配置用起来是有问题的&#xff0c;所以。。。本文主要就是改配置及简单测试效果。 …

基于ssm框架实现的Java web在线考试系统

一、项目简介 本项目是一套基于ssm框架实现的Java web在线考试系统&#xff0c;主要针对计算机相关专业的正在做毕设的学生与需要项目实战练习的Java学习者。 包含&#xff1a;项目源码、数据库脚本等&#xff0c;该项目附带全部源码可作为毕设使用。 项目都经过严格调试&…

HITCON2022--ctf驱动逆向题

作者&#xff1a;selph HITCON CTF 2022 Writeup-checker 挺有意思的一道题&#xff0c;这里的关键函数是使用的动态生成执行操作&#xff0c;按照特定参数序列进行解密才能正常执行&#xff0c;否则一定会报错异常 checker 一共给了两个文件&#xff1a;checker.exe和chec…

VR数字政务:多功能一体化,政务服务更便民

越来越多的政务单位都是通过网络等线上信息化渠道&#xff0c;进行政务的推进以及落实、查询等功能的实施&#xff0c;在实际的政务应用中&#xff0c;VR技术可以打造集实景导航、窗口查询、在线申报等多功能为一体&#xff0c;实现数据多跑腿、群众少跑腿&#xff0c;有效提高…

Nginx 01篇——Nginx详细安装步骤以及Nginx各种启动方式

Nginx 01篇——Nginx详细安装步骤以及Nginx各种启动方式1. 前言2. 下载安装2.1 下载安装包2.2 安装2.2.1 离线安装2.2.1.1 安装步骤2.2.1.2 安装问题2.2.2 先安装所需依赖2.2.3 直接 yum 安装2.2.4 指定安装目录安装3. nginx 启动4. 启动后访问5. Nginx 启动等简单命令5.1 启动…

SVN,Git与Helix Core,谁的数据管理基础设施更安全?

2022年2月底&#xff0c;全球知名的半导体芯片公司英伟达被爆遭到勒索软件攻击&#xff0c;不久后&#xff0c;英伟达公司官方证实遭到入侵&#xff0c;攻击者已开始在线泄露员工凭据和私密信息。勒索软件组织表示&#xff0c;如果英伟达拒绝支付高达100万美元的赎金&#xff0…

写在2022年的末尾

今年是我人生的第24个年头。 最近了解稚晖君&#xff0c;xinglu师兄&#xff0c;以及很多优秀的同辈人的事迹之后&#xff0c;感觉到自己还差了很远。 读研已经过半&#xff0c;研二已经到了第二个学期&#xff0c;而自己还什么成果都没有。甚至开题的事情到现在都还没有头绪。…

C++简介

C是一种计算机高级程序设计语言&#xff0c;由C语言扩展升级而产生 &#xff0c;最早于1979年由本贾尼斯特劳斯特卢普在AT&T贝尔工作室研发。 C既可以进行C语言的过程化程序设计&#xff0c;又可以进行以抽象数据类型为特点的基于对象的程序设计&#xff0c;还可以进行以…