ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

UVM入门与进阶学习笔记12——TLM2通信(2)

2022-01-24 22:33:26  阅读:168  来源: 互联网

标签:TLM2 12 进阶 callback uvm phase data event UVM


目录


同步通信元件

  • SV用来做线程同步的几种元件,它们分别是semaphore、event、mailbox。
  • 在UVM中,需要同步线程不再只局限于同一个对象中,还需要解决不同组件之间的线程同步问题。
  • 一旦线程同步要求发生在不同组件,这就要求组件间可通过某种方法实现同步。
  • 考虑到UVM组件封闭性原则,最好不要通过层次索引的形式在组件中索引公共的event或者semaphore,UVM为了解决封闭性的问题,定义了如下的类满足组件之间的同步要求:
    • uvm_event、uvm_event_pool和uvm_event_callback
    • uvm_barrier、uvm_barrier_pool
  • 这两组类分别用于服务两个组件之间的同步和多个组件之间的同步
  • 回调函数作为一种实现基类复用的手段,在UVM中也被进一步封装为一个类uvm_callback,不但具备普通回调函数可以在函数执行前后调用的特点,还增加了丰富的特性来完成层次化的调用。

uvm_event

uvm_event类相比于event:

  • event被->触发之后,会触发使用@等待该事件的对象,uvm_event通过trigger()触发,会触发使用wait_trigger()等待该事件的对象。
  • 如果要再次等待事件触发,event只需要再次使用->来触发,而uvm_event需要先通过reset()方法重置初始状态,再使用trigger()触发。
  • event无法携带更多信息,而uvm_event可通过trigger(T data=null)的可选参数,将伴随触发的数据对象都写入到该触发事件中,而等待该事件的对象可以通过方法wait_trigger_data(output T data)来获取事件触发时写入的数据对象。
  • event触发时无法直接触发回调函数,而uvm_event可通过add_callback(uvm_event_callback cb,bit append=1)函数添加回调函数。
  • event无法直接获取等待它的进程数目,而uvm_event可通过get_num_waiters()来获取等待它的进程数目。
  • 不同组件可以共享一个uvm_event,这不用通过跨层次传递uvm_event对象句柄实现共享,该共享方式通过uvm_event_pool(全局资源池)实现,它是uvm_object_string_pool #(T)的子类,可以生成和获取通过字符串索引的uvm_event对象;环境中的任何组件都可以从这个全局资源池获取共享的对象句柄,这就避免了组件之间的互相依赖。

uvm_event应用

class edata extends uvm_object;
	int data;
	`uvm_object_utils(edata)
	...
endclass
class ecb extends uvm_event_callback;
	`uvm_object_utils(ecb)
	...
	function bit pre_trigger(uvm_event e, uvm_object data=null);
		`uvm_info("EPRETRIG", $sformatf("before trigger event %s", e.get_name()), UVM_LOW)
		return 0;
	endfunction
	function void post_trigger(uvm_event e, uvm_object data=null);
		`uvm_info("EPOSTTRIG", $sformatf("after trigger event %s", e.get_name()), UVM_LOW)
	endfunction
endclass
class comp1 extends uvm_component;
	uvm_event e1;
	`uvm_component_utils(comp1)
	...
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		e1 = uvm_event_pool::get_global("e1"); //notice
	endfunction
	task run_phase(uvm_phase phase);
		edata d = new();
		ecb cb = new();
		d.data = 100;
		#10ns;
		e1.add_callback(cb);
		e1.trigger(d);
		`uvm_info("ETRIG", $sformatf("trigger sync event at %t ps", $time), UVM_LOW)
	endtask
endclass
class comp2 extends uvm_component;
	uvm_event e1;
	`uvm_component_utils(comp2)
	...
	function void build_phase(uvm_phase phase);
		super.build_phase(phase)
		e1 = uvm_event_pool::get_global("e1"); //若没有e1会自动创建
	endfunction
	task run_phase(uvm_phase phase);
		uvm_object tmp;
		edata d;
		`uvm_info("ESYNC", $sformatf("wait sync event at %t ps", $time), UVM_LOW)
		e1.wait_trigger_data(tmp);  //等待触发同时等待数据
		void'($cast(d, tmp));  //父类转为子类
		`uvm_info("ESYNC", $sformatf("get data %0d after sync at %t ps", d.data, $time), UVM_LOW)
	endtask
endclass
class env1 extends uvm_env;
	comp1 c1;
	comp2 c2;
	`uvm_component_utils(env1)
	...
endclass
输出结果:
UVM_INFO @0: reporter [RNTST] Running test test1...
UVM INFO @0: uvm_test_top.env.c2 [ESYNC] wait sync event at 0 ps
UVM INFO @10000: reporter [EPRETRIG] before trigger event e1
UVM INFO @10000: reporter [EPOSTRIG] after trigger event e1
UVM INFO @10000: uvm_test_top.env.c1 [ETRIG] trigger sync event at 10000 ps
UVM INFO @10000: uvm_test_top.env.c2 [ESYNC] get data 100 after sync at 10000 ps
  • 组件c1和c2之间完成了从c1到c2的同步,而且在同步过程中通过uvm_event e1传递数据edata并调用回调函数类ecb的pre_trigger()post_trigger()方法。
  • 无论有多少个组件,只要它们寻求同个名称的uvm_event,就可以共享该uvm_event对象。例如上面c1和c2通过uvm_event_pool::get_global("e1")获取同一个名称的uvm_event对象,即便该对象不存在,uvm_event_pool资源池也会在第一次调用get_global()函数时创建这样一个对象以供使用。
  • 如果要传递数据,用户可定义扩展于uvm_object的数据子类,并通过uvm_event: :trigger(T data =null)传递数据对象。而在等待uvm_event一侧的组件,则需要通过uvm_event: :wait_trigger_data(output T data)获取该对象。
  • 用户也可以扩展uvm_event_callback类,定义uvm_event被trigger前后的调用方法pre_trigger()post_trigger()。pre_trigger()需要有返回值,如果返回值为1则表示uvm_event不会被trigger,也不会再执行post_trigger()方法;如果返回值为0,则会继续trigger该事件对象。
  • 如果用户无法确定在等待事件之前uvm_event是否已经被trigger,那么用户还可通过方法wait_ptrigger()wait_ptrigger_data()完成等待。这样即便在调用事件等待方法之前该事件已经被触发,等待方法仍然不会被阻塞并且可以继续执行结束。

uvm_event总结

  • 组件之间的常规数据流向是通过TLM通信方法实现的,比如sequence与driver之间,或者monitor与scoreboard之间。
  • 有些时候数据传输是偶然触发的且需要立即响应,这时需要uvm_event。
  • uvm_event同时也解决了一个重要问题,即在一些uvm_object和uvm_component对象之间如果要发生同步但无法通过TLM完成数据传输,因为TLM传输必须是在组件和组件之间进行的
  • 如果要在sequence与sequence之间进行同步,或sequence与driver之间进行同步,就可以使用uvm_event实现。

uvm_barrier

  • 在SV模块中,多个线程的同步除了可以通过semaphore和mailbox来进行,也可以通过fork_join的结构控制语句块来控制整体的运行节奏;然而对于UVM环境中的多个独立组件,SV的这些方法都受到了作用域的局限。
  • UVM提供一个新类uvm_barrier对多个组件进行同步协调,同时为了解决组件独立运作的封闭性需要,也定义了新类uvm_barrier_pool全局管理这些uvm_barrier对象。
  • uvm_barrier_pool同之前的uvm_event_pool一样,也是基于通用参数类uvm_object_string_pool来定义的
typedef uvm_object_string_pool #(uvm_barrier) uvm_barrier_pool;
typedef uvm_object_string_pool #(uvm_event #(uvm_object)) uvm_event_pool;
  • uvm_barrier可以设置一定的等待阈值(threshold),当有不少于该阈值的进程在等待该对象时才会触发该事件,同时激活所有正在等待的进程,使其可继续进行。

uvm_barrier应用

class comp1 extends uvm_component;
	uvm_barrier b1;
	`uvm_component_utils(comp1)
	...
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		b1 = uvm_barrier_pool::get_global("b1");  //类似于uvm_event
	endfunction
	task run_phase(uvm_phase phase);
		#10ns;
		`uvm_info("BSYNC", $sformatf("c1 wait for b1 at %0t ps", $time), UVM_LOW)
		b1.wait_for();  //等待阈值满足则立即执行下一行
		`uvm_info("BSYNC", $sformatf("c1 is activated at %0t ps", $time), UVM_LOW)
	endtask
endclass
class comp2 extends uvm_component;
	uvm_barrier b1;
	`uvm_component_utils(comp2)
	...
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		b1 = uvm_barrier_pool::get_global("b1");
	endfunction
	task run_phase(uvm_phase phase);
		#20ns;
		`uvm_info("BSYNC", $sformatf("c2 wait for b1 at %0t ps", $time), UVM_LOW)
		b1.wait_for();
		`uvm_info("BSYNC", $sformatf("c2 is activated at %0t ps", $time), UVM_LOW)
	endtask
endclass
class env1 extends uvm_env;
	comp1 c1;
	comp2 c2;
	uvm_barrier b1;
	`uvm_component_utils(env1)
	...
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		c1 = comp1::type_id::create("c1", this);
		c2 = comp2::type_id::create("c2", this);	
		b1 = uvm_barrier_pool::get_global("b1");
	endfunction
	task run_phase(uvm_phase phase);
		b1.set_threshold(3); //设置等待阈值为3
		`uvm_info("BSYNC", $sformatf("env set b1 threshold %d at %0t ps", b1.get_threshold(), $time), UVM_LOW)
		#50ns;
		b1.set_threshold(2);
		`uvm_info("BSYNC", $sformatf("env set b1 threshold %d at %0t ps", b1.get_threshold(), $time), UVM_LOW)
	endtask
endclass
输出结果:
UVM_INFO @0: reporter [RNTST] Running test test..
UVM_INFO @0: uvm_test_top.env [BSYNC] env set b1 threshold 3 at 0ps
UVM_INFO @ 10000: uvm_test_top.env.c1 [BSYNC] c1 wait for b1 at 10000 ps
UVM_INFO @ 20000: uvm_test_top.env.c2 [BSYNC] c2 wait for b1 at 20000 ps
UVM_INFO @ 50000: uvm_test_top.env [BSYNC] env set b1 threshold 2 at 50000 ps
UVM_INFO @ 50000: uvm_test_top.env.c1 [BSYNC] c1 is activated at 50000 ps
UVM_INFO @ 50000: uvm_test_top.env.c2 [BSYNC] c2 is activated at 50000 ps
  • 从示例来看c1和c2的run_phase任务之间需要同步,而同步它们的元件则来自于顶层的一个uvm_barrier b1
  • 由于c1、c2和env1都共享该对象,这使得c1和c2可通过wait_for()来等待激活,而env1可以设置阈值来调控什么时间来“开阀”。
  • 从仿真结果可以看到一开始阈值设置为3,但由于等待该barrier的进程只有2个,无法达到阈值条件,使得两个进程都无法激活。
  • 在env1将b1的阈值设置为2时,等待该barrier的两个进程都被激活。
  • 因此通过uvm_barrier::set_threshold()uvm_barrier::wait_for()这样的方式,可以实现多个组件之间的同步,同时可以保持各个组件之间的独立性

uvm_callback

  • 除了UVM提供新类方便组件之间的同步之外,另一种同步方式回调函数也方便类的封装复用。
  • 通常情况下得到一个封闭的包,其中的类如果有些成员方法需要修改,或者需要扩展新方法时,如果通过类的继承,又无法在该包环境中用新的子类替换原来的父类,那么此时可以使用UVM的覆盖机制。
  • 除了覆盖机制,还有callback也可以提供自定义的处理方法,这使得如果不需要添加新方法,而是想延展之前的方法,就无需通过继承类的方式,而只需通过在后期定义callback方法实现。
  • uvm_object本身提供了一些callback方法:
    • copy()/do_copy()
    • print()/do_print()
    • compare()/do_compare()
    • pack()/do_pack()
    • unpack()/do_unpack()
    • record()/do_record()
  • 默认情况下这些回调函数do_XXX定义为空的。
  • 如果执行了uvm_object::copy(),那么在函数执行末尾会自动执行uvm_object::do_copy();do_copy()是copy()的回调函数,uvm_object会在copy()的执行尾端勾住callback函数即do_copy()。
  • 如果自定义了这些回调函数,就可在对应函数执行结束后再执行扩展后的回调方法
  • 通过这个新添加的类,使得函数回调有了顺序和继承性,关于顺序和继承性的实现,UVM是通过两个相关类uvm_callback_iteruvm_callbacks #(T,CB)实现。

uvm_callback应用

class edata extends uvm_object;
	int data;
	`uvm_object_utils(edata)
	...
endclass
class cb1 extends uvm_callback;
	`uvm_object_utils(cb1)
	...
	virtual function void do_trans(edata d);
		d.data = 200;
		`uvm_info("CB", $sformatf("cb1 executed with data %0d", d.data), UVM_LOW)
	endfunction
endclass
class cb2 extends cb1;
	`uvm_object_utils(cb2)
	...
	function void do_trans(edata d); //这里不需要super,因为do_trans相当于override
		d.data = 300;
		`uvm_info("CB", $sformatf("cb2 executed with data %0d", d.data), UVM_LOW)
	endfunction
endclass
class comp1 extends uvm_component;
	`uvm_component_utils(comp1)
	`uvm_register_cb(comp1, cb1)  //comp1和cb1进行关联(注册/绑定)
	...
	task run_phase(uvm_phase phase);
		edata d = new();
		d.data = 100;
		`uvm_info("RUN", $sformatf("proceeding data %0d", d.data), UVM_LOW)
		`uvm_do_callbacks(comp1, cb1, do_trans(d)) //插入callback
	endtask
endclass
class env1 extends uvm_env;
	comp1 c1;
	cb1 m_cb1;
	cb2 m_cb2;
	`uvm_component_utils(env1)
	function new(string name, uvm_component parent);
		super.new(name, parent);
		m_cb1 = new("m_cb1");  //例化callback
		m_cb2 = new("m_cb2");
	endfunction
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		c1 = comp1::type_id::create("c1", this);
		uvm_callbacks #(comp1)::add(c1, m_cb1);  //顶层添加callback
		uvm_callbacks #(comp1)::add(c1, m_cb2);
	endfunction
endclass
输出结果
UVM_INFO @0: reporter [RNTST] Running test test1...
UVM_INFO @0: uvm_test_top.erv.c1 [RUN] proceeding data 100
UVM_INFO @0: reporter [CB] cb1 executed with data 200
UVM_INFO @0: reporter [CB] cb2 executed with data 300

uvm_callback总结:

  • uvm_callback类使得钩子属性变得更加容易控制和继承,需要注意的是:
    • uvm_callback可通过继承的方式满足更多的定制,例如上面的cb2继承于cb1。
    • 为了保证调用uvm_callback的组件类型T与uvm_callback类型CB保持匹配,最好在T中声明T与CB的匹配,该声明可通过宏'uvm_register_cb(T,CB)实现。在用户养成注册的习惯之后,如果以后调用的T与CB不匹配,那么在检查完匹配注册表之后系统会打印warning信息,提示用户使用回调函数的潜在问题。
    • uvm_callback建立了回调函数执行的层次性,因此在实现方面不再是在T的方法中直接呼叫某个回调方法,而通过宏'uvm_callbacks #(T,CB,METHOD)实现。它最直观的作用在于会循环执行已经与该对象结对的uvm_callback类的方法。此外宏'uvm_do_callbacks_exit_on #(T,CB,METHOD,VAL)可以进一步控制执行回调函数的层次,回调函数会保持执行直到返回值与给入的VAL值相同才会返回,这一点使得回调方法在执行顺序上面有了更多的可控性。
    • 有了'uvm_do_callbacks 宏还不够,在执行回调方法时,依赖的是已经例化的uvm_callback对象,所以最后一步需要例化uvm_callback对象,上例分别例化了cb1和cb2,通过“结对子”的方式,通过uvm_callbacks #(T,CB)类的静态方法add()添加成对的uvm_object对象和uvm_callback对象。
  • 具体使用流程:继承uvm_callback并定义CB -> 使用uvm_register_cb以及uvm_do_callbacks绑定和插入CB对应的方法 - > 在顶层例化组件和CB,add()添加CB。

标签:TLM2,12,进阶,callback,uvm,phase,data,event,UVM
来源: https://blog.csdn.net/weixin_41979380/article/details/122616995

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有