UVM入门进阶6:同步通信元件(解决组件同步与进程同步)

参考文档链接:https://verificationacademy.com/verification-methodology-reference/uvm/docs_1.2/html/

本节目录:

  • 同步通信元件
  • 第一组类:uvm_event、uvm_event_pool、uvm_event_callback
  • 第二组类:uvm_barrier, uvm_barrier_pool

1 同步通信元件

SV的同步通信元件:event, semaphore, mailbox

UVM的线程同步,不局限于同一对象中,而针对不同组件之间的同步问题

同步类(保证了封闭性):

  • uvm_event, uvm_event_pool和uvm_event_callback
  • uvm_barrier, uvm_barrier_pool

补充:

不推荐句柄的形式进行同步:

  • 什么是句柄的形式?

c2拿到了c1中例化的event1句柄


两组类指的是这两组:

第一组:uvm_event, uvm_event_pool和uvm_event_callback

第二组:uvm_barrier, uvm_barrier_pool

uvm_callback具有回调特点的同时还能有丰富特性供层次化调用

2 第一组类:uvm_event、uvm_event_pool、uvm_event_callback

学习内容:第一组类:

  • uvm_event
  • uvm_event_pool
  • uvm_event_callback

uvm_event解决问题(详细参考2.5节总结)

  • obj与comp的同步
  • comp之间的同步
  • seq与dri之间的同步
  • seq之间的同步

2.1 对比与注意

  • 有post/pre trigger回调
  • trigger时能传入数据

2.2 uvm_event介绍

不同组件共享一个名称相同的uvm_event,这个uvm_event创建、存放、例化、管理在uvm_pool这一个资源池中,uvm_pool是继承于uvm_object_string_pool#(T)一个参数类


关于uvm_object_string_pool #(T):

T pool [stirng] —>uvm_event pool [“event_name”]

2.3 部分总结(详细参考2.1)

(1) uvm_event_pool不需要例化,就在后台一直工作

(2) 如何在组件中拿到/创建event:

  • 声明一个uvm_event句柄e1

  • e1.uvm_event_pool::get_gobal("event_name");//创建或得到了一个event句柄

  • 真正的创建其实时在uvm_event_pool自动完成的

(3) 如何触发、被触发、重复触发:

  • 与SV不同
  • trigger()触发,同时可以传入数据
  • wait_trigger()/wait_trigger_data等待
  • 重复触发之前一定要reset

(4) 如何增加回调函数:

  • 创建一个回调类uvm_callback(按照组件创建的方式)
  • 在类里面实现:pre_trigger,post_trigger两种方法
    • pre_trigger()有返回值,返回1时表明uvm_event不会被trigger同时不会执行post_trigger(),否则反之
  • 最后在event被例化处使用add_callback添加回调函数

(5) 获取等待某个事件的进程数:

  • get_num_waiters()

(6) 查看uvm_event状态(即查看是否已被触发)或者说是电平触发

  • wait_ptrigger()与wait_ptrigger_data()

(7) 补充:wait_trigger与wait_ptrigger

  • wait_trigger边沿触发:同SV中的@
  • wait_ptrigger电平触发

2.4 示例代码

(1)transaction&uvm_callback

  • 定义了一个transaction类型edata

  • 定义ecb继承于uvm_event_callback注册肯定跑不掉

  • 回调函数的定义与实现
    • 回调函数只能是pre_trigger,post_trigger
    • 形参data解释:触发时可以传入某些数据

(2)comp1对event1由全局pool进行的例化

通过全局Pool进行例化

e1=uvm_event_pool::get_gobal("e1");

  • event pool里面有e1:直接拿到一个句柄
  • event pool里面没有e1:帮助创建一个名字是e1的句柄


task run_phase()中的要点:

  • edata d = new()
  • e1.trigger(d);//把数据传入triggered
  • 把callback与event1做个关联
    • e1.add_callback(cb)

(3)comp2&env

e1.wait_triiger_data(tmp):等待被触发同时等待一个数据过来

  • tmp默认obj类型

void`($cast(d, tmp)):tmp进行父类转子类的转化

e1.wait_triiger():直接等待被触发

(4)总结

2.5 应用场景总结

  • uvm_event的使用场景
  • uvm_event的使用场景
  • uvm_event什么时候使用到
    • obj与comp的同步
    • comp之间的同步
    • seq与dri之间的同步
    • seq之间的同步

3 第二组类:uvm_barrier, uvm_barrier_pool

学习内容:第二组类:

  • uvm_barrier
  • uvm_barrier_pool

3.1 进程同步与uvm_barrier&uvm_barrier_pool

P1:SV中多线程的同步

P2:UVM的组件独立,SV方法收到作用域限制

P3:uvm_barrier解决多个组件同步协调,uvm_barrier_pool全局管理uvm_barrier

P4:继承关系同uvm_event_pool

3.2 uvm_barrier_pool&uvm_event_pool继承于uvm_object_string_pool

3.3 阈值

可设置阈值threshold

等待线程不少于阈值时触发全体线程中的等待

3.4 部分总结

  • wait_for()进行等待
  • set_threshold()设置触发阈值

3.5 代码示例

(1)comp1&comp2及其获得b句柄

  • b.wait_for()

(2)env

env1做了一个裁判员

(3)结果与总结

4 正式进入uvm_callback

之前都是稍微提到组件或obj的回调方法,以及uvm_event_callback,今天我们正式分析uvm_callback机制

4.1 回调的作用

第一段,回调的好处

  • 另一种同步方式
  • 方便里类的封装复用

第二三四段,如何实现封闭的包扩展新方法

  • 类的继承
  • uvm覆盖机制
  • callback,无需继承何添加新方法,只需要后期定义

4.2 之前学习过什么uvm_callback?

  • 学过uvm_event_callback

  • 学过uvm_object提供了一些callback方法供用户们定义:

1
2
3
4
5
6
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()的执行尾端勾住(hook) callback 函数即do_copy()

如果用户自定义了这些回调函数,就可以在对应函数执行结束后再执行扩展后的回调方法

通过这个新添加的类,使得函数回调有了顺序和继承性

关于顺序和继承性的实现,UVM是通过两个相关类uvm_callback_iter和uvm_callbacks #(T, CB)来实现的

如何定义这些callback类:
UVM是通过两个相关类uvm_callback_iter和uvm_callbacks #(T, CB)来实现的

  • uvm_callback_iter
  • uvm_callbacks #(T, CB)

4.3 代码示例

(1)trans数据类以及callback定义

  • trans数据类
    • obj注册
  • 定义cb1
    • obj注册
    • 虚函数,名称随便起
  • 定义cb2继承于cb1
    • override do_trans

你可以在callback里面定义任何你想定义的函数名称:这里我们定义了do_trans函数

(2)comp1定义

  • 定义comp1
    • 注册(关联)comp1与cb1两个类的关系,方便之后检查(其实也可以不写,类型不同时无法提醒警告,参考4.4节)
      • uvm_register_cb(comp1, cb1)
    • run_phase中插入callback
      • 插入callbacks:uvm_do_callback(comp1, cb1, do_trans(d))
        • 参数解释:我在comp1里面调用cb1类型的callback,callback方法名为do_trans(d),d为传入参数

(3)env与输出结果

  • 构造函数
    • 例化comp1,cb1,cb2
  • build_phase
    • 添加callbacks进行绑定:uvm_callbacks # (comp1)::add(组件实例句柄, callback实例句柄1)
      • 实现callback与comp1进行绑定
    • 添加callbacks进行绑定:uvm_callbacks # (comp1)::add(组件实例句柄, callback实例句柄2)
      • 实现callback与comp1进行绑定
      • cb2没有注册为什么添加到m_cb2到c1可以?:因为cb2继承cb1,一旦发生继承关系你不需要再进行绑定

comp1的run_phase最终执行顺序:

  • run_phase原来部分程序
  • cb1.do_trans(d)
  • cb2.do_trans(d)

三个步骤:

①注册你的callback

②绑定以及插入callback

③添加callback

4.4 总结:结对子

UVMcallback与SVcallback没区别,建议使用UVMcallback,更为标准化

部分函数总结

  • 组件创建时注册:uvm_register_cb(组件类名, callback类名)
  • 组件对应位置插入:uvm_do_callback(组件类名, callback类名, callback内回调方法名(回调传入数据参数))

  • 环境中添加:uvm_callbacks # (组件类名)::add(组件实例句柄, callback实例句柄)

  • 控制回调函数的层次(了解):`uvm_do_callbacks_exit_on(T,CB,METHOD,VAL)