uvm学习之port

目录

UVM_PORT_BASE

        函数学习

        connect具体内容        

        m_check_relationship

        resolve_bindings

uvm_analysis_port

uvm_analysis_port简介

uvm_analysis_imp简介

uvm_analysis_export简介

UVM_TLM_FIFO

        uvm_tlm_analysis_fifo


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;

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
UVM的analysis_port是用于传递transaction的端口之一。它可以与多个uvm_analysis_imp相连,实现一对多的通信。与put和get系列端口不同,analysis_port没有阻塞和非阻塞的概念,因为它本身就是广播,不需要等待其他端口的响应。然而,如果一个analysis_port直接与一个analysis_export相连,会导致错误。为了避免这种情况,需要在analysis_export后面再连接一个uvm_analysis_imp。这样才能正确地使用uvm analysis port。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [【UVM】【UVM Class Reference Manual 1.2】uvm analysis port](https://blog.csdn.net/m0_38084579/article/details/84765010)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [UVM中analysis端口的使用方法](https://blog.csdn.net/Michael177/article/details/125710968)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [uvm_analysis通信端口学习](https://blog.csdn.net/m0_56242485/article/details/123285100)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值