AMBA APB · Module 11
Slave Reset Behaviour
The slave's behaviour under PRESETn — async-assert/sync-release of the active-low reset to avoid release metastability, every software-visible register to a defined reset value, the FSM coming up in IDLE with safe bus outputs, and handling the first post-reset access cleanly.
Reset is the one input that defines where the slave begins. Every other chapter in this module has described what the slave does once it is running — how it decodes, when it asserts PREADY, when it raises PSLVERR. This chapter is about the instant before any of that: what the slave looks like while PRESETn is asserted, and how it crosses cleanly into normal operation when reset releases. The single idea to carry: reset is the slave's known-good starting state — PRESETn is active-LOW, applied async-assert / sync-release, and on its release every software-visible register sits at a defined value while the FSM comes up in IDLE driving safe bus outputs, ready to handle the first access cleanly. Get this right and the slave boots into a state firmware can trust; get it wrong and the first transfer after reset hangs, double-completes, or branches firmware on garbage it read at offset zero.
1. Problem statement
The problem is defining a single, deterministic state the slave enters on PRESETn and leaves cleanly on release — a state in which every software-visible register holds a known value, the bus outputs are safe, and the control FSM is parked where the next transfer can start correctly.
PRESETn is the APB reset: a shared, active-LOW signal that travels alongside PCLK to every peripheral on the bus. When it is asserted (driven low), the slave must abandon whatever it was doing and converge to a defined baseline. That baseline is not a vague "cleared" state — it is a precise contract with three audiences:
- Firmware, which reads the registers at boot. The first thing software does after release is read configuration and status. Every software-visible register must therefore power up at a defined reset value — enables off, status cleared, interrupt masks in their safe (usually masked) state — or boot firmware branches on whatever the flop happened to settle to.
- The bus, which must not see a spurious transfer. While reset is asserted and across its release, the slave must drive its outputs safe:
PREADYlow (or at its defined idle),PSLVERRlow,PRDATAto a defined value. No half-finished transfer may complete, and no access may be latched as reset releases. - The slave's own FSM, which must come up parked. The state machine that sequences
SETUP/ACCESSand generatesPREADYmust reset into IDLE, not into the middle of a transfer — so the first access after release runs the full, correct handshake.
And there is a timing requirement underneath all of it: PRESETn must be applied so that assertion is immediate (even with no clock) but release is clean (re-timed to PCLK), so no flop in the slave goes metastable the moment reset lets go. That combination — async-assert, sync-release — is what turns "set everything to zero" into "leave reset deterministically."
So the job is not "clear the flops." It is to define a complete reset state — register values, bus outputs, FSM position — and a reset release discipline that lands the slave in that state cleanly on a known clock edge.
2. Why previous knowledge is insufficient
The earlier chapters in this module built the slave's running behaviour and quietly assumed a clean starting point. This chapter is where that assumption is paid for.
- Chapter 11.1 — Register-Bank Design introduced that each register has a reset value (
if (!presetn) ctrl <= 32'h0;) but treated it as a one-line default. Reset is not a default — it is a specification. Which value each register takes at reset is a design decision firmware depends on (enables off so the peripheral is inert at boot, status cleared so no stale event is reported, masks safe so no spurious interrupt fires). This chapter makes the reset value a first-class part of the register's contract. - Chapter 11.5 — PREADY Generation built the FSM and the
PREADYlogic — but aPREADYFSM is only correct if it starts correct. If the FSM comes out of reset inACCESSinstead ofIDLE, the first transfer never runs itsSETUPphase andPREADYeither hangs low forever or is already high and the access double-completes. The reset position of that FSM is exactly what this chapter pins down. - Chapter 11.6 — PSLVERR Generation defined when
PSLVERRis raised — and reset is the one time it must be guaranteed low. A slave that comes out of reset with a stalePSLVERRhigh reports a phantom error on the first access. The reset state of every bus output is this chapter's concern. - The protocol knowledge says nothing about release metastability. Nothing earlier explained that de-asserting
PRESETnis itself a clock-domain hazard: if reset releases asynchronously, different flops can capture it on different edges, and the FSM can resolve into an illegal state. That is a reset-architecture problem, not a protocol one.
So the model to add is the slave's initial condition and release discipline: defined reset values for every visible register, the FSM parked in IDLE, all bus outputs safe, and PRESETn applied async-assert / sync-release so release lands on a clean edge. With that nailed, Chapter 11.8 — Slave RTL Templates can assemble the whole slave knowing its starting state is sound.
3. Mental model
The model: reset is the slave's "power-on snapshot" — the exact frame the slave freezes into and unfreezes from. Think of a vending machine being switched on: while the power-on relay is held (reset asserted), the machine shows nothing, accepts no coins, and dispenses nothing — it is inert and safe. The instant the relay releases, it must be sitting at its home screen (IDLE), with the price display showing the configured defaults (registers at reset values) and the coin slot ready for the first coin (first access). It must never unfreeze halfway through dispensing a drink nobody ordered.
Three refinements make it precise:
PRESETnis active-LOW and asynchronous on assert. "Asserted" means driven to 0. The slave's flops use an asynchronous reset path so that the momentPRESETngoes low — even ifPCLKis stopped or gated — every flop is forced to its reset value immediately. You do not wait for a clock edge to become safe.- Release is synchronised to
PCLK. De-asserting reset is the dangerous part. IfPRESETnrises at a random point relative toPCLK, some flops may sample it as 1 and others as 0 on the same edge, and a multi-flop FSM can land in an illegal state — or a single flop can go metastable. The fix is a reset synchroniser: routePRESETnthrough two flip-flops clocked byPCLKso its release is re-timed to a clean edge, while its assert stays asynchronous. This is the async-assert / sync-release idiom. - The reset state is a complete tuple, not just "zero." It is
{ every register = its defined reset value, FSM = IDLE, PREADY = idle/low, PSLVERR = 0, PRDATA = defined }. Each element serves a different audience — registers serve firmware, the FSM serves the next transfer, the outputs serve the bus — and all of them must hold simultaneously the moment release completes.
4. Real SoC implementation
In RTL, three things must be true: (1) PRESETn reaches the slave through a synchroniser so release is clean; (2) every flop uses the active-LOW asynchronous-reset form with a defined reset value; (3) the FSM and bus outputs reset to IDLE and safe. Here is the standard structure.
// ----------------------------------------------------------------------------
// Reset synchroniser: async ASSERT, sync RELEASE.
// The raw reset asserts the two flops asynchronously (no clock needed), but
// its de-assertion ripples through FF1 -> FF2 and so is re-timed to PCLK.
// The slave uses `presetn` (the synchronised output), never `presetn_raw`.
// ----------------------------------------------------------------------------
module apb_reset_sync (
input logic pclk,
input logic presetn_raw, // active-LOW, may release asynchronously
output logic presetn // active-LOW, release synchronised to pclk
);
logic rst_ff1, rst_ff2;
always_ff @(posedge pclk or negedge presetn_raw)
if (!presetn_raw) begin // ASYNC assert: forced low immediately
rst_ff1 <= 1'b0;
rst_ff2 <= 1'b0;
end else begin // SYNC release: walks 0 -> 1 across two edges
rst_ff1 <= 1'b1;
rst_ff2 <= rst_ff1;
end
assign presetn = rst_ff2;
endmodule
// ----------------------------------------------------------------------------
// Slave core, clocked by the SYNCHRONISED presetn.
// Note every always_ff uses (posedge pclk or negedge presetn): async assert.
// ----------------------------------------------------------------------------
module apb_slave_core (
input logic pclk, presetn, // presetn = synchronised reset
input logic psel, penable, pwrite,
input logic [31:0] pwdata,
output logic pready, pslverr,
output logic [31:0] prdata
);
// --- control/status registers: each with a DEFINED reset value (11.1) ---
logic [31:0] ctrl; // RW config : reset = 0 -> peripheral disabled at boot
logic iflag; // W1C flag : reset = 0 -> no stale event reported
logic [31:0] imask; // RW mask : reset = 0 -> interrupts masked (safe)
always_ff @(posedge pclk or negedge presetn)
if (!presetn) begin
ctrl <= 32'h0000_0000; // enables OFF: nothing runs until firmware writes it
iflag <= 1'b0; // status CLEARED: handler sees no phantom event
imask <= 32'h0000_0000; // masks SAFE: no spurious interrupt out of reset
end else begin
if (psel && penable && pwrite && pready) begin
ctrl <= pwdata; // normal write path (decode elided for brevity)
imask <= pwdata;
end
end
// --- PREADY FSM (11.5): MUST come up in IDLE ---
typedef enum logic [1:0] {IDLE, SETUP, ACCESS} state_t;
state_t state;
always_ff @(posedge pclk or negedge presetn)
if (!presetn) state <= IDLE; // parked in IDLE out of reset
else unique case (state)
IDLE : state <= psel ? SETUP : IDLE;
SETUP : state <= penable ? ACCESS : SETUP;
ACCESS : state <= IDLE;
default: state <= IDLE; // illegal state recovers to IDLE
endcase
// --- bus outputs: SAFE values in/after reset (11.5 / 11.6) ---
always_ff @(posedge pclk or negedge presetn)
if (!presetn) begin
pready <= 1'b0; // not ready -> no transfer completes in reset
pslverr <= 1'b0; // no phantom error on the first access
prdata <= 32'h0000_0000; // defined, not X
end else begin
pready <= (state == SETUP); // single-cycle ready (11.5)
pslverr <= 1'b0; // raised by error logic (11.6)
prdata <= ctrl; // read mux (elided)
end
endmoduleTwo facts make this the right structure. First, PRESETn is asynchronous on assert but synchronous on release: each always_ff triggers on negedge presetn so assertion forces flops to their reset value with no clock, while the synchroniser guarantees the presetn those flops actually see only releases on a clean PCLK edge — so no flop, and no FSM, resolves into a metastable or illegal state when reset lets go. Second, the reset branch of every flop is a deliberate value, not a reflex zero: ctrl/imask reset off so the peripheral is inert and interrupts are masked at boot, iflag resets clear so no stale event is reported, the FSM resets to IDLE so the first transfer runs its full handshake, and pready/pslverr/prdata reset to safe so the bus sees nothing spurious. That tuple is the slave's contract with firmware and the bus — and it is exactly what Chapter 11.8 — Slave RTL Templates drops into the full slave.
5. Engineering tradeoffs
Every signal and register in the slave has a reset state, and choosing it is a deliberate trade between firmware safety, area, and bus correctness. This table is the slave's reset contract.
| Signal / register | Reset value | Why this value |
|---|---|---|
PRESETn (the input) | active-LOW asserted (0) | The bus reset is active-LOW; "in reset" means PRESETn == 0. Applied async-assert / sync-release so release never causes metastability. |
| FSM state | IDLE | The slave must come up parked so the first access runs the full SETUP/ACCESS handshake; any other start state hangs or double-completes the first transfer. |
PREADY | 0 (idle / not ready) | No transfer may complete while in reset or across release; ready must come from the FSM, not survive reset. |
PSLVERR | 0 | No phantom error on the first access; error is asserted only by the error logic on a real access (11.6). |
PRDATA | defined (0), not X | A defined read-bus avoids X-propagation into the master and gives a deterministic value if read before configuration. |
CTRL / enable bits | 0 (disabled) | The peripheral must be inert at boot — nothing runs until firmware explicitly enables it; a peripheral that auto-starts on reset is a safety and power hazard. |
| Interrupt mask | 0 (masked) | Interrupts default to masked so a peripheral cannot fire an IRQ before firmware has installed a handler. |
IFLAG / status (W1C) | 0 (cleared) | Status comes up clean so the first handler does not service a phantom event; software reads a true "nothing happened" state. |
| FIFO / data RAM contents | usually not reset | Large arrays are left un-reset to save area and routing; they are not software-visible at boot value and are filled before use. Only the pointers/flags that are visible reset. |
| Shadow / pipeline regs | defined if observable, else free | Reset only what is software-visible or affects bus behaviour; resetting everything costs area and reset-tree fanout for no benefit. |
The throughline: reset everything software reads at boot or that drives the bus, to a value chosen for firmware safety (enables off, status cleared, masks safe) — and leave large internal arrays un-reset to save area. The discriminator is visibility: if firmware or the bus can observe it before it is otherwise written, it needs a defined reset value; if not, resetting it is wasted silicon.
6. Common RTL mistakes
7. Debugging scenario
The signature reset bug is a slave whose first transfer after reset hangs or double-completes, caused by an unsynchronised reset release that lets the PREADY/state FSM leave reset in a non-IDLE state — and it only appears occasionally, because it depends on exactly where PRESETn releases relative to PCLK.
- Observed symptom: the very first APB access after a reset intermittently misbehaves — sometimes the transfer never completes (the master waits on
PREADYforever and the bus locks up), and sometimes it completes instantly and a second time (a double-complete). After the second access everything is normal. The failure rate changes with reset timing, supply, and temperature — the hallmark of a metastability/skew bug, not a logic bug. - Waveform clue: zoom in on the
PRESETnrelease. In the failing runsPRESETnrises between twoPCLKedges, and the FSM's state flops disagree — one captures the released value a cycle later than the others — so on the first post-reset edge the FSM register reads a non-IDLE encoding (or briefly anX). ThePREADYline then either stays low through the first access or is already high as the access begins. - Root cause: the slave's
PRESETnwas driven directly from an asynchronous reset source with no reset synchroniser, so reset release was not re-timed toPCLK. The multi-bit FSM state register sampled the release on different edges across its bits, resolving into an illegal/non-IDLE state — the FSM came out of reset already "mid-transfer," so the first access did not run a cleanSETUP/ACCESSsequence. The bug is in the reset architecture, not in the FSM logic, which is why the FSM looks correct in isolation. - Correct RTL: route
PRESETnthrough a two-flop reset synchroniser (async-assert / sync-release) so thepresetnthe slave's flops see only releases on a cleanPCLKedge, and give the FSM adefault: state <= IDLE;so even a momentary illegal encoding recovers —always_ff @(posedge pclk or negedge presetn) if (!presetn) state <= IDLE; else unique case(state) ... default: state <= IDLE; endcase. With release synchronised, every state bit leaves reset on the same edge and the FSM comes up inIDLE. - Verification assertion: check that one cycle after reset de-asserts, the FSM is in IDLE and every bus output is safe —
assert property (@(posedge pclk) $rose(presetn) |=> (state == IDLE) && (pready == 1'b0) && (pslverr == 1'b0) && !$isunknown(prdata));. Add a companion that no transfer is ever in flight at the moment of reset release:assert property (@(posedge pclk) $rose(presetn) |-> $past(!pready, 1));. - Debug habit: when the first access after reset misbehaves intermittently but later accesses are fine, do not debug the FSM logic — go straight to the reset release. Check whether
PRESETnis synchronised before it reaches the flops, and whether the FSM comes up in IDLE. Intermittent, timing-sensitive first-transfer failures are almost always an unsynchronised reset release or a missing default state, not a bug in the transfer logic itself.
8. Verification perspective
Reset behaviour is verified by treating "the slave out of reset" as a checkable contract: a defined value on every visible register, the FSM in IDLE, safe bus outputs, and a clean first access — all on a synchronised release.
- Reset-value check for every register. After
PRESETnde-asserts and before any write, read (or back-door peek) every software-visible register and assert it equals its specified reset value. A register model (UVM RAL or equivalent) automates this across the whole map; the test is only meaningful if the model declares the correct reset value per register — enables off, status cleared, masks safe — which is the design intent from this chapter. - FSM-in-IDLE-after-reset. Assert that on the first
PCLKedge after release the control FSM is inIDLEand not in any transfer state. Pair it with an illegal-state assertion that the FSM never observes an encoding outside its legal set —assert property (@(posedge pclk) disable iff(!presetn) state inside {IDLE, SETUP, ACCESS});— to catch a corrupt release. - First-post-reset access. The single highest-value functional test: immediately after release, drive a full APB read and a full APB write and check each runs the complete handshake —
PREADYasserts exactly once, the write commits, the read returns the reset value,PSLVERRstays low. This is precisely the scenario the unsynchronised-release bug breaks, so it must be an explicit, directed test, not left to random stimulus. - Reset-release timing and X-propagation. Run the release at many phases relative to
PCLK(and, in gate-level sim, with X-injection / X-pessimism enabled) to confirm no flop the slave exposes resolves toXafter release and the FSM never lands non-IDLE. Reset-synchroniser CDC checks (the lint/CDC tool flagging an unsynchronised reset reaching sequential logic) belong here too — they catch the bug structurally before simulation does. - Reset assertion mid-transfer. Assert
PRESETnin the middle of an in-flight access and check the slave abandons it cleanly: outputs go safe, no partial write commits, and on release the slave is back in the full reset state with the FSM in IDLE. A slave that latches a half-finished transfer through a reset is broken.
The point: reset is a property, not an event. Verify the full reset tuple — every register's value, the FSM in IDLE, safe outputs — directly drive the first post-reset access, and sweep the release timing so the sync-release discipline is actually exercised, not assumed.
9. Interview discussion
"How does an APB slave behave under reset?" is a deceptively deep question: a weak answer says "everything goes to zero," while a strong one shows you think about async-assert / sync-release, defined reset values for firmware, and the FSM coming up in IDLE.
Lead with the reset architecture: PRESETn is active-LOW, applied async-assert / sync-release through a reset synchroniser — assert is asynchronous so flops are forced safe immediately even with no clock, but release is re-timed to PCLK across two flops so no flop and no FSM goes metastable or lands in an illegal state when reset lets go. This single point separates engineers who have built reset trees from those who have only typed if (!rst). Then cover the reset state: every software-visible register resets to a value chosen for boot safety — enables off so the peripheral is inert, status cleared so no phantom event is reported, interrupt masks safe so no IRQ fires before firmware is ready — because firmware reads these registers at boot before it writes anything. Add that the control FSM must reset to IDLE so the first access runs its full SETUP/ACCESS handshake, and that bus outputs reset safe — PREADY low, PSLVERR low, PRDATA defined — so the bus sees nothing spurious. Close with judgment: you do not reset everything — large data arrays are left un-reset to save area; the discriminator is visibility, and you reset exactly what firmware or the bus can observe before it is otherwise written. Mentioning that the intermittent "first transfer after reset hangs" bug is an unsynchronised release, not an FSM logic error, signals you have actually debugged one.
10. Practice
- Classify the reset. State what "async-assert / sync-release" means for
PRESETn, why assertion is asynchronous and release is synchronous, and what failure each half prevents. - Write the synchroniser. From memory, write the two-flop reset synchroniser (
always_ff @(posedge pclk or negedge presetn_raw)) and explain how it makes release clean while keeping assert immediate. - Fill the reset contract. For a peripheral with a config register, an interrupt-mask register, a W1C status register, and a 16-entry data FIFO, give each a reset value (or "not reset") and one sentence of justification.
- Place the FSM. Explain why the
PREADYFSM must reset to IDLE, and describe exactly how the first post-reset access fails if it resets toACCESSinstead. - Find the bug. Given a slave whose first transfer after reset intermittently hangs while later transfers are fine, name the most likely root cause and the one-line architectural fix.
11. Q&A
12. Key takeaways
PRESETnis active-LOW and is the slave's known-good starting state. "In reset" meansPRESETn == 0; on its release the slave must land in a complete, defined state — registers, FSM, and bus outputs all at known values simultaneously.- Apply reset async-assert / sync-release. Assertion is asynchronous so flops go safe immediately even with no clock; release is re-timed to
PCLKthrough a two-flop synchroniser so no flop or FSM goes metastable or lands illegal when reset lets go. A sync reset alone does not fix an asynchronous release. - Every software-visible register resets to a value chosen for firmware safety — enables off (inert peripheral), status cleared (no phantom event), interrupt masks safe (no IRQ before a handler). Firmware reads these at boot, so an un-reset register is a real bug.
- The control FSM must reset to IDLE and bus outputs must reset safe —
PREADYlow,PSLVERRlow,PRDATAdefined — so the first access runs the full handshake and the bus sees nothing spurious. Adefault: state <= IDLE;recovers any illegal encoding. - You do not reset everything. Reset exactly what firmware or the bus can observe before it is otherwise written; leave large data arrays (FIFOs, RAMs) un-reset to save area and reset-tree fanout. The discriminator is visibility.
- The intermittent "first transfer after reset hangs" bug is an unsynchronised reset release, not FSM logic. Verify reset as a property — every register's value, FSM in IDLE, safe outputs, a clean first access — and sweep the release timing so the sync-release discipline is actually exercised.