UVM--TLM通信

TLM通信

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

系统原型&芯片验证

系统原型一般通过硬件功能描述文档模拟硬件行为, 行为要求不同于RTL模型。 系统原型提供一个准确到硬件比特级别、 按照地址段访问、 不依赖于时钟周期的模型, 该模型通常基于SystemC语言,而系统原型中各模块通过TLM可以实现宽松时间范围内的数据包传输。

芯片验证是在RTL模型初步建立后, 通过验证语言和方法学如SV/UVM来构建验证平台。该平台的特点是验证环境整体基于面向对象开发,组件之间的通信基于TLM, 而在driver与硬件接口之间需将TLM抽象事务降解到基于时钟的信号级别。 

系统原型阶段和芯片验证阶段均使用了TLM通信方式。前者是为了更快地实现硬件原型之间的数据通信,后者是为了更快地实现验证组件之间的数据通信
仿真速度是TLM对项目进度的最大贡献,同时TLM传输中的事务又可以保证足够大的信息量和准确性。

TLM并不是某一种语言产物,而是作为一种提高数据传输抽象级的标准存在。

高抽象级的数据通信,可以用来表示宽松时间跨度内的硬件通信数据,而通过降低颗粒硬件周期内的数据打包为一个大数据,非常有利于整体环境的仿真速度。

TLM的运用越来越广泛,包括emulator同硬件仿真器的协同仿真框架中,也建议使用这种方式来降低混合环境之间的通信频率,以便提高整体的运算效率。

TLM 是一种基于事务 (transaction) 的通信方式, 通常在高抽象级语言如 SystemC 或SV/UVM中作为模块之间的通信方式。TLM成功地将模块内的计算和模块之间的通信从时间跨度方面剥离开了。 在抽象语言建模体系中, 各模块通过一系列并行的进程实现, 同时利用通信和计算模拟出正确的行为。

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

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端口,继而在更高层次中将这两个对 象进行连接。

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

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

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

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

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

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

如果将传输方向和端口类型加以组合,综合下来, TLM 端口一共分为 6 类:

•     uvm_UNDIR_port #(trans_t)     uvm_单双向_端口类型 # (类型参数)
•     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 只能作为数据传送的终点, 无法扩展连接。

class request extends uvm_transaction;//声明了一个request类继承与transaction
  byte cmd; 
  int addr;
  int req; //定义传递的数据
  endclass 
  
  class response extends uvm_transaction;//声明了一个response类继承与transaction
  byte cmd; 
  int addr; 
  int rsp;
  int status;
  endclass 
  
  class compl extends uvm_agent;//声明了一个comp1组件继承与uvm_agent
  uvm_blocking_get_port #(request) bg_port; //定义端口
  `uvm_component_utils(compl) //注册
  ...  //缺少例化
  endclass 
  
  class comp2 extends uvm_agent;//声明了一个comp2组件继承与uvm_agent
  uvm_blocking_get_port #(request) bg_port; //定义端口
  uvm_nonblocking_put_imp #(request,comp2) nbp_imp; //comp2作为接收方,还需要指定它所在的 component 类型
  `uvm_component_utils(comp2)//注册
  function bit try_put (request req); //在target端定义方法,注意try_put,非阻塞function中使用
  function bit can_put(); 
  endclass 
  
  class comp3 extends uvm_agent;//声明了一个comp3组件继承与uvm_agent
  uvm_blocking_transport_port #(request, response) bt_port;//双向端口的定义
  `uvm_component_utils(comp3)//注册
  ...//缺少例化
  endclass 
  
  class comp4 extends uvm_agent;//声明了一个comp4组件继承与uvm_agent
  uvm_blocking_get_imp #(request, comp4) bg_imp; //comp4作为接收方,还需要指定它所在的 component 类型
  uvm_nonblocking_put_port #(request) nbp_port;  //定义端口
  `uvm_component_utils(comp4) //注册
  ... //缺少例化
  task get (output request req); //定义任务,get()为阻塞型,用task
  endclass 
  
  class comp5 extends uvm_agent; 
  uvm_blocking_transport_imp #(request, response, comp5) bt_imp; 
  `uvm_component_utils(comp5) 
  ...//缺少例化
  task transport (request req, output response rsp); 
  endclass 
  
  class agent1 extends uvm_agent;//声明了一个agent组件继承与uvm_agent
  uvm_blocking_get_port #(request) bg_port;
  uvm_nonblocking_put_export #(request) nbp_exp; 
  uvm_blocking_transport_port #(request, response) bt_port;
  compl cl; 
  comp2 c2; 
  comp3 c3;
  'uvm_component_utils(agentl)
  ...
  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); 
  c3 = comp3::type_id::create("c3", this);//组件例化在build.phase
  endfunction 
  function void connect_phase(uvm_phase phase);
  super.connect_phase(phase); 
  cl.bg_port.connect(this.bg_port); 
  c2.bg_port.connect(this.bg_port); 
  this.nbp_exp.connect(c2.nbp_imp); 
  c3.bt_port.connect(this.bt_port); //端口连接
  endfunction 
  endclass 
  
  class envl extends uvm_env; 
  agentl al; 
  comp4 c4; 
  comps c5; 
  `uvm_component_utils(envl) 
   function void build_phase(uvm_phase phase);
   super.build_phase(phase); 
   al = agentl::type_id::create("al", this); 
   c4 = comp4::type_id::create ("c4", this); 
   c5 = comp5::type_id::create ("c5", this); //组件例化在build.phase
   endfunction:build_phase 
   function void connect_phase(uvm_phase phase);
   super.connect_phase(phase); 
   al.bg_port.connect(c4.bg_imp); 
   c4.nbp_port. connect(al.nbp_exp); 
   al.bt_port.connect(c5.bt_imp); //端口连接
   endfunction: connect_phase 
  endclass 

从示例中可以得出关于建立TLM通信的常规步骤:
    定义TLM传输中的数据类型,上面分别定义了request类和response 类。
•    分别在各个层次的component中声明和创建TLM端口对象。
•    通过connect()函数完成端口之间的连接。
•    在imp端口类中要实现需要提供给initiator的可调用方法。例如,在 comp2中由于有一个uvm_nonblocking_put_imp #(request, comp2) nbp_imp, 因此需要实现两个方法try_pu( )和can_put();而comp4中有一个uvm blocking_get_imp #(request, comp4) bg_imp, 则需要实现对应的方法get()。
•    需要注意的是,必须在imp端口类中实现对应方法,否则端口即使连接也无法实现数据传输。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

创芯人

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

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

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

打赏作者

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

抵扣说明:

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

余额充值