(1)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的函数。
要实现图4-13中所示的连接关系,A的代码为:
`ifndef A__SV
`define A__SV
class A extends uvm_component;
`uvm_component_utils(A)
static integer fileA = $fopen("A.txt", "w" );
uvm_analysis_port#(my_transaction) A_ap;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
extern function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
endclass
function void A::build_phase(uvm_phase phase);
super.build_phase(phase);
A_ap = new("A_ap", this);
endfunction
task A::main_phase(uvm_phase phase);
my_transaction tr;
repeat(10) begin
#10;
tr = new("tr");
assert(tr.randomize());
$fdisplay (fileA, "tr.dmac = %h,tr.smac=%h ,tr.ether_type=%h ,tr.pload=%h ,tr.crc=%h \n", tr.dmac,tr.smac,tr.ether_type,tr.pload,tr.crc);
A_ap.write(tr);
end
endtask
`endif
A的代码很简单,只是简单地定义一个analysis_port,并在main_phase中每隔10个时间单位写入一个transaction。 B的代码为:
`ifndef B__SV
`define B__SV
class B extends uvm_component;
`uvm_component_utils(B)
uvm_analysis_imp#(my_transaction, B) B_imp;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
static integer fileB = $fopen("B.txt","w");
extern function void build_phase(uvm_phase phase);
extern function void connect_phase(uvm_phase phase);
extern function void write(my_transaction tr);
extern virtual task main_phase(uvm_phase phase);
endclass
function void B::build_phase(uvm_phase phase);
super.build_phase(phase);
B_imp = new("B_imp", this);
endfunction
function void B::connect_phase(uvm_phase phase);
super.connect_phase(phase);
endfunction
function void B::write(my_transaction tr);
`uvm_info("B", "receive a transaction", UVM_LOW)
$fdisplay (fileB, "tr.dmac = %h,tr.smac=%h ,tr.ether_type=%h ,tr.pload=%h ,tr.crc=%h \n", tr.dmac,tr.smac,tr.ether_type,tr.pload,tr.crc);
tr.print();
endfunction
task B::main_phase(uvm_phase phase);
endtask
`endif
如前所述,B是B_imp所在的component,因此要在B中定义一个名字为write的函数。在B的main_phase中不需要做任何操作。 C的代码与B完全相似,只要把相应的B替换为C即可。
`ifndef C__SV
`define C__SV
class C extends uvm_component;
`uvm_component_utils(C)
uvm_analysis_imp#(my_transaction, C) C_imp;
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
extern function void build_phase(uvm_phase phase);
extern function void connect_phase(uvm_phase phase);
extern function void write(my_transaction tr);
extern virtual task main_phase(uvm_phase phase);
endclass
function void C::build_phase(uvm_phase phase);
super.build_phase(phase);
C_imp = new("C_imp", this);
endfunction
function void C::connect_phase(uvm_phase phase);
super.connect_phase(phase);
endfunction
function void C::write(my_transaction tr);
`uvm_info("C", "receive a transaction", UVM_LOW)
tr.print();
endfunction
task C::main_phase(uvm_phase phase);
endtask
`endif
env中的连接关系为:
`ifndef MY_ENV__SV
`define MY_ENV__SV
class my_env extends uvm_env;
A A_inst;
B B_inst;
C C_inst;
function new(string name = "my_env", uvm_component parent);
super.new(name, parent);
endfunction
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
A_inst = A::type_id::create("A_inst", this);
B_inst = B::type_id::create("B_inst", this);
C_inst = C::type_id::create("C_inst", this);
endfunction
extern virtual function void connect_phase(uvm_phase phase);
`uvm_component_utils(my_env)
endclass
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
`endif
在env中,可以看到A_ap分别与B和C中相应的imp连接到了一起。上面只是一个analysis_port与IMP相连的例子。analysis_export和IMP也可以这样相连接,只需将上面例子中的 uvm_analysis_port改为uvm_analysis_export就可以。
与put系列端口的PORT和EXPORT直接相连会出错的情况一样,analysis_port如果和一个analysis_export直接相连也会出错。只 有在analysis_export后面再连接一级uvm_analysis_imp,才不会出错。