uvm 通信

tlm通信

通信继承于uvm_void,不是object或者component,不能type_id::create
端口分类:
port:经常作为initiator的发起端,initiator凭借port才可以访问target的TLM通信方法。
export:作为initiator和target中间层次的端口。
imp:只能作为target接收request的末端,它无法作为中间层次的端口,所以imp的连接无法再次延伸。
根据传输方向跟端口类型排列组合,共有6类。

  • uvm_UNDIR_port #(trans_t)
  • uvm_UNDIR_export #(trans_t)
  • uvm_UNDIR_imp#(trans_t,imp_parent_t)
  • uvm_BIDIR_port#(req_trans_t,rsp_trans_t)
  • uvm_BIDIR_export #(req_trans_t,rsp_trans_t)
  • uvm_BIDIR_imp #(req_trans_t,rsp_trans_t,imp_parent_t)

单项通信

分类命名uvm(_blocking or _nonblocking)_(put get peek get_peek)_(imp port export)

  • 第一项决定是阻塞还是非阻塞,省略的话两者都支持。
  • 第二项决定数据传输方式。
  • 第三项决定端口类型。
    两者共同决定需要实现的函数
  • 以blocking,put为例,在target中需要实现函数为task put(Tr t);
  • 以nonblocking,put为例,在target中需要实现函数为function bit try_put(Tr t); function bit can_put();

双向通信

分类命名uvm(_blocking or _nonblocking)_(transport master slave)_(imp port export)

  • 第一项决定是阻塞还是非阻塞,省略的话两者都支持。
  • 第二项决定数据传输方式。
    transport的话需要实现task transport(REQ req,output RSP rsp)或者task nb_transport(REQ req,output RSP rsp),发req并用rsp收。
    master(以blocking为例)需要实现task put(REQ t),task get(output RSP t),task peek(output RSP t)
    slave(以blocking为例)则相反,task put(RSP t),task get(output REQ t),task peek(output REQ t)
  • 第三项决定端口类型。

多个单项端口

一个组件中有多个端口时,函数名相同,会互相冲突。
在组件之前`组件名_decl(_uvc),定义使用该端口对应的方法也要加上后缀。

单对多通信

uvm_analysis_port ap; uvm_analysis_imp aimp;
需要在uvm_analysis_imp中需要自己实现write函数。
在port中write时,会遍历所有连接到的imp,执行其中的write函数。

fifo

单项通信

uvm_tlm_fifo,里面包含了多种配置端口。

双向通信

uvm_tlm_req_rsp_fifo
uvm_tlm_transport_fifo

单对多通信

针对这种端口,fifo使用uvm_tlm_analysis_fifo
使用示例如下:

//发送端
uvm_analysis_port #(uvc_transaction) trans_ap;
trans_ap.write();
//接收端
uvm_blocking_get_port #(uvc_transaction) trans_ap;
trans_ap.get();
//连接端
uvm_tlm_analysis_fifo #(uvc_transaction) uvc_fifo;
tx.trans_ap.connect(uvc_fifo.analysis_export);
rx.trans_ap.connect(uvc_fifo.blocking_get_export);

uvm_event

uvm_event不需要通过跨层次传递对象句柄,通过uvm_event_pool全局资源池来实现。

class ecb extends uvm_event_callback;
    `uvm_object_utils(ecb)
    function bit pre_trigger(uvm_event e,uvm_object data = null);
    endfunction
    function bit post_trigger(uvm_event e,uvm_object data = null);
    endfunction
endclass

class comp1 extends uvm_component;
    uvm_event e1;
    edata d;
    e1 = uvm_event_pool::get_global("e1");//有就直接拿到,没有就自动创建
    e1.add_callback(cb);//添加回调函数
    e1.trigger(d);//可以传递任何一个继承自uvm_object的类型,也可以不传递
endclass

class comp2 extends uvm_component;
    uvm_event e1;
    uvm_object tmp;
    edata d;
    e1 = uvm_event_pool::get_global("e1");
    e1.wait_trigger_data(tmp);//边沿触发
    //没传数据的话e1.wait_trigger();就可以
    void'($cast(d,tmp));
endclass

uvm_event可以通过get_num_waiters()来获取等待的进程数。
wait_ptrigger_data(tmp)与wait_ptrigger()可以等待电平触发。

uvm_barrier

class comp1 extends uvm_component;
    uvm_barrier b1;
    b1 = uvm_event_pool::get_global("b1");//有就直接拿到,没有就自动创建
    b1.wait_for();//可以传递任何一个继承自uvm_object的类型,也可以不传递
endclass

class comp2 extends uvm_component;
    uvm_barrier b1;
    b1 = uvm_event_pool::get_global("b1");
    b1.wait_for();//可以传递任何一个继承自uvm_object的类型,也可以不传递
endclass

在外界,b1.set_threshold(n),等到调用wait_for的数量达到n以后,同时释放所有进程。

uvm_callback

class cb1 extends uvm_callback;
    `uvm_object_utils(cb1)
    function void func_name();
    endfunction 
endclass
class comp1 extends uvm_component;
    `uvm_register_cb(comp1,cb1)
    task run_phase(uvm_phase phase);
    ...
    `uvm_do_callbacks(comp1,cb1,func_name)//也可以在func_name里面传参
    ...
    endtask
endclass
class env1 extends uvm_env;
    cb1 m_cb1;
    comp1 c1;
    function void build_phase
        uvm_callbacks #(comp1)::add(c1,m_cb1);
    endfunction
endclass

如果有cb2继承于cb1,就不需要uvm_register_cb(comp1,cb2),也不需要uvm_do_callbacks,
只需要uvm_callbacks #(comp1)::add(c1,m_cb2);

posted @ 2025-01-02 15:55  心比天高xzh  阅读(76)  评论(0)    收藏  举报