《UVM白皮书》关于reference_model的介绍
reference_model用来模拟DUT的功能,并将结果送入scoreboard与DUT的结果进行比较。
在reference_model的示例中会出现很多新的UVM内容,这些会在后边更详细地介绍,现在我们先理解并记住就好了。
另外这个组件并不是每个验证平台中必须的,其他组件也是。验证平台包含什么,这都是根据我们的实际需要来确定的。
一、一个reference_model示例
class my_model extends uvm_component;//UVM库中并没有uvm_referce_model这种源文件
//所以 my_model 直接派生自uvm_component
uvm_blocking_get_port #(my_transaction) port;//注意!!
uvm_analysis_port #(my_transaction) ap;//注意!!
`uvm_component_utils(my_model)
function new(string name="my_model",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 my_mode::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
二、在env中例化reference_model
和前面文章中例化组件相同,在env中加入 my_model mdl; 在env的build_phase中加入 mdl=my_model::type_id::create("mdl",this); 即可。
此时的uvm树形结构为:
三、介绍reference_model
验证平台各个组件之间的信息传递都是基于transaction的。reference_model的作用是实现和DUT相同的功能,并将输出传递给scoreboard组件与真实dut的输出进行比较。
简单示例my_model结构并不复杂。但是其中重要的是,其中my_transaction的传递方式。
my_model是从i_agt获得my_transaction,并把my_transaction传递给scoreboard。
在UVM中,通常使用TLM实现component之间transaction级通信。
1. TLM(transaction level modeling事务级建模)通信
要实现通信,有两个问题:① 怎么发送数据?②怎么接收数据?
① 一种数据发送方式—uvm_analysis_port
uvm_analysis_port 是一个参数化的类,其参数就是它要传递的数据类型。即#()中的内容。
发送transaction是在monitor中进行的。
- 在monitor中声明uvm_annalysis_port类变量 ap;
- 在monitor的build_phase中实例化ap;
- 在main_phase中,当收集完一个transaction后,需要将其写入ap;
...
uvm_analysis_port #(my_transaction) ap;//声明ap
...
virtual function void build_phase(uvm_phase phase);
...
ap = new ("ap",this);//实例化ap
endfunction
task my_monitor::main_phase(uvm_phase phase);
my_transaction tr;
while(1) begin
tr = new("tr");
collect_one_pkt(tr);
ap.write(tr);//write是uvm_annalysisi_port的一个内建函数
end
endtask
至此,在my_monitor中需要为transaction通信准备的工作已经全部完成。
② 一种数据接收方式—uvm_blocking_get_port
uvm_blocking_get_port 也是一个参数化的类,其参数就是它要传递的数据类型。即#()中的内容。
在reference_model中接收transaction。
- 在my_model中定义uvm_blocking_get_port类变量 port;
- 在my_model的build_phase中实例化port;
- 在main_phase中,通过port.get任务来接收从i_agt的monitor中发出的transaction;
class my_model extends uvm_component;
uvm_blocking_get_port #(my_transaction) port;//注意!!
uvm_analysis_port #(my_transaction) ap;//注意!!
...
extern function void build_phase(uvm_phase phase);
endclass
function void my_mode::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;
super.main_phase(phase);
while(1)begin
port.get(tr);
...
end
endtask
在my_monitor和my_model中定义并实现了各自的端口后,通信功能并没有实现,还需要在my_env中使用fifo将两个端口联系在一起。
③ 用fifo连接①②中的两个端口
- 在my_env中定义1个fifo;
- 在my_env的build_phase中实例化fifo;
- 在my_env的connect_phase中将fifo分别与monitor中的ap和model中的port相连。
class my_env extends uvm_env;
...
uvm_tlm_analysis_fifo #(my_transaction) agt_mdl_fifo;
...
virtual function void build_pase(uvm_phase phase);
...
agt_mdl_fifo = new("agt_mdl_fifo",this);
endfunction
extern virtual function void connect_phase(uvm_phase phase);
endclass
fifo的类型是uvm_tlm_analysis_fifo,它本身也是一个参数化的类,其参数就是存储在其中的transaction的类型,这里是my_transaction。
function void my_env::connect_phase(uvm_phase phase);
super.connect_phase(phase);
i_agt.ap.connect(agt_mdl_fifo.analysis_export);//注意fifo这里都是.xx_export
mdl.port.connect(agt_mdl_fifo.blocking_get_export);//注意fifo这里都是.xx_export
endfunction
注意这里是i_agt.ap而不是mon.ap,因为前边我们已经把driver和monitor封装在agent里了。遵循UVM树形组织结构。
我们需要在my_agent中定义一个ap:
uvm_analysis_port #(my_transaction) ap;
然后在my_agent的connect_phase中,把monitor的ap值赋给agent中的ap即可:
virtual function void connect_phase(uvm_phase phase);
super.connect_phase(phase);
ap = mon.ap;
endfunction
2. 为什么需要使用fifo?
——为什么需要使用fifo? 为什么不能直接把ap和port相连接呢?
——由于annlysis_port是非阻塞性质的,ap.write函数调用完成后马上返回,不会等待数据被接受。 那么假如write函数调用时,blocking_get_port正在忙其他事情,尚未准备好接受新数据时,那么那么此时被write函数写入的my_trannsaction就需要一个暂存的位置,这就是fifo。
3. connect_phase
connect_phase也是UVM的内建phase之一。
- 它在build_phase之后执行;
- 它的执行顺序是从UVM树的树叶到树根,和build_phase的执行顺序相反;
至此结束,主要介绍了transaction怎么从i_agt.monitor传递给reference_model。其他组件之间的transaction传递也是类似的。