基本定义
A的方框称之为PORT,B的圆圈称之为EXPORT
要注意:无论是get还是put操作, 其发起者拥有的都是PORT端口, 而不是EXPORT
transport操作, 如 transport操作相当于一次put操作加一次get操作, 数据流先从A流向B, 再从B流向A。
//put
uvm_blocking_put_port#(T);
uvm_nonblocking_put_port#(T);
uvm_put_port#(T);
//get
uvm_blocking_get_port#(T);
uvm_nonblocking_get_port#(T);
uvm_get_port#(T);
//peek
uvm_blocking_peek_port#(T);
uvm_nonblocking_peek_port#(T);
uvm_peek_port#(T);
//get peek
uvm_blocking_get_peek_port#(T);
uvm_nonblocking_get_peek_port#(T);
uvm_get_peek_port#(T);
//transport
uvm_blocking_transport_port#(REQ, RSP);
uvm_nonblocking_transport_port#(REQ, RSP);
uvm_transport_port#(REQ, RSP);
在这种控制流中, PORT具有高优先级, 而EXPORT具有低优先级。 只有高优先级的端口才能向低优先级的端口发起三种操作。
还有一个imp:中转站
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);
互连
class A extends uvm_component;`uvm_component_utils(A)uvm_blocking_put_port#(my_transaction) A_port;
endclassfunction void A::build_phase(uvm_phase phase);super.build_phase(phase);A_port = new("A_port", this); //第二个参数是父节点
endfunctiontask A::main_phase(uvm_phase phase);
endtask
A中要定义端口,要在build phase中例化,不在new中。
但是不能在env层直接进行链接
function void my_env::connect_phase(uvm_phase phase);super.connect_phase(phase);A_inst.A_port.connect(B_inst.B_export);
endfunction
需要加入imp
我们需要在B例化一个imp、链接。然后在B实现put
B_export.connect(B_imp);
然后在
结构函数定义
- 当A_port的类型是nonblocking_put,B_imp的类型是nonblocking_put时,那么就要在B中定义一个名字为try_put的函数和一个名为can_put的函数。
- 当A_port的类型是put,B_imp的类型是put时,那么就要在B中定义3个接口,一个是put任务/函数,一个是try_put函数,一个是can_put函数。
- 当A_port的类型是blocking_get,B_imp的类型是blocking_get时,那么就要在B中定义一个名字为get的任务/函数
- 当A_port的类型是nonblocking_get,B_imp的类型是nonblocking_get时,那么就要在B中定义一个名字为try_get的函数和一个名为can_get的函数。
- 当A_port的类型是get,B_imp的类型是get时,那么就要在B中定义3个接口,一个是get任务/函数,一个是try_get函数,一个是can_get函数。
- 当A_port的类型是blocking_peek,B_imp的类型是blocking_peek时,那么就要在B中定义一个名字为peek的任务/函数。
- 当A_port的类型是nonblocking_peek,B_imp的类型是nonblocking_peek时,那么就要在B中定义一个名字为try_peek的函数和一个名为can_peek的函数。
- 当A_port的类型是peek,B_imp的类型是peek时,那么就要在B中定义3个接口,一个是peek任务/函数,一个是try_peek函数,一个是can_peek函数。
- 当A_port的类型是blocking_get_peek,B_imp的类型是blocking_get_peek时,那么就要在B中定义一个名字为get的任务/函数,一个名字为peek的任务/函数。
- 当A_port的类型是nonblocking_get_peek,B_imp的类型是nonblocking_get_peek时,那么就要在B中定义一个名字为try_get的函数,一个名为can_get的函数,一个名字为try_peek的函数和一个名为can_peek的函数。
- 当A_port的类型是get_peek,B_imp的类型是get_peek时,那么就要在B中定义6个接口,一个是get任务/函数,一个是try_get函数,一个是can_get函数,一个是peek任务/函数,一个是try_peek函数,一个是can_peek函数。
- 当A_port的类型是blocking_transport,B_imp的类型是blocking_transport时,那么就要在B中定义一个名字为transport的任务/函数。
- 当A_port的类型是nonblocking_transport,B_imp的类型是nonblocking_transport时,那么就要在B中定义一个名字为nb_transport的函数。
- 当A_port的类型是transport,B_imp的类型是transport时,那么就要在B中定义两个接口,一个是transport任务/函数,一个是nb_transport函数。
对于所有blocking系列的端口,可以定义函数or任务,但是nonblocking只能定义函数。
注意port和port可以连接、export和export也可以连接
不过通信的话还是要挂载imp
blocking get
get依旧是在A实现imp和export的链接,并且实现get函数,作为动作的接收者。数据的发起者。
transport
analysis port
一个analysis_port( analysis_export) 可以连接多个IMP(uvm_analysis_imp),一对多传输
不存在阻塞的概念,因为是广播。
对于analysis_port和analysis_export来说, 只有一种操作: write。
与put系列端口的PORT和EXPORT直接相连会出错的情况一样, analysis_port如果和一个analysis_export直接相连也会出错。 只有在analysis_export后面再连接一级uvm_analysis_imp, 才不会出错
链接
在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
endclassfunction void my_env::connect_phase(uvm_phase phase);o_agt.ap.connect(scb.scb_imp);
endfunction
广播机制的write要实现不同的write函数,格式如下,imp_decl()声明imp端口,
使用FIFO
FIFO的本质是一块缓存加两个IMP
monitor中依然是analysis_port, FIFO中是uvm_analysis_imp, 数据流和控制流的方向相同。 在scoreboard与FIFO的连接关系中, scoreboard中使用blocking_get_port端口
等于说ap先写fifo,sb再get。
链接
实际上, FIFO中的analysis_export和blocking_get_export虽然名字中有关键字export, 但是其类型却是IMP。 UVM为了掩饰IMP的存在
所以要这样来链接。
i_agt.ap.connect(agt_mdl_fifo.analysis_export);
mdl.port.connect(agt_mdl_fifo.blocking_get_export);
。。。连两个就行了
上述是FIFO的诸多端口,export依旧是imp
注意
当get任务被调用时, FIFO内部缓存中会少一个transaction
而peek被调用时, FIFO会把transaction复制一份发送出去, 其内部缓存中的transaction数量并不会减少
put_ap:当FIFO上的blocking_put_export或者put_export被连接到一个blocking_put_port或者put_port上时, FIFO内部被定义的put任务被调用, 这个put任务把传递过来的transaction放在FIFO内部的缓存里, 同时, 把这个transaction通过put_ap使用write函数发送出去
get_ap:当FIFO的get任务被调用时, 同样会有一个transaction从get_ap上发出
除此之外, FIFO的get_export、 get_peek_export和blocking_get_peek_export被相应的PORT或者EXPORT连接时, 也能会调用FIFO的get任务
FIFO or AP
fifo在使用的时候可以for循环,ap只能一个一个链接。但是FIFO增加了env的代码复杂度