Firmware field guide · 02 / 05

Memory Maps
and MMIO

A pointer is not a register contract. Learn to decode the physical path, select the exact transfer, preserve side effects, and prove that a write reached the place that matters.

ControlNode lab assumption: every address, register, reset value, clock rate, and timing number on this page belongs to a fictional 80 MHz Cortex-M4 teaching platform. Replace them with the reference manual and errata for your exact silicon revision.

Terms such as MMIO, W1C, and Device memory carry short definitions.

01 / 06

Address decoding

Turn a number into a physical path

Before dereferencing an address, prove the region, slave window, register offset, transfer width, alignment, security or privilege attribute, and clocked destination.

Region

Decode top-down

The processor emits an address and transfer attributes. Interconnect logic selects a memory region, then a bus bridge, then a peripheral window, and finally a register offset. A valid hexadecimal number can still select the wrong slave.

Design rule: write the chain as address → region → peripheral base → offset → register before writing C.

Transfer

Width and alignment are part of the address

A 32-bit access covers four byte addresses and normally starts on a four-byte boundary. A peripheral may reject byte or halfword transfers even when the bus can carry them. The register contract, not the C type alone, decides legality.

ControlNode assumption: UART2 accepts aligned 32-bit accesses only.

Attributes

Normal and Device memory are not interchangeable

Normal memory is suited to code and data. Device regions represent observable hardware and carry ordering restrictions. On a real part, the architecture map, MPU configuration, and vendor memory map jointly determine the effective attributes.

See the Armv7-M Architecture Reference Manual.

Interactive lab 1 · Address decoder

Follow one ControlNode-M4 access

Choose an address and transfer width. The decoder reports the selected region, offset, alignment, and the first contract violation. This lab uses the fictional map printed below, so the full default lesson remains visible without JavaScript.

Decode path Processor → APB bridge → UART2 → SYNC
Region and attribute Peripheral · Device · read/write
Arithmetic 0x40021014 − 0x40021000 = offset 0x14
Contract result Valid aligned 32-bit access to UART2.SYNC

UART2.SYNC is selected with the required width and alignment.

RangeLab ownerAttributeContract
0x00000000–0x0007FFFFBoot alias to internal flashNormalTarget remap exposes the selected boot source here
0x08000000–0x0807FFFF512 KiB internal flashNormalCanonical read/execute window; programming uses a controller
0x20000000–0x2003FFFF256 KiB SRAMNormalRead/write data
0x40000000–0x4003FFFFAPB peripheralsDeviceVendor register contracts apply
0xE0000000–0xE00FFFFFPrivate peripheral busDeviceArchitectural core registers; privilege can apply

Checkpoint · Address first

Can you prove the destination?

Easy What is MMIO?

Answer: Memory-mapped input/output places hardware control and status registers at addresses in the processor memory space. Load and store instructions become bus transactions to a selected peripheral rather than ordinary RAM operations.

Failure boundary: the address syntax looks like memory, but reads and writes can trigger hardware side effects. The interviewer is testing whether you distinguish an addressable register from storage.

Easy What is the UART2 offset of 0x40021014?

Answer: Under the ControlNode lab assumption, UART2 starts at 0x40021000. Subtracting the base gives 0x14, which the fictional register sheet assigns to SYNC.

Assumptions: both values are byte addresses and the base is correct for this silicon view. The interviewer is testing base-plus-offset arithmetic and whether you state the source of the map.

Medium Is 0x40021015 valid for a 32-bit UART2 transfer?

Answer: No. It is not divisible by four, so it is misaligned for a 32-bit access. It also points one byte into the fictional SYNC register, while UART2 requires aligned 32-bit accesses.

Tradeoff: some processors can split certain unaligned normal-memory accesses, but a Device access may fault or behave outside the peripheral contract. The interviewer is testing architecture versus device-specific guarantees.

Medium Why can a peripheral address fault even when it is inside the documented peripheral region?

Answer: Region decode is only the first gate. The selected slave may be unclocked or held in reset, the offset may be reserved, the transfer size may be unsupported, or privilege and protection attributes may reject the transaction.

Debug proof: capture the fault status and address, then verify bridge clock, reset, access width, and register availability. The interviewer is testing complete-path reasoning.

Hard Design a review checklist for a new register address supplied only as a macro.

Answer: Trace the address through the architecture and vendor maps; identify region attributes, peripheral window, offset, register name, supported widths, alignment, access permission, side effects, reserved fields, clock/reset dependencies, privilege, and silicon revision. Then compare the header or SVD with the signed-off reference manual and errata.

Acceptance evidence: retain the source revision and add a bounded probe that reads a harmless identification register before touching state. The interviewer is testing whether you can convert an untrusted constant into a reviewable hardware contract.

02 / 06

Register access contracts

The same bits can obey different verbs

A register is defined by more than its offset. Width, direction, reset value, reserved bits, read side effects, write side effects, and cross-field rules determine the only safe operations.

Permissions

RO, RW, and WO name legal directions

Read-only values report state. Read/write fields store configuration. Write-only command ports turn written values into actions. A debugger or generic dump routine can violate a read-side-effect contract even when firmware never writes the register.

Side effects

W1C is a command, not ordinary storage

For a W1C bit, writing one requests that exact condition be cleared. Writing zero preserves it. A read-modify-write sequence can echo every observed one back to the register and clear events that software did not intend to acknowledge.

Reserved fields

Preserve what the manual tells you to preserve

Reserved bits may require zero writes, reset-value writes, or preservation. Never infer a rule from the word “reserved.” Use field masks generated from a reviewed description and re-check the errata for the silicon revision.

CMSIS-SVD records register access and side-effect metadata in a machine-readable form. See the official Open-CMSIS-SVD specification.

Interactive lab 2 · Register semantics

Apply an operation to the right contract

All registers below are fictional ControlNode UART2 contracts and require aligned 32-bit bus transfers. Select a register and operation. Each trial starts from the printed initial value, so results are deterministic.

Starting contract EVENTS = 0x00000007 · bits 2:0 pending · W1C
Bus effect Write 0x00000001; clear event bit 0 only
Result EVENTS becomes 0x00000006
Review verdict SAFE · targeted W1C acknowledgement

Writing one clears only the selected W1C event.

OffsetNameAccessReset / initialLab-only rule
0x04CONTROLRW0x00000004Bits 7:0 implemented; bits 31:8 write as zero
0x08EVENTSW1C0x00000007 in labWriting one clears the corresponding pending event
0x0CRXDATARO, read-popFIFO head 0x41One read returns and consumes the oldest byte
0x10COMMANDWONot readableWriting bit 0 starts a transfer; reads are undefined
0x14SYNCRW0x00000000Readback is documented to drain UART2's posted-write path

Checkpoint · Use the register's verb

Can you preserve every unintended bit?

Easy What does W1C mean?

Answer: Write one to clear means each written one acknowledges and clears the corresponding status bit, while a written zero leaves that bit unchanged. Reading usually reports current status, but the exact read behavior still comes from the manual.

What this tests: whether you treat the write as a per-bit command instead of ordinary value storage.

Easy Why should a generic register dump avoid read-pop registers?

Answer: A read-pop access changes hardware state by consuming the FIFO head. A debugger watch window or logging loop can steal data from the real driver and make the original failure disappear or move.

Safe alternative: use a documented non-destructive status or snapshot path. The interviewer is testing awareness that observation can perturb the system.

Medium Why is EVENTS |= BIT0 unsafe for a W1C register currently equal to 0x7?

Answer: The read returns 0x7. OR with bit 0 still produces 0x7, and writing 0x7 clears all three pending event bits. The intended targeted acknowledgement should write the mask 0x1 directly.

Failure mode: concurrent or previously pending evidence is lost. The interviewer is testing whether you expand C shorthand into its bus transactions.

Medium How do you update one field of an ordinary RW register while respecting reserved bits?

Answer: Start from the vendor-prescribed policy. If the register is safely readable and reserved bits must be preserved, read once, clear the implemented field mask, insert the bounded new value, and write once. If reserved bits must be written as zero, mask them explicitly. Use a dedicated set/clear alias when supplied.

Tradeoff: read-modify-write can still race with another owner. The interviewer is testing both bit preservation and ownership.

Hard How would you review a register that mixes RW configuration and W1C status fields?

Answer: First look for separate aliases or a vendor-prescribed write sequence. If one write word truly combines semantics, construct it from explicit masks: place desired configuration values only in RW positions, zeros in W1C positions that must remain pending, and ones only for statuses intentionally acknowledged. Do not seed the word from a raw register read unless the manual explicitly defines that operation as safe.

System rule: serialize all writers and test simultaneous hardware status changes. The interviewer is testing contract composition under concurrency.

03 / 06

C layout and exact access

Make the source express the bus transfer

A trustworthy accessor makes the base, offset, width, permission, field masks, and side-effect operation reviewable. volatile matters, but it does not solve width, atomicity, inter-thread synchronization, or hardware completion.

Layout

Prove offsets at compile time

Use fixed-width unsigned types when the target provides them, explicit reserved members for documented holes, and _Static_assert on critical offsets and total size. Avoid packing a register block because it can create unaligned accesses.

Qualifier

volatile constrains compiler treatment

A volatile access tells the implementation that the object can change outside the abstract program and that the access is observable. It does not make a compound expression one transaction, order unrelated actors, or guarantee that a posted write reached the peripheral.

Generation

Use SVD as input, not unquestioned truth

CMSIS device headers provide standard access qualifiers and typed layouts. Review generated definitions against the reference manual and errata, especially side effects, alternate register views, reset values, and revision-specific differences.

See CMSIS-Core Cortex-M peripheral access conventions.

Interactive lab 3 · C access review

Review the bus transaction hidden in the code

Select a snippet. The review checks width, alignment, layout, semantics, and what volatile does not guarantee. Snippets are conceptual C11 for the fictional ControlNode UART2 block.

#include <stddef.h>
#include <stdint.h>

typedef struct {
  volatile uint32_t CONTROL; /* 0x00 in this view */
  volatile uint32_t EVENTS;  /* 0x04, W1C */
} cn_uart_regs_t;

_Static_assert(offsetof(cn_uart_regs_t, EVENTS) == 0x04,
               "UART layout drift");

#define CN_UART2 ((cn_uart_regs_t *)(uintptr_t)UINT32_C(0x40021004))

static inline void uart_ack(uint32_t mask) {
  CN_UART2->EVENTS = mask & UINT32_C(0x7);
}
Verdict PASS WITH CONTRACT CHECKS
Why Exact 32-bit members, asserted offset, one targeted W1C store
Still prove Base address, allowed mask, alignment, clock/reset, and ordering at the call site

The typed layout makes the intended 32-bit W1C store reviewable.

Preferred pattern · conceptual C11

Separate address, layout, and operation

#include <stddef.h>
#include <stdint.h>

typedef struct {
  volatile uint32_t CONTROL;  /* +0x04, RW */
  volatile uint32_t EVENTS;   /* +0x08, W1C */
  volatile const uint32_t RXDATA; /* +0x0C, read-pop */
  volatile uint32_t COMMAND;  /* +0x10, WO by contract */
  volatile uint32_t SYNC;     /* +0x14, RW */
} cn_uart2_regs_t;

_Static_assert(offsetof(cn_uart2_regs_t, EVENTS) == 0x04, "EVENTS");
_Static_assert(sizeof(cn_uart2_regs_t) == 0x14, "UART2 view");

#define CN_UART2_BASE UINT32_C(0x40021004)
#define CN_UART2 ((cn_uart2_regs_t *)(uintptr_t)CN_UART2_BASE)
#define CN_UART_EVENT_MASK UINT32_C(0x00000007)

static inline void cn_uart_ack_events(uint32_t events)
{
    CN_UART2->EVENTS = events & CN_UART_EVENT_MASK;
}

ControlNode lab note: the struct view begins at UART2 offset 0x04, not at the peripheral base, so the asserted member offsets are relative to that view. Production code should prefer the reviewed vendor header when it accurately expresses the contract.

Checkpoint · Make every access inspectable

Can a reviewer predict the transfer?

Easy Why use uint32_t instead of unsigned long for a 32-bit register?

Answer: When supplied by the implementation, uint32_t has exactly 32 value bits and no padding. The width of unsigned long depends on the data model, so it can be 32 or 64 bits and may request the wrong transfer or layout.

Source: exact-width types are defined by WG14 N1570. The interviewer is testing ABI awareness.

Easy What does an offset _Static_assert protect?

Answer: It fails the build if compiler layout places a member at a different byte offset than the reviewed register map. It catches missing reserved holes, wrong member widths, and accidental reordering in the source definition.

Limit: it cannot prove the base address or runtime clock state. The interviewer is testing layered proof rather than trust in one check.

Medium Does volatile uint32_t *p make (*p)++ one hardware transaction?

Answer: No. Increment is conceptually a volatile read, arithmetic, and a volatile write. Hardware can change the register between those transfers, and side-effect semantics can make either transfer destructive.

Design response: use a dedicated set/clear register, a direct command write, or an ownership/serialization mechanism appropriate to the contract. The interviewer is testing source-to-bus expansion.

Medium Why is a packed peripheral struct risky?

Answer: Packing can reduce member alignment and lead the compiler to emit byte transfers or unaligned multi-byte accesses. Both can violate a peripheral that accepts only naturally aligned 32-bit accesses.

Preferred method: represent documented holes with reserved members, assert offsets, and inspect generated code for critical accessors. The interviewer is testing layout and bus-width coupling.

Hard Design a portable review boundary around a target-specific MMIO block.

Answer: Keep target addresses and register types in one reviewed hardware layer. Expose semantic functions such as ack_events(mask), set_baud(divider), and start_tx(), not raw pointers. Assert layout, constrain masks, document side effects and required barriers, and provide a fake backend for host tests.

Acceptance evidence: compile for every supported ABI, inspect representative assembly, run target smoke tests, and version the vendor header/reference manual pairing. The interviewer is testing how you contain non-portable truth.

04 / 06

Ordering versus completion

A retired store is not always an observed effect

Reason across four layers: compiler emission, processor ordering, interconnect acceptance, and peripheral action. Name which boundary must be crossed before selecting a compiler barrier, DMB, DSB, ISB, or documented readback.

Compiler

First preserve the intended accesses

Language and compiler mechanisms determine which operations are emitted and how the compiler may move ordinary accesses. A CPU barrier cannot restore a store that optimization removed. Conversely, volatile alone is not a hardware ordering primitive.

Processor

DMB orders; DSB waits; ISB refetches

At architecture level, DMB prevents specified memory accesses from being observed out of order. DSB also blocks later instruction execution until the required prior explicit memory accesses complete. ISB flushes the pipeline so later instructions execute with updated context.

Device path

Peripheral completion is contract-specific

A bridge can acknowledge a write before the peripheral finishes an action. A same-block readback drains the posted path only when the vendor documents it. A status poll proves the named device condition, which is often stronger than proving bus completion.

Barrier semantics are defined by the Armv7-M architecture; the device manual defines peripheral completion.

Interactive lab 4 · Posted-write timeline

Choose the boundary you need to prove

ControlNode lab assumption: writing UART2.SYNC crosses a posted APB bridge. Reading UART2.SYNC is documented to drain that bridge path. The fictional peripheral exposes STATUS.SYNCED when its internal action actually finishes.

1 · CoreStore enters the memory system
2 · BridgeBridge accepts and posts the write
3 · FirmwareNext instruction can run
4 · PeripheralSYNC may take effect later
Proven boundary No completion boundary is proven
Use when Later work does not depend on when SYNC becomes visible
Missing proof Bridge drain and peripheral SYNCED state

The write can be posted while firmware continues.

Decision model

Ask one precise question

Question to proveCandidate mechanismWhat it does not prove alone
Did the compiler emit this access here?Language/compiler contract, volatile MMIO accessorCPU or device completion
Are explicit memory accesses ordered?DMB with the correct scope and placementPeripheral finished an internal action
Must prior explicit accesses complete before execution proceeds?DSB with the correct scope and placementUndocumented device-internal state
Must subsequent instructions use changed execution context?DSB where required, then ISB per architecture ruleUnrelated peripheral work
Has this posted path drained?Documented same-path readbackAction finished unless the manual says so
Has the peripheral reached READY?Bounded status pollFuture progress after the sampled state

Checkpoint · Name the boundary

Can you prove what “done” means?

Easy What does DMB establish?

Answer: A data memory barrier enforces the required ordering of explicit memory accesses before and after the barrier according to its domain and access class. It is an ordering primitive, not a generic declaration that a peripheral action finished.

What this tests: whether you can separate ordering from completion.

Easy When is ISB relevant?

Answer: An instruction synchronization barrier makes subsequent instructions refetch and execute using the updated architectural context. It is used after specific changes that affect instruction execution, such as some control register or MPU updates, following the architecture-defined sequence.

Failure boundary: ISB is not a substitute for draining a peripheral write. The interviewer is testing barrier purpose.

Medium Why can a GPIO readback fail to prove a prior UART write completed?

Answer: The GPIO read may travel through a different slave or bridge path. It does not necessarily force the UART path to drain. A readback proves the intended boundary only when the interconnect and device documentation define that relationship.

Preferred proof: use a documented UART readback or poll the exact UART status condition. The interviewer is testing topology-aware evidence.

Medium Does DSB prove that a commanded peripheral calibration is finished?

Answer: Not generally. DSB can ensure required explicit memory accesses complete before later instructions execute, but the peripheral may accept a command and continue calibrating asynchronously. Firmware must poll a documented DONE state or wait for another specified completion signal.

Tradeoff: the poll needs a timeout and fault path. The interviewer is testing bus completion versus device state.

Hard Design a safe sequence for disabling a peripheral before removing its clock.

Answer: Stop new producers, request peripheral disable, poll its documented IDLE or DISABLED state with a bounded timeout, clear or capture pending errors as specified, then perform any required documented readback or architecture barrier before changing the clock gate. Verify the gate state through the clock controller and preserve a recoverable error path.

Assumptions: exact ordering comes from both peripheral and clock-controller manuals plus errata. The interviewer is testing cross-block completion, timeout design, and evidence.

05 / 06

Bus and access faults

Capture evidence before changing the code

A fault is a transaction report. Preserve the stacked context and fault registers first, then classify address, width, alignment, permission, clock/reset reachability, and slave response.

Evidence

Keep the first reliable fault record

Capture the stacked PC and LR, configurable fault status, hard-fault status, fault address validity bits and values, active exception, and a firmware build identifier. Do not print through the suspect peripheral from the fault handler.

Classification

Separate precise from imprecise reports

A precise bus fault can identify the instruction associated with the failing access. A buffered write can be reported later, so an imprecise fault may not point directly at the originating store. Temporarily changing write-buffer behavior for diagnosis is target-specific and not a production fix.

Dependency

A register can disappear electrically

A correct offset can still fail when the peripheral clock is off, reset remains asserted, the power domain is unavailable, a bridge denies privilege, or the block is absent on that product variant. Prove dependencies using independent registers and board evidence.

Fault behavior and core registers are described in the Cortex-M4 Devices Generic User Guide.

Fault localization ladder

Change one uncertainty at a time

1 · PreserveCopy fault status and stacked context to always-on RAM
2 · AttributeDisassemble the faulting PC and identify the exact transfer width
3 · DecodeRecompute region, base, offset, alignment, and permission
4 · ReachVerify power, clock, reset, bridge, and product-variant presence
5 · MinimizeReplace the path with one documented harmless access
6 · RegressTurn the proven cause into a startup or target test
ObservationLeading hypothesesNext independent proofAvoid
Precise fault, valid address = 0x40021015Misalignment or wrong offsetInspect instruction width; recompute alignmentCasting to a smaller pointer until it stops faulting
Precise fault at valid UART2 offsetClock/reset/permission or unsupported widthRead clock and reset controllers; inspect bus transferAdding arbitrary delays
Imprecise fault after several storesBuffered write to an invalid or unreachable slaveReduce to one store and add documented diagnostic completion pointsBlaming the current PC without evidence
Debugger read changes failureRead side effect or debugger draining a posted pathDisable peripheral watch views; use non-destructive captureAssuming observation is passive
Works on one board revision onlyVariant map, power wiring, errata, or clock sourceCompare device IDs, BOM, schematics, and errata revisionEncoding the board name as a delay constant

Checkpoint · Evidence before workaround

Can you localize the failed transfer?

Easy What is the first thing a fault handler should preserve?

Answer: Preserve the architectural evidence needed to reconstruct the event: stacked registers including PC and LR, fault status and valid fault addresses, active exception, and the exact build identity. Store it through a path that does not depend on the failing peripheral.

What this tests: whether diagnosis starts with immutable evidence instead of logging convenience.

Easy Why is the validity bit for a fault address important?

Answer: The address register is meaningful only when the corresponding validity flag says hardware captured a valid address for that fault. Reading a stale value without the flag can send the investigation toward an unrelated transaction.

What this tests: whether you read status registers as contracts rather than isolated values.

Medium A valid register read faults only before clock initialization. What is the likely mechanism?

Answer: The address decode can select the peripheral while the destination or bridge clock is gated, so the transaction receives an error or no legal response. Reset or power-domain state can create the same symptom.

Proof: read the clock/reset controller through an independent live path and compare the documented dependency order. The interviewer is testing reachability beyond address correctness.

Medium Why can the stacked PC be misleading for an imprecise bus fault?

Answer: A buffered store can fail after the core has executed later instructions. The exception is therefore taken after the originating instruction retired, so the current stacked PC may identify where the error became visible, not the store that caused it.

Debug method: minimize the write sequence and introduce architecture- and device-approved diagnostic completion points. The interviewer is testing temporal attribution.

Hard Design a production-safe MMIO fault record and recovery policy.

Answer: Use a versioned fixed-size record in retained or always-on memory with a validity marker written last. Capture build ID, reset count, stacked context, fault registers, selected clock/reset state, and the driver operation ID. Avoid dynamic allocation and suspect peripherals. On reboot, validate the record, rate-limit upload, and clear it only after durable handoff.

Recovery: fail closed for safety-critical control, bound retries, and enter a diagnosable safe mode after repeated identical faults. The interviewer is testing evidence integrity plus system behavior.

06 / 06

System synthesis

Review one complete polled transaction

A reliable driver ties the software request to a documented address, exact transfer, ownership rule, bounded wait, semantic acknowledgement, recovery action, and measurable acceptance test.

Before access

Establish reachability and ownership

Confirm device identity, power, clock, reset release, base address, register revision, and the single owner of the operation. Validate inputs before encoding them into fields.

During access

Use semantic operations

Write only implemented bits, never probe destructive registers casually, and use the architecture/device completion sequence that matches the actual dependency. Poll a named state with a deadline, not an arbitrary loop count.

After access

Close both success and failure paths

Capture terminal status once, acknowledge only intended W1C bits, return a typed result, and leave the peripheral in a documented state after timeout. Record enough evidence to distinguish busy, bus fault, bad input, and hardware error.

Capstone · ControlNode UART2

A bounded command path

Fictional lab contract: COMMAND.START is write-only, STATUS.BUSY is read-only, EVENTS.DONE and EVENTS.ERROR are W1C, the 80 MHz cycle counter is already enabled, and the longest legal transaction is 2 ms. The 3 ms deadline below is an illustrative engineering margin, not a recommendation for real hardware.

typedef enum {
    CN_OK,
    CN_BAD_ARGUMENT,
    CN_TIMEOUT,
    CN_DEVICE_ERROR
} cn_result_t;

/* Conceptual C11. Target accessors and clock source are reviewed separately. */
cn_result_t cn_uart2_run(uint32_t command)
{
    const uint32_t deadline_cycles = UINT32_C(240000); /* 3 ms at 80 MHz */
    const uint32_t terminal = CN_EVENT_DONE | CN_EVENT_ERROR;

    if ((command & ~CN_COMMAND_ALLOWED_MASK) != 0u) {
        return CN_BAD_ARGUMENT;
    }

    CN_UART2->EVENTS = terminal;       /* targeted W1C: discard stale terminal state */
    const uint32_t start_cycle = cycle_now();
    CN_UART2->COMMAND = command;       /* one exact 32-bit command write */

    while ((CN_UART2->EVENTS & terminal) == 0u) {
        if ((uint32_t)(cycle_now() - start_cycle) >= deadline_cycles) {
            cn_uart2_recover_timeout(); /* target-specific bounded recovery */
            return CN_TIMEOUT;
        }
    }

    const uint32_t events = CN_UART2->EVENTS & terminal;
    CN_UART2->EVENTS = events;         /* acknowledge exactly what was observed */
    return (events & CN_EVENT_ERROR) ? CN_DEVICE_ERROR : CN_OK;
}
Timing calculation 80,000,000 cycles/s × 0.003 s = 240,000 cycles
Review boundary This function still depends on exclusive ownership and a verified wrap-safe cycle source

Acceptance proof

Test the contract, not just the happy path

  • Inspect emitted loads and stores for register width and count.
  • Start with stale DONE and ERROR bits and prove the targeted clear.
  • Inject DONE at the deadline boundary and define whether it wins or times out.
  • Hold BUSY forever and prove bounded recovery without repeated commands.
  • Trigger ERROR and DONE together and verify the documented precedence.
  • Run with the peripheral clock removed and preserve the resulting fault evidence.
  • Repeat on every supported device ID and silicon revision.

Checkpoint · Full-system reasoning

Can you defend the whole path?

Easy Why use a timeout in a polled transaction?

Answer: Hardware, clocking, configuration, or interconnect failure can prevent the terminal state forever. A timeout converts an unbounded hang into a typed failure with a defined recovery and evidence path.

Assumption: the time source must continue running and subtraction must be wrap-safe over the deadline. The interviewer is testing liveness.

Easy Why acknowledge only the terminal bits that were observed?

Answer: A direct W1C mask clears exactly the sampled conditions and avoids echoing unrelated ones back as ones. It makes the software action auditable and preserves events outside the owned mask.

What this tests: whether status capture and acknowledgement follow the register semantics.

Medium Recalculate a 3 ms deadline for an 80 MHz counter.

Answer: 80,000,000 cycles/s × 0.003 s = 240,000 cycles. The units cancel seconds, leaving cycles. Use an unsigned type wide enough for the value and wrap-safe elapsed-time subtraction.

Tradeoff: 3 ms is a fictional ControlNode assumption. A real deadline comes from worst-case hardware timing plus system margin, not average measurements. The interviewer is testing units and boundary choice.

Medium What race exists if two tasks call the polled function concurrently?

Answer: They can clear each other's terminal events, overwrite COMMAND, and attribute one completion to the wrong caller. volatile does not serialize the protocol.

Design response: assign one driver owner and send requests through a queue, or protect the complete transaction with an appropriate mutex if blocking and priority rules permit. The interviewer is testing protocol ownership.

Hard How would you sign off this driver for a new silicon revision?

Answer: Diff the reference manual, SVD/header, and errata; verify device identification; review every used register's offset, width, reset value, permission, side effects, completion rules, and clock/reset dependency. Rebuild layout assertions, inspect emitted access sequences, and run positive, timeout, error, stale-status, and power/clock fault tests on the new revision.

Release evidence: retain source document revisions, test logs, fault records, and a compatibility decision. The interviewer is testing controlled migration rather than “it still boots.”

Primary references

Read the guarantee at its owning layer

The fictional ControlNode-M4 values on this page are teaching inputs, not a primary hardware source. For production work, the exact device reference manual, datasheets, board schematics, vendor headers, and silicon-revision errata outrank these examples.