IC验证学霸笔记4——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

与 event 相比, uvm_event 类有下面几个重要特性:

•    event 被 ->触发之后,触发使用@等待该事件的对象; uvm_event 通过 trigger()来触发, 触发使用 wait_trigger()等待该事件的对象。要再次等待事件触发,event 只需再次用 ->触发, 而 uvm_eveot 需要先通过 reset()方法重置初始状态, 再使用 trigger()来触发。
•    event 无法携带更多的信息, 而 uvm_event 可以通过 trigger(T data = null)的可选参数,将伴随触发的数据对象都写入到该触发事件中, 而等待该事件的对象可以通过方法wait_ trigger_ data(output T data)来获取事件触发时写入的数据对象。
•    event 触发时无法直接触发回调函数,而 uvm_event 可以通过 add_callback(uvm_event_ callback ch, bit append = 1)函数来添加回调函数。
•    event 无法直接获取等待它的进程数目, 而 uvm_eveot 可以通过 get_num_waiters()来获取等待它的进程数目。

不同组件可以共享同一个 uvm_event, 这不需要通过跨层次传递 uvm_event 对象句柄来实现共享, 因为这不符合组件环境封闭的原则, 该共享方式是通过 uvm_event_pool 这一全局资源池来实现的。这个资源池类是 uvm_object_string_pool #(T)的子类, 它可以生成和获取通过字符串来索引的 uvm_event 对象。通过全局资源池(唯一的), 环境中的任何组件都可以从资源池获取共享的对象句柄, 这就避免了组件之间的互相依赖。

典型用例

 

在上面的例子中, 组件 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()来完成等待。这样即便在调用事件等待方法之前该事件已经被触发, 等待方法仍然不会被阻塞并且可以继续执行结束。

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

uvm_barrier
多个线程的同步除了可以通过 semaphore 和 mailbox 来进行, 也可以通过 fork-join 的结构控制语句块来控制整体的运行节奏。 然而, 对于 UVM 环境中的多个独立组件, SV 的这些方法都受到了作用域的局限。 UVM 提供了一个新的类uvm _ barrier 对多个组件进行同步协调, 同时为了解决组件独立运作的封闭性需要, 定义了新的类 uvm_barrier_pool 来全局管理这些 uvm_barrier 对象。 uvm_ barrier _pool与之前的 uvm_event_pool 一样, 也是基于通用参数uvm_object_string _pool 来定义的。
uvm _ barrier 可以设置一定的等待阙值 (threshold), 仅在有不少于该阙值的进程在等待该对象时才触发该事件, 同时激活所有正在等待的进程, 使其继续进行。
从这个例子来看, c1 和c2 的 run_phase 任务之间需要同步, 而同步它们的元件则是来自于顶层的一个 uvm_barrier b1。由千cl、c2 和 envl 共享该对象, cl 和 c2 可以通过 wait_for()来等待激活, 而 env1 可以设置阈值来调控什么时间来 ” 开阀 "。 从仿真结果可以看到, 在一开始的时候, 阈值设置为 3, 但由于等待该 barrier 的进程只有 2 个, 无法达到阙值条件, 两个进程都无法激活。而在 envl 将 bl 的阙值设置为 2 时,等待该 barrier 的两个进程都被激活。 因此通过 uvm_ barrier: :set_ threshold()和 uvm_ barrier: :wait_ for()这样的方式, 可以实现多个组件之间的同步, 同时可以保待各个组件之间的独立性。

uvm_callback
除了UVM提供新的类方便组件之间的同步之外,另外一种同步方式回调函数(callback)也方便了类的封装复用。
通常情况下得到了一个封闭的包, 其中的类如果有些成员方法需要修改, 或者需要扩展新的方法时, 应该怎么做呢?如果这个包是外来的, 那么维护方法不建议去修改这个类本身。
如果我们通过类的继承来满足这一要求, 又无法在该包环境中用新的子类替换原来的父类, 那么UVM的覆盖机制(override)可以帮忙。
除了覆盖机制, 还有callback也可以为用户提供自定义的处理方法, 这就使得如果用户不需要添加新方法, 而是想延展之前的方法, 就无需通过继承类的方式而只需要通过在后期定义callback方法来实现。

uvm_object 本身提供了一些 callback 方法供用户定义:
默认情况下, 这些回调函数 do_xxx 是定义为空的, 如果用户执行了 uvm_object:: copy() 函数, 那么在该函数执行末尾会自动执行 uvm_ object: :do_ copy()。这里 do_copy()是 copy()的回调函数, uvm_object 会在 copy()的执行尾端勾住 (hook) callback 函数即 do_copy()。如果用户自定义了这些回调函数, 就可以在对应函数执行结束后执行扩展后的回调方法。 那么,这种普通的回调函数定义就足够了, 为什么还要专门定义一个 uvm_callback 类呢?可以说,这个新添加的类使得函数回调有了顺序和继承性。 UVM 通过两个相关类 uvm_callback_ iter 和 uvm_callbacks #(T, CB)来实现顺序和继承性。

•    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_do_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对象。

注:优秀验证学员随堂笔记,已经征求到学生的同意,会持续给牛友们分享!
大家看完记得 一键三连!多多支持

#深度学习##做项目##芯片IC验证工程师##你为什么选择硬件行业##你的秋招进展怎么样了#
全部评论
牛客就是牛
点赞 回复 分享
发布于 2022-09-30 17:19 河南

相关推荐

不愿透露姓名的神秘牛友
10-04 05:12
瑞雪兆丰年_:可以贴个超级大的校徽,以防HR眼拙
点赞 评论 收藏
分享
评论
2
3
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务