UVM 事务级建模TLM 单向/多向通信 端口 FIFO通信

事务级建模 TLM(transaction level modeling):

TLM通信是基于组件请求的发起和响应,组件之间涉及事务(transaction, trans)的传输,如sequencer与driver、monitor与scoreboard、monitor与coverage model。TLM仅仅适用于component之间的连接

TLM在各组件之间建立一个专用通道,让信息只在这个通道里流动,因此避免了全局变量,public成员变量和config机制通讯时带来的不便

TLM port

数据流方法 put/get/transport

TLM通信通信类型: put/get/transport

put操作: 组件 A 将一个 transaction 交给组件 B
在这里插入图片描述

get 操作(拿回来一个transaction):是组件 A 向组件 B 请求一个transaction
在这里插入图片描述
对于get系列端口,有get()、try_get() 和 can_get() 等操作

  • get() 是一个阻塞调用,如果FIFO中没有transsaction,任务get()将等待
  • try_get() 是一个非阻塞调用,没从FIFO中拿到transaction,也会立即返回,try_get()的返回值指示是否返回成功,事务立即可用,则将其写入输出参数并返回 1;否则,不修改输出参数并返回 0。
  • can_get() 是否可以提供新事务,返回 1,否则返回 0。均不修改输出参数

transport 操作(先丢出去然后再拿回来一个transaction):组件 A 将一个 transaction 交给组件 B,然后向组件 B 请求一个 transaction

在这里插入图片描述

peek:initiator 向 target 索取 trans 的复制

参考链接 https://verificationacademy.com/verification-methodology-reference/uvm/docs_1.2/html/

在这里插入图片描述


组件的通信端口阻塞性:

连接端口实现通信

  • 发起通信请求的组件是initiator,响应请求的组件就是target

  • blocking(阻塞)与 nonblocking(非阻塞)

  • 阻塞 (函数只有在得到结果之后才会返回):阻塞 PUT 表示 A 将该 transaction 交给 B后,等待 B 接收完成。

  • 非阻塞 (函数没得到结果立刻返回):非阻塞 PUT 表示 A 将该 transaction 交给 B后,PUT不再等待 B 接收完成


控制流 port 、export 与 imp:

表示从initiator向target发起的请求的传输方向

  • port:表示请求的发送方,initiator 中的端口常为 port
  • export:表示请求的中转方,在 initiator 和 target 的中间层端口常为 export
  • imp:即 import,表示请求的接收方,target中的端口常为 imp
    在这里插入图片描述
    如图,组件1和组件2有两对端口建立通信,第一对端口是put型、第二对端口是get型

根据 uvm_阻塞性_数据流_控制流 组合成 端口类:

例如端口类 uvm_blocking_put_port、uvm_nonblocking_get_imp 等等

  • 端口类 不是object类,也不是component类,而是uvm_void,创建的时候要用new(),而不是type_id::create()
  • 用new()创建对象可以放在func new()中,也可以放在build_phase中
  • 用type_id::create()创建对象只能放在build_phase中
  • 在创建组件的时候,一定要用type_id::create(),提供override的可能,还可以实现字符串的层次化

建立 TLM port 单向通信步骤

  • 定义TLM传输的数据类型trans
  • 确定TLM通信的initiator component 和 target component,并且initiator、target和中间层,在各自的build_phase中使用new函数,分别例化port端口、相同类型的imp端口 和 相同类型的export端口
  • 在target实现该端口的数据流方法
  • initiator和target的上层,在connect_phase使用connect方法建立连接
  • initiator在run_phase调用相应的数据流方法,注意initiator调用的数据流方法是在target内实现的数据流方法

在这里插入图片描述
1.定义 transaction 事务类型

class trans1 extends uvm_sequence_item;			//定义事务类型
	rand int id;
	rand int data;
	...
endclass

class trans2 extends uvm_sequence_item;
	int id;
	int data;
	...
endclass

2.确定TLM通信的 initiator component 和 target component,在各自build_phase中使用new函数,分别例化端口

注意端口是uvm_void, uvm_void的例化方式只能是new(string name, uvm_component parent);不能用factory(create)机制例化,会编译报错!!!

// 定义 initiator component

class comp1 extends uvm_component;
	`uvm_component_utils(comp1)			// component注册uvm 			
	
	uvm_blocking_put_port#(trans1) bp_port;				//initiator声明端口,类型为阻塞的put,用于发送trans1。 port端口传入1个参数(trans)
	uvm_nonblocking_get_port#(trans2) ng_port;			//initiator声明端口,类型为非阻塞的get,用于请求trans2
	
	function void build_phase(uvm_phase phase);   // 在build_phase中使用new函数,例化端口, uvm_void的例化方式只能是new
		super.build_phase(phase);
		bp_port = new("bp_port",this);			//initiator例化端口,注意不继承于uvm_object或uvm_component,不可用factory机制例化!!!
		ng_port = new("ng_port",this);			//initiator例化端口,注意继承于uvm_object或uvm_component,不可用factory机制例化!!!
		...
	endfunction
	
	task run_phase(uvm_phase phase);   // 在run_phase调用相应的数据流方法:put/get/transport
		trans1 t1;
		trans2 t2;
		fork
			repeat(5) begin
				t1 = trans1::type_id_create("t1");    // component用create例化,并随机化trans
				assert(t1.randomize());    //用断言检查随机化,随机化成功,函数返回1;失败返回0
				bp_port.put(t1);					// 端口调用方法put
			end
			forever begin
				if(ng_port.try_get(t2))				// 端口调用get的非阻塞方法 try_get,没拿到transaction,也会立即返回,
					break;
			end
		join
	endtask
	
endclass

initiator调用的数据流方法put/get/transport,是在target内实现的数据流方法

// 定义 target component

class comp2 extends uvm_component;
	`uvm_component_utils(comp2)		// component注册uvm
	
	uvm_blocking_put_imp#(trans1,comp2) bp_imp;		// target声明端口imp,imp端口传入两个参数(trans,comp)
	uvm_nonblocking_get_imp#(trans2,comp2) ng_imp;	// target声明端口imp
	trans1 tq[$];									// target数据缓存 队列
	
	function void build_phase(uvm_phase phase);    // 在build_phase中使用new函数,例化端口
		super.build_phase(phase);
		bp_imp = new("bp_imp",this);				//target例化端口
		ng_imp = new("ng_imp",this);				//target例化端口
		...
	endfunction
	
	// initiator调用的数据流方法put/get/transport,是在target内实现的数据流方法
	task put(trans1 t);
		tq.push_back(t);     // target被发送数据流, 数据缓存队列收下trans1
	endtask
	function bit try_get(output trans2 t);			// target 被请求数据流, 对于target来说,是output
		...
	endfunction
	function bit can_get();						// target 被询问是否可以请求数据流
		return (tq.size() > 0);   // 从initiator接收到的trans1放进了队列,队列里有数据,则可以返回
	endfunction
	
endclass	

3.在env的build_phase例化initiator component 和 target component,在connect_phase使用connect方法建立连接,连接 initiator component 和 target component

class env extends uvm_env;
	`uvm_component_utils(env);     // component注册uvm
	
	comp1 c1;	  // 声明 initiator component 和 target component
	comp2 c2;
	
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		c1 = comp1::type_id::create("c1",this);    // 例化compoent组件用create
		c2 = comp2::type_id::create("c2",this);
		...
	endfunction
	
	function void connect_phase(uvm_phase phase);		// 在connect_phase使用connect方法连接initiator端口连接target端口
		super.connect_phase(phase);
		c1.bp_port.connect(c2.bp_imp);    // initiator.port_put端口.连接(target.imp_put端口)
		c1.ng_port.connect(c2.ng_imp);    // initiator.port_get端口.连接(target.imp_get端口)
		...
	endfunction
endclass

单向通信补充情况:

  • PORT to PORT
    UVM支持带层次的连接关系;PORT与PORT之间的连接不止局限于两层,可以有无限多层
    port to port
  • EXPORT to EXPORT
    UVM也支持EXPORT与EXPORT之间的连接;EXPORT与EXPORT之间的连接也不只限于两层,也可以有无限多层
    在这里插入图片描述

TLM 多向通信(multi-directional communication)

多向通信指的是,如果initiator与target之间的相同TLM端口超过一个时的处理解决办法。
在这里插入图片描述

对于端口的例化可以给不同的名字,而连接也可以通过不同的名字来索引,但是问题在于comp2中需要实现两个task put(trans t)。因为不同的端口组之间要求在imp端口一侧实现专属的方法,这种要求造成了暂时的方法命名冲突,即无法在comp2中定义两个同名的put任务。

UVM通过延伸的端口宏声明方式来解决这一问题: 在全局通过宏注册后缀,例化加了后缀的新类型端口

// 定义 initiator

class comp1 extends uvm_component;
	`uvm_component_utils(comp1)
	
	uvm_blocking_put_port#(trans1) bp_port1;				
	uvm_blocking_put_port#(trans2) bp_port2;	
				
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		bp_port1 = new("bp_port1",this);		// build_phase中new例化端口port	
		bp_port2 = new("bp_port2",this);			
		...
	endfunction
	
	task run_phase(uvm_phase phase);
		trans1 t1;
		repeat(5) begin
			t1 = trans1::type_id_create("t1");      // run_phase中create例化组件initiator
			assert(t1.randomize());
			bp_port1.put(t1);					// initiator端口调用方法put。注意了,此处依然是put!!不是put1、也不是put2!!!
			bp_port2.put(t1);
		end
	endtask
	
endclass

注意initiator调用的数据流方法名称不会改变!!
对于comp1调用put方法而言,它只需要选择bp_port1或者bp_port2,而不需要更替put方法名,即仍然按照put()来调用而不是put_p1或者put_p2。

// 定义 target 

`uvm_blocking_put_imp_decl(1);   //宏注册后缀,之后就有了uvm_blocking_put_imp1#(T, IMP)类型,数据流方法也变成put1
`uvm_blocking_put_imp_decl(2);	 //注册了后缀2

class comp2 extends uvm_component;
	`uvm_component_utils(comp2)
	
	uvm_blocking_put_imp1#(trans1,comp2) bp_imp1;	//看后缀	
	uvm_blocking_put_imp2#(trans1,comp2) bp_imp2;		
	trans1 tq[$];	
										
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		bp_imp1 = new("bp_imp1",this);				
		bp_imp2 = new("bp_imp2",this);				
		...
	endfunction
	
	// 不同的端口组之间在imp端口一侧实现专属的方法,即不同名的put
	task put1(trans1 t);	// 看后缀	
		tq.push_back(t);
	endtask
	task put2(trans1 t);
		tq.push_back(t);
	endtask
	
endclass	

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


一对一单向数据流 全部的通信端口

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

常见的PORT,EXPORT和IMP端口

//:PORT:UVM源代码
uvm_blocking_put_port#(T);
uvm_nonblocking_put_port#(T);
uvm_put_port#(T);
uvm_blocking_get_port#(T);
uvm_nonblocking_get_port#(T);
uvm_get_port#(T);
uvm_blocking_peek_port#(T);
uvm_nonblocking_peek_port#(T);
uvm_peek_port#(T);
uvm_blocking_get_peek_port#(T);
uvm_nonblocking_get_peek_port#(T);
uvm_get_peek_port#(T);
uvm_blocking_transport_port#(REQ, RSP);
uvm_nonblocking_transport_port#(REQ, RSP);
uvm_transport_port#(REQ, RSP);

EXPORT:UVM源代码
uvm_blocking_put_export#(T);
uvm_nonblocking_put_export#(T);
uvm_put_export#(T);
uvm_blocking_get_export#(T);
uvm_nonblocking_get_export#(T);
uvm_get_export#(T);
uvm_blocking_peek_export#(T);
uvm_nonblocking_peek_export#(T);
uvm_peek_export#(T);
uvm_blocking_get_peek_export#(T);
uvm_nonblocking_get_peek_export#(T);
uvm_get_peek_export#(T);
uvm_blocking_transport_export#(REQ, RSP);
uvm_nonblocking_transport_export#(REQ, RSP);
uvm_transport_export#(REQ, RSP);

IMP:UVM源代码
uvm_blocking_put_imp#(T, IMP);
uvm_nonblocking_put_imp#(T, IMP);
uvm_put_imp#(T, IMP);
uvm_blocking_get_imp#(T, IMP);
uvm_nonblocking_get_imp#(T, IMP);
uvm_get_imp#(T, IMP);
uvm_blocking_peek_imp#(T, IMP);
uvm_nonblocking_peek_imp#(T, IMP);
uvm_peek_imp#(T, IMP);
uvm_blocking_get_peek_imp#(T, IMP);
uvm_nonblocking_get_peek_imp#(T, IMP);
uvm_get_peek_imp#(T, IMP);
uvm_blocking_transport_imp#(REQ, RSP, IMP);
uvm_nonblocking_transport_imp#(REQ, RSP, IMP);
uvm_transport_imp#(REQ, RSP, IMP);

TLM analysis port

在这里插入图片描述


端口

在这里插入图片描述

UVM中有两种特殊端口:analysis_port 和 analysis_export

  • 默认情况下这两个端口可以连接多个IMP,也就是默认情况下是一对多通讯
  • 而 put, get 默认情况下是一对一通信,所以这两个端口更像广播
  • analysis_port 可以同时和多个 IMP 进行通信,但IMP的类型必须是 uvm_analysis_imp 类型,否则会报 error

无阻塞

  • put, get 都有阻塞和非阻塞的区分,但 analysis_port 和 analysis_export 端口没有

数据流方法

只有write (广播)

  • analysis_port 和 analysis_export 只有一种操作:write 。所以在 analysis_imp 所在的 component 里必须定义一个名字为 write(Tt) 的函数,注意是函数不耗时
  • 如果一个 component 里需要定义两个 write 函数,则需要使用 uvm_analysis_imp_decl 宏来解决,类名后缀的注册`uvm_analysis_imp_decl(SFX)

FIFO通信

UVM提供的继承自uvm_component的类uvm_tlm_analysis_fifo类实例化后就是一种FIFO,它可以让通信的两个component都作为initiator,这来自于FIFO上的众多imp型端口。

FIFO的本质是一块缓存(使用mailbox实现)加上两个IMP(虽然关键字是export但实质还是IMP)

直接通信
在这里插入图片描述
使用FIFO通信
使用FIFO通信
uvm_tlm_analysis_fifo #(T)
在这里插入图片描述

TLM FIFO中的get方法和peek方法,try_get有什么区别?

  • get()操作将从TLM FIFO中返回一个事务(如果可用),并从FIFO中删除该事务。如果FIFO中没有可用的事务,它将阻塞并等待,直到FIFO至少有一个事务
  • peek()操作将从TLM FIFO中返回一个事务(如果可用),而不会实际从FIFO中删除该项目。它也是一个阻塞调用,如果FIFO没有可用的条目,它将等待
  • try_get()是一个非阻塞调用,即使FIFO中没有可用的事务,它也会立即返回。try_get()的返回值指示是否返回成功。

在这里插入图片描述

  • FIFO上拥有transport之外的12种IMP,用于分别和相应的PORT和EXPORT连接
  • 除此外还有put_ap和get_ap,当FIFO上的blocking_put_export或者put_export被连接到一个blocking_put_port或者put_port上时,FIFO内部定义的put任务被调用,这个put任务把传递过来的transaction放在FIFO的缓存里,同时,把这个transaction通过put_ap使用write函数发送出去
  • Analysis FIFO含有uvm_analysis_port类对象put_ap和get_ap,说明Anylsis FIFO也可作为initiator向其他target做广播,支持一对多广播通信
  • Analysis FIFO相连的initiator每次调用put或get数据流方法时,FIFO就会调用write广播,看源码
  • 除了put_ap和get_ap,其他端口虽都以_export结尾,但实际上都是imp类型的
// `uvm_tlm_analysis_fifo #(T)源码
//...\questasim64_2020.1\verilog_src\uvm-1.2\src\tlm1\uvm_tlm_fifos.svh

class uvm_tlm_fifo #(type T=int) extends uvm_tlm_fifo_base #(T);
  ...
  virtual task put( input T t );
    m.put( t );
    put_ap.write( t );					//广播
  endtask

  virtual task get( output T t );
    m_pending_blocked_gets++;
    m.get( t );
    m_pending_blocked_gets--;
    get_ap.write( t );					//广播
  endtask
  ...
endclass

class uvm_tlm_analysis_fifo #(type T = int) extends uvm_tlm_fifo #(T);

查看FIFO内部数据方法

//...\questasim64_2020.1\verilog_src\uvm-1.2\src\tlm1\uvm_tlm_fifos.svh
class uvm_tlm_fifo #(type T=int) extends uvm_tlm_fifo_base #(T);

  function new(string name, uvm_component parent = null, int size = 1); //创建函数,size可指定FIFO容量,0为无限大小
  virtual function int used();			//返回FIFO中存储的trans的个数
  virtual function bit is_empty();		//FIFO是否空
  virtual function bit is_full();		//FIFO是否满
  virtual function void flush();		//清空FIFO
  endfunction

FIFO建立通信和调用

  • FIFO还是直接使用IMP通信:在用FIFO通信的方法中,完全隐蔽了这个UVM中特有,而TLM中根本就没有的东西,用户可以完全不关心IMP。因此,对用户来讲,只需要知道analysis_port,blocking_get_port即可,这大大简化了初学者的工作量,尤其是在scoreboard中面临多个IMP时,这种优势更加明显。但是FIFO连接的方式增加了env中代码的复杂度,尤其当连接的端口数量众多时,这个缺点更加明显

将需要发送trans的component内创建put_port端口,需要接受trans的component内创建get_port端口,再在上层component创建一个Anylsis FIFO,将三者连接即可

如下例子,monitor将数据广播给scoreboard
1. 在 monitor 创建 uvm_analysis_port#(trans) ,analysis_port 用于 initiator 广播

// monitor组件

class monitor extends uvm_monitor;
	`uvm_component_utils(monitor)       // 注册component
	
	virtual reg_intf 			vif;
	uvm_analysis_port#(trans) 	a_port;
	...
	function void build_phase(uvm_phase phase);     // build_phase中new例化端口port	
		super.build_phase(phase);
		ap = new("ap",this);				// 
		if(!uvm_config_db#(virtual reg_intf)::get(this,"","vif",vif))
			uvm_report_error("INTF",$sformatf("%s gets interface failed",get_full_name()),UVM_NONE);
	endfunction
	
	task run_phase(uvm_phase phase);
		forever begin
			trans t;
			t = trans::type_id::create("t");   // 在run_phase中create例化transaction
			@(posedge vif.clk iff(vif.mon_ck.cmd != IDLE))
			t.cmd = vif.mon_ck.cmd;
			t.cmd_addr = vif.mon_ck.cmd_addr;
			a_port.write(t);
		end
	endtask
	
endclass

2.在scoreboard 例化 uvm_blocking_get_port(trans), uvm_blocking_get_port 用于 target 主动请求数据

// scoreboard组件

class scoreboard extends uvm_scoreboard;
	`uvm_component_utils(scoreboard)
	
	uvm_blocking_get_port bg_port;
	...
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		bg_port = new("bg_port",this);    // build_phase中new例化端口port	
	endfunction
	
	task run_phase(uvm_phase phase);
		forever begin
			trans t;     // 在run_phase中声明trans,并接收
			bg_port.get(t);
			...
		end
	endtask
	
endclass

3.在monitor和scb的上层env,例化uvm_tlm_analysis_fifo#(trans),建立FIFO通信
uvm_tlm_analysis_fifo#(trans) 端口也不属于component,在build_phase中new例化

// env

class env extends uvm_env;
	`uvm_component_utils(env)
	
	uvm_tlm_analysis_fifo#(trans) 	agt_scb_fifo;    
	agent 							agt;
	scoreboard 						scb;
	...
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		agt_scb_fifo = new("agt_scb_fifo",this);     // build_phase中new例化FIFO端口	
		...
	endfunction
	
	function void connect_phase(uvm_phase phase);   //在connect_phase与FIFO连接
		agt.mon.a_port.connect(agt_scb_fifo.analysis_export);   // agt的mon的ap连接fifo的analysis_export
		scb.bg_port.connect(agt_scb_fifo.blocking_get_export);  // scb的bp连接fifo的blocking_get_export
		...
	endfunction
	
endclass

在这里插入图片描述


TIPs:

analysis port和TLM port有什么区别?analysis FIFO和TLM FIFO的区别是什么?何时使用analysis FIFO 和 TLM FIFO?

  • TLM port/FIFOs用于具有使用put/get方法建立的通信通道的两个组件之间的事务级通信。
  • analysis port/FIFOs是另一种事务性通信通道,用于组件将事务广播给多个组件。
  • TLM port/FIFO用于driver和sequencer之间的连接,而analysis port/FIFOs用于monitor广播事务,这些事务可由scoreboard或覆盖率收集组件接收。
  • 2
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值