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_event 需要先通过 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 对象。通过全局资源池(唯一的), 环境中的任何组件都可以从资源池获取共享的对象句柄, 这就避免了组件之间的互相依赖。

典型用例

class edata extends uvm_object;
int data;
`uvm_object_utils(edata)
...
endclass 

class ecb extends uvm_event_callback; //定义event callback,object类型 
'uvm_object_utils(ecb)
...
function bit pre_trigger(uvm_event e, uvm_object data = null); //pre_trigger  uvm_object data = null trigger event时还可以trigger data
`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);  //post_trigger
`uvm_info("EPOSTRIG", $sformatf("after trigger event %s", e.get_name ()),UVM_LOW) 
endfunction 
endclass 

class compl extends uvm_component;
uvm_event el; //例化uvm_event
`uvm_component_utils(compl) 
function void build_phase(uvm_phase phase); 
super.build_phase(phase); 
el = uvm_event_pool::get_global("el"); //通过uvm_event_pool get global,寻找e1,如果没有自动创建一个
endfunction 
task run_phase(uvm_phase phase); 
edata d = new(); 
ecb cb = new (); 
d.data = 100;
#l0ns;
e1.add_callback (cb) ; //add_callback 同时将e1与callback关联
el.trigger(d); //trigger 可以带data
`uvm_info ("ETRIG", $sformatf ("trigger sync event at %t ps", $time), UVM LOW)
endtask 
endclass 

class comp2 extends uvm_component;
uvm_event el;
`uvm_component_utils(comp2)
...
function void build_phase(uvm_phase phase);
super.build_phase{phase);
el = uvm_event_pool::get_global("el");
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) 
el.wait_trigger_data (tmp); //等待e1被触发,同时等待data,如果没有data用wait_trigger().
void'($cast (d, tmp));   //tmp是父类句柄
`uvm_info ("ESYNC", $sformatf ("get data %0d after sync at %t ps", d.data, $time), UVM LOW) 
endtask 
endclass 

class envl extends uvm_env;
compl cl; 
comp2 c2; 
`uvm component_utils(envl)
...
endclass

//输出结果:
  UVM_INFO@ 0: reporter [RNTST] Running test testl ... 
  UVM_INFO@ 0: uvm_test_top.env.c2 [ESYNC] wait sync event at 0ps 
  UVM_INFO@ 10000: reporter [EPRETRIG] before trigger event el 
  UVM_INFO@ 10000: reporter [EPOSTRIG] after trigger event el 
  UVM_INFO@ 10000: uvm_test_top.env.cl [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()来完成等待。这样即便在调用事件等待方法之前该事件已经被触发, 等待方法仍然不会被阻塞并且可以继续执行结束。

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

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), 仅在有不少于该阙值的进程在等待该对象时才触发该事件, 同时激活所有正在等待的进程, 使其继续进行。

class compl extends uvm_component;
uvm_barrier bl;
`uvm_component_utils(compl)
...
function void build_phase(uvm_phase phase);
super.build_phase(phase);
bl= uvm_barrier_pool::get_global("bl");
endfunction
task run_phase(uvm_phase phase};
#lOns;
`uvm_info("BSYNC", $sformatf ("cl wait for bl at%0t ps", $time), UVM _ LOW) 
bl.wait_for(); //comp1 10ns 后等待barrier
`uvm info("BSYNC", $sformatf("cl is activated at %0t ps", $time} , UVM _LOW)
endtask
endclass
class comp2 extends uvm_component;
uvm_barrier bl;
`uvm_component_utils(comp2)
...
function void build_phase(uvm_phase phase);
super.build_phase(phase); 
bl = uvm_barrier_pool::get_global ("bl") ;
endfunction 
task run_phase(uvm_phase phase);
#20ns; 
`uvm_info("BSYNC", $sformatf("c2 wait forbl at %0t ps", $time), UVM_LOW)
bl.wait_for (); //comp2 20ns 后等待barrier
`uvm info ("BSYNC", $sformatf ("c2 is activated at %0t ps", $time), UVM_LOW)
endtask 
endclass 
class envl extends uvm_env;
compl cl; 
comp2 c2; 
uvm_barrier bl;
`uvm_component_utils(envl)
...
function void build_phase(uvm_phase phase);
super.build_phase(phase); 
cl = compl::type_id::create("cl", this); 
c2 = comp2::type_id::create("c2", this);
bl = uvm_barrier_pool::get_global("bl");
endfunction: build_phase 
task run_phase(uvm_phase phase);
bl.set_threshold(3);  //阈值为3,两个等不到
`uvm_info("BSYNC", $sformatf("env set bl threshold %ct at %0t ps", bl.get_threshold (), $time), UVM_LOW) 
#50ns; 
bl.set_threshold(2); //阈值改为2
`uvm_info("BSYNC", $sformatf("env set bl threshold %d at %0t ps", bl.get_threshold(), $time), UVM_LOW) 
endtask

//输出结果为:
UVM_INFO@ 0: reporter [RNTST) Running test testl ... 
UVM_INFO@ 0: uvm_test_top.env [BSYNC) env set bl threshold 3 at 0ps 
UVM_INFO@ 10000: uvm_test_top.env.cl [BSYNC) cl wait for bl at 10000 ps 
UVM_INFO @ 20000: uvm_test_top.env.c2 (BSYNC] c2 wait for bl at 20000 ps 
UVM_INFO@ 50000: uvm_test_top.env [BSYNC] env set bl threshold 2 at 50000 ps 
UVM_INFO@ 50000: uvm_test_top.env.cl [BSYNC) cl is activated at 50000 ps //由于阈值50ns active
UVM_INFO@ 50000: uvm_test_top.env.c2 [BSYNC] c2 is activated at 50000 ps 

从这个例子来看, 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 方法供用户定义:

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_callback 类呢?可以说,这个新添加的类使得函数回调有了顺序和继承性。 UVM 通过两个相关类 uvm_callback_ iter 和 uvm_callbacks #(T, CB)来实现顺序和继承性。

class edata extends uvm_object;
int data;
`uvm_object_utils(edata)
...
endclass 
class cbl extends uvm_callback; //必须继承于uvm_callback
'uvm_object_utils(cbl)
...
virtual function void do_trans(edata d);
d.data = 200;
`uvm_info ("CB", $sformatf ("cbl executed with data %Od", ct.data), UVM LOW)
endfunction endclass 
class cb2 extends cbl;
`uvm_object_utils (cb2)
...
function void do_trans(edata d); //1.定义函数
ct.data = 300; 
uvm info ("CB", $sformatf ("cb2 executed with data %0d", ct.data), UVM LOW)
endfunction 
endclass 
class compl extends uvm_component;
`uvm_companent_utils(comp1)
`uvm_register_cb(compl, cbl) //2.注册comp1 和cb1
...                          // cb2没有注册,因为它是继承与cb1
task run_phase(uvm_phase phase);
edata d = new();
ct.data = 100; 
`uvm_info("RUN", $sformatf("proceeding data %0d", ct.data), UVM_LOW) 
`uvm_do_callbacks(compl, cbl, do_trans(d))//3.插入callback,注意格式
endtask                                    //cb2没有插入,因为它是继承与cb1
endclass 
class envl extends uvm_env;
compl cl; 
cbl m_cbl; 
cb2 m_cb2; 
`uvm_component_utils(envl) 
function new(string name, uvm_component parent);
super.new(name, parent); 
m_cbl = new("m cbl"); //例化
m_cb2 = new("m_cb2"); 
endfunction 
function void build_phase(uvm_phase phase);
super.build_phase(phase); 
cl = compl::type_id::create("cl", this);
uvm_callbacks #(compl) ::add(cl, m_cbl); //4.添加callback
uvm_callbacks #(compl) ::add(cl, m_cb2); 
endfunction: build_phase 
endclass 
//输出结果为:
OVM_INFO@ 0: reporter [RNTST) Running test testl ... 
UVM_INFO@ 0: uvm_test_top.env.cl [RUN] proceeding data 100 
UVM_INFO@ 0: reporter [CB) cbl executed with data 200 
UVM INFO@ 0: reporter [CB] cb2 executed with data 300 

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
UVM-Info是一种用于在Universal Verification Methodology(UVM)中进行消息传递和报告的结构。它提供了一种机制,用于在测试环境中传递信息、警告和错误消息,并将其报告给用户。 UVM-Info结构由以下几个主要组成部分组成: 1. `uvm_info`类:这是UVM-Info结构的主要类,用于创建和管理消息。它提供了一些方法,如`uvm_info::set_id()`用于设置消息的唯一标识符,`uvm_info::set_file()`用于设置消息所在的文件名,`uvm_info::set_line()`用于设置消息所在的行号等。 2. `uvm_report_object`类:这是一个基类,用于派生出具体的报告对象类。它提供了一些方法,如`uvm_report_object::uvm_report_info()`用于创建并发送信息消息,`uvm_report_object::uvm_report_warning()`用于创建并发送警告消息,`uvm_report_object::uvm_report_error()`用于创建并发送错误消息等。 3. `uvm_report_handler`类:这是一个报告处理器类,用于管理和过滤UVM-Info结构中的消息。它提供了一些方法,如`uvm_report_handler::set_actions()`用于设置不同类型消息的处理动作,`uvm_report_handler::set_severity_id()`用于设置消息的严重程度和唯一标识符等。 4. `uvm_report_catcher`类:这是一个报告捕获器类,用于捕获和处理UVM-Info结构中的消息。它提供了一些方法,如`uvm_report_catcher::catch_info()`用于捕获信息消息,`uvm_report_catcher::catch_warning()`用于捕获警告消息,`uvm_report_catcher::catch_error()`用于捕获错误消息等。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

创芯人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值