前言
UVM实现通信的TLM机制中通常有put,get等术语,put操作是指通信的发起者A把一个transaction发送给B,而get操作指A向B索取一个transaction。同样,存在port与export,imp三种端口,代表了控制流的方向,port的优先级最高。除了这几种端口外,UVM中还有两种
特殊的端口:analysis_port和analysis_export。这两者其实与put和get系列端口类似,都用于传递transaction。
与blocking_port 和blocking_export区别
- 一个analysis_port(analysis_export)可以连接多个IMP,也就是说,analysis_port(analysis_export)与IMP之间的通信是一对多的通信,而put和get系列端口与相应IMP的通信是一对一的通信(除非在实例化时指定可以连接的数量,analysis_port(analysis_export)更像是一个广播。
- put与get系列端口都有阻塞和非阻塞的区分。但是对于analysis_port和analysis_export来说,没有阻塞和非阻塞的概念。因为它本身就是广播,不必等待与其相连的其他端口的响应,所以不存在阻塞和非阻塞。
- 与put系列端口的PORT和EXPORT直接相连会出错的情况一样,analysis_port如果和一个analysis_export直接相连也会出错。只有在analysis_export后面再连接一级uvm_analysis_imp,才不会出错。
使用实例
使用方法如下:(以白皮书中代码为例)
为了将monitor中监视到的数据发送到scoreboard中,需要首先在声明一个
uvm_analysis_port
端口。
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
同时,由于monitor与scoreboard在UVM树中并不是平等的兄妹关系,其中间还间隔了o_agt,所以可以在o_agent中再例化一个analysis_port端口,并例化它,并将其与monitor中的端口连接即可。
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,这样就可以直接把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
uvm_analysis_imp_decl宏的用法
当scoreboard需要接受多路数据时,一个imp端口是不足够的,如scoreboard除了接收monitor的数据之外,还要接收reference model的数据。相应的scoreboard就要再添加一个
uvm_analysis_imp的IMP,如model_imp,那么monitor中的write任务如何区分写到scoreboard中的数据应该做何种处理呢,此时我们就需要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;
…//如上,分别声明了两个imp。分别指monitor_imp,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);
当与monitor_imp相连接的analysis_port执行write函数时,会自动调用write_monitor函数,而与model_imp相连接的analysis_port执行write函数时,会自动调用write_model函数。
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
利用fifo通信
通过ap与imp方式相连的monitor和scoreboard之间的通信,monitor占据主动地位,而scoreboard只能被动地接收,若想使scoreboard也实现主动的接收,则可利用fifo机制。在agent和scoreboard之间添加一个uvm_tlm_analysis_fifo。在monitor与FIFO的连接关系中,monitor中依然是analysis_port,FIFO中是uvm_analysis_imp,数据流和控制流的方向相同。在scoreboard与FIFO的连接关系中,scoreboard中使用blocking_get_port端口。
示意图如下:
class my_env extends uvm_env;
my_agent i_agt;
my_agent o_agt;
my_model mdl;
my_scoreboard scb;
uvm_tlm_analysis_fifo #(my_transaction) agt_scb_fifo;
uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
uvm_tlm_analysis_fifo #(my_transaction) mdl_scb_fifo;
…
endclass
function void my_env::connect_phase(uvm_phase phase);
super.connect_phase(phase);
i_agt.ap.connect(agt_mdl_fifo.analysis_export);
mdl.port.connect(agt_mdl_fifo.blocking_get_export);
mdl.ap.connect(mdl_scb_fifo.analysis_export);
scb.exp_port.connect(mdl_scb_fifo.blocking_get_export);
o_agt.ap.connect(agt_scb_fifo.analysis_export);
scb.act_port.connect(agt_scb_fifo.blocking_get_export);
endfunction
注意:FIFO中有两个IMP,但是在上面的连接关系中,FIFO中却是EXPORT,这是为什么呢?实际上,FIFO中的analysis_export和blocking_get_export虽然名字中有关键字export,但是其类型却是IMP。UVM为了掩饰IMP的存在,在它们的命名中加入了export关键字