What's the diffirence betwen *_export and *_imp?

Dear all,
I wonder what’s the difference between *_export and *_imp?
Thank you!

You can think of an imp as a special kind of export, one that is at the end of the connectivity chain.

Both are connection points, but an export is expected to pass on the connection chain to some other connection point on an internal object (either another export or an imp). An imp is a connection point on an object that will actually provide the functions of the interface (e.g. put, try_put).

Say you had a monitor that writes out data through an analysis_port, and that the analysis port is connected to a scoreboard that contains an analysis_fifo inside.

The connectivity chain would be monitor_analysis_Port->sb_analysis_Export->analysis_fifo_Imp

The imp connects and acts like an export, but it is always the terminal of the connection chain - the connection point of the object that implements the functions.

-Kurt

If it wasn’t obvious from Kurt’s answer: imp means implementation. _imp port has burden to provide implementation of the API (write(), put(), get(), etc.). This is exact opposite of port or export who simply uses the API without providing implementation.

To create analogy with RTL world:
port = source of the signal
export = wires at each level of hierarchy that connects “RTL ports”
imp = destination of the signal

Thank you two!
My another concern is
1.when will we use *_imp like port or export(instanciating in code)? or it just is used internally.

2.If we want to create our own channel or port/export,how can we do that?

Thank you!

Hi Ahan,

1.when will we use *_imp like port or export(instanciating in code)? or it just is used internally.

A component that provides the implementation of the interface uses *_imp for its export, e.g.

class compB extends ovm_threaded_component;
     ovm_blocking_put_imp #(int,compB) put_export;

   virtual function void build();
      super.build();
      put_export = new("put_export",this);
   endfunction: build

   task put(int val);
      ...
   endtask: put

 ...
endclass: compB

You would use *_export if the interface is defined by a child component, e.g. an embedded channel:

class consumer extends ovm_component;

    ovm_blocking_put_export #(transaction) put_export;
    
    tlm_fifo #(transaction) f;
    
    function void connect();
      put_export.connect(f.blocking_put_export);
    endfunction
    
    ...
  endclass

2.If we want to create our own channel or port/export,how can we do that?

The channels and port/export approach used by OVM is based on SystemC (you might find it useful to take a look at some of the on-line SystemC tutorials - there is one on our web site: http://www.doulos.com/knowhow/systemc/tutorial/hierarchical_channels/). However, OVM requires a more complex implementation since SystemVerilog does not support multiple inheritance.

If you need to create your own channels and ports/exports, I suggest having a look at the code in the tlm directory (especially tlm_ifs.svh, ovm_imps.svh, tlm_fifo_base.svh) and ovm_port_base.svh. If you are still not sure how to do this, I can post a simple example.

Regards,
Dave

Hi David,

before some days I looked to that files in TLM folder but It was out my thoughts to understand the logic of developing our own channel.

I would be happy if u can put a simple example on how to develop such channel.

Regards,
Shunty:cool:

Hi Shunty,

Here are the main points from my simple example. I have put the source code and a compiler options file for QuestaSim into the enclosed zip archive. I have not been able to get this to compile with IUS 8.1 (for some unknown reason svpp generates a forward typedef for the my_if class in the specialization for ovm_port_base_base which masks the actual definition???).

//virtual interface class
   virtual class my_if extends ovm_report_object;
      pure virtual function void msg();
   endclass: my_if   

   //implementation of interface (ovm_port_base extends ovm_port_base_base extends my_if1)
   class my_if_imp #( type IMP = int ) extends ovm_port_base #(my_if);

      local IMP m_imp;
      
      function new( string name , IMP imp );
         super.new( name , imp , OVM_IMPLEMENTATION , 1 , 1 ); 
         m_if = this;
         m_imp = imp;
         m_if_mask = 0;  //if set, this value is checked against port mask during connect 
         m_if_name = "my_if";
         assert( this.m_connector.add_if( m_if ) );
      endfunction: new

      //implementation of interface methods - calls implementation in parent component/channel
      virtual function void msg();
         m_imp.msg();
      endfunction: msg

   endclass: my_if_imp

   //port (ovm_port_base extends ovm_port_base_base extends my_if)
   class my_if_port  extends ovm_port_base #(my_if);

      function new( string name , ovm_component parent ,
                    int min_size = 1 , int max_size = 1 );

         super.new( name , parent ,
                    OVM_PORT ,
                    min_size , max_size );
         m_if_mask = 0;  //if set, this value is checked against port mask during connect 
         m_if_name = "my_if";
      endfunction: new

      //implementation of interface methods - calls pure virtual method in interface
      virtual function void msg();
         m_if.msg();
      endfunction: msg
 
   endclass: my_if_port

   //channel - contains implementation (my_if_export) and provides actual method implementation
   class my_channel extends ovm_component;
      
      my_if_imp #(my_channel) my_if_export;

      function new( string name, ovm_component parent = null);
         super.new(name, parent);
         my_if_export = new("my_if_export",this);
      endfunction: new

      function void msg();
         ovm_report_info("MY_CHANNEL","Called msg");
      endfunction:msg
      
   endclass: my_channel

Hope that helps you to understand the main points.

Regards,
Dave

Thanks Dave