Skip to content

AMBA APB · Module 8

PREADY Timing

How a subordinate actually drives PREADY — combinational versus registered, the sample window the manager reads it in, and the glitch and setup hazards that make a logically-correct PREADY still wrong on silicon. The signal-integrity layer beneath the PREADY handshake.

You already know what PREADY means — high completes the transfer, low inserts a wait. This chapter is about something subtler and more dangerous: how a subordinate drives PREADY in time, and why a PREADY that is logically correct can still be electrically wrong. The manager samples PREADY on one clock edge per access cycle, so PREADY must be a clean, settled, glitch-free value at that edge. The single idea to carry: PREADY has two valid implementations — combinational (fast, zero added latency, glitch-prone) and registered (clean, glitch-free, one cycle slower) — and choosing wrong, or building the combinational form carelessly, is how a wait-state design that passes a quick simulation fails timing or glitches in silicon.

1. Problem statement

The problem is presenting a stable, correct PREADY to the manager at the exact instant it samples — the rising clock edge in the access phase — regardless of how the subordinate computes "am I ready?"

A wait-state handshake is only as good as the timing of the one bit that drives it. The manager's transfer FSM samples PREADY on each PCLK rising edge while in access (PENABLE high) and decides, on that edge, whether the access completes or holds. For that decision to be correct, PREADY must satisfy two electrical requirements that "it is logically 1 when ready" does not capture:

  • It must be stable in the sample window. PREADY has to be settled from setup-time before the rising edge through hold-time after it. A value that is still rippling as the edge arrives is a setup violation or a metastability risk.
  • It must be glitch-free into that edge. If PREADY is combinational off a decode that settles in stages, it can momentarily pulse the wrong way mid-cycle; if that transient lands in the sample window, the manager reads a lie.

So the job is not "compute readiness" — it is "deliver readiness as a clean value the manager can trust on its sampling edge," which is a timing and signal-integrity problem, not just a logic one.

2. Why previous knowledge is insufficient

Module 3 named PREADY as the completion-and-backpressure control, and Module 5 drilled the per-cycle sampling cadence — that the manager checks PREADY every access edge. Both treat PREADY as an idealized bit that is simply "1 or 0." Real RTL has to produce that bit, and that is where this chapter goes:

  • A logically-correct PREADY can be electrically wrong. "Drive PREADY high when the data is ready" is a logic statement. Whether that high arrives stable and glitch-free at the sampling edge depends on whether it is combinational or registered and on the depth of the cone behind it. The handshake view never asks how PREADY is built.
  • Combinational and registered PREADY are different transfers, not just styles. A combinational PREADY can complete a fast access in its first cycle; a registered PREADY cannot — its earliest high is one cycle later. So the choice changes the minimum latency of every transfer on the bus, which the idealized view hides.
  • The sample window is the whole game. The manager does not care what PREADY does mid-cycle; it cares what PREADY is at the rising edge. Understanding wait states at the RTL level means understanding that window and what can corrupt it — glitch, setup violation, or an X propagating from an undriven path.

So the model to add is the physical one: PREADY as a signal that must be clean in a specific window, built either combinationally (fast, risky) or registered (clean, slower).

3. Mental model

The model: PREADY is a ready light the manager glances at once per beat — and it must be steady at the glance, not flickering. What matters is the light's state at the instant of the glance (the rising edge), not what it did between glances.

A combinational PREADY is a light wired straight through a network of switches: flip the inputs and the light eventually shows the right state — but while the switches settle, it can flicker. If the manager glances during a flicker, it sees the wrong thing. A registered PREADY is a light driven by a latch that only updates on the clock: it is rock-steady between edges and never flickers — but it shows last cycle's decision, so it is always one beat behind. The engineering choice is "fast but must be kept flicker-free" versus "clean but one beat slower."

Three refinements make it precise:

  • The glance is the rising edge in access. The manager samples PREADY on each PCLK rising edge while PENABLE is high. Only that instant matters; PREADY must meet setup and hold around it.
  • Combinational PREADY trades latency for cleanliness. It can complete a ready access in the first access cycle (zero wait states), but the cone that computes it must settle inside the cycle and not glitch into the sample window. The faster the bus clock, the harder that is.
  • Registered PREADY trades a cycle for safety. A flop guarantees a glitch-free, setup-friendly value, at the cost of one extra cycle of minimum latency. On a slow control bus, that cycle is usually free — which is why registered PREADY is the safe default.
A structural diagram with two paths to PREADY: the top combinational path runs ready logic straight into PREADY with a glitch-and-setup warning and zero added latency; the bottom registered path runs the ready logic through a flip-flop into PREADY, glitch-free but one cycle later, with the manager's sampling rising edge marked.
Figure 1 — the two ways to drive PREADY. Top, combinational: the ready logic (address decode plus a data-ready term) feeds PREADY directly through combinational gates, so PREADY can be high in the first access cycle (zero added latency) — but the cone must settle within the cycle and must not glitch into the manager's sample window, and on a fast clock it can violate setup. Bottom, registered: a flip-flop drives PREADY, so it is glitch-free and easy to time, but its earliest high is one cycle later, adding a cycle of minimum latency to every transfer. The figure frames the choice as fast-but-must-stay-clean versus clean-but-one-beat-slower, and marks the manager's sampling edge as the only instant that matters.

4. Real SoC implementation

In silicon, PREADY is the subordinate's output, and the two implementations are a few lines apart but worlds apart in behaviour. The combinational form drives readiness directly; the registered form clocks it.

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// --- Combinational PREADY: fast (zero added latency) but must be glitch-free ---
// data_ready is a combinational term: is the addressed source available NOW?
// A fast subordinate that is always ready ties this high.
assign pready_comb = sel ? data_ready : 1'b1;   // unselected -> 1 (do not hold the bus)
// HAZARD: data_ready often depends on the PADDR decode; if that decode ripples,
// pready_comb can glitch. It must settle and be stable in the manager's sample
// window (setup before the PCLK rising edge through hold after it).
 
// --- Registered PREADY: glitch-free and timing-friendly, +1 cycle latency ---
// pready is now a clean flop output; its earliest high is one cycle after select.
always_ff @(posedge pclk or negedge presetn) begin
  if (!presetn)            pready_reg <= 1'b1;        // default-ready out of reset
  else if (sel && penable) pready_reg <= data_ready;  // sampled, then presented clean
  else                     pready_reg <= 1'b1;        // UNSELECTED -> 1, not 0
end
// The unselected value is 1, not 0 - the same rule the combinational form above
// obeys with `sel ? data_ready : 1'b1`. Writing 0 here is the easy mistake,
// because 0 reads as "not ready yet" and the two forms then look equivalent.
// They are not: a bridge that ANDs its subordinates' PREADY outputs hangs on
// every access to any OTHER subordinate. See the Debug Lab in beat 7b.
// Trade-off in one line: pready_comb can complete in the first access cycle;
// pready_reg cannot — but pready_reg is rock-steady at the sampling edge.

Two facts drive the real decision. First, the sample window is the only timing constraint that matters: whichever form you pick, PREADY must be stable across the PCLK rising edge the manager samples on — a static-timing tool checks this as a normal setup/hold path, and a combinational PREADY with a deep decode cone is exactly where that path fails on a fast clock. Second, the default-ready rule: an unselected subordinate must drive PREADY high (or, more precisely, must not pull the shared/muxed PREADY low), so that the bus does not hang — which is why the combinational form returns 1'b1 when !sel. (How PREADY=0 then extends the access cycle by cycle is the transfer-extension mechanics; this chapter is about getting the PREADY value clean at the edge.)

An APB timing diagram showing PENABLE high across two access cycles, PCLK with dashed sample lines on its rising edges, and PREADY low-and-stable on the first sampled edge (hold) then high-and-stable on the second (complete), with the setup-and-hold sample window highlighted around each rising edge.
Figure 2 — the PREADY sample window across an access with one wait state, against PCLK. PENABLE is high throughout the access. The manager samples PREADY on each PCLK rising edge (the dashed sample lines). On the first access cycle PREADY is low and stable through the sample window — the access holds (a wait). On the second, PREADY is high and stable through the sample window — the access completes. The figure highlights the sample window around each rising edge (a setup region before and a hold region after) and stresses that PREADY must be settled and glitch-free across that window: what PREADY does between edges is irrelevant, but a transient inside the window is sampled as a real value.

5. Engineering tradeoffs

The combinational-versus-registered choice is the core PREADY design decision. Each row is a real consequence.

PREADY implementationWhat it gives upWhat it buysWhen to choose it
CombinationalGlitch immunity; easy timingZero added latency — completes in the first access cycleSlow clock, shallow ready cone, latency matters
RegisteredOne cycle of minimum latencyGlitch-free, setup-friendly, trivially timedFast clock, deep decode, or when robustness matters most
Always-ready (tie high)Any backpressureThe simplest, fastest pathA register that genuinely answers every cycle
!sel → 1 defaultA subordinate-side "busy when idle"A bus that never hangs on an unselected slaveAlways — an unselected slave must not pull PREADY low
Deep combinational coneTiming closure on a fast clock(nothing — this is the trap)Never on a fast bus; register it instead

The throughline: PREADY is the one bit whose timing — not just logic — decides whether a wait-state design works. On a slow control bus the combinational form is fine and saves a cycle; as the clock rises or the decode deepens, register it. When unsure, register it: a cycle of latency on sparse control traffic is cheap, a metastable manager is not.

6. Common RTL mistakes

7. Debugging scenario

A wait-state design that "works in a quick sim" but misbehaves on a faster clock or in gate-level is almost always a PREADY timing bug, not a logic bug — and it is invisible in a zero-delay RTL run.

  • Observed symptom: an access intermittently completes a cycle early (capturing garbage) or the design fails timing closure on the real clock, even though the RTL PREADY logic "looks right" and passes a fast zero-delay simulation. It gets worse as the clock frequency rises.
  • Waveform clue: in a gate-level or annotated-delay waveform (Figure 3), PREADY is combinational and glitches during the access cycle — it pulses high briefly as the PADDR decode settles, before the data is actually ready, and that transient lands inside the manager's sample window, so the manager samples a premature 1 and completes early.
  • Root cause: PREADY was driven combinationally off a multi-level cone that includes the PADDR decode; as the address bits settle at different times, the decode (and thus PREADY) ripples through intermediate states, one of which is a spurious high that coincides with the sampling edge.
  • Correct RTL: either register PREADY so it is a clean flop output — always_ff: if (sel && penable) pready <= data_ready; else pready <= 1'b1;, keeping the unselected default high — or, if the combinational form is required, drive it from a settled, registered data_ready term and a registered select so the cone has no rippling decode in it: assign pready = sel_q ? data_ready_q : 1'b1;.
  • Verification assertion: assert PREADY is stable across the sampling edge and clean — e.g. assert property (@(posedge pclk) disable iff(!presetn) (psel && penable) |-> !$isunknown(pready)); plus a glitch check in gate-level sim that PREADY does not change within a guard window before the clock edge; and a property that the access does not complete before data_ready is genuinely asserted.
  • Debug habit: when a wait-state design works in RTL sim but fails on the real clock or in gate-level, do not re-read the PREADY logic — look at the PREADY timing: is it combinational off a decode? Run a delay-annotated or gate-level sim and watch PREADY across the sample window for a glitch. The fix is almost always to register PREADY or its inputs.
Two stacked APB timing diagrams of the same access: the top correct case has a registered PREADY that is clean and stable across both sampling edges; the bottom buggy case has a combinational PREADY that glitches high mid-cycle inside the first sample window, drawn in red, causing the manager to complete the access early.
Figure 3 — a combinational PREADY glitch versus a clean registered PREADY, on the same access. Top (correct, green): a registered PREADY is a clean flop output — low through the wait, high at completion, rock-steady across both sample windows, so the manager samples exactly the intended values. Bottom (bug, red): a combinational PREADY driven off a rippling PADDR decode pulses high briefly mid-cycle, before the data is ready, and that glitch lands inside the manager's sample window at the first access edge; the manager samples a premature high and completes the access a cycle early, capturing garbage. The figure shows that the bug is invisible in a zero-delay RTL run and only appears with real or annotated delays, which is why PREADY timing is debugged in gate-level, not RTL.

7b. The full timing contract, on one waveform

Everything above concerns a single access. This is the shape the manager actually drives — zero-wait, then a waited access, then a back-to-back pair — which is where the PENABLE and stability rules become visible.

SETUP, zero-wait ACCESS, waited ACCESS, and a back-to-back transfer

8 cycles
Three APB transfers showing a one-cycle SETUP, a zero-wait completion, a two-wait access with stable address and data, and PENABLE dropping before the next SETUPSETUP: PSEL high, PENABLE low — exactly one cycleSETUP: PSEL high, PENABLElow — exactly one cycleACCESS with PREADY already high: zero-wait completionACCESS with PREADY alreadyhigh: zero-wait completionPREADY low — the access is held; PADDR/PWDATA must not movePREADY low — the access isheld; PADDR/PWDATA must notmovePSEL & PENABLE & PREADY all high: the transfer completes herePSEL & PENABLE & PREADY allhigh: the transfercompletes herePENABLE dropped, so the next access re-enters through SETUPPENABLE dropped, so thenext access re-entersthrough SETUPPCLKPSELPENABLEPADDRA0A0A1A1A1A1A2--PWDATAD0D0D1D1D1D1D2--PREADYcompletet0t1t2t3t4t5t6t7
Figure 4 — the APB timing contract across three transfers. Cycle 1 is SETUP for the first access: PSEL high, PENABLE low, exactly one cycle. Cycle 2 is its ACCESS with PREADY already high, so it completes in the first access cycle — a zero-wait transfer. Cycle 3 is SETUP for the second access, and cycles 4 to 6 are its ACCESS with PREADY low for two of them: the transfer is held, and PADDR, PWRITE and PWDATA must remain stable throughout, because the manager is not permitted to change them once the transfer has started. Completion happens only on the cycle where PSEL, PENABLE and PREADY are all high. Cycle 7 shows the rule that catches designs out on back-to-back traffic: PENABLE must drop between transfers, so a following access re-enters through its own SETUP cycle rather than running ACCESS into ACCESS. PRDATA and PSLVERR are meaningful only on the completing cycles, marked here; what they do at any other time is unconstrained.

Three rules the waveform makes concrete. SETUP is exactly one cycle — it is not a state the manager can linger in. The request is frozen once ACCESS begins, so a subordinate may safely capture PADDR and PWDATA in SETUP and use the captured copies for the rest of the transfer. And PENABLE drops between transfers, so back-to-back traffic costs a minimum of two cycles per access even with a permanently-ready subordinate.

7c. The checker

apb_timing_sva.sv — the contract, with policy separated from protocol
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
module apb_timing_sva #(
  // ENGINEERING POLICY, not an APB limit. AMBA places no maximum on the number
  // of wait states a subordinate may insert. The bound exists so that a stuck
  // PREADY fails on a named cycle instead of hanging the simulation - written
  // unbounded as ##[1:$] the property could never fail, because an attempt
  // that never completes stays pending until end of test.
  parameter int MAX_WAIT_POLICY = 32
) (
  input logic pclk, presetn,
  input logic psel, penable, pready, pwrite, pslverr,
  input logic [31:0] paddr, pwdata, prdata
);
  // ---- Protocol requirements ------------------------------------------
  // SETUP lasts exactly one cycle: PSEL high with PENABLE low always advances.
  a_setup_one_cycle: assert property (@(posedge pclk) disable iff (!presetn)
    (psel && !penable) |=> (psel && penable));
 
  // PENABLE is never high without PSEL.
  a_enable_implies_sel: assert property (@(posedge pclk) disable iff (!presetn)
    penable |-> psel);
 
  // ACCESS persists until PREADY - a manager cannot abort a started transfer.
  a_access_holds: assert property (@(posedge pclk) disable iff (!presetn)
    (psel && penable && !pready) |=> (psel && penable));
 
  // PENABLE drops after completion, so a following transfer gets its own
  // SETUP. Running ACCESS into ACCESS is a violation even where tolerated.
  a_penable_drops: assert property (@(posedge pclk) disable iff (!presetn)
    (psel && penable && pready) |=> !penable);
 
  // The request is frozen for the whole transfer - this is what makes a
  // SETUP-phase capture safe in the subordinate.
  a_request_stable: assert property (@(posedge pclk) disable iff (!presetn)
    (psel && !(penable && pready)) |=>
      $stable(paddr) && $stable(pwrite) && $stable(pwdata));
 
  // PSLVERR is only meaningful on the completing cycle.
  a_pslverr_qualified: assert property (@(posedge pclk) disable iff (!presetn)
    !(psel && penable && pready) |-> !pslverr);
 
  // PREADY must be a known value whenever it is sampled. An X here propagates
  // straight into the manager's completion decision.
  a_pready_known: assert property (@(posedge pclk) disable iff (!presetn)
    (psel && penable) |-> !$isunknown(pready));
 
  // ---- Engineering policy ---------------------------------------------
  a_completes_within_policy: assert property (@(posedge pclk) disable iff (!presetn)
    (psel && penable && !pready) |-> ##[1:MAX_WAIT_POLICY] pready)
    else $error("PREADY did not rise within %0d cycles (policy bound)",
                MAX_WAIT_POLICY);
 
  // ---- Coverage: the properties above are vacuous without these --------
  c_zero_wait:  cover property (@(posedge pclk) (psel && !penable) ##1 (penable && pready));
  c_waited:     cover property (@(posedge pclk) psel && penable && !pready);
  c_back2back:  cover property (@(posedge pclk)
                  (psel && penable && pready) ##1 (psel && !penable));
endmodule

c_zero_wait and c_waited both need to hit. A suite that only ever runs zero-wait transfers satisfies every stability property vacuously, because the antecedent psel && !(penable && pready) is only true during a stall.

1

A registered-PREADY peripheral hung the bus on every access to a different peripheral

REGISTERED-PREADY-UNSELECTED-LOW
Symptom

A new peripheral worked perfectly in its own block-level environment: reads, writes, wait states, and PSLVERR all behaved. Integrated onto the APB bus, the system hung on the first access to any other peripheral — the UART, the timer, the GPIO — while accesses to the new peripheral itself still worked.

Removing the new peripheral from the build restored the bus. That pointed at it, and the block-level tests continued to pass, which pointed away.

Buggy Code
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
// The combinational form in the same file is correct...
assign pready_comb = sel ? data_ready : 1'b1;      // unselected -> 1
 
// ...and the registered form, written to look equivalent, is not.
always_ff @(posedge pclk or negedge presetn) begin
  if (!presetn)            pready_reg <= 1'b0;
  else if (sel && penable) pready_reg <= data_ready;
  else                     pready_reg <= 1'b0;     // ✗ UNSELECTED -> 0
end
Diagnostic Evidence

Probing the bridge's inputs during a hung UART access showed it immediately:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
  psel_uart      = 1        <- the UART is the addressed subordinate
  pready_uart    = 1        <- and it is ready
  psel_new       = 0        <- the new peripheral is NOT selected
  pready_new     = 0        <- but it is driving PREADY low
  pready_bridge  = 0        <- AND of all subordinates -> held low

The addressed subordinate was ready and the bus was not. That combination points at the combining logic rather than at either subordinate, and the bridge in this SoC ANDs its subordinates' PREADY outputs rather than muxing by PSEL.

The block-level environment could not have found it: with one subordinate, its PREADY is the bus PREADY, and the unselected value is never combined with anything.

Root Cause

The registered form drove PREADY low when unselected, and this bridge combines subordinate PREADY outputs with an AND — so one subordinate holding low held the whole bus low for every transfer.

Two things make the mistake easy. First, 0 reads as "not ready yet", which is the natural value for an idle subordinate to hold, and it is the correct value during a stall — the distinction between "stalling my own transfer" and "not participating" does not appear in the signal name. Second, the combinational form sitting a few lines above gets it right with sel ? data_ready : 1'b1, so the two appear to be the same design expressed two ways. A reviewer comparing them for timing differences, which is what the surrounding section is about, has no reason to compare their unselected values.

It is worth being precise about the protocol status. AMBA defines PREADY as a per-subordinate output and leaves the combining to the bridge, so a mux-based bridge is immune to this and the low value is not itself a protocol violation. Since a subordinate generally cannot know which bridge it will sit behind, driving high when unselected is the portable choice rather than a rule — which is why the symptom is an integration surprise rather than a compile or lint error.

Fix
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
always_ff @(posedge pclk or negedge presetn) begin
  if (!presetn)            pready_reg <= 1'b1;      // default-ready out of reset
  else if (sel && penable) pready_reg <= data_ready;
  else                     pready_reg <= 1'b1;      // unselected -> 1
end

Note the reset value changes too. A subordinate coming out of reset holding PREADY low blocks an AND-combining bridge from the first cycle, before any transfer is attempted.

Verifying it needs a second subordinate, which is the part the original plan lacked. The minimal reproduction is a two-subordinate testbench where the test addresses subordinate A and asserts that the transfer completes while subordinate B is idle — a check that is meaningless with one subordinate and decisive with two.

The durable guard is an assertion on the subordinate's own output, which holds regardless of the bridge it ends up behind:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
a_unselected_is_ready: assert property (@(posedge pclk) disable iff (!presetn)
  !psel |-> pready)
  else $error("subordinate drove PREADY low while unselected");

The general habit: a block-level testbench that instantiates one subordinate cannot test how that subordinate behaves alongside others. Anything a fabric combines across blocks — PREADY here, HREADYOUT on AHB, error and interrupt ORs, credit returns — needs at least a two-instance testbench to be exercised at all. See PREADY common design bugs for the failure catalogue this belongs to and transfer extension mechanics for how a low PREADY extends the access it does own.

8. Verification perspective

PREADY timing bugs are the canonical "passes RTL, fails silicon" class, so verification has to attack them at the right level and with the right checks — a zero-delay RTL run will never catch a glitch.

  • Assert the sample-window contract. Bind properties that PREADY is never X while PSEL && PENABLE (catches undriven paths), and — in gate-level or delay-annotated simulation — that PREADY does not transition within a setup guard-band before each PCLK rising edge (catches glitches landing in the window). The X-check runs in RTL; the glitch check needs delays.
  • Check readiness causality, not just value. Beyond "is PREADY clean," assert that the access does not complete before the data is genuinely available: (complete) |-> (data_was_ready). This catches the asserted-too-early bug, which is a different failure from a glitch and which a stability check alone will miss.
  • Cover both implementations and the corners. Functional coverage should distinguish a combinational-ready completion (complete in the first access cycle) from a registered-ready completion (complete one cycle later), and hit: zero-wait, one-wait, multi-wait, an unselected slave during another's access (default-ready), and back-to-back transfers. A wait-state suite that never exercises the first-cycle completion path has a hole exactly where the combinational glitch lives. (Wait-state coverage is a chapter of its own.)

The point: PREADY correctness is a timing property, so the verification plan must specify the level (gate-level for glitches) and the causality (ready-before-complete), not just sample the value in RTL.

9. Interview discussion

"Combinational or registered PREADY — which, and why?" is a sharp senior question because it separates people who know the handshake from people who have closed timing on one. A weak answer picks one; a strong answer states the trade and the hazard.

Frame it as latency versus integrity: combinational PREADY adds zero latency and can complete an access in its first cycle, but its cone must settle within the cycle and stay glitch-free across the manager's sample window — risky on a fast clock or a deep PADDR decode. Registered PREADY is glitch-free and trivially timed but adds one cycle of minimum latency to every transfer. Then deliver the depth points: the only instant that matters is the manager's sampling edge (a transient outside the window is harmless; one inside it is sampled as real), and the classic bug — a combinational PREADY glitching off a rippling decode — is invisible in zero-delay RTL and only shows up in gate-level, which is why you either register PREADY or drive it from registered inputs, and verify it with a glitch check plus a ready-before-complete property. Closing with "on a slow control bus I register it by default — a cycle is cheap, a metastable manager is not" signals real silicon experience.

10. Practice

  1. Place the window. On an access with one wait state, mark each PCLK edge the manager samples PREADY and the setup/hold window around it; state what PREADY between edges is allowed to do.
  2. Compare latency. For the same always-ready subordinate, state the earliest completion cycle with a combinational PREADY versus a registered PREADY, and why they differ by one.
  3. Spot the glitch. Given a combinational PREADY = decode(paddr) & data_ready, explain how a rippling PADDR can glitch PREADY and what must be true for that glitch to corrupt a transfer.
  4. Write both forms. From memory, write the combinational and the registered PREADY, and the !sel → 1 default, and explain the latency and integrity trade between them.
  5. Pick the level. State which PREADY bug a zero-delay RTL simulation can catch and which requires gate-level or delay-annotated simulation, and the assertion for each.

11. Q&A

11b. Where This Is Specified

  • Arm AMBA APB Protocol Specification (ARM IHI 0024). The IDLE → SETUP → ACCESS model; PSEL asserted with PENABLE low for exactly one SETUP cycle; PENABLE asserted throughout ACCESS and dropping after completion; the transfer completing only when PSEL && PENABLE && PREADY; the requirement that address, direction and write data remain stable for the whole transfer; and PRDATA/PSLVERR being valid only on the completing cycle.
  • Arm AMBA APB — PREADY. Defined as a per-subordinate output, with the combining of several subordinates' PREADY left to the bridge implementation. AMBA places no maximum on the number of wait states a subordinate may insert.
  • IEEE 1800-2023 §16 — Assertions. The concurrent properties and cover property used above, $stable, $isunknown, and the ##[m:n] bounded range that makes the policy completion bound falsifiable in simulation.

12. Key takeaways

  • PREADY correctness is a timing property, not just a logic one. It must be stable and glitch-free in the manager's sample window — the PCLK rising edge in the access phase — or a logically-right PREADY is sampled wrong.
  • Combinational PREADY is fast but risky: zero added latency (can complete in the first access cycle), but its cone must settle within the cycle and not glitch into the sample window.
  • Registered PREADY is clean but a beat slower: glitch-free and trivially timed, at the cost of one cycle of minimum latency on every transfer. It is the safe default on a slow control bus.
  • An unselected subordinate must drive PREADY high (the !sel → 1 default), or it stalls another slave's access or hangs the bus on an unmapped address.
  • The classic bug — a combinational PREADY glitch off a rippling decode — is invisible in zero-delay RTL and only appears in gate-level or delay-annotated simulation; the fix is to register PREADY or its inputs.
  • Verify PREADY at the right level: X-checks and ready-before-complete causality in RTL, and a glitch guard-band check in gate-level — and cover both the first-cycle (combinational) and one-later (registered) completion paths.