目录
UVM_PORT_BASE
本文参考:https://blog.csdn.net/qq_41360172/article/details/125688873?spm=1001.2014.3001.5506
所有的tlm端口都是继承于uvm_port_base,大致先看一下几个简单的函数
函数学习
function int max_size ();
return m_max_size;
endfunction
返回m_max_size,该变量在例化时由传入的max_size所决定,max_size是该端口能连接的最大imp数量;
所以 function int min_size ()不再赘述,返回m_min_size,同样由例化时传入参数决定;
function bit is_unbounded ();
return (m_max_size == UVM_UNBOUNDED_CONNECTIONS);
endfunction
如果m_max_size等于UVM_UNBOUNDED_CONNECTIONS返回1,否则返回0,m_max_size声明为UVM_UNBOUNDED_CONNECTIONS意味着没有为该port设置最大imp连接限制
function bit is_port ();
return m_port_type == UVM_PORT;
endfunction
该函数有一系列分别为is_export等,用于判断port类型,内置了uvm_port_type_e m_port_type枚举类型记录port类型,在例化时对该变量进行了赋值
function int size ();
return m_imp_list.num();
endfunction
返回m_imp_list的大小,m_imp_list是一个关联数组,用于存放port连接的imp句柄
function int m_get_if_mask();
return m_if_mask;
endfunction
m_if_mask好像是对端口兼容进行检查,比如blocking_put_port与blocking_put_imp兼容,mask会在端口例化的时候通过传入的参数打上标记
function void set_if (int index=0);
m_if = get_if(index);
if (m_if != null)
m_def_index = index;
endfunction
set_if函数可以为m_if进行赋值,值通过调用get_if来获得,先看m_if是什么东西再看get_if函数
m_if也是this_type类型
function uvm_port_base #(IF) get_if(int index=0);
string s;
if (size()==0) begin
m_comp.uvm_report_warning("get_if",
"Port size is zero; cannot get interface at any index", UVM_NONE);
return null;
end
if (index < 0 || index >= size()) begin
$sformat(s, "Index %0d out of range [0,%0d]", index, size()-1);
m_comp.uvm_report_warning(s_connection_error_id, s, UVM_NONE);
return null;
end
foreach (m_imp_list[nm]) begin
if (index == 0)
return m_imp_list[nm];
index--;
end
endfunction
首先调用size进行判断,当m_imp_list是空时也就是该port并没有与任何imp连接,直接return
其次如果index < 0 以及> size(),那么是不合法的直接return
最后排除了以上情况后对m_imp_list进行遍历,return 对应的imp句柄
connect具体内容
接下来看一些我们最经常使用的函数connect是如何实现的
virtual function void connect (this_type provider);
uvm_root top;
uvm_coreservice_t cs;
cs = uvm_coreservice_t::get();
top = cs.get_root();
if (end_of_elaboration_ph.get_state() == UVM_PHASE_EXECUTING || // TBD tidy
end_of_elaboration_ph.get_state() == UVM_PHASE_DONE ) begin
m_comp.uvm_report_warning("Late Connection",
{"Attempt to connect ",this.get_full_name()," (of type ",this.get_type_name(),
") at or after end_of_elaboration phase. Ignoring."});
return;
end
以上代码还没研究
if (provider == null) begin
m_comp.uvm_report_error(s_connection_error_id,
"Cannot connect to null port handle", UVM_NONE);
return;
end
if (provider == this) begin
m_comp.uvm_report_error(s_connection_error_id,
"Cannot connect a port instance to itself", UVM_NONE);
return;
end
if ((provider.m_if_mask & m_if_mask) != m_if_mask) begin
m_comp.uvm_report_error(s_connection_error_id,
{provider.get_full_name(),
" (of type ",provider.get_type_name(),
") does not provide the complete interface required of this port (type ",
get_type_name(),")"}, UVM_NONE);
return;
end
// IMP.connect(anything) is illegal
if (is_imp()) begin
m_comp.uvm_report_error(s_connection_error_id,
$sformatf(
"Cannot call an imp port's connect method. An imp is connected only to the component passed in its constructor. (You attempted to bind this imp to %s)", provider.get_full_name()), UVM_NONE);
return;
end
// EXPORT.connect(PORT) are illegal
if (is_export() && provider.is_port()) begin
m_comp.uvm_report_error(s_connection_error_id,
$sformatf(
"Cannot connect exports to ports Try calling port.connect(export) instead. (You attempted to bind this export to %s).", provider.get_full_name()), UVM_NONE);
return;
end
void'(m_check_relationship(provider));
m_provided_by[provider.get_full_name()] = provider;
provider.m_provided_to[get_full_name()] = this;
endfunction
首先需要判断该port所连接的provider的属性,不可以是null,不可以是本port,mask具体含义还不清楚
其次本port不可以是imp,如果是export的话provider不可以是port
m_check_relationship
接着进行了m_check_relationship。大致的内容就是UVM会进行tlm连接间的层次关系,建议tlm连接最大跨越层次为1层,打个比方就是driver与agent之间的连接是最佳的,而driver跨越agent与env连接则是不建议的;
最后在port的m_provided_by中放入imp句柄,imp的m_provided_to中放入port句柄,可知m_provided_by中存放的是所连接的imp句柄,而m_provided_to中存放的是被连接到的port句柄
local function bit m_check_relationship (this_type provider);
string s;
this_type from;
uvm_component from_parent;
uvm_component to_parent;
uvm_component from_gparent;
uvm_component to_gparent;
// Checks that the connection is between ports that are hierarchically
// adjacent (up or down one level max, or are siblings),
// and check for legal direction, requirer.connect(provider).
// if we're an analysis port, allow connection to anywhere
if (get_type_name() == "uvm_analysis_port")
return 1;
from = this;
from_parent = get_parent();
to_parent = provider.get_parent();
// skip check if we have a parentless port
if (from_parent == null || to_parent == null)
return 1;
from_gparent = from_parent.get_parent();
to_gparent = to_parent.get_parent();
// Connecting port-to-port: CHILD.port.connect(PARENT.port)
//
if (from.is_port() && provider.is_port() && from_gparent != to_parent) begin
s = {provider.get_full_name(),
" (of type ",provider.get_type_name(),
") is not up one level of hierarchy from this port. ",
"A port-to-port connection takes the form ",
"child_component.child_port.connect(parent_port)"};
m_comp.uvm_report_warning(s_connection_warning_id, s, UVM_NONE);
return 0;
end
// Connecting port-to-export: SIBLING.port.connect(SIBLING.export)
// Connecting port-to-imp: SIBLING.port.connect(SIBLING.imp)
//
else if (from.is_port() && (provider.is_export() || provider.is_imp()) &&
from_gparent != to_gparent) begin
s = {provider.get_full_name(),
" (of type ",provider.get_type_name(),
") is not at the same level of hierarchy as this port. ",
"A port-to-export connection takes the form ",
"component1.port.connect(component2.export)"};
m_comp.uvm_report_warning(s_connection_warning_id, s, UVM_NONE);
return 0;
end
// Connecting export-to-export: PARENT.export.connect(CHILD.export)
// Connecting export-to-imp: PARENT.export.connect(CHILD.imp)
//
else if (from.is_export() && (provider.is_export() || provider.is_imp()) &&
from_parent != to_gparent) begin
s = {provider.get_full_name(),
" (of type ",provider.get_type_name(),
") is not down one level of hierarchy from this export. ",
"An export-to-export or export-to-imp connection takes the form ",
"parent_export.connect(child_component.child_export)"};
m_comp.uvm_report_warning(s_connection_warning_id, s, UVM_NONE);
return 0;
end
return 1;
endfunction
resolve_bindings
最后还有一个比较重要的函数resolve_bindings,其实刚才还漏了一个比较重要的函数,我们还不知道m_imp_list中是何时进行填充的,其实是通过调用m_add_list进行填充,m_add_list会在resolve_bindings中被调用
在resolve_bindings中,如果是imp则在m_imp_list中放入自己,否则遍历m_provided_by,分别对m_provided_by中的成员进行调用resolve_bindings,并且通过调用m_add_list将句柄存入m_imp_list;
因为port在遍历m_provided_by中的imp时会依次调用imp的resolve_bindings,为了避免imp的该函数被多次调用,m_resolved被引用了进来。
最后如果size()是1的话,则会调用set_if,对m_if进行f赋值
以上讨论时基于1对1的情况,至于1对多下次再说
接下来看resolve_bindings是在哪里调用的,参考大佬的博客,具体我还没有细看
所以最重要的就是m_if,在端口类中使用m_if指向imp,继而可以调用到imp的函数
virtual function void resolve_bindings();
if (m_resolved) // don't repeat ourselves
return;
if (is_imp()) begin
m_imp_list[get_full_name()] = this;
end
else begin
foreach (m_provided_by[nm]) begin
this_type port;
port = m_provided_by[nm];
port.resolve_bindings();
m_add_list(port);
end
end
m_resolved = 1;
if (size() < min_size() ) begin
m_comp.uvm_report_error(s_connection_error_id,
$sformatf("connection count of %0d does not meet required minimum of %0d",
size(), min_size()), UVM_NONE);
end
if (max_size() != UVM_UNBOUNDED_CONNECTIONS && size() > max_size() ) begin
m_comp.uvm_report_error(s_connection_error_id,
$sformatf("connection count of %0d exceeds maximum of %0d",
size(), max_size()), UVM_NONE);
end
if (size())
set_if(0);
endfunction
local function void m_add_list (this_type provider);
string sz;
this_type imp;
for (int i = 0; i < provider.size(); i++) begin
imp = provider.get_if(i);
if (!m_imp_list.exists(imp.get_full_name()))
m_imp_list[imp.get_full_name()] = imp;
end
endfunction
uvm_analysis_port
uvm_analysis_port简介
uvm_analysis_port继承于uvm_port_base,是一个参数化的类, 需要定义端口所传输的数据类型,例化时会为m_if_mask赋值为`UVM_TLM_ANALYSIS_MASK,不同的端口会有mask不一样
// primitive interfaces
`define UVM_TLM_BLOCKING_PUT_MASK (1<<0)
`define UVM_TLM_BLOCKING_GET_MASK (1<<1)
`define UVM_TLM_BLOCKING_PEEK_MASK (1<<2)
`define UVM_TLM_BLOCKING_TRANSPORT_MASK (1<<3)
`define UVM_TLM_NONBLOCKING_PUT_MASK (1<<4)
`define UVM_TLM_NONBLOCKING_GET_MASK (1<<5)
`define UVM_TLM_NONBLOCKING_PEEK_MASK (1<<6)
`define UVM_TLM_NONBLOCKING_TRANSPORT_MASK (1<<7)
`define UVM_TLM_ANALYSIS_MASK (1<<8)
`define UVM_TLM_MASTER_BIT_MASK (1<<9)
`define UVM_TLM_SLAVE_BIT_MASK (1<<10)
ap端口通过write函数来进行传输数据,因为ap端口是可以连接多个imp的,所以遍历调用get_if,获得imp句柄,并调用imp的write函数
function void write (input T t);
uvm_tlm_if_base # (T, T) tif;
for (int i = 0; i < this.size(); i++) begin
tif = this.get_if (i);
if ( tif == null )
uvm_report_fatal ("NTCONN", {"No uvm_tlm interface is connected to ", get_full_name(), " for executing write()"}, UVM_NONE);
tif.write (t);
end
endfunction
endclass
uvm_analysis_imp简介
class uvm_analysis_imp #(type T=int, type IMP=int)
extends uvm_port_base #(uvm_tlm_if_base #(T,T));
`UVM_IMP_COMMON(`UVM_TLM_ANALYSIS_MASK,"uvm_analysis_imp",IMP)
function void write (input T t);
m_imp.write (t);
endfunction
endclass
imp十分简单,与port不同的是端口声明的时候除了需要声明传输数据类型还需要定义例化所在组件,看一下UVM_IMP_COMMON做了什么
`define UVM_IMP_COMMON(MASK,TYPE_NAME,IMP) \
local IMP m_imp; \
function new (string name, IMP imp); \
super.new (name, imp, UVM_IMPLEMENTATION, 1, 1); \
m_imp = imp; \
m_if_mask = MASK; \
endfunction \
主要就是对例化进行了封装,不过还定义了local的m_imp,m_imp通过在例化时传入的参数来获得所在组件的句柄,同时也进行了mask,`UVM_TLM_ANALYSIS_MASK,与ap可以兼容,从上面摘出来connect的一段代码,连接的端口类型必须是一致的,mask必须相同
if ((provider.m_if_mask & m_if_mask) != m_if_mask) begin
m_comp.uvm_report_error(s_connection_error_id,
{provider.get_full_name(),
" (of type ",provider.get_type_name(),
") does not provide the complete interface required of this port (type ",
get_type_name(),")"}, UVM_NONE);
return;
end
最后UVM_TLM_GET_TYPE_NAME中重载了get_type_name,name为uvm_analysis_imp
`define UVM_TLM_GET_TYPE_NAME(NAME) \
virtual function string get_type_name(); \
return NAME; \
endfunction
uvm_analysis_export简介
function void write (input T t);
uvm_tlm_if_base #(T, T) tif;
for (int i = 0; i < this.size(); i++) begin
tif = this.get_if (i);
if (tif == null)
uvm_report_fatal ("NTCONN", {"No uvm_tlm interface is connected to ", get_full_name(), " for executing write()"}, UVM_NONE);
tif.write (t);
end
endfunction
endclass
export需要连接imp才可以调用write函数否则会会报错
对于以上三种端口,在例化时分别会对端口属性进行设置,uvm_analysis_port是UVM_PORT,以此类推
UVM_TLM_FIFO
uvm_tlm_fifo内建了一个默认大小为1的mailbox,可以通过例化传参改变信箱大小
tlm为用户内建了一系列的调用函数以及端口,一系列的调用函数都是对内建的信箱进行操作
一系列的端口在父类uvm_tlm_fifo_base中实现
virtual task get( output T t );
m_pending_blocked_gets++;
m.get( t );
m_pending_blocked_gets--;
get_ap.write( t );
endtask
virtual task peek( output T t );
m.peek( t );
endtask
virtual function bit try_get( output T t );
if( !m.try_get( t ) ) begin
return 0;
end
get_ap.write( t );
return 1;
endfunction
uvm_tlm_analysis_fifo
uvm_tlm_analysis_fifo继承于uvm_tlm_fifo并扩展了一个aimp
uvm_analysis_imp #(T, uvm_tlm_analysis_fifo #(T)) analysis_export;