1.reference model完成和DUT相同的功能:
`ifndef MY_MODEL__SV
`define MY_MODEL__SV
class my_model extends uvm_component;
uvm_blocking_get_port #(my_transaction) port;
uvm_analysis_port #(my_transaction) ap;
extern function new(string name, uvm_component parent);
extern function void build_phase(uvm_phase phase);
extern virtual task main_phase(uvm_phase phase);
`uvm_component_utils(my_model)
endclass
function my_model::new(string name, uvm_component parent);
super.new(name, parent);
endfunction
function void my_model::build_phase(uvm_phase phase);
super.build_phase(phase);
port = new("port", this);
ap = new("ap", this);
endfunction
task my_model::main_phase(uvm_phase phase);
my_transaction tr;
my_transaction new_tr;
super.main_phase(phase);
while(1) begin
port.get(tr);
new_tr = new("new_tr");
new_tr.my_copy(tr);
`uvm_info("my_model", "get one transaction, copy and print it:", UVM_LOW)
new_tr.my_print();
ap.write(new_tr);
end
endtask
`endif
1)line 6:从前级monitor 得到transaction 的接口。
2)line 7:发送给后级scoreboard 的接口。
3)new只是my_model 本身。里面的两个 port 实在build_phase 中做的。
4)line 27,tr只是声明了句柄,并没有new 占用内存空间,line 31只是把monitor得到的tr(占内存) 的地址赋给model 这个句柄。(猜的,应该是这样。get只不过得到个指针)
5)line 28 声明的new_tr 在line32被 new,说明占用内存了,line 33进而copy 到这个内存。
6)line36是把赋值得到的new_tr,给后面的scoreboard。
2.my_copy 是在my_transaction中定义的函数,如下:
function void my_copy(my_transaction tr);
if(tr == null)
`uvm_fatal("my_transaction", "tr is null!!!!")
dmac = tr.dmac;
smac = tr.smac;
ether_type = tr.ether_type;
pload = new[tr.pload.size()];
for(int i = 0; i < pload.size(); i++) begin
pload[i] = tr.pload[i];
end
crc = tr.crc;
endfunction
3.需要在my_env中例化,如下:
class my_env extends uvm_env;
************
my_model mdl;
***********
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);
************
mdl = my_model::type_id::create("mdl", 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);
******************
endfunction
4.UVM树变为:
5.在UVM中,通常使用TLM实现component 之间transaction 级别的通信。有两点要考虑:如何发送,如何接受。发送有多种方式,其中一种是使用uvm_analysis_port。
1)在my_monitor定义如下:
class my_monitor extends uvm_monitor;
**************
uvm_analysis_port #(my_transaction) ap;
2)声明ap后,build中例化:
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
if(!uvm_config_db#(virtual my_if)::get(this, "", "vif", vif))
`uvm_fatal("my_monitor", "virtual interface must be set for vif!!!")
ap = new("ap", this);
endfunction
new只是monitor本身,里面的port (ap)需要在build_phase中实现。
task my_monitor::main_phase(uvm_phase phase);
my_transaction tr;
while(1) begin
tr = new("tr");
collect_one_pkt(tr);
ap.write(tr);//<=======
end
endtask
1). write 是uvm_analysis_port 的一个内建函数。到此,my_monitor为transaction通信准备的工作已全部完成。
6.接受方式也有多种。其中之一使用uvm_blocking_get_port。上面的monitor中,通过port.get 来得到从i_agt 的monitor中发出的transaction。
7.在my_monitor 和my_model 中各自定义并实现接口后,还要在my_env中使用fifo将两个端口连接到一起(在env中因为:看树结构图)。在my_env定义一个fifo,在build_phase例化:
class my_env extends uvm_env;
uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
virtual function void build_phase(uvm_phase phase);
super.build_phase(phase);
****
agt_mdl_fifo = new("agt_mdl_fifo", this);
endfunction
8.在env 的connect_phase中,将fifo的两侧的口,分别与my_monitor中的analysis_port 和 my_model 中的blocking_get_port相连:
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);
endfunction
1)connect_phase也是UVM内建的一个phase。在build_phase后马上执行。但connect_phase是从树叶到树根。
2)之前ap例化在monitor中,但上面代码是i_agt和fifo 相连。my_agent 也声明个句柄,但不例化,当指针用,指向my_monitor的ap。
3)用fifo而不直接连接的原因是:analysis_port是非阻塞的,ap.write调用完后马上返回,不会等待数据被接收。加入write调用时,blocking_get_port忙于其他事,此时被write写入的my_transaction需要一个暂存的位置,就是fifo。(深度呢???)
4)my_agent的connect_phase 执行顺序早于my_env 的connect_phase,可以保证env中连接的时候,i_agt.ap已经被赋值,不是一个空指针。
9.上面的连接中,用的是i_agt 的 ap,定义和my_monitor完全一样,但是不用例化,需在my_agent 的connect_phase中将monitor 的ap赋值给它,相当于指向my_monitor的ap 的指针,my_agent 如下:
`ifndef MY_AGENT__SV
`define MY_AGENT__SV
class my_agent extends uvm_agent ;
my_driver drv;
my_monitor mon;
uvm_analysis_port #(my_transaction) ap;//<==============
function new(string name, uvm_component parent);
super.new(name, parent);
endfunction
extern virtual function void build_phase(uvm_phase phase);
extern virtual function void connect_phase(uvm_phase phase);
`uvm_component_utils(my_agent)
endclass
function void my_agent::build_phase(uvm_phase phase);
super.build_phase(phase);
if (is_active == UVM_ACTIVE) begin
drv = my_driver::type_id::create("drv", this);
end
mon = my_monitor::type_id::create("mon", this);
endfunction
function void my_agent::connect_phase(uvm_phase phase);
super.connect_phase(phase);
ap = mon.ap;//<================
endfunction
`endif
10. 整体结构如下图:
可以看出现uvm 的一些明明规则:
1)components 输出都叫analysis_port(往外给的),输入都叫uvm_blocking_get_port(往回拿的),还有其他的TLM。
2)上面的名字和fifo都是对应的,analysis 连到analysis,blocking_get 链接到blocking_get。
3)components的都叫port,fifo 的都叫export。