UVM——TLM通信

1. 概述

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

  • TLM是一种基于事务(transaction)的通信方式,通常在高抽象级语言例如SystemC或者SV/UVM中作为模块之间的通讯方式
  • TLM成功地将模块内的计算模块之间的通信从时间跨度方面剥离
  • 在抽象语言建模体系中,各个模块通过一系列并行的进行实现,并通过通信和计算来模拟出正确的行为
  • 如果要提高系统模型的仿真性能,要考虑两个方面:
    • 一个是建模自身的运算优化,另外一个是模型之间的通信优化
    • 前者需要一开开发者的经验还有性能分析工具来逐步优化模型
    • 后者则可以通过将通信频率降低、内容体积增大的方式,来减少由不同进程之间同步带来的资源损耗
    • TLM正是从通信优化角度提出的一种抽象通信方式

2. 基本概念

  • TLM通信需要两个通信的对象,这两个对象分别称为initiatortarget。谁先发起通信请求,谁就属于initiator,而谁作为发起通信的相应方,谁就属于target
  • 通信发起方并不代表了transaction的流向起点,即不一定数据是从initiator流向target,也可能是从target流向了initiator
  • 因此按照transaction的流向,又可以将两个对象分成producerconsumer。区分它们的方法是,数据从哪里产生,它就属于producer,数据流向哪里,它就属于consumer
  • initiator和target的关系同producer与consumer的关系是不固定的
  • 有了两个参与通信的对象之后,用户需要将TLM通信方法在target一端中实现,以便于initiator将来作为发起方可以调用target的通信方法,实现数据传输
  • 在target实现了必要的通信方法之后,最后一步需要将两个对象进行连接,这需要在两个对象中创建TLM端口,继而在更高层次中将这两个对象进行连接 put / get
    在这里插入图片描述

3. 分类

  • 可以将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端口一共可以分为六类:
    • 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没有继承于object,而是继承于uvm_void,所以在创建的时候不能使用typed_id::create(),

4. 端口的使用

在这里插入图片描述

  • 单向端口而言,声明portexport作为request发起方,需要指定transaction类型参数,而声明imp作为request接收方,不但需要指令transaction类型,也需要指定它所在的component类型
  • 就声明双向端口而言,指定参数需要考虑双向传输的因素,将传输类型transaction拆分为request_transaction类型和response_transaction类型
  • 从对应连接关系得出TLM端口连接的一般做法:
    • initiator端例化port,在中间层次例化export,在target端例化imp
    • 多个port可以连接到同一个export或者imp,但是单个port或者export无法连接多个imp
    • port应为request起点,imp应为request终点,而中间可以穿越多个层次。基于单元组件的自闭考虑,在穿越的中间层次声明export,继而通过层次连接实现数据通路
    • port可以连接portexport或者impexport可以连接export或者impimp只能作为数据传送的终点,无法扩展连接

单向通信:
多个initiator可以对同一个target发起request
同一个initiator只能对一个target发起请求


代码实现

class request extends uvm_transaction;
	byte cmd;
	int addr;
	int req;
endclass

class response extends uvm_transaction;
	byte cmd;
	int addr;
	int rsp;
	int status;
endclass

class comp1 extends uvm_agent;
	uvm_blocking_get_port #(request) bg_port;
	`uvm_component_utils(comp1)
	...
endclass

class comp2 extends uvm_agent;
	uvm_blocking_get_port #(request) bg_port;
	uvm_noblocking_put_imp #(request, comp2) nbp_imp;
	`uvm_component_utils(comp2)
	...
	function bit try_put(request req);
	
	function bit can_put();
endclass

class comp3 extends uvm_agent;
	uvm_blocking_transport_port #(request, response) bt_port;
	`uvm_component_utils(comp3)
	...
endclass

class comp4 extends uvm_agent;
	uvm_blocking_get_imp #(request, comp4) bg_imp;
	uvm_noblocking_put_port #(request) nbp_port;
	`uvm_component_utils(comp4)
	...
	task get(output request req);
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;
	uvm_blocking_get_port #(request) bg_port;
	uvm_noblocking_put_export #(request) nbp_exp;
	uvm_blocking_transport_port #(request, response) bt_port;
	comp1 c1;
	comp2 c2;
	comp3 c3;
	`uvm_component_utils(agent1)
	...
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		c1 = comp1::type_id::create("c1", this);
		c2 = comp2::type_id::create("c2", this);
		c3 = comp3::type_id::create("c3", this);
	endfunction
	function void connect_phase(uvm_phase phase);
		super.connect_phase(phase);
		c1.bg_port.connect(this.bg_port);	// initiator.connect(target)
		c2.bg_port.connect(this.bg_port);
		this.nbp_exp.connect(c2.nbp_imp);
		c3.bt_port.connect(this.bt_port);
	endfunction
	
class env1 extends uvm_env;
	agent1 a1;
	comp4 c4;
	comp5 c5;
	`uvm_component_utils(env1)
	...
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		a1 = agent1::type_id::create("a1", this);
		c4 = comp4::type_id::create("c4", this);
		c5 = comp5::type_id::create("c5", this);
	endfunction
	function void connect_phase(uvm_phase phase);
		super.connect_phase(phase);
		a1.bg_port.connect(c4.bg_imp);
		c4.nbp_port.connect(a1.nbp_exp);
		a1.bt_port.connect(c5.bt_imp);
	endfunction
endclass

在这里插入图片描述


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

  1. 定义TLM传输中的数据类型,上面分别定义了request类和response类
  2. 分别在各个层次的component中声明和创建TLM端口对象
  3. 通过connect()函数完成端口之间的连接
  4. 在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();
  5. 需要注意的是,必须在imp端口类中实现对应方法,否则端口即使连接也无法实现数据传输
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值