电力电子转战数字IC20220728day58——uvm入门实验5

目录

UVM入门实验5

实验目标

问题清单

实验任务

1.1 实现reg2mcdf_adapter类的两个方法

1.2 在mcdf_env中声明并例化block、adapter、predictor,最后connect起来

2.1 reg block句柄的传递

2.2 mcdf_data_consistence_basic_virtual_sequence类中原来是由总线seq实现的reg读写,现在改为又rgm操作的reg读写方式

2.3 mcdf_full_random_virtual_sequence做相同的操作

3.1 利用寄存器的内建序列做测试


 

UVM入门实验5

实验目标

 

 

问题清单

  • 什么是uvm_reg类?
    • 用来例化和配置reg中各个field
  • 如何定义uvm_reg?
    • 是一个class,各个子类继承于它。子类(控制寄存器+只读寄存器)在完成field的声明后需要new函数和build函数(例化与配置都在build中)
  • 声明,例化和配置field的语句是什么?
	uvm_reg_field reserved;
	rand uvm_reg_field pkt_len;//声明域,且可以随机化
	rand uvm_reg_field prio_level;
	rand uvm_reg_field chnl_en;

	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);
  • 什么是uvm_reg_block?
    • 一个class包含了map,rgm和mem
  • 什么是uvm_reg_adapter?
    • 完成rgm到硬件reg的桥接,将reg_item转换成总线UVC需要的信息bus_seq_item,再由总线给到硬件reg
  • 如何编写adapter?
    • 主要是定义2个reg和bus之间的函数
class reg2mcdf_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
  • 实现了adapter之后,如何将其集成到环境中?
    • 从test层传入寄存器模型rgm句柄,在顶层例化后通过uvm_config_db进行配置
    • rgm在创建后要调用build()函数,因为uvm_reg_block是object类型,其预定义的build()不会自动执行
    • 顶层连接时,需要将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();//还没集成predictor,所以调用set_auto_predict(),采用auto prediction方式
		reg2mcdf=reg2mcdf_adapter::type_id::create("");//例化adapter
	endfunction
	
	function void connect_phase(u p);
		rgm.map.set_sequencer(agent.sequencer, reg2mcdf);//!连接:将adapter连接到sequencer上
	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
  • uvm_reg_adapter和uvm_reg_block的关系?
    • block>rgm>map>adapter
  • uvm_reg_predictor是什么?
    • 跟踪寄存器值,分为自动预测ap和显式预测ep
    • 显式预测ep其实就是在物理总线上用monitor来捕捉trans,通过analysis port传递给在外部例化的predictor(集成在顶层环境),集成时需要将adapter和map的句柄也传给predictor
//和上一段代码的差别并不大,注释的部分就是新加的
class mcdf_bus_env extends uvm_env;
	mcdf_bus_agent agent;
	mcdf_rgm rgm;
	reg2mcdf_adapter reg2mcdf;
	uvm_reg_predictor mcdf2reg_predictor;//先声明
	...
	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();
		mcdf2reg_predictor=uvm_reg_predictor::type_id::create("",this);//例化
		reg2mcdf=reg2mcdf_adapter::type_id::create("");
		mcdf2reg_predictor.map=rgm.map;//把rgm的map给到predictor的map
		mcdf2reg_predictor.adapter=reg2mcdf;//把rgm的adapter给到predictor的map
	endfunction
	
	function void connect_phase(u p);
		rgm.map.set_sequencer(agent.sequencer, reg2mcdf);
		agent.monitor.ap.connect(mcdf2reg_predictor.bus_in);//!连接:将adapter连接到sequencer上
	endfunction
endclass
  • adapter,block,predictor三者的关系是什么?
    • 见示意图

 

  • 如何用uvm_reg的操作方式去发送reg序列?
  • 如何用内建寄存器序列做全面的reg测试?
    • 在body()中例化内建序列,将rgm给到内建序列的model并调用序列的start即可,参数是m_sequencer
    • 放置功能覆盖率covergroup在rgm中,通过reg model generator的功能可以使生成的rgm模型自动包含各个field的功能覆盖率,rgm已经内置了方法来使能covergroup,同时调用读写方法时会自动调用covergroup::sample()完成功能覆盖率的收集
class mcdf_example_seq extends uvm_reg_sequence;
	mcdf_rgm rgm;
	`uvm_object_utils()
	`uvm_declare_p_sequencer()
	...
	task body();
		uvm_status_e status;
		uvm_reg_data_t data;
		uvm_reg_hw_reset_seq reg_rst_seq=new();//先例化
		uvm_reg_bit_bash_seq reg_bit_bas_seq=new();
		uvm_reg_access_seq reg_acc_seq=new();
		if(!uvm_config_db#(mcdf_rgm)::get(null, get_full_name(), "rgm", rgm)) begin
			`uvm_error() end
		@(negedge p_sequencer.vif.rstn);
		@(posedge p_sequencer.vif.rstn);
		`uvm_info()
		reg_rst_seq.model=rgm;//给模型
		reg_rst_seq.start(m_sequencer);//start即可
		`uvm_info()
		`uvm_info()
		reg_bit_bash_seq.model=rgm;
		reg_bit_bash_seq.start(m_sequencer);
		`uvm_info()
		`uvm_info()
		reg_acc_seq.model=rgm;
		reg_acc_seq.start(m_sequencer);
		`uvm_info()
	endtask
endclass
  • reg功能覆盖率收集方式1:内部自动收集
//缺点:默认采样所有field,不够灵活不够智能
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;

	covergroup value_cg;//定义一个覆盖组
			option.per_instance=1;
			reserved: coverpoint reserved.value[25:0];
			pkt_len: coverpoint pkt_len.value[2:0];
			prio_level: coverpoint prio_level.value[1:0];
			chnl_en: coverpoint chnl_en.value[0:0];
	endgroup

	function new(string name ="ctrl_reg");
		super.new(name, 32, UVM_CVR_ALL);//UVM_NO_COVERAGE改为UVM_CVR_ALL
		set_coverage(has_coverage(UVM_CVR_FIELD_VALS)); begin
	//has_coverage()查询ctrl_reg::m_has_cover是否具备特定的覆盖率类型,判断是否需要例化
	//是否具备对应的covergroup?->是否例化?
	//由于上面new已经是UVM_CVR_ALL,所以可以例化
			value_cg=new(); end//covergroup例化
	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

	function void sample(uvm_reg_data_t data,//理解为读写方法的回调函数
											 uvm_reg_data_t byte_en,
											 bit is_read,
											 uvm_reg_map ,ap);
		super.sample(data, byte_en, is_read, map);
		sample_values();
	endfunction

	function void sample_values();//外部调用时ctrl_reg::sample_values()
		super.sample_values();
		if(get_coverage(UVM_CVR_FIELD_VALS)) begin
	//是否允许使用对应的covergroup进行采样,->是否采样?
			value_cg.sample(); end
		endfunction
endclass
  • reg功能覆盖率收集方式2:事件触发外部收集
//定义一个覆盖率class
class mcdf_coverage extends uvm_subscriber #(mcdf_bus_trans);
//信息来自于mcdf_bus_monitor,通过其uvm_analysis_port发到
//mcdf_coverage的uvm_analysis_export
	mcdf_rgm rgm;
	`uvm_component_utils(mcdf_coverage)

	covergroup reg_value_cg;
	option.per_instance=1;
	//指定了感兴趣的field和值范围
	CH0LEN: coverpoint rgm.chnl0_ctrl_reg.pkt_len.value[2:0] {bins len[]={0,1,2,3,[4:7]};}
	CH0PRI: coverpoint rgm.chnl0_ctrl_reg.prio_level.value[1:0];
	CH0CEN: coverpoint rgm.chnl0_ctrl_reg.chnl_en.value[0:0];
	CH1LEN: coverpoint rgm.chnl1_ctrl_reg.pkt_len.value[2:0] {bins len[]={0,1,2,3,[4:7]};}
	CH1PRI: coverpoint rgm.chnl1_ctrl_reg.prio_level.value[1:0];
	CH1CEN: coverpoint rgm.chnl1_ctrl_reg.chnl_en.value[0:0];
	CH2LEN: coverpoint rgm.chnl2_ctrl_reg.pkt_len.value[2:0] {bins len[]={0,1,2,3,[4:7]};}
	CH2PRI: coverpoint rgm.chnl2_ctrl_reg.prio_level.value[1:0];
	CH2CEN: coverpoint rgm.chnl2_ctrl_reg.chnl_en.value[0:0];
	CH0AVL: coverpoint rgm.chnl0_stat_reg.fifo_avail.value[7:0] {bins avail[]={0,1,[2:7],[8:55],[56:61], 62, 63};}
	CH1AVL: coverpoint rgm.chnl1_stat_reg.fifo_avail.value[7:0] {bins avail[]={0,1,[2:7],[8:55],[56:61], 62, 63};}
	CH2AVL: coverpoint rgm.chnl2_stat_reg.fifo_avail.value[7:0] {bins avail[]={0,1,[2:7],[8:55],[56:61], 62, 63};}
	LEN_COMB: cross CH0LEN, CH1LEN, CH2LEN;//cross是干嘛的?待查
	PRI_COMB: cross CH0PRI, CH1PRI, CH2PRI;
	CEN_COMB: cross CH0CEN, CH1CEN, CH2CEN;
	AVL_COMB: cross CH0AVL, CH1AVL, CH2AVL;
	endgroup

	function new(string name, uvm_component parent);//这个是component,要有parent
		if(!uvm_config_db #(mcdf_rgm)::get(this, "", "rgm", rgm)) begin
			`uvm_info() end
		reg_value_cg=new();
	endfunction
	
	function void write(T t);
		reg_value_cg.sample();
	endfunction
endclass
  • 寄存器模型rgm是什么?

    • 用软件描述了硬件的寄存器模型,类比mcdf的model,这里就是由3个控制寄存器+3个只读寄存器构成的软件模型。包含的信息一样
  • rgm包含了什么?

    声明所有的寄存器+map,class必有new函数。在build函数对所有reg进行例化,配置,build(),对map例化,加入寄存器到map

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
  • 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

实验任务

1.1 实现reg2mcdf_adapter类的两个方法

这里是对应adapter的定义,主要就是两个方法的书写,一个是reg2bus,另一个是bus2reg,在做reg和总线的转换之间。

先复习一下之前的例码

	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

	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
		`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

实验5代码如下

class reg2mcdf_adapter extends uvm_reg_adapter;
    `uvm_object_utils(reg2mcdf_adapter)
    function new(string name = "reg2mcdf_adapter");
      super.new(name);
      provides_responses = 1;//一样需要置1
    endfunction
    function uvm_sequence_item reg2bus(const ref uvm_reg_bus_op rw);
      reg_trans t = reg_trans::type_id::create("t");//例化寄存器事务的句柄
      t.cmd = (rw.kind == UVM_WRITE) ? `WRITE : `READ;
      t.addr = rw.addr;//完全相同,判断cmd后将信息给到对象上
      t.data = rw.data;
      return t;
    endfunction
    function void bus2reg(uvm_sequence_item bus_item, ref uvm_reg_bus_op rw);
      reg_trans t;//声明
      if (!$cast(t, bus_item)) begin//总线句柄转换成子类reg事务句柄
        `uvm_fatal("CASTFAIL","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.data;
      rw.status = UVM_IS_OK;
    endfunction
  endclass

1.2 在mcdf_env中声明并例化block、adapter、predictor,最后connect起来

也就是集成到env中,先声明,build_phase中创建对象并传递对应地图,最后connect_phase连接ap端口。

先复习下例码的继承adapter+predictor

//和上一段代码的差别并不大,注释的部分就是新加的
class mcdf_bus_env extends uvm_env;
	mcdf_bus_agent agent;
	mcdf_rgm rgm;
	reg2mcdf_adapter reg2mcdf;
	uvm_reg_predictor mcdf2reg_predictor;//先声明
	...
	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();
		mcdf2reg_predictor=uvm_reg_predictor::type_id::create("",this);//例化
		reg2mcdf=reg2mcdf_adapter::type_id::create("");
		mcdf2reg_predictor.map=rgm.map;//把rgm的map给到predictor的map
		mcdf2reg_predictor.adapter=reg2mcdf;//把rgm的adapter给到predictor的map
	endfunction
	
	function void connect_phase(u p);
		rgm.map.set_sequencer(agent.sequencer, reg2mcdf);
		agent.monitor.ap.connect(mcdf2reg_predictor.bus_in);//!连接:将adapter连接到sequencer上
	endfunction
endclass

实验5代码如下:

class mcdf_env extends uvm_env;
    chnl_agent chnl_agts[3];
    reg_agent reg_agt;
    fmt_agent fmt_agt;
    mcdf_checker chker;
    mcdf_coverage cvrg;
    mcdf_virtual_sequencer virt_sqr;
    mcdf_rgm rgm;//声明三者的句柄
    reg2mcdf_adapter adapter;
    uvm_reg_predictor #(reg_trans) predictor;

    `uvm_component_utils(mcdf_env)

    function new (string name = "mcdf_env", uvm_component parent);
      super.new(name, parent);
    endfunction

    function void build_phase(uvm_phase phase);
      super.build_phase(phase);
      this.chker = mcdf_checker::type_id::create("chker", this);
      foreach(chnl_agts[i]) begin
        this.chnl_agts[i] = chnl_agent::type_id::create($sformatf("chnl_agts[%0d]",i), this);
      end
      this.reg_agt = reg_agent::type_id::create("reg_agt", this);
      this.fmt_agt = fmt_agent::type_id::create("fmt_agt", this);
      this.cvrg = mcdf_coverage::type_id::create("cvrg", this);
      virt_sqr = mcdf_virtual_sequencer::type_id::create("virt_sqr", this);
      //进行例化,注意rgm还要手动调用build
      rgm = mcdf_rgm::type_id::create("rgm", this);
      rgm.build();
      adapter = reg2mcdf_adapter::type_id::create("adapter", this);
      predictor = uvm_reg_predictor#(reg_trans)::type_id::create("predictor", this);
    endfunction

		function void connect_phase(uvm_phase phase);
      super.connect_phase(phase);
      chnl_agts[0].monitor.mon_bp_port.connect(chker.chnl0_bp_imp);
      chnl_agts[1].monitor.mon_bp_port.connect(chker.chnl1_bp_imp);
      chnl_agts[2].monitor.mon_bp_port.connect(chker.chnl2_bp_imp);
      reg_agt.monitor.mon_bp_port.connect(chker.reg_bp_imp);
      fmt_agt.monitor.mon_bp_port.connect(chker.fmt_bp_imp);
      virt_sqr.reg_sqr = reg_agt.sequencer;
      virt_sqr.fmt_sqr = fmt_agt.sequencer;
      foreach(virt_sqr.chnl_sqrs[i]) virt_sqr.chnl_sqrs[i] = chnl_agts[i].sequencer;
		  //进行连接
      rgm.map.set_sequencer(reg_agt.sequencer, adapter);
			//连接adapter到reg的agent中的sequencer。rgm的连接时调用map中的set_sequencer
      reg_agt.monitor.mon_ana_port.connect(predictor.bus_in);
			//monitor的ap连接到predictor的bus_in
      predictor.map = rgm.map;//rgm的map和adapter配置到predictor
      predictor.adapter = adapter;
      //TODO-2.1 connect the virtual sequencer's rgm handle with rgm object
      virt_sqr.rgm = rgm;
    endfunction
  endclass: mcdf_env

2.1 reg block句柄的传递

在上面例码中的connect_phase,将rgm对象给到vser中的rgm句柄

在vser类mcdf_base_virtual_sequence中,将p_sequencer的rgm赋给rgm句柄

class mcdf_base_virtual_sequence extends uvm_sequence;
    idle_reg_sequence idle_reg_seq;
    write_reg_sequence write_reg_seq;
    read_reg_sequence read_reg_seq;
    chnl_data_sequence chnl_data_seq;
    fmt_config_sequence fmt_config_seq;
    mcdf_rgm rgm;

    `uvm_object_utils(mcdf_base_virtual_sequence)
    `uvm_declare_p_sequencer(mcdf_virtual_sequencer)

    function new (string name = "mcdf_base_virtual_sequence");
      super.new(name);
    endfunction

    virtual task body();
      `uvm_info(get_type_name(), "=====================STARTED=====================", UVM_LOW)
      //TODO-2.1 connect rgm handle
      rgm = p_sequencer.rgm;

      this.do_reg();
      this.do_formatter();
      this.do_data();

      `uvm_info(get_type_name(), "=====================FINISHED=====================", UVM_LOW)
    endtask

    // do register configuration
    virtual task do_reg();
      //User to implment the task in the child virtual sequence
    endtask

    // do external formatter down stream slave configuration
    virtual task do_formatter();
      //User to implment the task in the child virtual sequence
    endtask

    // do data transition from 3 channel slaves
    virtual task do_data();
      //User to implment the task in the child virtual sequence
    endtask

    virtual function bit diff_value(int val1, int val2, string id = "value_compare");
      if(val1 != val2) begin
        `uvm_error("[CMPERR]", $sformatf("ERROR! %s val1 %8x != val2 %8x", id, val1, val2)) 
        return 0;
      end
      else begin
        `uvm_info("[CMPSUC]", $sformatf("SUCCESS! %s val1 %8x == val2 %8x", id, val1, val2), UVM_LOW)
        return 1;
      end
    endfunction
  endclass

2.2 mcdf_data_consistence_basic_virtual_sequence类中原来是由总线seq实现的reg读写,现在改为又rgm操作的reg读写方式

总线seq实现reg读写是利用了uvm提供的`uvm_do_on_with(),不知道是不是宏,这里还要等再复习一下

class mcdf_data_consistence_basic_virtual_sequence extends mcdf_base_virtual_sequence;
    `uvm_object_utils(mcdf_data_consistence_basic_virtual_sequence)
    function new (string name = "mcdf_data_consistence_basic_virtual_sequence");
      super.new(name);
    endfunction
    task do_reg();
      bit[31:0] wr_val, rd_val;
uvm_status_e status;//这里多声明了status
      // slv0 with len=8,  prio=0, en=1
      wr_val = (1<<3)+(0<<1)+1;
      //`uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV0_RW_ADDR; data == wr_val;})
      //`uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV0_RW_ADDR;})
//将原来用do on with做的换成rgm的read和write
rgm.chnl0_ctrl_reg.write(status, wr_val);
rgm.chnl0_ctrl_reg.read(status, rd_val);
      //rd_val = read_reg_seq.data;
      void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));

      // slv1 with len=16, prio=1, en=1
      wr_val = (2<<3)+(1<<1)+1;
      //`uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV1_RW_ADDR; data == wr_val;})
      //`uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV1_RW_ADDR;})
      //rd_val = read_reg_seq.data;
rgm.chnl1_ctrl_reg.write(status, wr_val);
rgm.chnl1_ctrl_reg.read(status, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV1_WR_REG"));

      // slv2 with len=32, prio=2, en=1
      wr_val = (3<<3)+(2<<1)+1;
      //`uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV2_RW_ADDR; data == wr_val;})
      //`uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV2_RW_ADDR;})
      //rd_val = read_reg_seq.data;
rgm.chnl2_ctrl_reg.write(status, wr_val);
rgm.chnl2_ctrl_reg.read(status, rd_val);
      void'(this.diff_value(wr_val, rd_val, "SLV2_WR_REG"));

      // send IDLE command
      `uvm_do_on(idle_reg_seq, p_sequencer.reg_sqr)
    endtask
    task do_formatter();
      `uvm_do_on_with(fmt_config_seq, p_sequencer.fmt_sqr, {fifo == LONG_FIFO; bandwidth == HIGH_WIDTH;})
    endtask
    task do_data();
      fork
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[0], {ntrans==100; ch_id==0; data_nidles==0; pkt_nidles==1; data_size==8; })
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[1], {ntrans==100; ch_id==1; data_nidles==1; pkt_nidles==4; data_size==16;})
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[2], {ntrans==100; ch_id==2; data_nidles==2; pkt_nidles==8; data_size==32;})
      join
      #10us; // wait until all data haven been transfered through MCDF
    endtask
  endclass: mcdf_data_consistence_basic_virtual_sequence

2.3 mcdf_full_random_virtual_sequence做相同的操作

class mcdf_full_random_virtual_sequence extends mcdf_base_virtual_sequence;
    `uvm_object_utils(mcdf_base_virtual_sequence)
    function new (string name = "mcdf_base_virtual_sequence");
      super.new(name);
    endfunction
//最顶格开始的为修改代码
    task do_reg();
      //bit[31:0] wr_val, rd_val;
bit[31:0] ch0_wr_val;//换成三个,不需要rd_val
bit[31:0] ch1_wr_val;
bit[31:0] ch2_wr_val;
uvm_status_e status;
rgm.reset();//重启reg block
// slv0 with len={4,8,16,32},  prio={[0:3]}, en={[0:1]}原来是要*3
      //wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
      //`uvm_do_on_with(write_reg_seq, p_sequencer.reg_sqr, {addr == `SLV0_RW_ADDR; data == wr_val;})
      //`uvm_do_on_with(read_reg_seq, p_sequencer.reg_sqr, {addr == `SLV0_RW_ADDR;})
      //rd_val = read_reg_seq.data;
      //void'(this.diff_value(wr_val, rd_val, "SLV0_WR_REG"));
ch0_wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
ch1_wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
ch2_wr_val = ($urandom_range(0,3)<<3)+($urandom_range(0,3)<<1)+$urandom_range(0,1);
rgm.chnl0_ctrl_reg.set(ch0_wr_val);
rgm.chnl1_ctrl_reg.set(ch1_wr_val);//通过uvm_reg::set()设置rgm中reg的wr_val
rgm.chnl2_ctrl_reg.set(ch2_wr_val);
rgm.update(status);//通过uvm_reg_block::update()刷新rgm数据
#100ns;//等待dut中update完成
rgm.chnl0_ctrl_reg.mirror(status, UVM_CHECK, UVM_BACKDOOR);//后门访问获取reg值
rgm.chnl1_ctrl_reg.mirror(status, UVM_CHECK, UVM_BACKDOOR);//比较所有读写值
rgm.chnl2_ctrl_reg.mirror(status, UVM_CHECK, UVM_BACKDOOR);
      // send IDLE command
      `uvm_do_on(idle_reg_seq, p_sequencer.reg_sqr)
    endtask
    task do_formatter();
      `uvm_do_on_with(fmt_config_seq, p_sequencer.fmt_sqr, {fifo inside {SHORT_FIFO, ULTRA_FIFO}; bandwidth inside {LOW_WIDTH, ULTRA_WIDTH};})
    endtask
    task do_data();
      fork
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[0], 
          {ntrans inside {[400:600]}; ch_id==0; data_nidles inside {[0:3]}; pkt_nidles inside {1,2,4,8}; data_size inside {8,16,32};})
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[1], 
          {ntrans inside {[400:600]}; ch_id==0; data_nidles inside {[0:3]}; pkt_nidles inside {1,2,4,8}; data_size inside {8,16,32};})
        `uvm_do_on_with(chnl_data_seq, p_sequencer.chnl_sqrs[2], 
          {ntrans inside {[400:600]}; ch_id==0; data_nidles inside {[0:3]}; pkt_nidles inside {1,2,4,8}; data_size inside {8,16,32};})
      join
      #10us; // wait until all data haven been transfered through MCDF
    endtask
  endclass: mcdf_full_random_virtual_sequence

3.1 利用寄存器的内建序列做测试

用到的三个内建序列 -uvm_reg_hw_reset_seq -uvm_reg_bit_bash_seq -uvm_reg_access_seq

先复习一下例码,在body中例化对应序列后,将rgm给到序列的model,然后调用序列的start(参数是m_sequencer,应该是说对应的user自己的寄存器sequencer)

class mcdf_example_seq extends uvm_reg_sequence;
	mcdf_rgm rgm;
	`uvm_object_utils()
	`uvm_declare_p_sequencer()
	...
	task body();
		uvm_status_e status;
		uvm_reg_data_t data;
		uvm_reg_hw_reset_seq reg_rst_seq=new();
		uvm_reg_bit_bash_seq reg_bit_bas_seq=new();
		uvm_reg_access_seq reg_acc_seq=new();
		if(!uvm_config_db#(mcdf_rgm)::get(null, get_full_name(), "rgm", rgm)) begin
			`uvm_error() end
		@(negedge p_sequencer.vif.rstn);
		@(posedge p_sequencer.vif.rstn);
		`uvm_info()
		reg_rst_seq.model=rgm;
		reg_rst_seq.start(m_sequencer);
		`uvm_info()
		`uvm_info()
		reg_bit_bash_seq.model=rgm;
		reg_bit_bash_seq.start(m_sequencer);
		`uvm_info()
		`uvm_info()
		reg_acc_seq.model=rgm;
		reg_acc_seq.start(m_sequencer);
		`uvm_info()
	endtask
endclass

实验5代码如下

class mcdf_reg_builtin_virtual_sequence extends mcdf_base_virtual_sequence;
    `uvm_object_utils(mcdf_reg_builtin_virtual_sequence)
    function new (string name = "mcdf_reg_builtin_virtual_sequence");
      super.new(name);
    endfunction

    task do_reg();//这里是do_reg不是body?回头复习整理时查查
      uvm_reg_hw_reset_seq reg_rst_seq = new(); //一样的先例化内建序列
      uvm_reg_bit_bash_seq reg_bit_bash_seq = new();
      uvm_reg_access_seq reg_acc_seq = new();

      // 等待复位信号
      @(negedge p_sequencer.intf.rstn);
      @(posedge p_sequencer.intf.rstn);

      `uvm_info("BLTINSEQ", "register reset sequence started", UVM_LOW)
      rgm.reset();//先调用reset
      reg_rst_seq.model = rgm;
      reg_rst_seq.start(p_sequencer.reg_sqr);//这里的参数是p_sequencer的reg_sqr?
      `uvm_info("BLTINSEQ", "register reset sequence finished", UVM_LOW)

      `uvm_info("BLTINSEQ", "register bit bash sequence started", UVM_LOW)
      // 在执行下个序列前先复位
      p_sequencer.intf.rstn <= 'b0;
      repeat(5) @(posedge p_sequencer.intf.clk);
      p_sequencer.intf.rstn <= 'b1;

      rgm.reset();
      reg_bit_bash_seq.model = rgm;
      reg_bit_bash_seq.start(p_sequencer.reg_sqr);
      `uvm_info("BLTINSEQ", "register bit bash sequence finished", UVM_LOW)

      `uvm_info("BLTINSEQ", "register access sequence started", UVM_LOW)
      p_sequencer.intf.rstn <= 'b0;
      repeat(5) @(posedge p_sequencer.intf.clk);
      p_sequencer.intf.rstn <= 'b1;
      rgm.reset();
      reg_acc_seq.model = rgm;
      reg_acc_seq.start(p_sequencer.reg_sqr);
      `uvm_info("BLTINSEQ", "register access sequence finished", UVM_LOW)
    endtask
  endclass: mcdf_reg_builtin_virtual_sequence
  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值