Skip to content
DenseChip
Learn
PracticeCode labInterview

DenseChip

Navigate

Menu

LearnPracticeCode labInterview

Tools

All toolsSV editorCode repoResume
DenseChip

Practice, courses, and chip-design tools in one focused workspace.

LearnPracticeCode labInterviewToolsLearning paths

© 2026 DenseChip

AboutContactPrivacyTerms
Skip to lesson

Course contents

  • Start Here4
  • UVM Foundations14
  • UVM Phases & Simulation Lifecycle26
  • Factory & Configuration15
  • Transaction-Level Modeling (TLM)13
  • UVM Sequences & Stimulus21
  • UVM Agents & Protocol VIP21
    • Agent Architecture
    • Reusable VIP: Active, Passive and Protocol Agents
      • Active and Passive in Delivery: What a VIP Owes Its Integrator
      • Two Passive Monitors, One Bus
      • VIP Parameterization: Widths Up Front, Behavior at Runtime
      • Protocol Agent Patterns: APB, AXI and Streaming
      • Versioning, Delivery and Release Readiness
    • Error and Fault Injection
    • Agent Patterns: Slaves, Reset, Interrupts and Pipelines
  • Environment & Test Classes11
  • Checking & Coverage in UVM21
  • UVM Register Abstraction Layer (RAL)22
  • Advanced UVM in Practice13
  • Debug & Regression10
  • Coding Practice47
  • Verification in Industry Practice10
  • Senior Prep: Architecture, Leadership and Interviews32
Loading lesson

‹Reusable VIP: Active, Passive and Protocol Agents
  1. Topics
  2. UVM Verification
  3. UVM Agents & Protocol VIP
  4. Reusable VIP: Active, Passive and Protocol Agents
  5. Protocol Agent Patterns: APB, AXI and Streaming

Part 6 · Agents & Protocol IP · Intermediate

Protocol Agent Patterns: APB, AXI and Streaming

What APB, AXI and AXI-Stream agents must each get right in the driver and the monitor, how to choose item granularity, and why protocol checks belong in the interface.

On this page

    In a senior interview you will be asked to sketch an agent for a protocol, and the interviewer is not checking whether you know that a driver calls get_next_item. They want to hear the two or three things that make this protocol's agent hard: the access phase APB drivers forget, the outstanding IDs AXI drivers must track, the stability rule every stream driver must keep under backpressure. Every protocol agent has the same skeleton. What changes between protocols, and where do the protocol's own rules get checked?

    The idea: same skeleton, different joints

    Every protocol agent has an item, a driver loop, a monitor that reconstructs items from the pins, and a sequencer. The protocol decides three things about that skeleton: when a transfer completes (the monitor's publish condition), what the driver must hold still or wait for (its timing rules), and how many transfers can be in flight (whether the driver is a simple loop or a set of threads). Think of a skeleton with the same bones in every animal; what differs is how the joints move.

    The picture: three protocols side by side

    APB

    AXI (full)

    AXI-Stream

    Channels

    One, with setup and access phases

    Five: AW, W, B, AR, R, each valid/ready

    One, valid/ready

    Transfer completes when

    PSEL, PENABLE and PREADY are all 1 at a clock edge

    Per channel: valid and ready at an edge; a transaction when its last data or response arrives

    TVALID and TREADY at an edge

    Driver timing rule

    Setup one clock (PENABLE=0), then hold access until PREADY

    Hold each channel's payload stable until its handshake; never wait for ready before asserting valid

    TVALID must not wait for TREADY; hold TDATA, TLAST, TKEEP stable while TVALID is high and TREADY low

    In flight at once

    One

    Many, tracked by ID; same-ID in order

    One beat; packets are a higher layer

    Driver shape

    A loop

    One thread per channel and an outstanding table

    A loop

    Slave side

    PREADY wait states, PSLVERR

    Reactive slave with a memory, reordering by ID

    Random TREADY (a reactive slave)

    Build it

    APB: the access phase is the whole difficulty

    The bench's apb_driver is the reference: setup on one clock, PENABLE raised on the next, then do @(posedge clk); while (!pready);, which always spends at least one edge with PENABLE high before it looks at PREADY. A driver that tests PREADY in the same timestep it raised PENABLE ends the transfer before the access phase ever reaches the bus when the slave has no wait states. The edge-by-edge walk-through is in The driver. Two senior details on top: a production APB driver bounds the PREADY wait with a timeout error instead of hanging, and the monitor publishes on the completing edge only, with PRDATA and PSLVERR sampled there.

    AXI-Stream: never wait for ready, never move while stalled

    The stream driver in the bench is short, and two of its lines are the protocol:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    // snippet: from the course DMA bench
    class axis_driver extends uvm_driver #(axis_item);
      `uvm_component_utils(axis_driver)
      virtual axis_if vif;
    
      function new(string name, uvm_component parent);
        super.new(name, parent);
      endfunction
    
      function void build_phase(uvm_phase phase);
        super.build_phase(phase);
        if (!uvm_config_db#(virtual axis_if)::get(this, "", "vif", vif))
          `uvm_fatal("NOVIF", "axis_if not set for this driver")
      endfunction
    
      task run_phase(uvm_phase phase);
        vif.tvalid <= 1'b0;
        @(posedge vif.rst_n);
        forever begin
          axis_item beat;
          seq_item_port.get_next_item(req);
          beat = pre_drive(req);
          if (beat != null) begin
            repeat (beat.idle) @(posedge vif.clk);
            @(posedge vif.clk);
            vif.tdata  <= beat.data;
            vif.tlast  <= beat.last;
            vif.tvalid <= 1'b1;
            do @(posedge vif.clk); while (!vif.tready);   // the beat moves on TVALID && TREADY
            vif.tvalid <= 1'b0;
          end
          seq_item_port.item_done();
        end
      endtask
    
      // Hook for derived drivers: return the beat to drive (the sequence's item, or a
      // modified copy), or null to drop it. The base driver drives every item as is.
      virtual function axis_item pre_drive(axis_item it);
        return it;
      endfunction
    endclass
    • It raises TVALID without looking at TREADY first. AXI-Stream forbids a source from waiting for TREADY before asserting TVALID, because a sink may legally wait for TVALID before asserting TREADY, and two sides waiting for each other deadlock.

    • Once TVALID is high, nothing changes until do @(posedge clk); while (!vif.tready); sees the handshake. TDATA and TLAST stay stable for as many stall cycles as the sink wants.

    Then decide the item granularity. The bench uses one item per beat, which suits the DMA scoreboard because every word is predicted on its own. A packet-level item (a queue of beats plus TKEEP and TUSER) suits protocols where meaning lives at packet level, and makes packet-length coverage a coverpoint instead of a counter. Many VIPs publish both: beats on one analysis port, assembled packets on another, so each subscriber takes the level it needs.

    AXI: threads, IDs and ordering rules

    A full AXI master cannot be one loop, because the protocol's whole point is overlap: addresses issued before earlier data returns, responses out of order across IDs. The driver becomes one thread per channel sharing an outstanding table that holds a queue per ID. Same-ID transactions complete in order, different IDs may not, write data is never interleaved between transactions in AXI4, and read data beats of different IDs may interleave. That driver is built, run and explained in Pipelined and out-of-order drivers. The monitor mirrors it: per-channel samplers feed a correlator that joins address, data and response into one transaction by ID, and the scoreboard keys a queue per ID as well.

    Where protocol rules are checked: in the interface

    A protocol rule such as "TDATA is stable while stalled" can be checked by the monitor in code, but the natural home is a concurrent assertion in the interface. It works in active and passive mode alike, it fires at the exact cycle of the violation, and formal tools can prove it on the RTL with no testbench at all:

    design.sv
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    // snippet: AXI-Stream source rules as assertions inside axis_if
    property p_stable_while_stalled;
      @(posedge clk) disable iff (!rst_n)
        (tvalid && !tready) |=> (tvalid && $stable(tdata) && $stable(tlast));
    endproperty
    a_stable: assert property (p_stable_while_stalled)
      else $error("AXIS: TVALID dropped or payload changed while stalled");
    
    a_no_x_valid: assert property (@(posedge clk) disable iff (!rst_n) !$isunknown(tvalid))
      else $error("AXIS: TVALID is X out of reset");

    Give each assertion a switch (an interface bit the agent sets from its config) so an error-injection test can turn off exactly the rule it breaks on purpose, and nothing else.

    Watch it run: two protocols on one timeline

    dma_smoke_test with +UVM_VERBOSITY=UVM_HIGH shows what each monitor considers one transfer. An excerpt:

    Diagram
    UVM_INFO dma_tb.sv(151) @ 75000: uvm_test_top.env.apb.mon [APB_MON] WR addr=0c data=a5a50f0f
    UVM_INFO dma_tb.sv(151) @ 115000: uvm_test_top.env.apb.mon [APB_MON] WR addr=08 data=00000008
    UVM_INFO dma_tb.sv(151) @ 155000: uvm_test_top.env.apb.mon [APB_MON] WR addr=00 data=00000003
    UVM_INFO dma_tb.sv(299) @ 205000: uvm_test_top.env.in_agt.mon [AXIS_MON] data=1364cd0b
    UVM_INFO dma_tb.sv(299) @ 215000: uvm_test_top.env.out_agt.mon [AXIS_MON] data=b6c1c204
    UVM_INFO dma_tb.sv(299) @ 435000: uvm_test_top.env.in_agt.mon [AXIS_MON] data=aee114a1 LAST
    UVM_INFO dma_tb.sv(151) @ 475000: uvm_test_top.env.apb.mon [APB_MON] RD addr=04 rdata=00000002
    • APB writes complete 40 ns apart: setup, access, one wait state from the DMA, and the driver's idle clock. One transfer at a time, as the protocol says.

    • Each stream word appears at the output one clock after the input (205 ns in, 215 ns out), XORed with KEY: the DMA's single output register. Data values are random per seed.

    • The STATUS read at 475 ns returns DONE (0x2), published on the edge where PREADY was high, with PRDATA from that edge.

    What breaks: the stream driver that waited for ready

    A team's stream VIP passed every block test for a year. At subsystem level it was connected to a third-party sink IP and the first packet never moved. The waveform showed TVALID low and TREADY low forever. The VIP's driver did wait (vif.tready); vif.tvalid <= 1;, waiting for the sink to be ready before offering data. Every sink it had met so far raised TREADY while idle, so it had never mattered. The new sink, legally, waited for TVALID before raising TREADY.

    Both sides waited for each other: a protocol deadlock written into the testbench. The fix was the bench's shape (assert TVALID, then wait for the handshake), and the lesson was the assertion above plus a slave-side test: run the VIP's own regression against a sink that never asserts TREADY before TVALID, which is exactly what the bench's reactive slave does.

    Interview

    "Sketch an AXI-Stream agent. What are the protocol-specific traps?"

    A weak answer: "Driver drives TDATA and TVALID, monitor samples when TVALID and TREADY are high." True, and it is what every agent does; nothing protocol-specific was said.

    A strong answer: "The source must assert TVALID without waiting for TREADY, or it deadlocks against a sink that waits for TVALID, and it holds TDATA, TLAST, TKEEP and TUSER stable while stalled. The monitor publishes on valid-and-ready only, with a fresh object, and assembles packets on TLAST, dropping a partial packet on reset. I decide item granularity up front, often beats and packets on two ports. Stability and X checks live as assertions in the interface so they work passive. The slave side is a reactive agent with random TREADY that must include ready-before-valid and back-to-back cases."

    Follow-ups to expect: How does an AXI agent differ? (five channels, threads, an outstanding table per ID) and Why assertions in the interface rather than the monitor? (they work in every mode and at the exact cycle, and formal can reuse them).

    Check yourself An APB monitor publishes on every clock where PSEL and PENABLE are high. The DMA inserts one wait state per access. What does the scoreboard receive?

    Two items per access: one on the wait-state cycle, where PREADY is 0 and PRDATA is not yet valid, and one on the completing cycle. The transfer completes only on the edge where PSEL, PENABLE and PREADY are all 1, which must be the publish condition.

    Check yourself A stream sink asserts TREADY only after it sees TVALID, and a source asserts TVALID only after it sees TREADY. Which one breaks the AXI-Stream rules?

    The source. A sink may wait for TVALID before asserting TREADY; a source must not wait for TREADY before asserting TVALID. Together they deadlock, and the source is the side at fault.

    Check yourself Why give each interface assertion an enable bit?

    So an error-injection test can switch off exactly the rule it violates on purpose, keeping every other check active. Without enables, the only options are to tolerate the assertion failure (which breaks the pass criterion) or to disable assertions wholesale (which hides real violations).

    Next: a VIP that is right today still has to change tomorrow without breaking its users. Versioning and delivery covers how.

    Related topics

    • The driver

    • Pipelined and out-of-order drivers

    • Reactive slave agents

    • Versioning and delivery

    Previous

    105 / 280

    Next: Versioning, Delivery and Release Readiness

    Up next

    Up nextVersioning, Delivery and Release Readiness

    Sign in to save progress

    Practice this lessonReport an error

    See also

    • Active and Passive in Delivery: What a VIP Owes Its Integrator
    • Two Passive Monitors, One Bus