电力电子转战数字IC20220726day56——寄存器模型

发布于:2023-02-12 ⋅ 阅读:(642) ⋅ 点赞:(0)

 

目录

uvm_reg

寄存器模型集成

总线UVC的实现

寄存器访问方式

前门访问

后门访问


  • 寄存器:硬件模块之间交谈的窗口,读出reg状态获取硬件当前的状态,配置reg使其工作在一定模式下
  • 通过硬件的reg模型和总线UVC建立一个验证环境
    • reg有关的设计流程
    • reg model相关类
    • 将reg model集成到现有环境,与总线UVC桥接,与dut模型绑定
    • reg model常用方法和预定义的seq
    • reg测试和功能覆盖率
  • reg的域:WO,RO,RW,RC读后擦除clean_on_read,W1S只写一次write_one_to_set

  • reg按照地址索引的关系是按字对齐
  • 寄存器列表(块):将寄存器按照地址排列

  • 寄存器的中心化管理:保证通过软件建立reg模型与硬件reg的内容属性保持一致

uvm_reg

 

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("");
		pkt_len    = uvm_reg_field::type_id::create("");
		prio_level = uvm_reg_field::type_id::create("");
		chnl_en    = uvm_reg_field::type_id::create("");//例化
		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()
	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("");
		fifo_avail= uvm_reg_field::type_id::create("");
		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 stat_reg chnl0_stat_reg;
	rand stat_reg chnl1_stat_reg;
	rand stat_reg chnl2_stat_reg;
	uvm_reg_map map;
	function new(string name ="")
		super.new(name, UVM_NO_COVERAGE);
	endfunction

virtual function build();
	chnl0_ctrl_reg=ctrl_reg::type_id::create("");
	chnl0_ctrl_reg.configure(this);//创建,配置,
	chnl0_ctrl_reg.build();
	chnl1_ctrl_reg=ctrl_reg::type_id::create("");
	chnl1_ctrl_reg.configure(this);
	chnl1_ctrl_reg.build();
	chnl2_ctrl_reg=ctrl_reg::type_id::create("");
	chnl2_ctrl_reg.configure(this);
	chnl2_ctrl_reg.build();
	chnl0_stat_reg=stat_reg::type_id::create("");
	chnl0_stat_reg.configure(this);
	chnl0_stat_reg.build();
	chnl1_stat_reg=stat_reg::type_id::create("");
	chnl1_stat_reg.configure(this);
	chnl1_stat_reg.build();
	chnl2_stat_reg=stat_reg::type_id::create("");
	chnl2_stat_reg.configure(this);
	chnl2_stat_reg.build();
	map=create_map("map", 'h0, 4, UVM_LITTLE_ENDIAN);//map名字,基地址,位宽,endianess
	map.add_reg(chnl0_ctrl_reg, 32'h00000000, "RW");//偏移地址offset addr
	map.add_reg(chnl1_ctrl_reg, 32'h00000004, "RW");//完整reg地址addr=base addr+offset addr
	map.add_reg(chnl2_ctrl_reg, 32'h00000008, "RW");
	map.add_reg(chnl0_stat_reg, 32'h00000010, "RO");//添加每个reg的偏移地址和访问属性(模式)
	map.add_reg(chnl1_stat_reg, 32'h00000014, "RO");
	map.add_reg(chnl2_stat_reg, 32'h00000018, "RO");
	lock_model();//reg构建后锁住这个模型,不允许外部访问寄存器内部,只可以用预测组件来改变
endfunction
endclass

  • 寄存器模型可以用来写一些激励

寄存器模型集成

总线UVC的实现

  • 控制reg接口在每一个clk解析cmd:写,读
  • 以下是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] data;
	bit[31:0] rdata;//从总线读回来的,不可随机化
	`uvm_object_utils_begin()...//注册以及域的自动化
...endclass

class mcdf_bus_sequencer extends uvm_sequencer;
	virtual mcdf_if vif;
	`uvm_component_utils()
	...//注册,例化
	function void build_phase(u p);
		if(!uvm_config_db#(virtual mcdf_if)::get(this, "", "vif", vif))begin
		`uvm_error("", "") end
	endfunction
endclass

class mcdf_bus_monitor extends uvm_monitor;//等下连接到uvm_reg_predictor
	virtual mcdf_if vif;
	uvm_analysis_port #(mcdf_bus_trans) ap;//monitor要广播
	`uvm_component_utils()
	...
	function void build_phase(u p);
		if(!uvm_config_db#(virtual mcdf_if)::get(this, "", "vif", vif))begin
		`uvm_error("", "") end
		ap =new("", this);//不能用create,因为port不是object
	endfunction
	task run_phase(u p);
		forever begin
		mon_trnas() 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

class mcdf_bus_driver extends uvm_driver;
	virtual mcdf_if vif;
	...//注册+例化
	function void build_phase(u p);
		if(!uvm_config_db#(virtual mcdf_if)::get(this, "", "vif", vif))begin
		`uvm_error("", "") end
	endfunction
	task run_phase(u p);
		REQ tmp;
		mcdf_bus_trans req, rsp;
		reset_listener();
		forever begin
			seq_item_port.get_next_item(tmp);
			void'($cast(req,tmp));
			`uvm_info("got")
			void'($cast(rsp, req.clone()));
			rsp.set_sequence_id(req.get_sequence_id());
			rsp.set_transaction_id(req.get_transaction_id());
			driver_bus(rsp);
			set_item_port.item_done(rsp);
			`uvm_info("sent")
		end
	endtask
	task reset_listener();
		fork
			forever begin
			@(negdege 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();
		default: `uvm_error()
		endcase
	endtask
	task drive_write(mcdf_bus_trans t);
		@(posedge vif.clk);
		vif.cmd<=t.cmd;
		vif.addr<=t.addr;
		vif.data<=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.data<='h0;
	endtask
endclass

class mcdf_bus_agent extends uvm_agent;
	mcdf_bus_driver driver;
	mcdf_bus_sequencer sequencer;
	mcdf_bus_monitor monitor;
	...
	function void build_phase(u p);
		driver   =mcdf_bus_driver::type_id::create("", this);
		sequencer=mcdf_bus_sequencer::type_id::create("", this);
		monitor  =mcdf_bus_monitor::type_id::create("", this);
	endfunction
	function void connect_phase(u p);
		driver.seq_item_port.connect(sequencer.seq_item_export);
	endfunction
endclass

硬件reg

module ctrl_regs();

还差个adapter和predictor

class ref2mcdf_adapter extends uvm_reg_adapter;
	`uvm_object_utils()
	function new(string name ="");
		super.new(name);
		provides_responses=1;//enable了provides_responses,总线可以返回rsp的数据
	endfunction
	//预定义的,也是必须实现的,名字一个字都不能差
	function uvm_sequence_item reg2bus (const ref uvm_reg_bus_op rw);
		mcdf_bus_trans t=mcdf_bus_trnas::type_id::create("t");//创建子类对象
		t.cmd=(rw.kind == UVM_WRITE)? `WRITE : `READ;
		t.addr=rw.addr;
		t.wdata=rw.data;
		return t;//做了隐式转换
	endfunction//完成了寄存器级别操作rw到bus上的桥接,下面的func反之

	function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
//从driver将数据写会sequencer,adapter从sqr拿到rsp(t)后自动调用
		mcdf_bus_trans t;
		if(!$cast(t, bus_item)) begin//父类句柄,转换成子类mcdf_bus_trans句柄
		`uvm_fatal("", "")
		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

 uvm_reg_bus_op包含这些成员变量,要转换成mcdf_bus_trans

  • 如何将adapter集成到环境中?

    • 从test层传入寄存器模型rgm句柄,在顶层例化后通过uvm_config_db进行配置
    • rgm在创建后要调用build()函数,因为uvm_reg_block是object类型,其预定义的build()不会自动执行
    • 这里由于还没集成predictor,所以调用set_auto_predict(),采用auto prediction方式
    • 顶层连接时,需要将rgm的map组件和bus sequencer和adapter连接(reg信息-总线侧激励驱动-reg级别和硬件总线级别的桥接),adapter的桥接功能才可以工作
    class mcdf_bus_env extends uvm_env;
    	mcdf_bus_agent agent;
    	mcdf_rgm rgm;
    	reg2mcdf_adapter reg2mcdf;
    	...//注册+例化
    	function void build_phase(u p);
    		agent=mcdf_bus_agent::type_id::create("", this);
    		if(!uvm_config_db#(mcdf_rgm)::get(this, "", "rgm", rgm)) begin
    			`uvm_info()
    			rgm=mcdf_rgm::type_id::create("", this);
    			end
    		rgm.build();//创建,配置reg,调用各个reg的build,以及它们的field
    		rgm.map.set_auto_predict();
    		reg2mcdf=reg2mcdf_adapter::type_id::create("");
    	endfunction
    	
    	function void connect_phase(u p);
    		rgm.map.set_sequencer(agent.sequencer, reg2mcdf);//!连接
    	endfunction
    endclass
    
    class test1 extends uvm_test;
    	mcdf_rgm rgm;
    	mcdf_bus_env env;
    	...//注册+例化
    	function void build_phase(u p);
    		rgm=mcdf_rgm::type_id::create("", this);
    		uvm_config_db#(mcdf_rgm)::set(this, "env*", "rgm", rgm)
    		env=mcdf_bus_env::type_id::create("", this);
    	endfunction
    	task run_phase(u p); ...
    endclass
    

寄存器访问方式

前门访问

  • 在rgm上做读写操作,最终会通过总线UVC来实现总线上的物理时序访问
class mcdf_example_seq extends uvm_reg_sequence;
	mcdf_rgm rgm;
	`uvm_object_utils()
	`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("", "") end
//方式1:uvm_reg::read()/write()
		rgm.chnl0_ctrl_reg.read (status, data, UVM_FRONTDOOR, .parent(this));
		rgm.chnl0_ctrl_reg.write(status, 'h11, UVM_FRONTDOOR, .parent(this));
		rgm.chnl0_ctrl_reg.read (status, data, UVM_FRONTDOOR, .parent(this));
//方式2:uvm_reg_sequence::read_reg()/write_reg()
		read_reg  (rgm.chnl1_ctrl_reg.read, status, data, UVM_FRONTDOOR);
		write_reg (rgm.chnl1_ctrl_reg.read, status, 'h22, UVM_FRONTDOOR);
		read_reg  (rgm.chnl1_ctrl_reg.read, status, data, UVM_FRONTDOOR);
	endtask
endclass

后门访问

  • 利用UVM DPI (uvm_hdl_read()和uvm_hdl_deposit()),将reg的操作直接作用到DUT内的寄存器变量,不通过物理总线访问,不是真实的硬件行为,速度快不消耗时间
  • 首先需要确保rgm在建立时是否将各个reg映射到DUT一侧的HDL路径,如下
class mcdf_rgm extends uvm_reg_block;
...//reg成员,map声明
	virtual function build();
		...//创建
		add_hdl_path("reg_backdoor_access,dut");
		chnl0_ctrl_reg.add_hdl_path_slice($sformat("regs[%0d]", `SLV0_RW_REG), 0,32);
		chnl1_ctrl_reg.add_hdl_path_slice($sformat("regs[%0d]", `SLV1_RW_REG), 0,32);
		chnl2_ctrl_reg.add_hdl_path_slice($sformat("regs[%0d]", `SLV2_RW_REG), 0,32);
		chnl0_stat_reg.add_hdl_path_slice($sformat("regs[%0d]", `SLV0_R_REG), 0,32);
		chnl1_stat_reg.add_hdl_path_slice($sformat("regs[%0d]", `SLV1_R_REG), 0,32);
		chnl2_stat_reg.add_hdl_path_slice($sformat("regs[%0d]", `SLV2_R_REG), 0,32);
		lock_model();//结束地址映射关系,保证model不会被其他用户修改
	endfunction
endclass

class mcdf_example_seq extends uvm_reg_sequence;
	mcdf_rgm rgm;
	`uvm_object_utils()
	`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("", "") end
		//read()/write()
		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));
		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));
		//peek()/poke()
		read_reg  (rgm.chnl2_ctrl_reg.read, status, data, UVM_BACKDOOR);
		write_reg (rgm.chnl2_ctrl_reg.read, status, 'h22, UVM_BACKDOOR);
		read_reg  (rgm.chnl2_ctrl_reg.read, status, data, UVM_BACKDOOR);
		peek_reg (rgm.chnl2_ctrl_reg, status, data);//peek读取reg
		poke_reg (rgm.chnl2_ctrl_reg, status, 'h33);//poke修改reg
		peek_reg (rgm.chnl2_ctrl_reg, status, data);

	endtask
endclass

二者比较如下


网站公告

今日签到

点亮在社区的每一天
去签到

热门文章