UVM实战 卷I学习笔记7——UVM中的TLM通信(2)


*blocking_get端口的使用

get系列端口与put系列端口在某些方面完全相反。若要实现从A到B的通信,使用blocking_get系列端口的框图如下图所示:
在这里插入图片描述
在这种连接关系中数据流依然是从A到B,但A由动作发起者变成了动作接收者,而B由动作接收者变成了动作发起者。B_port的类型为uvm_blocking_get_port,A_export的类型为uvm_blocking_get_export,A_imp的类型为uvm_blocking_get_imp。与uvm_blocking_put_imp所在的component要实现一个put的函数/任务类似,uvm_blocking_get_imp所在的component要实现一个名字为get的函数/任务。A的代码为:

class A extends uvm_component;
	`uvm_component_utils(A)
	uvm_blocking_get_export#(my_transaction) A_export;
	uvm_blocking_get_imp#(my_transaction) A_imp;
	my_transaction tr_q[$];
	...
endclass
function void A::build_phase(uvm_phasree phase);
	super.build_phase(phase);
	A_export = new("A_export", this);
	A_imp = new("A_imp", this);
endfunction
function void A::connect_phase(uvm_phase phase);
	super.connect_phase(phase);
	A_export.connect(A_imp);
endfunction
task A::get(output my_transaction tr);
	whilt(tr_q.size() == 0) #2;
	tr = tr_q.pop_front();
endtask
task A::main_phase(uvm_phase phase);
	my_transaction tr;
	reoeat(10) begin
		#10;
		tr = new("tr");
		tr_q.push_back(tr);
	end
endtask

在A的get任务中,每隔2个时间单位检查tr_q中是否有数据,有就发送出去。当B在其main_phase调用get任务时会最终执行A的get任务。在A的connect_phase,需要把A_export和A_imp连接起来。

B的代码为:

class B extends uvm_component;
	`uvm_component_utils(B)
	uvm_blocking_get_port#(my_transaction) B_port;
	...
endclass
function void B::build_phase(uvm_phase phase);
	super.build_phase(phase);
	B_port = new("B_port", this);
endfunction
task B::main_phase(uvm_phase phase);
	my_transaction tr;
	while(1) begin
	 	B_port.get(tr);
	 	`uvm_info("B", "get a transaction", UVM_LOW)
	 	tr.print();
	 end
endtask

env中的连接关系变为:

function void my_env::connnect_phase(uvm_phase phase);
	super.connect_phase(phase);
	B_inst.B_port.connect(A_inst.A_export);
endfunction

与blocking_put系列端口类似,blocking_get_port也可以直接连接到blocking_get_imp,同时blocking_get_port也可以连接到blocking_get_port,blocking_get_export也可连接到blocking_get_export。在这些连接关系中,需要谨记的是连接的终点必须是一个IMP

*blocking_transport端口的使用

transport系列端口与put和get系列端口都不一样。在put和get系列端口中,所有通信都是单向的,而在transport系列端口中,通信变成了双向的。
在这里插入图片描述
在A中定义一个transport:

class A extends uvm_component;
	`uvm_component_utils(A)
	uvm_blocking_transport_port#(my_transaction, my_transaction) A_transport;
endclass
task A::main_phase(uvm_phase phase);
	my_transaction tr;
	my_transaction rsp;
	repeat(10) begin
		#10;
		tr = new("tr");
		assert(tr.randomize());
		A_transport.transport(tr, rsp);
		`uvm_info("A", "received rsp", UVM_MEDIUM)
		rsp.print();
	end
endtask

B中需要定义一个类型为uvm_blocking_transport_imp的IMP:

class B extends uvm_component;
	`uvm_component_utils(B);
	uvm_blocking_transport_imp#(my_transaction, my_transaction, B) B_imp;
	...
endclass
task B::transport(my_transaction req, output my_transaction rsp);
	`uvm_info("B", "receive a transaction", UVM_LOW)
	req.print();
	//do something according to req
	#5;
	rsp = new("rsp");
endtask

env中的连接关系为:

function void my_env::connect_phase(uvm_phase phase);
	super.connect_phase(phase);
	A_inst.A_transport.connect(B_inst.B_imp);
endfunction

在A中调用transport任务并把生成的transaction作为第一个参数。B中的transaport任务接收到这笔transaction, 根据这笔transaction做某些操作,并把操作结果作为transport的第二个参数发送出去。A根据接收到的rsp决定后面的行为。

上例是blocking_transport_port直接连接到blocking_transport_imp,前者还可连接blocking_transport_export, 这三者之间的连接关系与blocking_put系列端口类似。

nonblocking端口的使用

nonblocking端口的所有操作都是非阻塞的, 即必须用函数实现。以nonblocking_put端口为例介绍nonblocking端口的使用,图示的连接关系需要在A中定义一个nonblocking_put_port端口:
在这里插入图片描述

class A extends uvm_component;
	`uvm_component_utils(A)
	uvm_nonblocking_put_port#(my_transaction) A_port;
	…
endclass
…
task A::main_phase(uvm_phase phase);
	my_transaction tr;
	repeat(10) begin
		tr = new("tr");
		assert(tr.randomize());
		while(!A_port.can_put()) #10;
		void'(A_port.try_put(tr));
	end
endtask

由于端口变为非阻塞,所以在送出transaction之前需调用can_put函数来确认是否能够执行put操作。can_put最终会调用B中的can_put:

class B extends uvm_component;
	`uvm_component_utils(B)
	uvm_nonblocking_put_imp#(my_transaction, B) B_imp;
	my_transaction tr_q[$];
	…
endclass
…
function bit B::can_put();
	if(tr_q.size() > 0)
		return 0;
	else
		return 1;
endfunction
function bit B::try_put(my_transaction tr);
	`uvm_info("B", "receive a transaction", UVM_LOW)
	if(tr_q.size() > 0)
		return 0;
	else begin
		tr_q.push_back(tr);
		return 1;
	end
endfunction
task B::main_phase(uvm_phase phase);
	my_transaction tr;
	while(1) begin
	if(tr_q.size() > 0)
		tr = tr_q.pop_front();
	else
		#25;
	end
endtask

在A中使用can_put来判断是否可以发送,其实这里还可以不用can_put(不使用can_put的话,在B中依然需要定义一个名字为can_put的函数,这个函数里可以是一个空函数),而直接使用try_put:

task A::main_phase(uvm_phase phase);
	my_transaction tr;
	repeat(10) begin
		tr = new("tr");
		assert(tr.randomize());
		while(!A_port.try_put(tr)) #10;
	end
endtask

env中的连接关系为:

function void my_env::connect_phase(uvm_phase phase);
	super.connect_phase(phase);
	A_inst.A_port.connect(B_inst.B_imp);
endfunction

nonblocking_get系列端口和nonblocking_transport系列端口的使用与nonblocking_put类似。

UVM中的通信方式

UVM中的analysis端口

UVM中还有两种端口:analysis_port和analysis_export。这两者其实与put和get系列端口类似,都用于传递transaction。它们的区别是:

  • 默认情况下,一个analysis_port(analysis_export)可连接多个IMP, 即analysis_port(analysis_export)与IMP之间的通信是一对多的通信,而put和get系列端口与相应IMP的通信是一对一的通信(除非在实例化时指定可连接的数量)。analysis_port(analysis_export)更像是一个广播
  • put与get系列端口有阻塞和非阻塞的区分。但analysis_port和analysis_export没有阻塞和非阻塞的概念。它本身就是广播,不必等待与其相连的其他端口响应
  • 一个analysis_port可以和多个IMP连接进行通信,但IMP的类型必须是uvm_analysis_imp,否则会报错。
  • 对于put系列端口,有put、try_put、can_put等操作,对于get系列端口,有get、try_get和can_get等操作。对于analysis_port和analysis_export来说只有write操作在analysis_imp所在的component必须定义write函数
    在这里插入图片描述
    A的代码为:定义一个analysis_port,并在main_phase中每隔10个时间单位写入一个transaction。
class A extends uvm_component;
	`uvm_component_utils(A)
	uvm_analysis_port#(my_transaction) A_ap;
	…
endclass
…
task A::main_phase(uvm_phase phase);
	my_transaction tr;
	repeat(10) begin
		#10;
		tr = new("tr");
		assert(tr.randomize());
		A_ap.write(tr);
	end
endtask

B的代码为:

class B extends uvm_component;
	`uvm_component_utils(B)
	uvm_analysis_imp#(my_transaction, B) B_imp;
	…
endclass
…
function void B::write(my_transaction tr);
	`uvm_info("B", "receive a transaction", UVM_LOW)
	tr.print();
endfunction

B是B_imp所在的component,故要在B中定义名为write的函数。在B的main_phase中不需要做任何操作。C的代码与B完全相似,只要把相应的B替换为C即可。

env中的连接关系为:

function void my_env::connect_phase(uvm_phase phase);
	super.connect_phase(phase);
	A_inst.A_ap.connect(B_inst.B_imp);
	A_inst.A_ap.connect(C_inst.C_imp);
endfunction

analysis_export和IMP也可进行类似的连接;与put系列端口的PORT和EXPORT直接相连会出错的情况一样,analysis_port如果和analysis_export直接相连也会出错。只有在analysis_export后面再连接一级uvm_analysis_imp才不会出错

*一个component内有多个IMP

在这里插入图片描述
图中o_agt的monitor与scoreboard之间的通信使用analysis_port实现。在monitor中:

class monitor extends uvm_monitor;
	uvm_analysis_port#(my_transaction) ap;
	task main_phase(uvm_phase phase);
		super.main_phase(phase);
		my_transaction tr;
		…
		ap.write(tr);
		…
	endtask
endclass

在scoreboard中:

class scoreboard extends uvm_scoreboard;
	uvm_analysis_imp#(my_transaction, scoreboard) scb_imp;
	task write(my_transaction tr);
		//do something on tr
	endtask
endclass

在env中可使用connect连接,由于monitor与scoreboard在UVM树中并不是平等的关系,中间还间隔了o_agt,所以这里有三种连接方式,第一种是直接在env中跨层次引用monitor中的ap:

function void my_env::connect_phase(uvm_phase phase);
	o_agt.mon.ap.connect(scb.scb_imp);
	… 
endfunction

第二种是在agent中声明一个ap并实例化它,在connect_phase将其与monitor的ap相连,并可以在env中把agent的ap直接连接到scoreboard的imp:

class my_agent extends uvm_agent ;
	uvm_analysis_port #(my_transaction) ap;
	…
	function void build_phase(uvm_phase phase);
		super.build_phase(phase);
		ap = new("ap", this);
		…
	endfunction
	function void my_agent::connect_phase(uvm_phase phase);
		mon.ap.connect(this.ap);
		…
	endfunction
endclass
function void my_env::connect_phase(uvm_phase phase);
	o_agt.ap.connect(scb.scb_imp);
	…
endfunction

第三种是在agent中声明一个ap但不实例化它,让其指向monitor中的ap。在env中可直接连接agent的ap到scoreboard的imp:

class my_agent extends uvm_agent ;
	uvm_analysis_port #(my_transaction) ap;
	…
	function void my_agent::connect_phase(uvm_phase phase);
		ap = mon.ap;
		…
	endfunction
endclass
function void my_env::connect_phase(uvm_phase phase);
	o_agt.ap.connect(scb.scb_imp);
	…
endfunction

上述三种方式中第一种最简单,但其层次关系并不好,第二种稍显麻烦,第三种既具有明显的层次关系,同时其实现也较简单。

上面的monitor和scoreboard之间的通信是通过采用一个analysis_port和一个anslysis_imp相连的方式实现的。 对于analysis_imp来说,必须在其实例化的uvm_component中定义一个write的函数。在上例中,scoreboard只接收一路数据,但在现实情况中,scoreboard除了接收monitor的数据之外,还要接收reference model的数据。 相应的scoreboard就要再添加一个uvm_analysis_imp的IMP,如model_imp。此时问题就出现了,由于接收到的两路数据应该做不同的处理,所以这个新的IMP也要有一个write任务与其对应。但write只有一个,怎么办?

UVM考虑到了这种情况, 它定义了一个宏uvm_analysis_imp_decl来解决这个问题, 其使用方式为:

	`uvm_analysis_imp_decl(_monitor)
	`uvm_analysis_imp_decl(_model)
class my_scoreboard extends uvm_scoreboard;
	my_transaction expect_queue[$];
	uvm_analysis_imp_monitor#(my_transaction, my_scoreboard) monitor_imp;
	uvm_analysis_imp_model#(my_transaction, my_scoreboard) model_imp;extern function void write_monitor(my_transaction tr);
	extern function void write_model(my_transaction tr);
	extern virtual task main_phase(uvm_phase phase);
endclass

通过宏uvm_analysis_imp_decl声明了两个后缀_monitor和_model。UVM会根据这两个后缀定义两个新的IMP类:uvm_analysis_imp_monitor和uvm_analysis_imp_model,并在my_scoreboard中分别实例化这两个类: monitor_imp和model_imp。当与monitor_imp和model_imp相连接的analysis_port执行write函数时,会自动调用write_monitor和write_model函数。所以,只要完成后缀的声明,并在write后面添加上相应后缀就可以正常工作了:

function void my_scoreboard::write_model(my_transaction tr);
	expect_queue.push_back(tr);
endfunction
function void my_scoreboard::write_monitor(my_transaction tr);
	my_transaction tmp_tran;
	bit result;
	if(expect_queue.size() > 0) begin
		…
	end
endfunction
  • 2
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值