Skip to content

AMBA APB · Module 15

APB Monitors

The passive UVM monitor for APB — how it samples the interface on the clock, detects SETUP and ACCESS-completion edges to reconstruct each transfer into an apb_seq_item, and broadcasts it through a uvm_analysis_port to the scoreboard and coverage. The component that turns raw bus wiggles into clean transaction objects without ever driving a signal.

The catalogue (15.1) and the assertions (15.2) tell you whether the bus is legal. They do not give you a stream of transactions to score or cover. The single idea to carry through this chapter: the APB monitor is a passive observer that samples the interface on the clock, detects transfer boundaries, reconstructs each completed transfer into a clean transaction object, and broadcasts that object — via a UVM analysis port — to the scoreboard and coverage, while flagging the protocol issues it sees procedurally. It never drives a signal. Its entire job is to convert raw bus wiggles into typed, timestamped apb_seq_item objects that the rest of the environment can reason about — and to do so with rigorous sampling discipline so that the transactions it emits are exactly what happened on the wire.

1. Problem statement

The problem is reconstructing, from nothing but the signal-level activity on the APB interface, the sequence of completed transfers as structured transaction objects — passively, in real time, and sampled at exactly the right edge — so the scoreboard and coverage model can consume transfers instead of wiggles.

The DUT and its testbench drive PADDR, PWRITE, PSEL, PENABLE, PWDATA, PRDATA, PREADY, PSLVERR across many clock cycles. A single read with one wait state spans four cycles and touches eight signals; the meaning of those cycles — "this was a read of 0x4000_0010 that returned 0xCAFEBABE with no error after one wait" — is not written anywhere. It has to be inferred by watching the bus. That inference has three hard requirements:

  • It must be passive. The monitor observes; it must never drive any interface signal, must never apply backpressure, and must work identically whether the agent around it is active (driving stimulus) or passive (watching a real DUT-to-DUT bus). The instant a monitor drives, it stops being a faithful witness.
  • It must respect transfer boundaries. A transfer is not one cycle — it is a SETUP cycle followed by one or more ACCESS cycles, completing only when PSEL & PENABLE & PREADY are all high. The monitor must capture address and control at SETUP, then hold and wait, then capture data and response at completion, emitting exactly one transaction per completed transfer — not one per cycle, and not one at SETUP before the data exists.
  • It must sample at the right edge. Every captured value must be taken synchronously, on the clock edge the protocol defines as valid for that signal — through the virtual interface's clocking block — so the transaction reflects the value the receiver would have latched, not a stale or racing combinational value.

So the job is not "know APB" — you do. It is to witness APB: to stand at the interface, sample it correctly, segment the cycle stream into transfers, and hand the rest of the environment a clean object per transfer.

2. Why previous knowledge is insufficient

You have the rulebook and you have the referees. In 15.1 you enumerated every APB rule; in 15.2 you encoded those rules as concurrent SVA that fires when a rule breaks. Both are necessary and neither produces what the scoreboard and coverage need:

  • Assertions check; they do not reconstruct. An assertion answers a yes/no question — "did PADDR stay stable?" — and stays silent when the answer is yes. It never builds an object that says "a write of 0xDEAD_BEEF to 0x4000_0000 completed at time T with no error." The scoreboard (15.4) needs that object to compare against its reference model, and the coverage model (15.5) needs it to bin direction, address ranges, wait counts, and error responses. SVA emits no transactions; the monitor's whole purpose is to emit them.
  • The catalogue is a list, not a runtime component. The catalogue is a static document. The monitor is a live UVM component that runs every cycle of every simulation, sampling and segmenting. It is the procedural arm of verification — it complements the declarative SVA: SVA proves the temporal rules continuously and concisely; the monitor reconstructs the data-carrying transactions and can flag rule violations procedurally (with full transaction context in the message) where that is more natural than an assertion. Neither replaces the other.
  • Knowing the phases is not the same as knowing the sample edge. You know that ACCESS completes when PREADY is high. Turning that into a correct monitor means knowing which clock edge to latch PRDATA on, why it must come from a clocking block, and why sampling one edge too early produces a flood of false mismatches that look exactly like DUT bugs. That discipline is new here.

So the model to add is the passive witness: the runtime component that consumes the same interface the assertions watch, but instead of answering yes/no, rebuilds the story — one transaction object per completed transfer — and broadcasts it downstream.

3. Mental model

The model: the monitor is a court stenographer. It sits in the room, says nothing, touches nothing, influences nothing — and produces a perfect, timestamped transcript of what was said. It does not judge (that is the scoreboard), it does not summarise statistics (that is coverage), and it certainly does not speak (it never drives). It listens on the clock, recognises when one statement ("a transfer") begins and ends, and writes down exactly what happened, word for word, then hands copies to everyone who needs them.

Three refinements make it precise:

  • A transfer is a two-edge event, so capture happens in two steps. At the SETUP edge (PSEL high, PENABLE low) the monitor opens a fresh apb_seq_item and records the address-phase fields: PADDR, PWRITE, and on writes PWDATA/PSTRB. It then waits — possibly several cycles of wait states — until the completion edge (PSEL & PENABLE & PREADY), where it records the data-phase fields: PRDATA on a read, PSLVERR, the wait count it tallied, and the timestamps. Only then does it emit. Emit at SETUP and you would broadcast a transaction with no data and the wrong response.
  • Sampling is synchronous, via the clocking block. The monitor reads signals through the virtual interface's clocking block on @(posedge pclk), so every captured value is the stable, settled value the protocol guarantees at that edge — the same value a real receiver latches. Sampling combinationally (on signal change, or in a always_comb-style block) invites delta-cycle races and captures values mid-flight. The clocking block is what makes the transcript trustworthy.
  • Broadcast is one-writer-to-many-readers, decoupled. The reconstructed item leaves through a uvm_analysis_port. The monitor calls ap.write(item) and moves on; it neither knows nor cares who is listening. Subscribers — the scoreboard, the coverage collector, a logger, a protocol-checker — connect their analysis exports/imps and each receives a copy. This is the publish/subscribe seam that keeps the monitor ignorant of, and independent from, everything downstream.
A block diagram of the passive APB monitor: APB interface signals flow one-way into the monitor, which samples on the PCLK rising edge via a clocking block, detects the SETUP edge to capture address and control and the completion edge (PSEL and PENABLE and PREADY) to capture data and PSLVERR, reconstructs an apb_seq_item with address, direction, data, error, wait count, and timestamps, and broadcasts it through a uvm_analysis_port to the scoreboard and the coverage collector.
Figure 1 — the passive APB monitor as a one-way observer. On the left, the APB interface signals enter through a virtual interface; every arrow points INTO the monitor — it samples, never drives. Inside, the monitor samples on the PCLK rising edge through a clocking block, detects the SETUP edge (PSEL high, PENABLE low) to capture address and control, then detects the completion edge (PSEL & PENABLE & PREADY) to capture data and PSLVERR, reconstructing a clean apb_seq_item with address, direction, data, error flag, wait count, and timestamps. The item leaves through a uvm_analysis_port whose write() broadcasts a copy to two downstream subscribers — the scoreboard (15.4) and the functional-coverage collector (15.5). The figure stresses that the monitor exists in both active and passive agents, that it emits at completion and never at SETUP, and that its procedural reconstruction complements — never replaces — the concurrent SVA of 15.2.

4. Real SoC implementation

In a real UVM agent the monitor is a uvm_monitor subclass holding a virtual interface handle and a typed analysis port. Its run_phase is a forever loop that samples on the clock, segments transfers, and writes one item per completion. Idiomatic and commented:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
class apb_monitor extends uvm_monitor;
  `uvm_component_utils(apb_monitor)
 
  virtual apb_if vif;                       // handle to the DUT interface (from config_db)
  uvm_analysis_port #(apb_seq_item) ap;     // broadcast port — one writer, many readers
 
  function new(string name, uvm_component parent);
    super.new(name, parent);
    ap = new("ap", this);                   // analysis ports are constructed, not factory-built
  endfunction
 
  function void build_phase(uvm_phase phase);
    super.build_phase(phase);
    if (!uvm_config_db#(virtual apb_if)::get(this, "", "vif", vif))
      `uvm_fatal(get_type_name(), "no virtual interface set for monitor")
  endfunction
 
  // The monitor is PASSIVE: run_phase only READS vif.mon_cb — it never assigns to it.
  task run_phase(uvm_phase phase);
    apb_seq_item tr;
    forever begin
      // ---- Wait for a SETUP edge: PSEL asserted, PENABLE still low ----
      @(vif.mon_cb);                                  // sample on the clock, via clocking block
      if (!(vif.mon_cb.psel && !vif.mon_cb.penable)) continue;
 
      // ---- SETUP cycle: open a new item, latch the address phase ----
      tr            = apb_seq_item::type_id::create("tr");
      tr.addr       = vif.mon_cb.paddr;
      tr.is_write   = vif.mon_cb.pwrite;
      if (tr.is_write) begin
        tr.data     = vif.mon_cb.pwdata;
        tr.strb     = vif.mon_cb.pstrb;
      end
      tr.t_setup    = $time;
      tr.wait_count = 0;
 
      // ---- ACCESS cycles: advance until completion, tallying wait states ----
      // Completion is defined ONLY as PSEL & PENABLE & PREADY all high.
      do begin
        @(vif.mon_cb);
        if (!vif.mon_cb.pready) tr.wait_count++;       // PREADY low this cycle ⇒ a wait state
      end while (!(vif.mon_cb.psel && vif.mon_cb.penable && vif.mon_cb.pready));
 
      // ---- Completion edge: latch the data phase — read data is ONLY valid now ----
      if (!tr.is_write) tr.data = vif.mon_cb.prdata;   // gated on PREADY by construction
      tr.slverr     = vif.mon_cb.pslverr;              // PSLVERR is meaningful only at completion
      tr.t_complete = $time;
 
      // ---- Optional procedural protocol flag (complements SVA of 15.2) ----
      if (tr.slverr)
        `uvm_info(get_type_name(),
                  $sformatf("ERROR response: %s addr=0x%08h", tr.is_write?"WR":"RD", tr.addr),
                  UVM_HIGH)
 
      // ---- Broadcast the completed transaction to all subscribers ----
      ap.write(tr);                                    // non-blocking; scoreboard + coverage get a copy
    end
  endtask
endclass

Two facts make this the right shape. First, the loop emits exactly one item per completed transfer, at completion — the do…while absorbs an arbitrary number of wait states and the data phase is latched only after PREADY is observed high, so a transfer with three waits and one with none both produce one correct item, never a partial or premature one. Second, every read of the interface goes through vif.mon_cb — the monitor's clocking block — so the sampled values are the protocol-stable ones, and the run_phase contains no assignment to any interface signal, which is what makes it passive. The ap.write(tr) is fire-and-forget: the monitor does not block on, or even know about, the scoreboard (15.4) and coverage (15.5) that subscribe to it.

5. Engineering tradeoffs

The monitor's design is a set of decisions about what to capture, when, and how passive to stay. The first table is the capture contract — what lands in the item, when, and from which signal:

Item fieldWhen sampledSource signal(s)Notes
addrSETUP edgePADDRAddress phase; stable through the transfer
is_writeSETUP edgePWRITEDirection; fixes which data field is meaningful
data (write)SETUP edgePWDATAWrite data is valid from SETUP
strb (write)SETUP edgePSTRBAPB4 byte strobes
data (read)completion edgePRDATARead data is valid only when PREADY is high
slverrcompletion edgePSLVERRMeaningful only at completion; don't-care otherwise
wait_counttallied across ACCESSPREADY low cyclesOne increment per wait state
t_setup / t_completeeach respective edge$timeLatency = t_complete − t_setup

The second table is the behaviour the monitor must and must not have — and how that differs by agent mode:

ConcernActive agentPassive agentRule
Driver present?Yes (drives stimulus)NoMonitor is identical either way
Monitor present?YesYesMonitor is always instantiated
Drives any signal?NoNoPassive — never, in either mode
Source of trafficTestbench sequencesReal DUT-to-DUT busMonitor doesn't care; it just witnesses
SamplingVia clocking blockVia clocking blockSynchronous, race-free, always

The throughline: capture is split across two edges because data and response are valid at different times, and passivity is absolute and mode-independent. The most consequential single decision is gating read-data capture on PREADY — sample one edge early and every read in regression mismatches. The second is committing to the clocking block — it is the difference between a transcript you can trust and one riddled with races.

6. Common RTL mistakes

7. Debugging scenario

The signature monitor failure is a sampling-discipline bug masquerading as a DUT failure: the monitor captures read data one edge too early, so the scoreboard sees the wrong data on every read and floods the log with mismatches that look like the DUT is broken — when the DUT is fine and the witness is wrong.

  • Observed symptom: the scoreboard reports read-data mismatches on a large fraction of transfers — and conspicuously, the mismatches cluster on reads that took at least one wait state, while zero-wait reads pass. A "DUT regression" suddenly shows hundreds of read failures after a monitor change, with the reference model unchanged.
  • Waveform clue: on a failing read, PRDATA carries a stale/undefined value during the wait cycle (PREADY low) and the correct value only at the completion edge (PREADY high). The transaction the monitor emitted holds the stale value, not the value present at completion — the captured data matches the bus one cycle before PREADY rose, not at it.
  • Root cause: the monitor latched PRDATA on the ACCESS edge gated only by PENABLE, not by PREADY — e.g. if (psel && penable) tr.data = vif.mon_cb.prdata;. On a zero-wait read, completion coincides with the first ACCESS cycle, so it happens to be right; on any read with a wait, it samples the wait-cycle value, which is not yet valid. The DUT returned correct data; the monitor recorded the wrong cycle.
  • Correct RTL: gate the read-data capture on completion — sample PRDATA only when PSEL & PENABLE & PREADY are all high, exactly as the do…while loop and the post-loop assignment in beat 4 do: if (!tr.is_write) tr.data = vif.mon_cb.prdata; after the loop has confirmed PREADY high. Read data is valid only at the completing edge; capture must wait for it.
  • Verification assertion: to catch a recurrence at the source, add an SVA on the interface that PRDATA is stable from the cycle it is first valid through completion is overkill here; the targeted check is on the monitor — a self-check (beat 8) that re-derives each emitted read's data against the bus value at the cycle where PSEL & PENABLE & PREADY first hold, and a coverage check that wait-state reads are exercised so the bug cannot hide behind only zero-wait traffic.
  • Debug habit: when the scoreboard "finds DUT bugs" right after a monitor or interface change, suspect the witness before the suspect. Reproduce one failing transfer, open the waveform, and confirm the monitor sampled the same cycle a real receiver would. A pattern where failures correlate with wait states is the tell of a sampling-edge bug, not a DUT bug — the monitor, not the design, is reading the wrong cycle.
A two-case waveform of a one-wait APB read. In the top buggy case the monitor samples PRDATA during the wait cycle and captures the stale value, causing false scoreboard mismatches; in the bottom correct case it samples PRDATA at the completion edge where PSEL, PENABLE, and PREADY are all high and captures the valid value, so the scoreboard matches — showing the bug is the sample edge, not the DUT.
Figure 2 — the same read transfer reconstructed two ways. Across four cycles a read has one wait state: SETUP in T0, ACCESS-wait in T1 (PREADY low, PRDATA stale 0xXXXX), ACCESS-ready in T2 (PREADY high, PRDATA valid 0xCAFEBABE). Top (bug, red): the monitor samples PRDATA on the ACCESS edge gated only by PENABLE, captures the stale wait-cycle value, and emits the wrong read data — producing a flood of false scoreboard mismatches that look like DUT failures but are a monitor sampling-discipline bug. Bottom (correct, green): the monitor samples PRDATA only at the completion edge where PSEL & PENABLE & PREADY are all high, captures the true 0xCAFEBABE, and the scoreboard matches. The figure teaches that the bug is the sample edge, not the bus value: read data is valid only at completion, and failures that correlate with wait states are the signature of a wrong-edge monitor.

8. Verification perspective

The monitor is the golden observer the rest of the environment trusts, so it is the one component whose own correctness must be verified independently — a wrong monitor doesn't fail loudly, it lies quietly, and every scoreboard and coverage conclusion downstream inherits the lie.

  • Verify reconstruction completeness: every transfer in, exactly one item out. The fundamental property is a bijection between completed bus transfers and emitted apb_seq_item objects — no dropped transfers (the loop missed a SETUP), no duplicate emits (it wrote twice), no phantom items (it emitted at SETUP). The cheapest check is a counter cross-check: an independent cycle-level tally of completion edges (PSEL & PENABLE & PREADY rising) must equal the analysis-port write count over the run. A divergence is a segmentation bug.
  • Verify the sample edge against a known stimulus. Drive a directed sequence of reads and writes with known addresses, data, wait counts, and error responses, and assert that each emitted item's fields equal the injected values — including the wait-state reads, which are where edge bugs hide (beat 7). This is the monitor's self-test: it makes the monitor's correctness a measured property, not an assumption. Crucially, include error transfers so PSLVERR capture is exercised, and multi-wait transfers so wait_count is exercised.
  • Use the monitor as a sanity oracle, but never as the only one. The monitor is excellent for sanity: "did the address we drove appear on the bus we observed?" But because driver-side stimulus and monitor-side reconstruction can share an interface assumption, the monitor must not be the sole check — the scoreboard's reference model is the independent authority on data correctness, and the SVA is the independent authority on temporal legality. The monitor's job is faithful reconstruction; let the scoreboard and SVA be the judges, so a shared blind spot can't pass unnoticed.

The point: you verify the monitor by bijection (one item per transfer), by directed self-test against known stimulus including waits and errors, and by keeping it an observer rather than the final judge — because everything downstream is only as trustworthy as the transcript the monitor produces.

9. Interview discussion

"Walk me through the APB monitor" is a senior verification question, and the answer that signals real experience is structural and discipline-first.

Lead with the role: the monitor is a passive observer — it samples the interface on the clock through a clocking block, never drives, and exists in both active and passive agents. Then the core mechanism: a transfer is captured in two steps — at the SETUP edge it latches the address phase (address, direction, write data), and at the completion edge where PSEL & PENABLE & PREADY are all high it latches the data phase (read data, PSLVERR), tallying wait states in between — then it emits exactly one apb_seq_item per completed transfer. Make the broadcast explicit: the item leaves through a uvm_analysis_port via ap.write(item), a non-blocking one-writer-many-readers seam that decouples the monitor from the scoreboard and coverage that subscribe to it. The depth flourishes that distinguish a strong answer: sampling discipline — explain why read data is gated on PREADY and why the clocking block prevents races, and that emitting at SETUP would broadcast garbage data; complementarity with SVA — the monitor's procedural reconstruction does not replace the declarative assertions of 15.2, they cover different needs (transactions vs. continuous rule proofs); and the killer closer: "a sampling-edge bug in the monitor looks exactly like a DUT failure — false mismatches that correlate with wait states — so when the scoreboard suddenly finds bugs after a monitor change, I suspect the witness before the suspect." That last line shows you understand the monitor is the trusted oracle whose own correctness must be verified.

10. Practice

  1. Trace the two-step capture. For a write with two wait states, list exactly which field is latched on which edge and what wait_count ends up as.
  2. Gate the read. Write the one-line condition that must hold before the monitor latches PRDATA, and explain what breaks if you drop PREADY from it.
  3. Passive in both modes. Explain why the monitor is instantiated in a passive agent and what is absent there compared to an active agent.
  4. Analysis-port broadcast. Describe what ap.write(item) does, why it is non-blocking, and how the scoreboard and coverage each receive the same item without the monitor knowing about either.
  5. Self-test design. Sketch a directed test that proves the monitor reconstructs wait-state reads and error responses correctly — name the stimulus and the field checks.

11. Q&A

12. Key takeaways

  • The APB monitor is a passive observer — it samples the interface on the clock through a clocking block and never drives any signal, in both active and passive agents.
  • A transfer is captured in two steps: address/control at the SETUP edge, then data/response at the completion edge (PSEL & PENABLE & PREADY), with wait states tallied in between — emitting exactly one apb_seq_item per completed transfer, at completion, never at SETUP.
  • Read data and PSLVERR are valid only at completion — gate their capture on PREADY; sampling one edge early produces a flood of false scoreboard mismatches that look like DUT bugs but are a monitor sampling-discipline error.
  • The item is broadcast through a uvm_analysis_port via ap.write() — a non-blocking, one-writer-many-readers seam that decouples the monitor from the scoreboard (15.4) and coverage (15.5) that subscribe to it.
  • The monitor complements the SVA of 15.2, it does not replace it — procedural transaction reconstruction (and optional context-rich flags) alongside continuous declarative rule proofs derived from the catalogue (15.1).
  • The monitor is the golden observer, so verify it — bijection (one item per transfer), directed self-test against known stimulus including waits and errors, and never letting it be the sole judge — because everything downstream is only as trustworthy as the transcript it produces.