UVM实验2

1. 代码讲解

1.1 问题1:为什么要用$cast( ),以及为什么要用void’( )

task do_drive();
		chnl_trans req, rsp;
		@(posedge intf.rstn);
		forever begin	
			this.req_mb.get(req);
			this.chnl_write(req);
			void'($cast(rsp, req.clone()));
			//req.clone调用的核心基类的方法,永远返回的是uvm_object类型(父类),但指向的是子类对象,所有转化可以成功
			rsp.rsp = 1;
			this.rsp_mb.put(rsp);
		end
endtask

req是通过uvm的type_id::create创建的,在创建时做了域的自动化的声明,所以可以直接用核心基类里的方法,比如clong。但需要注意的是,req.clong返回的句柄是uvm_object类型(父类),但指向的是子类对象,所以赋值给子类rsp时需要转化,同时转化可以成功。
PS:调用clong方法会新创建一个对象,复制req到新创建的对象中,并返回新创建对象的句柄)

使用void’( )的原因:避免出现warning
许多函数都有返回值,比如$cast( ),在进行类型转化的同时会返回1,或者0,表示转化成功或者失败,此时使用void’( )可以把转化值变为空,告诉编译器不用理会返回值;如果不使用void’( ),编译器会有warning

1.2 问题2:使用type_id::create创建组件的好处

	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		driver = fmt_driver::type_id::create("driver", this);
		monitor = fmt_monitor::type_id::create("monitor", this);
	endfunction

创建组件的时候一定要用type_id::create,因为它不但提供了做override的可能,更重要的是它实现了一个字符串的层次化,字符串的层次化有助于我们配置对象

继承于uvm_object和uvm_component的类建议都用create创建。但是端口类port继承于uvm_void,无法用type_id::create创建(uvm_void里没有该方法),port在后面的通信章节里会讲

1.3 问题3:当使用set_interface将接口传递给底层组件时,为什么需要在connect_phase里使用set_interface连接,而不能在build_phase里连接?

clas mcdf_base_test extends uvm_test;
	//第一部分
	chnl_generator chnl_gens[3];
	reg_generator reg_gen;
	fmt_generator fmt_gen;
	mcdf_env env;
	local int timeout = 10; // 10 * ms
	virtual chnl_intf ch0_vif;
	virtual chnl_intf ch1_vif;
	virtual chnl_intf ch2_vif;
	virtual reg_intf reg_vif;
	virtual arb_intf arb_vif;
	virtual fmt_intf fmt_vif;
	virtual mcdf_intf mcdf_vif;
		
	`uvm_component_utils(mcdf_base_test)
		
	function new(string name = "mcdf_base_test", uvm_component parent);
		super.new(name, parent);
	endfunction
		
	//第二部分	
	function void build_phase(uvm_phase);
		super.build_phase(phase);
		// get virtual interface from top TB
		if(!uvm_config_db#(virtual chnl_intf)::get(this, "", "ch0_vif", ch0_vif)) begin
			`uvm_fatal("GETVIF", "cannot get vif handle from config DB")
		end
		if(!uvm_config_db#(virtual chnl_intf)::get(this, "", "ch1_vif", ch1_vif)) begin
			`uvm_fatal("GETVIF", "cannot get vif handle from config DB")
		end
		if(!uvm_config_db#(virtual chnl_intf)::get(this, "", "ch2_vif", ch2_vif)) begin
			`uvm_fatal("GETVIF", "cannot get vif handle from config DB")
		end
		if(!uvm_config_db#(virtual reg_intf)::get(this, "", "reg_vif", reg_vif)) begin
			`uvm_fatal("GETVIF", "cannot get vif handle from config DB")
		end
		if(!uvm_config_db#(virtual arb_intf)::get(this, "", "arb_vif", arb_vif)) begin
			`uvm_fatal("GETVIF", "cannot get vif handle from config DB")
		end
		if(!uvm_config_db#(virtual fmt_intf)::get(this, "", "fmt_vif", ch2_vif)) begin
			`uvm_fatal("GETVIF", "cannot get vif handle from config DB")
		end
		if(!uvm_config_db#(virtual mcdf_intf)::get(this, "", "mcdf_vif", mcdf_vif)) begin
			`uvm_fatal("GETVIF", "cannot get vif handle from config DB")
		end
			
		this.env = mcdf_env::type_id::create("env", this);
		foreach(this.chnl_gen[i]) begin	
			this.chnl_gens[i] = chnl_generator::type_id::create($sformatf("chnl_gens[%0d]", i), this);
			end
			this.reg_gen = reg_generator::type_id::create("reg_gen", this);
			this.fmt_gen = fmt_generator::type_id::create("fmt_gen", this);
				
			// rpt_pkg::logname = {this.m_name, "_check.log"};
			// rpt_pkg::clean_log();
	endfunction
			
	//第三部分	
	function void connect_phase(uvm_phase phase);
		super.connect_phase(phase);
		// After get virtual interface from config_db, and then set them to
		// child components 
		this.set_interface(ch0_vif, ch1_vif, ch2_vif, reg_vif, arb_vif, fmt_vif, mcdf_vif);
				
		foreach(this.chnl_gens[i]) begin
			this.env.chnl_agts[i].driver.req_mb = this.chnl_gens[i].req_mb;
			this.env.chnl_agts[i].driver.rsp_mb = this.chnl_gens[i].rsp_mb;
		end
		this.env.reg_agt.driver.req_mb = this.reg_gen.req_mb;
		this.env.reg_agt.driver.rsp_mb = this.reg_gen.rsp_mb;
		this.env.fmt_agt.driver.req_mb = this.fmt_gen.req_mb;
		this.env.fmt_agt.driver.rsp_mb = this.fmt_gen.rsp_mb;
	endfunction
		
	......
	//第三部分,set_interface方法的补充
	virtual function void set_interface(virtual chnl_intf ch0_vif
										   ,virtual chnl_intf ch1_vif
										   ,virtual chnl_intf ch2_vif
										   ,virtual reg_intf reg_vif
										   ,virtual arb_intf arb_vif
									       ,virtual fmt_intf fmt_vif
									       ,virtual mcdf_intf mcdf_vif
									   );
		this.env.chnl_agts[0].set_interface(ch0_vif);
		this.env.chnl_agts[1].set_interface(ch1_vif);
		this.env.chnl_agts[2].set_interface(ch2_vif);
		this. env.reg_agt.set_interface(reg_vif);
		this. env.fmt_agt.set_interface(fmt_vif);
		this.env.chker.set_interface(mcdf_vif, '{ch0_vif, ch1_vif, ch2_vif}, arb_vif);
		this.env.cvrg.set_interface('{ch0_vif, ch1_vif, ch2_vif}, reg_vif, arb_vif, fmt_vif, mcdf_vif);
	endfunction

对于接口来说:将上面的代码分为了三个部分,第一部分是对接口的声明,第二部分是在mcdf_base_test环境里得到tb层配置下来的接口,第三部分是将test层的接口传递到下面的各个子组件。

set_interface方法(将test里的接口传递到后续层次的组件中)在connect_phase里使用的原因是,在build_phase里环境还没有完成建立,build_phase是一个自顶向下的建立过程,test以下的组件都还没有建立,接口无法传递,在build_phase里只能先用config_db去得到tb层配置下的接口。在test或者env的build_phase阶段只能得到接口(config_db(# T)::get),不能往下传递接口(set_interface)

1.4 问题4:使用uvm的run_test时,在tb里只能通过config_db(#T)::set将接口配置给test或者env,而不能通过set_interface函数去传递接口的原因?

initial begin
	//do interface configuration from top tb (HW) yo verification env (SW)
	uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(),"uvm_test_top", "ch0_vif", chnl0_if);
	uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(),"uvm_test_top", "ch1_vif", chnl1_if);
	uvm_config_db#(virtual chnl_intf)::set(uvm_root::get(),"uvm_test_top", "ch2_vif", chnl2_if);
	uvm_config_db#(virtual reg_intf)::set(uvm_root::get(),"uvm_test_top", "reg_vif", reg_if);
	uvm_config_db#(virtual arb_intf)::set(uvm_root::get(),"uvm_test_top", "arb_vif", arb_if);
	uvm_config_db#(virtual fmt_intf)::set(uvm_root::get(),"uvm_test_top", "fmt_vif", fmt_if);
	uvm_config_db#(virtual mcdf_intf)::set(uvm_root::get(),"uvm_test_top", "mcdf_vif", mcdf_if);
		
	//If no external configured via +UVM_TESTNAME=my_test, the default test is mcdf_data_consistence_basic_test
	run_test("mcdf_data_consistence_basic_test");
	......

因为使用set_interface时需要各个组件的句柄,如果将set_interphase放在run_test之前,则当执行set_interface函数时,run_test还没有执行,组件还没有建立;
如果将set_interface放在run_test之后,接口还没有传递,程序开始执行就会报错。所以需要用config_db去配置接口。

2 使用VCS理解验证层次和执行顺序

2.1 验证层次

打开vcs —— run0
Hierarchy:查看设计的层次 + import导入的package。
object:显示UVM的层次结构
UVM Debug图标

点击UVM Debug图标
Resource:查看uvm_config_db::{set和get}的变量配置
phase(用的非常多):阶段调试,告诉你所处阶段,以及objection的挂起调试
此时处于 run 0 的阶段,即仿真刚刚开始,从图中可以看出,此时组件的创建以及连接已经完成,开始进入run_phase阶段

在run 0 时刻,组件已经建立,可以利用object窗口查看层次结构了,但是需要选择UVM Component选项,选择后可看到此时各组件的层次结构。
如果选择UVM Object选项,会看到uvm_test类里面原有的一些类型,不方便我们了解层次结构,所以不用管。

查看组件的成员变量和方法:例如选中refmod,在member窗口中查看成员变量和方法,此时要取消勾选Base member,否则会出现许多refmod的父类成员和变量,不利于我们观察

通过watch窗口时刻关注某一个类的变化

先搜索需要观察的类,再通过class窗口观察某一个类的层次结构

2.2 执行顺序

通过设断点查看run_phase执行顺序,在各个组件的run_phase里设置断点,然后点run(不要点run 0,因为run_phase里有些方法也是不耗时的,run 0之后会直接执行完该方法,那么我们就无法定位到该方法了)
通过设断点可以知道各组件run_phase是并行执行的,0时刻各组件都有相应的task在执行

3. 源代码

以下代码均来源于路科验证

package chnl_pkg;
	import uvm_pkg::*;
	`include "uvm_macros.svh"
	
	// channel sequence item
	class chnl_trans extends uvm_sequence_item;//建议在定义sequence item类时,完成域的自动化声明
		rand bit[31:0] data[];
		rand int ch_id;
		rand int pkt_id;
		rand int data_nidles;
		rand int pkt_nidles;
		bit rsp;
	
		constraint cstr{
			soft data.size inside {[4:32]};
			foreach(data[i]) data[i] == 'hC000_0000 + (this.ch_id<<24) + (this.pkt_id<<8) + i;
			soft ch_id == 0;
			soft pkt_id == 0;
			soft data_nidles inside {[0:2]};
			soft pkt_nidles inside {[1:10]};
		};
		
		`uvm_object_utils_begin(chnl_trans)
			`uvm_filed_array_int(data, UVM_ALL_ON)
			`uvm_filed_int(ch_id, UVM_ALL_ON)
			`uvm_filed_int(pkt_id, UVM_ALL_ON)
			`uvm_filed_int(data_nidles, UVM_ALL_ON)
			`uvm_filed_int(pkt_nidles, UVM_ALL_ON)
			`uvm_filed_int(rsp, UVM_ALL_ON)
		`uvm_object_utils_end
	
		function new(string name = "chnl_trans");//组件的例化和连接放在对应的build和connect_phase里
			super.new(name);
		endfunction
		
		//NOTE:: field automation already implemented clone() method
		//function chnl_trans clone();
		//	chnl_trans c = new();
		//	c.data = this.data;
		//	c.ch_id = this.ch_id;
		//	c.data_nidles = this.data_nidles;
		//	c.pkt_nidles = this.pkt_nidles;
		//	c.rsp = this.rsp;
		//	return c;
		//endfunction
		
		//NOTE::field automation already implemented sprint() method
		//function string sprint();
		//	string s;
		//	s = {s, $sformatf("======================================\n")};
		//	s = {s, $sformatf("chnl_trans object content is as below: \n")};
		//	foreach(data[i]) s = {s, $sformatf("data[%0d] = %8x \n", i, this.data[i])};
		//	s = {s, $sformatf("ch_id = %0d \n", this.ch_id)};
		//	s = {s, $sformatf("pkt_id = %0d \n", this.pkt_id)};
		//	s = {s, $sformatf("data_nidles = %0d \n", this.data_nidles)};
		//	s = {s, $sformatf("pkt_nidles = %0d \n", this.pkt_nidles)};
		//	s = {s, $sformatf("rsp = %0d \n", this.rsp)};
		//	s = {s, $sformatf("======================================\n")};
		//	return s;
		//endfunction
	endclass:chnl_trans
	
	//channel_driver
	class chnl_driver extends uvm_driver #(chnl_trans);
		local virtual chnl_intf intf;
		mailbox #(chnl_trans) req_mb;
		mailbox #(chnl_trans) rsp_mb;
		
		`uvm_component_utils(chnl_driver)
		
		function new (string name = "chnl_driver", uvm_component parent);
			super.new(name, parent);
		endfunction
		
		function void set_interface(virtual chnl_intf intf);
			if(intf = null)
				$error("interface handle is NULL, please check if target interface has been intantiated");
			else
				this.intf = intf;
		endfunction
		
		task run_phase(uvm_phase phase);
			fork
				this.do_drive();
				this.do_reset();
			join
		endtask
		
		task do_reset();
			forever begin
				@(negedge intf.rstn);
				intf.ch_valid <= 0;
				intf.ch_data <= 0;
			end
		endtask
		
		task do_drive();
			chnl_trans req, rsp;
			@(posedge intf.rstn);
			forever begin	
				this.req_mb.get(req);
				this.chnl_write(req);
				void'($cast(rsp, req.clone()));
				//req.clone调用的核心基类的方法,永远返回的是uvm_object类型(父类),但指向的是子类对象,所有转化可以成功
				rsp.rsp = 1;
				this.rsp_mb.put(rsp);
			end
		endtask
		
		task chnl_write(input chnl_trans t);
			foreach(t.data[i]) begin
				@(posedge intf.clk);
				intf.drv_ck.ch_valid <= 1;
				intf.drv_ck.ch_data <= t.data[i];
				@(negedge intf.clk);
				wait(intf.ch_ready === 'b1);
				`uvm_info(get_type_name(), $sformatf("send data 'h%8x", t.data[i]),UVM_HIGH)
				repeat(t.data_nidles) chnl_idle();
			end
		endtask
		
		task chnl_idle();
			@(posedge intf.clk);
			intf.drv_ck.ch_valid <= 0;
			intf.drv_ck.ch_data <= 0;
		endtask
	endclass:chnl_driver
	
	//channel generator and to be replaced by sequence + sequencer later
	class chnl_generator extends uvm_component;
		rand int pkt_id = 0;
		rand int ch_id = -1;
		rand int data_nidles = -1;
		rand int pkt_nidles = -1;
		rand int data_size = -1;
		rand int ntrans = 10;
		
		mailbox #(chnl_trans) req_mb;
		mailbox #(chnl_trans) rsp_mb;
		
		constraint cstr{
			soft ch_id == -1;
			soft pkt_id == 0;
			soft data_size == -1;
			soft data_nidles == -1;
			soft pkt_nidles == -1;
			soft ntrans ==10;
		}
		
		`uvm_component_utils_begin(chnl_generator)
			`uvm_filed_int(pkt_id, UVM_ALL_ON)
			`uvm_filed_int(ch_id, UVM_ALL_ON)
			`uvm_filed_int(data_nidles, UVM_ALL_ON)
			`uvm_filed_int(pkt_nidles, UVM_ALL_ON)
			`uvm_filed_int(data_size, UVM_ALL_ON)
			`uvm_filed_int(ntrans, UVM_ALL_ON)
		`uvm_component_utils_end
		
		function new(string name = "chnl_generator", uvm_component parent);
			super.new(name, parent);
			this.req_mb = new();
			this.rsp_mb = new();
		endfunction
		
		task send_trans();
			chnl_trans req, rsp;
			req = chnl_trans::type_id::create("req");//继承于uvm_object和uvm_component类建议都用create创建
			//但是端口类port继承于uvm_void,无法用type_id::create创建,需要用new()
			assert(req.randomize with {local::ch_id >= 0 -> ch_id == local::ch_id;
									   local::pkt_id >= 0 -> pkt_id ==local::pkt_id;
									   local::data_nidles >= 0 -> data_nidles ==local::data_nidles;
									   local::pkt_nidles >= 0 -> pkt_nidles ==local::pkt_nidles;
									   local::data_size >= 0 -> data.size ==local::data_size;
									  })
				else $fatal("[RNDFALL] channel packet randomization failure");
			this.pkt_id++;
			`uvm_info(get_type_name(), req.sprint(), UVM_HIGH)
			this.req_mb.put(req);
			this.rsp_mb.get(rsp);
			`uvm_info(get_type_name(), rsp.sprint(), UVM_HIGH)
			assert(rsp.rsp)
				else $error("[RSPERR] %0t error response received!", $time);
		endtask
		
		function string sprint();
			string s;
			s = {s, $sformatf("======================================\n")};
			s = {s, $sformatf("chnl_generator object content is as below: \n")};
			s = {s, super.sprint()};//用super是避免与当前方法sprint同名
			//NOTE::field automation already implemented sprint() method
			//	s = {s, $sformatf("ntrans = %0d: \n", this.ntrans)};
			//	s = {s, $sformatf("ch_id = %0d \n", this.ch_id)};
			//	s = {s, $sformatf("pkt_id = %0d \n", this.pkt_id)};
			//	s = {s, $sformatf("data_nidles = %0d \n", this.data_nidles)};
			//	s = {s, $sformatf("pkt_nidles = %0d \n", this.pkt_nidles)};
			//	s = {s, $sformatf("data_size = %0d: \n", this.data_size)};
			s = {s, $sformatf("======================================\n")};
			return s;
		endfunction
		
		function void post_randomize();
			string s;
			s = {"AFTER RANDOMIZATION \n", this.sprint()};
			`uvm_info(get_type_name(), s, UVM_HIGH)
		endfunction
	endclass:chnl_generator
	
	typedef struct packed{
		bit[31:0] data;
		bit[1:0] id;
	} mon_data_t;
	
	//channel monitor
	class chnl_monitor extends uvm_component;
		local virtual chnl_intf intf;
		mailbox #(mon_data_t) mon_mb;
		
		`uvm_component_utils(chnl_monitor)
		
		function new(string name = "chnl_monitor", uvm_component parent);
			super.new(name, parent);
		endfunction
		
		function void set_interface(virtual chnl_intf intf);
			if(intf == null)
				$error("interface handle is NULL, please check if target interface has been intantiated");
			else	
				this.intf = intf;
		endfunction
		
		task run_phase(uvm_phase phase);
			this.mon_trans();
		endtask
		
		task mon_trans();
			mon_data_t m;
			forever begin	
				@(posedge intf.clk iff (intf.mon_ck.ch_valid === 'b1 && intf.mon_ck.ch_ready === 'b1));
				m.data = intf.mon_ck.ch_data;
				mon_mb.put(m);
				`uvm_info(get_type_name(), $sformatf("monitored channel data 'h%8x", m.data), UVM_HIGH)
			end
		endtask
	endclass:chnl_monitor	
	
	// channel agent
	class chnl_agent extends uvm_agent;
		chnl_driver driver;
		chnl_monitor monitor;
		local virtual chnl_intf vif;
		
		`uvm_component_utils(chnl_agent)
		
		function new(string name = "chnl_agent", uvm_component parent);
			super.new(name parent);
		endfunction
		
		function void build_phase(uvm_phase phase);
			super.build_phase(phase);
			driver = chnl_driver::type_id::create("driver", this);
			monitor = chnl_monitor::type_id::create("monitor", this);
		endfunction
		
		function void set_interface(virtual chnl_intf vif);
			this.vif = vif;
			driver.set_interface(vif);
			monitor.set_interface(vif);
		endfunction
		
		task run_phase(uvm_phase phase);//调用子类的run时省略,使用自己的方法时不能省略
			//NOTE::No more needs to call run manually
			//fork
			//	drive.run();
			//	monitor.run();
			//join
		endtask
	endclass:chnl_agent
	
endpackage
		

你可能感兴趣的:(SV学习,笔记,经验分享,材料工程)