• uvm通信


    TLM通信

    在芯片开发流程中,有两个地方对项目的助推起到了关键作用:系统原型、芯片验证

    • 系统原型一般是通过硬件功能描述文档来模拟硬件行为,而行为要求不同于RTL模型。系统原型可以提供一个准确到硬件比特级别、按照地址段访问、不依赖于时钟周期的模型,该模型通常基于SystemC语言,而系统原型中各个模块通过TLM可以实现宽松时间范围内的数据包传输。
    • 芯片验证是在RTL模型初步建立后,通过验证语言和方法学例如sV/UVM来构建验证平台。该平台的特点是验证环境整体基于面向对象开发,组件之间的通信基于TLM,而在driver与硬件接口之间需要将TLM抽象事务降解到基于时钟的信号级别。
    • 系统原型阶段和芯片验证阶段均使用了TLM通信方式。前者是为了更快地实现硬件原型之间的数据通信,后者是为了更快地实现验证组件之间的数据通信。
    • 仿真速度(不依赖于时钟)是TLM对项目进度的最大贡献,同时TLM传输中的事务又可以保证足够大的信息量和准确性。
    • TLM并不是某一种语言的产物,而是作为一种提高数据传输抽象级的标准存在的。
    • 高抽象级的数据通信,可以用来表示宽松时间跨度内的硬件通信数据,而通过将低颗粒硬件周期内的数据打包为一个大数据,非常有利于整体环境的仿真速度。
    • TLM的运用越来越广泛,包括emulator同硬件仿真器的协同仿真框架中,也建议使用这种方式来降低混合环境之间的通信频率,以便提高整体的运算效率。

            TLM是一种基于事务(transaction)的通信方式,通常在高抽象级语言例如SystemC或者SV/UVM中作为模块之间的通讯方式。
            TLM成功地将模块内的计算和模块之间的通信从时间跨度方面剥离开了。
            在抽象语言建模体系中,各个模块通过一系列并行的进程实现,并通过通信和计算来模拟出正确的行为。
            如果要提高系统模型的仿真性能,需要考虑两个方面

    • —个是建模自身的运算优化,另外一个是模型之间的通信优化。
    • 前者需要依靠开发者的经验还有性能分析工具来逐步优化模型。
    • 后者则可以通过将通信频率降低、内容体积增大的方式,来减少由不同进程之间同步带来的资源损耗。
    • TLM正是从通信优化角度提出的—种抽象通信方式。

            TLM是一种基于事务(transaction)的通信方式,通常在高抽象级语言例如SystemC或者SV/UVM中作为模块之间的通讯方式。

            TLM通信需要两个通信的对象,这两个对象分别称为initiator和target。区分它们的方法在于,谁先发起通信请求,谁就属于initiator,而谁作为发起通信的响应方,谁就属于target。
            在初学过程中读者们还应该注意,通信发起方并不代表了transaction的流向起点,即不一定数据是从initiator流向target,也可能是从target流向了initiator。
            因此按照transaction的流向,我们又可以将两个对象分为producer和consumer。区分它们的方法是,数据从哪里产生,它就属于producer,而数据流向了哪里,它就属于consumer。

            initiator与target的关系同producer与consumer的关系不是固定的。有了两个参与通信的对象之后,用户需要将TLM通信方法在target一端中实现,以便于initiator将来作为发起方可以调用target的通信方法,实现数据传输

            在target实现了必要的通信方法之后,最后一步我们需要将两个对象进行连接,这需要在两个对象中创建TLM端口,继而在更高层次中将这两个对象进行连接

    比如monitor和scoreboard,monitor是发起端;driver和generator,driver是发起端,get到,消化后,put rsp回去。

    分类

    我们可以将TLM通信步骤分为:

    • 分辨出initiator和target,producer和consumer。
    • 在target中实现TLM通信方法。
    • 在两个对象中创建TLM端口。
    • 更高层次中将两个对象的端口进行连接

    从数据流向来看,传输方向可以分为单向(unidirection)和双向(bidirection) 。

    • 单向传输:由initiator发起request transaction。
    • 双向传输:由initiator发起request transaction,传送至target;而target在消化了request transaction后,会发起response transaction,继而返回给initiator。

    端口的按照类型可以划分为三种:

    • port:经常作为initiator的发起端,initiator凭借port才可以访问target的TLM通信方法
    • export:作为initiator和target中间层次的端口。(一定不是终点)
    • imp:只能作为target接收request的末端,它无法作为中间层次的端口,所以imp的连接无法再次延伸。

    如果将传输方向和端口类型加以组合,可帮助理解TLM端口的继承树。TLM端口一共可以分为六类:(既不是object也不是component,无法使用工厂create)

     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)

     端口的使用

            就单向端口而言,声明port和export作为request发起方,需要指定transaction类型参数,而声明imp作为request接收方,不但需要指定transaction类型,也需要指定它所在的component类型。
            就声明双向端口而言,指定参数需要考虑双向传输的因素,将传输类型transaction拆分为request transaction类型和response transaction类型。
    从对应连接关系得出TLM端口连接的—般做法:

    • 在initiator端例化port,在中间层次例化export,在target端例化imp。
    • 多个port可以连接到同一个export或者imp;但是单个port或者export无法连接多个imp。这可以抽象为多个initiator可以对同一个target发起request,但是同一个initiator无法连接多个target。
    • port应为request起点,imp应为request终点,而中间可以穿越多个层次。基于单元组件的自闭性考虑,建议在穿越的中间层次声明export,继而通过层层连接实现数据通路。
    • port可以连接port、export或者imp; export可以连接export或者imp;imp只能作为数据传送的终点,无法扩展连接。连接的话,一般是initiator作为发起连接的对象,从左往右如上。

    示例

    从示例中可以得出关于建立TLM通信的常规步骤:

    • 定义TLM传输中的数据类型,上面分别定义了request类和response类。
    • 分别在各个层次的component中声明和创建TLM端口对象。
    • 通过connect()函数完成端口之间的连接。
    • 在imp端口类中要实现需要提供给initiator的可调用方法。例如,在comp2中由于有一个uvm_nonblocking put_imp #(request,comp2) nbp_imp,因此需要实现两个方法try_put()和can_put();而comp4中有一个uvm_blocking get_imp #(request, comp4)bg _imp,则需要实现对应的方法get()。
    • 需要注意的是,必须在imp端口类中实现对应方法,否则端口即使连接也无法实现数据传输。

    单向通信

            单向通信(unidirectional communication)指的是从initiator到target之间的数据流向是单一方向的,或者说initiator和target只能扮演producer和consumer中的一个角色。
    在UVM中,单—数据流向的TLM端口有很多类型:(PORT分别表示为port,export,import)

    • uvm_blocking_put_PORT
    • uvm_nonblocking_put_PORT
    • uvm put_PORT
    • uvm blocking_get_PORT
    • uvm_nonblocking_get_PORT
    • uvm_get_PORT
    • uvm_blocking_peek_PORT
    • uvm_nonblocking_peek_PORT
    • uvm_peek_PORT
    • uvm_blocking_get_peek_PORT
    • uvm_nonblocking_get_peek_PORT
    • uvm_get_peek_PORT

    按照UVM端口名的命名规则,它们指出了通信的两个要素:

    • 是否是阻塞(blocking)的方式(即可以等待延时)
    • 何种通信方法

     方法

            阻塞传输方式将blocking前缀作为函数名的一部分,而非阻塞方式则名为nonblocking。阻塞端口的方法类型为task,这保证了可以实现事件等待和延时;非阻塞端口的方式类型为function,这确保了方法调用可以立即返回
            我们从方法名也可以发现,例如uvm_blocking _put_PORT提供的方法task put()会在数据传送完后返回,uvm_nonblocking_put_PORT对应的两个函数try_put()和can_put()是立刻返回的。
            uvm_put_PORT则分别提供了blocking和nonblocking的方法,这为通讯方式提供了更多选择。blocking阻塞传输的方法包含:

    • Put(): initiator先生成数据T t,同时将该数据传送至target。
    • Get(): initiator从target获取数据Tt,而target中的该数据T t则应消耗。
    • Peek(): initiator从target获取数据Tt,而target中的该数据T t还应保留。

            与上述三种任务对应的nonblocking非阻塞方法分别是:. try_put()

    • can_put()
    • try_get()
    • can_get()
    • try_peek()
    • can_peek()

            这六个非阻塞函数与对应阻塞任务的区别在于,它们必须立即返回,如果try_xxx函数可以发送或者获取数据,那么函数应该返回1,如果执行失败则应返回0。或者通过can_xxx函数先试探target是否可以接收数据,如果可以,再通过try_xxx函数发送,提高数据发送的成功率。

    (这些方法不是port的,port不实现任何方法,方法是在target里的)

    1. component1
    2. uvm_blocking_put_port #(itrans) bp_port; //注意参数化的类,参数为定义的transaction类型
    3. uvm_nonblockINg_get_port #(otrans) ngb_port;
    4. this.bp_port.put(itr);    //组件发送tr时,是不知道发送到哪里的(哪个组件),实现了组件之间更好的
    5. //隔离。put方法取决于接下来连接的组件是否实现put方法。
    6. this.nbg_port.try_get(otr);
    7. component2
    8. class comp2 extends uvm_component;
    9. umv_blocking_put_imp #(itrans,comp2) bp_imp; //多传递一个类型,连接可以找到import端// //口,要想通过import找到组件里的方法,就将组件的句柄一同作为参数传入。 第一步通过connect找到import;第二步通过参数关系找到组件的put方法
    10. uvm_nonblocking_get_imp #(otrans,comp2) ngb_imp;
    11. itrans itr_q[$];
    12. ……
    13. task put(itrans t);
    14. itr_q.push_back(t); //target实际作为buffer,存储数据。从而使用put、get等方法。
    15. endtask
    16. ……
    17. endclass
    1.  知道传输数据类型是什么
    2. 对应组件创建端口,端口成对出现
    3. target实现方法
    4. 在顶层环境将组建端口连接,左侧为initiator,右侧为target

    首先compl例化了两个port端口:

    • .uvm_blocking_put_port #(itrans) bp_port;.
    • uvm_nonblocking_get_port #(otrans) nbg_port;

    comp2作为target则相应例化了两个对应的imp端口:.

    • uvm_blocking_put_imp #(itrans, comp2) bp_imp;
    • uvm_nonblocking _get_imp #(otrans, comp2) nbg_imp;

    env1环境将comp1与comp2连接之前,需要在comp2中实现两个端口对应的方法:

    • task put(itrans t)
    • function bit try_get (output otrans t)
    • function bit can_get();

    双向通信

            与单向通信相同的是,双向通信(bidirectional communication)的两端也分为initiator和target,但是数据流向在端对端之间是双向的。
            双向通信中的两端同时扮演着producer和consumer的角色,而initiator作为request发起方,在发起request之后,还会等待response返回。
    UVM双向端口分为以下类型:(transport既有req又有rsp。调用mater或slave时则只有一个,需要调用两次才行)

    • uvm_blocking_transport_PORT
    • uvm _nonblocking_transport_PORT
    • uvm_transport_PORT
    • uvm_blocking_master_PORT
    • uvm nonblocking_master_PORT
    • uvm master_PORT
    • uvm_biocking_slave_PORT
    • uvm_nonblocking_slave_PORT
    • uvm_slave_PORT

    分类

             双向端口按照通信握手方式可以分为:

    •  transport双向通信方式
    • master和slave双向通信方式

            transport端口通过transport()方法,可以在同一方法调用过程中完成REQ和RSP的发出和返回。
            master和slave的通信方式必须分别通过put、get和peek的调用,使用两个方法才可以完成一次握手通信。
            master端口的slave端口的区别在于,当initiator作为master时,它会发起REQ送至target端,而后再从target端获取RSP;当initiator使用slave端口时,它会先从target端获取REQ,而后将RSP送至target端。
            对于master端口或者slave端口的实现方式,类似于之前介绍的单向通信方式,只是imp端口所在的组件需要实现的方法更多了。

    1. comp1:
    2. uvm_blocking_transport_port #(itrans,otrans) bt_port; //两个tr类型
    3. this.bt_port.transport(itr,otr); //两个tr句柄
    4. comp2:
    5. uvm_blocking_transport_imp #(itrans,otrans) bt_im;
    6. task transport(itrans req,output rsp);
    7. ……
    8. c1.bt_port.connect(c2.bt_imp)

    多向通信

            多向通信(multi-directional communication)这个概念听起来容易让读者产生歧义,因为这种方式服务的仍然是两个组件之间的通信,而不是多个组件之间的通信,毕竟多个组件的通信仍然可以由基础的两个组件的通信方式来构建。
            多向通信指的是,如果initiator与target之间的相同TLM端口数目超过一个时的处理解决办法。
            compl有两个uvm_blocking put_port,而comp2有两个uvm_blocking_put_imp端口,我们对于端口例化可以给不同名字,连接也可以通过不同名字来索引,但问题在于comp2中需要实现两个task put(itrans t),又因为不同端口之间要求在imp端口一侧实现专属方法,这就造成了方法命名冲突,即无法在comp2中定义两个同名的put任务

             UVM通过端口宏声明方式来解决这一问题,它解决问题的核心在于让不同端口对应不同名的任务,这样便不会造成方法名的冲突。UVM为解决多向通信问题的宏按照端口名的命名方式分为:

    • `uvm_blocking_put_imp_decl (SFX)
    • 'uvm_nonblocking_put_imp_decl (SFX)
    • 'uvm_put_imp_decl (SFX)
    • 'uvm_blocking_get_imp_decl (SFX)
    • 'uvm_nonblocking_get_imp_decl (SFX)
    • 'uvm_get_imp_decl (SFx)
    • 'uvm_blocking_peek_imp_decl(SFX)
    • 'uvm_nonblocking_peek_imp__decl (SFX)
    • 'uvm_peek_imp_decl (SFX)
    • 'uvm_blocking_get_peek_imp_decl (SFX)
    • 'uvm_nonblocking_get_peek_imp_decl (SFX)
    • 'uvm_get_peek_imp_decl (SFX)
    • 'uvm_blocking_transport_imp_decl(SFX)
    • 'uvm_nonblocking_transport_imp_decl(SFX)
    • 'uvm_transport_imp_decl (SFX)
    • 'uvm_blocking_master_imp_decl (SFX)
    • 'uvm_nonblocking_master_imp_decl (SFX)
    • 'uvm_peek_master_decl (SFX)
    • `uvm_blocking_slave_imp_decl (SFX)
    • `uvm_nonblocking_slave_imp_decl (SFX)
    • 'uvm_peek_slave_decl (SFX)

    (decl:表示声明;SFX表示后缀名称)

    不同名import,定义不同方法

    1. `uvm_blocking_put_imp_decl(_p1)
    2. `uvm_blocking_put_imp_decl(_p2)
    3. comp1
    4. uvm_blocking_put_port #(itrans) bp_port1;
    5. uvm_blocking_put_port #(itrans) bp_port2;
    6. this.bp_port1.put(itr1); //调用方法都是put,取决于connect。若调用put1,编译错误。
    7. this.bp_port2.put(itr2); //注意此时不知道组件连接到同一个组件还是两个组件
    8. comp2
    9. uvm_blocking_put_imp_p1 #(itrans,comp2) bt_imp_p1; //参数化的类也加了后缀,帮助找到p1任务
    10. uvm_blocking_put_imp_p2 #(itrans,comp2) bt_imp_p2;
    11. `uvm_component_utils(comp2)
    12. itrans itr_q[$];
    13. semaphore key;
    14. task put_p1(itrans t); //方法也加后缀,同端口一致
    15. task put_p2(itrans t);
    16. key.get();
    17. itr_q.push_back(t);
    18. key.put; //资源共享,用semaphore进行互斥保护。比如put_p1的时候,另外一个拿不到
    19. c1.bp_port1.connect(c2.bt_imp_p1);
    20. c1.np_port2.connect(c2.bt_imp_p2);

            当一个组件的两个端口通过相向方法(譬如task put())向另外一个组件传输数据时,就需要使用上述的宏,分别声明两个不同的imp类型,完整的实现步骤包括:

    • 选择正确的imp宏来定义不同的imp端口类型,而宏的参数SFX(后缀名)也会转化为相应的imp端口类型名。
    • 在imp所例化的组件中,分别实现不同的put_SFX方法。
    • 在port所例化的组件中,不需要对目标imp端口类型做区分,例如comp1中的bp_port1和bp_port2为相同的端口类型。
    • 对于compl调用put()方法,它只需要选择bp_port1或者bp_port2,而不需要更替put()方法名,即仍然按照put()来调用而不是put_p1()或者put_p2()。
    • 在上层环境应该连接comp1和comp2的TLM端口。

    总结

    • 用户只需要在例化多个imp端口的组件中实现不同名称的方法,使其与对应imp类型名保持—致。
    • 而对于port端口一侧的组件,则不需关心调用的方法名称,因为该方法名并不会发生改变
    • 所以通过这种方式可以防止通信方法名的冲突,从而解决多向通信的问题。

    通信管道

            TLM通信的实现方式,这些通信有一个共同的地方即都是端对端的,同时在target—端需要实现传输方法,例如put()或者get()。
            这种方式在实际使用也可能会给用户带来一些烦恼,如何可以不自己实现这些传输方法,同时可以享受到TLM的好处?(是否先可以先例化buffer,再实现方法,形成固定形式,不需要自己实现方法)
            对于monitor、coverage collector等组件在传输数据时,会存在—端到多端的传输,如何解决这一问题?
            几个TLM组件和端口可以帮助用户免除这些烦恼:

    • TLM FIFO 
    • analysis port
    • analysis TLM FIFO
    • request & response通信管道

    TLM_FIFO

            在一般TLM传输过程中,无论是initiator给target发起一个transaction,还是initiator从target获取一个transaction,transaction最终都会流向consumer中(initiator和target都可以是consumer) 。consumer在没有分析transaction时,我们希望将该对象先存储到本地FIFO中供稍后使用。用户需要分别在两个组件中例化端口,同时在target中实现相应的传输方法。多数情况下,需要实现的传输方法都是相似的,方法的主要内容即是为了实现一个数据缓存功能。
            TLM FIFO uvm_tlm_fifo类是一个新组件,它继承于uvm_component类,而且已经预先内置了多个端口以及实现了多个对应方法供用户使用

            uvm_tlm_fifo的功能类似于mailbox,不同的地方在于uvm_tlm_fifo提供了各种端口供用户使用。我们推荐在initiator端例化put_port或者get_peek_port,来匹配uvm_tlm_fifo的端口类型。            当然,如果用户例化了其它类型的端口,uvm_tlm_fifo还提供put、get以及peek对应的端口:

    buffer只有一个,存放的数据类型是固定的。端口都是import类型,即使名称为export。

    • uvm_put_imp #(T, this_type) blocking_put_export;
    • uvm put_imp #(T, this_type) nonblocking_put_export;
    • uvm_get_peek_imp #(T, this_type) blocking_get_export;
    • uvm_get _peek_imp #(T, this_type) nonblocking_get_export;
    • uvm_get_peek_imp #(T, this_type) get_export;
    • uvm_get _peek_imp #(T, this_type) blocking_peek_export;
    • uvm_get_peek_imp #(T, this_type) nonblocking_peek_export;
    • uvm_get_peek_imp #(T, this_type) peek_export;
    • uvm_get _peek_imp #(T, this_type) blocking_get_peek_export;
    • uvm_get _peek_imp #(T, this_type) nonblocking_get_peek_export;

    analysis port

            除了端对端的传输,在一些情况下还有多个组件会对同一个数据进行运算处理
            如果这个数据是从同一个源的TLM端口发出到达不同组件,这就要求该种端口可以满足从—端到多端的需求。
            如果数据源端发生变化需要通知跟它关联的多个组件时,我们可以利用软件的设计模式之一观察者模式(observer pattern)(广播模式)来实现。(端到端必须有两个,这种广播涉及的听众可能没有,也可能多个)observer pattern的核心在于:

    • 第一,这是从一个initiator端到多个target端的方式。
    • 第二,analysis port采取的是"push"模式,即从initiator端调用多个target端的write()函数来实现数据传输。(函数立即返回,不耗时)

    1. initiator.ap.connect(target1.aimp);
    2. initiator.ap.connect(target2.aimp);
    3. initiator.ap.connect(target3.aimp);

             一个典型的analysis port类型端口的连接方式,类似于其它TLM端口的是,按照传输方法和端口方向组合可以将analysis port分为uvm_analysis_port、uvm_analysis_export以及
    uvm_analysis_imp。
            target一侧例化了uvm_analysis_imp后还需要实现write()函数。

            在顶层可以将initiator端的uvm_analysis_port同多个target端的uvm_analysis_imp进行连接。
            在initiator端调用write()函数时,实际上它是通过循环的方式将所有连接的target端内置的write()函数进行了调用
            由于函数立即返回的特点,无论连接多少个target端,initiator端调用write()函数总是可以立即返回的。不同于之前单一端口函数调用的是,即使没有target与之相连,调用write()函数时也不会发生错误。(循环时发现没有target相连就不调用write)

    Analysis TLM FIFO

            由于analysis端口提出实现了一端到多端的TLM数据传输,而一个新的数据缓存组件类uvm_tlm_analysis_fifo为用户们提供了可以搭配uvm_analysis_port端口uvm_analysis_imp端口和write()函数。
            uvm_tlm_analysis_fifo类继承于uvm_tlm_fifo,这表明它本身具有面向单一TLM端口的数据缓存特性,而同时该类又有一个uvm_analysis_imp端口analysis_export并且实现了write()函数: uvm_analysis_imp #(T,uvm_tlm_analysis_fifo #(T)) analysis_export;

    (左侧一对多,右侧一对一,端到端)

            基于initiator到多个target的连接方式,用户如果想轻松实现一端到多端的数据传输,可以插入多个uvm_tlm_analysis_fifo,(相比maibox,支持各种形式端口)我们这里给出连接方式:

    • 将initiator的analysis port连接到tlm_analysis_fifo的get_export端口,这样数据可以从initiator发起,写入到各个tlm_analysis_fifo的缓存中。
    • 将多个target的get_port连接到tlm_analysis_fifo的get_export,注意保持端口类型的匹配,这样从target一侧只需要调用get()方法就可以得到先前存储在tlm_analysis_fifo中的数据。

    initiator.ap.connect(tlm_analysis_fifo1.analysis_export);

    target1.get_port.connect(tlm_analysis_fifo1.get_export)  (target是针对initiator而言)

    initiator.ap.connect(t1m_analysis_fifo2.analysis_export) ;

    target2.get _port.connect(tlm_analysis_fifo2.get_export); //注意这里是从target开始

    initiator.ap.connect(tlm_analysis_fifo3.analysis_export);

    target3.get_port.connect(tlm_analysis_fifo3.get_export) ;

    initiator和target不用实现方法,可以直接调用put和get,这些方法在analysis_fifo里都有。(这也是为什么taget调用get方法,连接时在前面吗?import变为port)

    Request&Response通信管道

            双向通信端口transport,即通过在target端实现transport()方法可以在一次传输中既发送request又可以接收response。
            UVM提供了两种简便的通信管道,它们作为数据缓存区域,既有TLM端口从外侧接收request和response,同时也有TLM端口供外侧获取request和response。这两种TLM通信管道分别是:

    • uvm_tlm_req_rsp_channel
    • uvm_tlm_transport_channel

    对于uvm_tlm_req_rsp_channel而言,它提供的端口首先是单一方向的,为了让端口列表清爽一些,我们只列出该类例化的端口:

    • uvm_put_export #(REQ) put_request_export;
    • uvm_put_export #(RSP) put_response_export;
    • uvm_get _peek_export #(RSP) get_peek_response_export;
    • uvm_get_peek_export #(REQ) get _peek_request_export;
    • uvm_analysis_port#(REQ) request_ap ;
    • uvm_analysis_port#(RSP) response_ap;
    • uvm_master_imp #(REQ,RSP,this_type,uvm_tlm_fifo #(REQ),uvm_tlm _fifo #(RSP) )master_export;
    • uvm_slave_imp#(REQ,RSP,this_type,uvm_tlm_fifo #(REQ),uvm_tlm_fifo #(RSP) )slave_export;

             有了这么多丰富的端口,用户可以在使用成对的端口进行数据的存储和访问。需要注意的是,uvm_tlm_req_rsp_channel内部例化了两个mailbox分别用来存储request和response:

    • protected uvm_tlm_fifo #(REQ) m_request_fifo;
    • protected uvm_tlm_fifo #(RSP)m_response_fifo;

            例如initiator端可以连接channel的put_request_export,target连接channel的get_peek_request_export,同时target连接channel的put_response_export,initiator连接channel的get_peek_response_export端口。

            通过这种对应的方式,使得initiator与target可以利用uvm_tlm_req_rsp_channel进行request与response的数据交换。

    • initiator.put_port.connect(req_rsp_channel.put_request_export);
    • target.get_peek_port.connect(req_rsp_channel.get_peek_request_export) ;
    • target.put _port.connect(req_rsp_channel.put_response_export);
    • initiator.get_peek_port.connect(req_rsp_channel.get_peek_response_export);

             也可以利用另外—种连接方式:(相比前面借助两个端口,发送两次(put、get)。这里只要一个端口,还是调用两次)

    • initiator.master_port.connect(req_rsp_channel.master_export);
    • target.slave_port.connect(req_rsp_channel.slave_export) ;

            通过所述的这些方式,我们可以实现initiator与target之间自由的request和response传输,而这两种连接方式仍然需要分别调用两次方法才可以完成request和response的传输。

            在uvm_tlm_req_rsp_channel的基础上,UVM又添加了具备transport端口的管道组件uvm_tlm_transport_channel类。它继承于uvm_tlm_req_rsp_channel,并且新例化了transport端口:
    uvm_transport_imp #(REQ,RSP,this_type) transport_export;

     左侧变为transport_port,右侧target依然是slave_port

            新添加的这个TLM FIFO组件类型是针对于一些无法流水化处理的request和response传输,例如initiator—端要求每次发送完request,必须等到response接收到以后才可以发送下一个request,这时transport()方法就可以满足这一需求。(前面只要单侧get、put就结束?)
            如果将上面的传输方式进行修改,需要变化的是initiator端到req_rsp_channel的连接,应该修改为︰
    initiator.transport_port.connect(transport_channel.transport_export)

            至于transport_channel和target之间的连接,则可以仍然保留之前的单向传输连接方式。

  • 相关阅读:
    k8s集群部署es
    App切换到后台后如何保持持续定位?
    【vue3学习笔记】compositionAPI的优势;Fragment组件;Teleport组件
    el-table通过这样封装可以实现校验-表格校验的原理
    【MySQL】源码编译MySQL8.x+升级gcc+升级cmake(亲测完整版)
    算法实战-Hacker News内容热度推荐算法
    Element-Ui el-table 动态添加行
    云原生之旅 - 12)使用 Kaniko 在 Kubernetes上构建 Docker 容器镜像
    IT30--制造业数字化战略规划(论文1)
    数据分析的流程是啥样?
  • 原文地址:https://blog.csdn.net/qq_44455456/article/details/126651542