← Design Verification Interviews

Interview Question · Concept

Ports, Exports and Imps

L1 JuniorL2 Mid-Leveluvmtlm

The Question

The interviewer asks

Explain ports, exports and imps using this put connection. Who implements put(), and which way does the connect call go?

systemverilog
class producer extends uvm_component;
  uvm_blocking_put_port #(pkt) out;
  task run_phase(uvm_phase phase); /* ... */ out.put(p); endtask
endclass
class consumer extends uvm_component;
  uvm_blocking_put_imp #(pkt, consumer) in;
  task put(pkt p); /* store it */ endtask
endclass
Reveal the Model Answer

A port is the calling side: it needs someone to implement the method. An imp is the side that implements it, inside the component that owns the method. An export passes a connection through a parent's boundary to an imp deeper inside. You always connect from the port toward the implementation: prod.out.connect(cons.in). Here the consumer implements put(). With a get port, the producer would implement get() instead.

Five Connect Calls, One Put CallArrows are connect calls, from the caller side towards the provider. Ports climb out of the sending environment, one export link crosses between environments, and exports descend to the imp.
port (calls out)export (forwards)imp (implements)connect() linkcall: who calls whom
Five connect calls, one put callArrows are `connect` calls, from the caller side towards the provider. Ports climb out of the sending environment, one export link crosses between environments, and exports descend to the imp. port src.put_port Port of src_env port src.agent.put_port Port of the agent port prod.put_port The producer calls puthere export snk.put_export Export of snk_env export snk.agent.put_export Export of the agent imp cons.put_imp Calls consumer.put(t) prod.put_port.connect(put_port)agent.put_port.connect(put_port)src.put_port.connect(snk.put_export)put_export.connect(agent.put_export)put_export.connect(cons.put_imp)at run time: put(t)call
  1. Up

    Each agent and environment connects its child's port to its own port: child.port.connect(parent_port). Two such calls lift the port to the edge of src.

  2. Across

    The test, parent of both environments, makes the one sibling link: port to export.

  3. Down

    Each receiving parent connects its own export to its child's export or imp: parent_export.connect(child.export).

  4. Resolve

    Just before end of elaboration, UVM walks every chain and gives each port the list of imps at its end. All four links report size() = 1.

  5. Call10 ns (illustrative)

    At 10 ns the producer calls put_port.put(#1). The resolved port calls the imp directly, which calls consumer.put. The producer's thread waits 3 ns inside it.

    Follow the calls, values and waits
    1. call put(#1): prod.put_port → cons.put_imp
    2. Waiting: prod.put_port — producer inside consumer.put until 13 ns
  6. Return13 ns (illustrative)

    At 13 ns consumer.put stores #1 and returns; the producer prints put(#1) returned.

    Follow the calls, values and waits
    1. Released: prod.put_port
Read the diagram as text
  • src.put_port: Port of src_env
  • src.agent.put_port: Port of the agent
  • prod.put_port: The producer calls put here
  • snk.put_export: Export of snk_env
  • snk.agent.put_export: Export of the agent
  • cons.put_imp: Calls consumer.put(t)
  • prod.put_port → src.agent.put_port: prod.put_port.connect(put_port)
  • src.agent.put_port → src.put_port: agent.put_port.connect(put_port)
  • src.put_port → snk.put_export: src.put_port.connect(snk.put_export)
  • snk.put_export → snk.agent.put_export: put_export.connect(agent.put_export)
  • snk.agent.put_export → cons.put_imp: put_export.connect(cons.put_imp)
  • prod.put_port → cons.put_imp: at run time: put(t) call

The five connect lines build a netlist. At run time, the call skips the intermediate ports and exports.

InterfaceWho callsWho implementsData moves
putproducer (port)consumer (imp)port to imp
getconsumer (port)producer (imp)imp to port
analysis writemonitor (analysis port)every subscriber (imp)port to all imps

The call always goes from port to imp. The data can go either way.

Self-Check

Tick each key point you covered in your answer.

What Interviewers Listen For (Junior)

A good junior answer says who implements the method for put and for get. It writes the connect call in the right direction.

Cross-Questions

Where the interviewer goes next. Open a node to practise that follow-up.

WhyWhat IfProve ItCompareScale It

  1. Ports, Exports and Imps
  2. CompareBlocking vs Non-Blocking TLM CallsL1 Junior

    Compare put() with try_put(). When does the difference matter?

    Reveal the Model Answer

    put() is a task and may wait until the receiver accepts. try_put() is a function: it returns at once, with 1 if the receiver took the item and 0 if not. Use the non-blocking form in code that must not wait, such as a function or a monitor sampling every clock.

    Both Ends Call into the FIFOSolid arrows labelled with a call leave the caller. The FIFO's two exports are imps; the producer and the consumer each own a port, so both are initiators.
    imp (implements)data: where the transaction goescall: who calls whom
    Both ends call into the FIFOSolid arrows labelled with a call leave the caller. The FIFO's two exports are imps; the producer and the consumer each own a port, so both are initiators. component producer put_port: puts #1 to #5back to back imp fifo.put_export An imp: put, try_put,can_put queue uvm_tlm_fifo, size 2 used(), is_full(), flush() imp fifo.get_peek_export An imp: get, peek and theirtry_ forms component consumer get_port: one get every10 ns put(t) call, t goes instores tget(t) calloldest tt returns
    1. Fill0 ns (illustrative)

      At 0 ns the producer puts #1 and #2; each put returns at once. used() is 2 and is_full() is 1.

      Follow the calls, values and waits
      1. data #1: producer → fifo.put_export
      2. data #1: fifo.put_export → uvm_tlm_fifo, size 2
      3. data #2: producer → fifo.put_export
      4. data #2: fifo.put_export → uvm_tlm_fifo, size 2
    2. Stall0 ns (illustrative)

      The producer calls put(#3). The FIFO is full, so the producer's thread waits inside the FIFO's put until space appears.

      Follow the calls, values and waits
      1. data #3: producer → fifo.put_export
      2. Waiting: producer — blocked in put(#3): FIFO full
    3. Drain10 ns (illustrative)

      At 10 ns the consumer calls get and receives #1. That frees one place.

      Follow the calls, values and waits
      1. call get(t): consumer → fifo.get_peek_export
      2. data #1: uvm_tlm_fifo, size 2 → fifo.get_peek_export
      3. return #1: fifo.get_peek_export → consumer
    4. Resume10 ns (illustrative)

      In the same time step #3 is stored and the producer's put returns. From now on the producer is paced by the consumer: one put per 10 ns.

      Follow the calls, values and waits
      1. data #3: fifo.put_export → uvm_tlm_fifo, size 2
      2. Released: producer
    Read the diagram as text
    • producer: put_port: puts #1 to #5 back to back
    • fifo.put_export: An imp: put, try_put, can_put
    • uvm_tlm_fifo, size 2: used(), is_full(), flush()
    • fifo.get_peek_export: An imp: get, peek and their try_ forms
    • consumer: get_port: one get every 10 ns
    • producer → fifo.put_export: put(t) call, t goes in
    • fifo.put_export → uvm_tlm_fifo, size 2: stores t
    • consumer → fifo.get_peek_export: get(t) call
    • uvm_tlm_fifo, size 2 → fifo.get_peek_export: oldest t
    • fifo.get_peek_export → consumer: t returns

    A bounded FIFO lets the producer run ahead by its size, then applies backpressure: the full FIFO blocks put.

    Self-Check

    Tick each key point you covered in your answer.

    Misconception

    "try_put retries until it succeeds."

    It tries once and returns 0 on failure. The caller decides whether to retry.

    Source Accellera, UVM 1.1 User's Guide, §2.3.5 Blocking versus Nonblocking (PDF p21)

  3. WhyWhy Exports ExistL2 Mid-Level

    Why not connect straight to the imp deep inside an agent? What does an export add?

    Reveal the Model Answer

    An export lets the agent offer one connection point on its boundary, whatever sits inside. The outside connects to agt.in_export, and the agent connects that to the right child. If the inside changes, outside code still works. Connecting to agt.child.grandchild.imp ties every user to today's structure.

    Self-Check

    Tick each key point you covered in your answer.

    Half-Truth

    "Exports are needed for the code to compile."

    A direct hierarchical connection to an imp compiles. The export is about hiding structure, not legality.

    Source Accellera, UVM 1.1 User's Guide, §2.4 Encapsulation and Hierarchy (PDF p22)

  4. What IfAn Export Left Unconnected InsideL2 Mid-Level

    What if the outside connects to the agent's export, but nothing inside connects the export to an imp?

    Reveal the Model Answer

    The export then resolves to no imp. Before the run starts, uvm-core reports an error: the connection count of 0 does not meet the required minimum of 1. An analysis port, by contrast, may legally have no listeners. So a forgotten inner link is caught, but a forgotten listener is not.

    Self-Check

    Tick each key point you covered in your answer.

    Missed Corner

    "An analysis port with no listeners is an error too."

    Analysis ports allow zero connections. A monitor with no subscribers is legal, so a missing scoreboard link passes without a message.

    Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, tlm1/uvm_analysis_port.svh (port minimum 0, export minimum 1)

  5. Prove ItSolve U3.2.1: Two Environments That Never TalkSimulator-graded

    Show it, don't just say it: this Silicon Lab problem is graded by the simulator.

    Open Problem U3.2.1

  6. Scale ItWhen to Use TLM-2.0 SocketsL3 Senior

    When would you use TLM-2.0 sockets instead of TLM-1 ports?

    Reveal the Model Answer

    TLM-2.0 sends one standard generic payload with timing notes, through sockets that carry calls in both directions. It fits memory-mapped buses and connecting to SystemC models or virtual platforms that already speak it. For ordinary agent-to-scoreboard traffic, TLM-1 ports and analysis ports are simpler and enough.

    Initiator, Target and One PayloadSolid arrows are calls on the forward and backward paths; the payload is one object both sides touch at agreed times.
    Initiator, target and one payloadSolid arrows are calls on the forward and backward paths; the payload is one object both sides touch at agreed times. predictor initiator b_sock, nb_sock,nb_transport_bw object generic payload command, address,data, length, status target mem_model b_transport,nb_transport_fw process responder thread one per nonblockingrequest creates, fillsforward callsets status, dataforknb_transport_bw
    1. Blocking0 ns (illustrative)

      b_transport runs in the target and returns with the status set: one call, one transaction.

      Follow the calls, values and waits
      1. data WRITE 01 = 11: initiator → generic payload
      2. call b_transport(gp, delay): initiator → mem_model
      3. data status OK: mem_model → generic payload
      4. return returns; delay 20 ns: mem_model → initiator
    2. Nonblocking: the request100 ns (illustrative)

      The forward call returns at once with END_REQ. The target has already read the memory and starts a responder.

      Follow the calls, values and waits
      1. data READ 12: initiator → generic payload
      2. call nb_transport_fw: BEGIN_REQ: initiator → mem_model
      3. data data 2d, status OK: mem_model → generic payload
      4. call start a responder: mem_model → responder thread
      5. return UPDATED, END_REQ: mem_model → initiator
      6. Waiting: responder thread — waits 40 ns for bank 1
    3. Nonblocking: the response140 ns (illustrative)

      A responder answers later on the backward path, with the same payload.

      Follow the calls, values and waits
      1. call nb_transport_bw: BEGIN_RESP: responder thread → initiator
      2. return COMPLETED: initiator → responder thread
      3. Released: responder thread
    Read the diagram as text
    • initiator: b_sock, nb_sock, nb_transport_bw
    • generic payload: command, address, data, length, status
    • mem_model: b_transport, nb_transport_fw
    • responder thread: one per nonblocking request
    • initiator → generic payload: creates, fills
    • initiator → mem_model: forward call
    • mem_model → generic payload: sets status, data
    • mem_model → responder thread: fork
    • responder thread → initiator: nb_transport_bw

    Initiator sockets connect to target sockets; connect is called on the initiator side.

    Self-Check

    Tick each key point you covered in your answer.

    Wrong Altitude

    "TLM-2.0 replaces TLM-1, so use it everywhere."

    Both are in UVM. TLM-2.0 earns its extra setup only when you need the generic payload or SystemC interoperability.

    Source Accellera, UVM 1.1 User's Guide, §2.2 TLM, TLM-1, and TLM-2.0 (PDF p17-18)

What People Get Wrong

Common wrong answers and why they fail.

Misconception

"Data always flows from the port to the export."

The call goes from port to imp. For a get, the data comes back the other way, from the imp to the port.

Source Accellera, UVM 1.1 User's Guide, §2.3.3-2.3.4 Basic TLM communication (PDF p19-20)

Buzzword Without Mechanism

"Ports and exports are the same thing with two names."

A port requires an implementation, and an export offers one from inside a component. A chain of ports with no imp at its end fails the connection check.

Source Accellera, UVM 1.1 User's Guide, §2.4.1 Hierarchical Connections (PDF p22)

Missed Corner

"An unconnected port is harmless until it is called."

At the end of elaboration the library checks every port's connection count and reports an error for one with too few connections.

Source uvm-core 2020.3.1 (IEEE 1800.2 library) source, base/uvm_port_base.svh, resolve_bindings: "connection count of 0 does not meet required minimum of 1"

Learn It, Then Prove It