Trace reset. Validate every handoff. Reach a state you can prove.
Follow one Cortex-M4 system from a physical reset source through vector fetch, runtime construction, clock configuration, image selection, and recovery. The goal is not merely to reach main(). It is to know why the selected image is safe to run and how the product recovers when any dependency fails.
Capture reset evidence before initialization changes it
Validate every vector, runtime, clock, and image handoff
Bound each startup wait and select a recoverable fallback
01Reset contract
Cause · domains · retained evidence
A reset is a system event, not a clean slate.
Power-on, brownout, watchdog, external pin, software, and lockup resets can affect different domains and preserve different evidence. Firmware must capture the target-defined flags and retained breadcrumbs before any initialization destroys them.
Mechanism
Separate source from scope
A reset controller combines physical and software sources, stretches pulses, and distributes reset into CPU, peripheral, backup, and debug domains. One source may reset the core while retained SRAM or an always-on domain survives.
Design rule: document the affected domains for every reset source. “The MCU reset” is not precise enough to reason about stale peripheral state or preserved crash records.
Evidence
Flags can be simultaneous
A watchdog reset may occur while a brownout flag is already latched. Some flags are write-one-to-clear, some clear on read, and some disappear when their clock domain resets. Capture the raw register once, then decode it.
Limit: a latched flag proves that a condition was observed, not necessarily which source occurred first. Preserve timestamps or stage breadcrumbs when ordering matters.
Ownership
One early reader owns reset cause
The first trusted startup code reads reset flags, retained boot stage, and reset-loop counters. It writes one immutable boot record for later software, then performs the documented clear sequence.
A library that clears the register before the boot owner runs silently destroys the most valuable startup evidence.
Interactive lab 1 · reset evidence explorer
What survives this reset?
Select a source. The explorer updates the affected domain, raw evidence, first safe action, and the conclusion you must not overstate.
Affected scopeCPU and most peripheral domains reset; backup domain and retained SRAM survive in this lab
First safe actionCopy the raw cause and retained stage into the immutable boot record before clearing the watchdog flag
What it cannot proveThe flag does not prove why software stopped servicing the watchdog or whether another condition preceded it
Checkpoint · 2 easy, 2 medium, 1 hard
Can you preserve and interpret reset evidence?
Easy Why must reset flags be read before normal peripheral initialization?
Initialization code may reset or clock-gate the controller, read a destructive register, or clear sticky flags. The boot owner should capture the raw value first, save it in a boot record, and decode it without rereading a side-effecting register.
What this tests: evidence lifetime and early-startup ownership.
Easy Does a watchdog reset guarantee that the watchdog itself is defective?
No. The watchdog only proves that its service contract was missed. Causes include deadlock, an unbounded clock wait, disabled service code, corrupted control flow, overload, or an incorrectly configured window. Preserve the last boot stage and application heartbeat before forming a hypothesis.
What this tests: separating an observed mechanism from a root cause.
Medium POR and watchdog flags are both set. Which one happened first?
The flags alone may not establish order. A POR flag can remain set across a later watchdog reset if software never cleared it, or both may be latched by a target-defined sequence. Use the manual’s clear/reset rules plus retained sequence numbers or timestamps. Report the ambiguity instead of inventing chronology.
What this tests: combined flags and limits of evidence.
Medium A warm software reset leaves one peripheral active. Is that automatically a hardware bug?
No. The peripheral might reside in a domain excluded from software reset, or its output might be held by an always-on safety block. Compare the reset-domain matrix with the actual source. Startup must either explicitly return that peripheral to a known state or choose a reset source that covers it.
What this tests: reset scope and stale hardware state.
Hard Design a reset record that survives repeated boot failures without treating corrupt retained RAM as truth.
Store a fixed magic, format version, monotonic sequence, raw reset flags, boot stage, selected slot, compact failure code, and CRC in retained memory. Validate magic, bounds, version, and CRC before use. Write the payload first and commit with the sequence or validity marker last. Keep a bounded reset-loop counter and fall back to recovery after the policy threshold.
The record is diagnostic evidence, not an authority for image authenticity. Security decisions still depend on protected counters, authenticated metadata, and target-defined trust storage.
What this tests: retention, torn writes, validation, and recovery policy.
02Vector entry
Boot alias · initial MSP · reset PC
The first two words decide whether code can begin.
At reset, the processor obtains an initial Main Stack Pointer and reset-handler address from the target’s reset vector location. Before a bootloader hands control to an application image, it must validate the same contract against that image’s own vector table.
Word 0
Validate the initial stack
The initial MSP must point into writable memory permitted by the target, satisfy the required stack alignment, and leave room for the startup call chain. A top-of-RAM value can be a legal initial pointer even though it is one byte beyond the addressable region because the stack grows downward.
ControlNode assumption: SRAM spans 0x20000000–0x2003FFFF; the initial MSP is 0x20040000 and is 8-byte aligned.
Word 1
Validate the executable entry
The reset vector must identify executable memory allowed by the selected image and carry the Thumb-state bit required by Cortex-M. For range checking, mask the state bit before comparing the address with the image’s executable region.
ControlNode assumption: slot A begins at 0x08004000; its reset vector is 0x08004101, which names code at 0x08004100.
Handoff
Relocation is a separate decision
The initial reset fetch and a later bootloader-to-application jump are different events. A bootloader typically disables owned activity, sets the application vector base where implemented, loads its MSP, and branches to the validated entry.
A wrong VTOR may let straight-line application code start, then route the first exception through the wrong image.
Interactive lab 2 · reset-to-C stepper
Walk the first seven gates and inject one fault.
Choose an image defect, then advance the sequence. The model stops at the first gate that cannot be justified.
1Reset release
2Boot alias
3Load MSP
4Load PC
5Reset_Handler
6Runtime init
7main()
Gate 1 of 7 · reset releaseProcessor reset is released on the safe internal clock
No C object exists yet. Only architectural reset state and target boot selection are available.
Validation resultPASS · advance to the target-defined reset alias
Checkpoint · 2 easy, 2 medium, 1 hard
Can you prove the first branch is legal?
Easy What do vector-table words 0 and 1 represent on Cortex-M?
Word 0 supplies the initial Main Stack Pointer. Word 1 supplies the reset-handler address, including the Thumb-state indicator in bit 0. They are data consumed by reset entry, not ordinary instructions executed in place.
What this tests: the architectural reset model.
Easy Why is 0x08004100 an invalid stored reset vector for this Cortex-M example?
Bit 0 is clear. Cortex-M executes Thumb instructions, so a callable vector value must have the state bit set, such as 0x08004101. Range checks should compare the masked address 0x08004100 with the image’s executable region.
What this tests: entry encoding versus instruction address.
Medium Is 0x20040000 outside a 256 KiB SRAM beginning at 0x20000000?
It is the exclusive upper bound of that range, so it cannot be dereferenced as an SRAM byte. It can still be a valid empty descending-stack pointer because the first push decrements the pointer into valid SRAM. The bootloader must also check alignment and reserve enough stack below it.
What this tests: bounds, exclusive ends, and descending-stack semantics.
Medium The application reaches main() but faults on the first timer exception after an A/B handoff. What boot evidence is most relevant?
Inspect the selected slot, application vector base, and VTOR value at handoff. A stale VTOR can leave straight-line code working while later vector lookup uses the bootloader or other slot’s table. Also verify that the selected table is aligned and lies in implemented VTOR address bits.
What this tests: separating initial branch success from exception-vector relocation.
Hard Specify a safe bootloader-to-application handoff contract.
Validate authenticated image metadata, vector-table bounds, initial MSP range/alignment, executable reset entry, and target policy. Stop bootloader-owned producers, clear or transfer pending state according to the platform contract, restore expected clock/cache/MPU state, set the application vector base where implemented, load the application MSP, and branch to its reset entry without returning.
The exact interrupt and peripheral cleanup is target-specific. The application and bootloader must share a written interface contract rather than each assuming reset defaults.
What this tests: cross-image state ownership and defensible handoff.
03C runtime
Linker contract · sections · one startup owner
C starts only after firmware constructs its memory model.
The linker chooses each section’s load and run address. Startup code copies initialized data, zeros uninitialized data, preserves intentional retention, establishes stack boundaries, and enters the language runtime. Duplicating any of those jobs can corrupt state before the application begins.
Placement
Know load address versus run address
.text and .rodata usually execute/read from flash. Initialized .data has an initial image in flash but a writable run address in SRAM. .bss occupies SRAM and is created by zeroing, so its zero bytes need not be stored in the image.
Read the linker map. Section names are conventions; the script and toolchain define the actual contract.
Retention
Exclude .noinit deliberately
A retained crash record or boot breadcrumb belongs in a linker section that startup intentionally does not zero. It still requires validation on every boot because power loss, debugger writes, or an incompatible previous image can leave arbitrary bytes.
Keep retained structures versioned, bounded, and checksummed. Never assume “not initialized” means “trustworthy.”
Ownership
Choose one runtime path
A CMSIS startup commonly calls SystemInit() and then __PROGRAM_START(), which enters the toolchain’s pre-main runtime. A custom assembly path may perform the section loops itself.
Do not copy .data or run constructors in both paths. Document which layer owns clocks, memory, constructors, and the call to main().
Worked contract · inspect the map, then the loops
ControlNode runtime construction
Section
Load address
Run address
Startup action
Lab size
.text/.rodata
Flash
Flash
No copy
180 KiB
.data
Flash image
SRAM
Copy exactly 12 KiB
12 KiB
.bss
None
SRAM
Zero exactly 28 KiB
28 KiB
.noinit
None
Retained SRAM
Preserve, then validate
4 KiB
Stack reserve
None
Top of SRAM
Set bounds and monitor
16 KiB
/* Conceptual startup. Use toolchain-provided symbols and ABI. */
copy_words(&__data_load, &__data_start, &__data_end);
zero_words(&__bss_start, &__bss_end);
validate_retained_record(&boot_record); /* .noinit */
__PROGRAM_START(); /* constructors, then main */
Worked timing assumption40 KiB of copy plus zero work ÷ an illustrative 20 MiB/s effective startup rate = about 2.0 ms
This is a lab estimate, not an architectural guarantee. Flash wait states, bus width, loop implementation, ECC, caches, and debugger state can change the measured rate.
Checkpoint · 2 easy, 2 medium, 1 hard
Can you reconcile the image, linker map, and startup code?
Easy Why does initialized .data need both a load and run address?
Its initial bytes must live in nonvolatile storage so they survive power loss, but C code needs the objects in writable SRAM at runtime. Startup copies from the flash load image to the SRAM run range before those objects are used.
What this tests: image storage versus runtime placement.
Easy Why does the binary not need to store every zero in .bss?
The image records the section’s address and size. Startup creates its required all-zero initial state in SRAM. Omitting explicit zero bytes reduces stored image size while preserving the C initialization contract.
What this tests: section semantics and image footprint.
Medium The map shows 12 KiB of .data and 28 KiB of .bss. At an assumed effective 20 MiB/s, what is the idealized construction time?
The work is 40 KiB. Using binary units, 40 × 1024 / (20 × 1024 × 1024) = 0.001953125 s, about 1.95 ms. Round to roughly 2.0 ms, then measure the real implementation and include margin in the watchdog budget.
What this tests: units, idealized throughput, and measurement discipline.
Medium A retained crash record becomes “valid” after a firmware downgrade. What is missing?
A CRC alone can accept an older but structurally different record. Include a format version and length, validate every field and enum, and define compatibility policy. Clear or migrate incompatible records before application code consumes them.
What this tests: retained-data versioning and semantic validation.
Hard Constructors execute twice after a startup refactor. How do you localize the owner conflict?
Trace from the vector’s reset handler through SystemInit(), custom section loops, the toolchain entry, constructor-array walkers, and main(). Use the link map and disassembly to find both calls. Then choose one documented runtime owner and remove the duplicate path, preserving required ABI setup.
Add a startup test object whose constructor increments a retained or probe-visible counter exactly once. Verify cold boot, warm reset, bootloader handoff, and debug reset.
What this tests: startup call graph, ABI ownership, and regression proof.
04Clock tree
Safe source · voltage · flash latency · bounded waits
Increase performance only after its dependencies are ready.
A robust clock sequence begins on a safe reset clock, prepares voltage and flash timing, starts and verifies sources with deadlines, configures the PLL within its legal range, and confirms the switch. Every wait has an escape path that still lets the product report or recover.
Dependency order
Prepare before switching
Raise regulator performance when required, program flash wait states for the destination frequency, enable the external source, wait with a timeout, program divisors/multipliers, wait for lock, then switch and read back status.
Lowering frequency can require the reverse safety order. Follow the target manual, not a universal recipe.
Calculation
Check every PLL node
For the lab, fVCO = fSRC ÷ M × N and fCPU = fVCO ÷ P. With 8 MHz, M=1, N=20, and P=2, the VCO is 160 MHz and CPU is 80 MHz.
The result is valid only if input, VCO, output, voltage, and flash constraints all pass.
Failure policy
Timeout is a state transition
An oscillator-ready loop without a deadline is a reset-loop generator. On timeout, record the failed gate, select a safe internal clock if allowed, revise peripheral divisors, and continue in a degraded mode or recovery image.
The watchdog budget must exceed the bounded nominal path plus justified margin, while still detecting a genuinely stuck boot.
Interactive lab 3 · clock dependency decision
Choose the next safe action under a clock fault.
Select a startup observation. The lab identifies the gate, calculation, recovery action, and proof point.
Observed gateExternal 8 MHz source is ready; voltage and flash prerequisites are confirmed
Next safe actionProgram M=1, N=20, P=2; wait for PLL lock with a bounded deadline; then switch and read back
ProofRead the selected-source status, measure a divided clock output, and derive a timer period from the measured frequency
Failure avoidedRunning flash above the configured latency or waiting forever for a source that never stabilizes
Checkpoint · 2 easy, 2 medium, 1 hard
Can you bound startup without violating a frequency dependency?
Easy Why set flash wait states before raising the CPU clock?
Flash access time may not meet the shorter CPU cycle at the higher frequency. Preparing latency first keeps instruction fetches within the target’s timing specification during and after the switch.
What this tests: dependency ordering and transient safety.
Easy What is wrong with while (!PLL_LOCKED) {} in boot code?
It has no bound. A missing crystal, invalid configuration, or damaged oscillator can trap every boot and eventually trigger an opaque watchdog reset. Use a time base independent of the unproven clock, record the failed stage, and enter a defined fallback.
What this tests: bounded waits and recovery.
Medium Calculate the ControlNode CPU clock for 8 MHz, M=2, N=24, P=2.
fVCO = 8 MHz ÷ 2 × 24 = 96 MHz. Then fCPU = 96 MHz ÷ 2 = 48 MHz. The arithmetic does not prove legality; check the PLL input and VCO ranges, voltage range, flash latency, and divider encodings.
What this tests: formula use plus constraint awareness.
Medium The crystal fails but the internal oscillator works. What else changes besides CPU speed?
Recalculate every derived peripheral clock and timing contract: UART baud, timer periods, watchdog source, flash programming timing, communication tolerances, and any certified control loop. Publish degraded-clock status so the application can disable features whose accuracy or throughput is no longer valid.
What this tests: clock-tree propagation and degraded-mode design.
Hard Set a watchdog boot budget when crystal startup is specified as 10 ms maximum, PLL lock as 2 ms maximum, runtime construction measures 2.3 ms worst case, and image verification measures 14 ms worst case.
The named maxima total 28.3 ms. Add measured control overhead and a justified environmental/production margin, then choose a representable watchdog window above that bound, for example 40 ms only if target and product analysis support the margin. Service the watchdog at explicit phase boundaries or use one whole-boot window, according to the safety contract.
Do not hide an unbounded operation by feeding the watchdog inside a polling loop. Record phase timing and fail to recovery when a phase exceeds its own deadline.
What this tests: worst-case arithmetic, margin, and meaningful watchdog service.
05Image recovery
Manifest · authenticity · trial · rollback
A valid checksum is not a trusted or bootable image.
A boot decision combines structural bounds, vector validity, cryptographic authenticity, compatibility, version policy, and a power-loss-safe slot state. A/B recovery separates installing an image from proving that the product can run it.
Validation layers
Structure before cryptography
Bounds-check the header and image length before hashing. Confirm that the vector table, reset entry, and every signed region fit inside the slot. Then verify the authenticated manifest and image with a protected trust anchor.
A CRC detects accidental corruption. It does not establish who authorized the image.
Policy
Compatibility and rollback matter
A correctly signed image can still target the wrong board revision or violate a minimum security version. Bind hardware compatibility and monotonic version policy into authenticated metadata.
Rollback protection needs a protected counter or equivalent trust decision. An untrusted flash flag can be rewritten with the image.
Recovery
Trial is not confirmation
The bootloader marks a new slot as trial, starts it with a bounded attempt count, and waits for the application to confirm only after critical self-tests and persistent services succeed. Reset before confirmation consumes an attempt or rejects the trial.
Metadata updates use redundant records, sequence numbers, CRCs, and commit-last semantics to survive power loss.
Interactive lab 4 · A/B boot-slot state machine
Step a candidate image through install, trial, confirmation, or rollback.
Select a failure story, then advance the persistent metadata one transition at a time.
EMPTYVALIDTRIALCONFIRMEDREJECTED
Transition 1 of 4 · candidate validatedSlot B is VALID; slot A remains CONFIRMED
The complete candidate fits its slot, passes structural and vector checks, and its authenticated manifest is accepted. It has not run yet.
Power-loss invariantAt least one previously confirmed image remains selectable throughout the update
Next boot decisionCommit the TRIAL record last, then boot slot B with a bounded attempt counter
Checkpoint · 2 easy, 2 medium, 1 hard
Can you keep one recoverable, authorized image?
Easy What does a CRC prove, and what does it not prove?
A suitable CRC is useful for detecting accidental bit errors against an expected value. It does not prove the identity or authorization of the image author because an attacker can modify both image and CRC. Authenticity requires a cryptographic verification rooted in protected trust material.
What this tests: integrity-detection versus authenticity.
Easy Why keep the previous slot after the new image first boots?
Passing static validation does not prove the new image can initialize hardware, migrate state, communicate, or remain alive. The old confirmed slot provides recovery until the trial image completes defined health checks and commits confirmation.
What this tests: install success versus runtime success.
Medium Power fails halfway through programming slot B. What should the next boot do?
Slot B must remain EMPTY or invalid because its commit metadata was never completed. The bootloader validates metadata records, ignores the incomplete candidate, and boots the still-confirmed slot A. It may resume or restart download later according to policy.
What this tests: commit-last metadata and power-loss invariants.
Medium A signed image has version 18 while the protected minimum is 21. Should it boot?
Not under a policy that enforces minimum security version 21. A valid signature establishes an authorized publisher, but anti-rollback policy independently rejects an older vulnerable version. Recovery exceptions, if any, must be explicit and protected.
What this tests: authenticity versus version policy.
Hard Design confirmation so power loss cannot leave both slots unbootable.
Keep slot A’s CONFIRMED record unchanged while B is VALID or TRIAL. Write B’s new metadata into an inactive redundant record with higher sequence, payload, CRC, and final commit marker. After B meets health criteria, atomically select its committed CONFIRMED record. Retire A only in a later transaction after B is independently recoverable.
On boot, validate both record copies and choose the highest valid sequence allowed by policy. A torn new record is ignored. Define attempt counters and a REJECTED transition without erasing the last confirmed image.
What this tests: redundant metadata, ordering, and whole-update invariants.
06Startup proof
Deadlines · breadcrumbs · recovery
Turn every silent reset loop into bounded evidence.
A production boot path has explicit stages, maximum times, observable proof points, and a defined fallback. The capstone combines reset cause, vector validation, runtime construction, clock gates, image state, and application confirmation into one auditable contract.
Breadcrumbs
Commit the next stage before entering it
Write a compact stage identifier and sequence into validated retained storage before a risky operation. On the next reset, the record identifies the operation that did not complete. Keep writes bounded and compatible with the storage medium.
Measurement
Separate physical and software proof
Probe reset release, an early GPIO point, clock output, application handoff, and confirmation. A debugger breakpoint changes timing and reset behavior, so preserve autonomous trace or retained records for failures that disappear under halt.
Recovery
Detect loops, then change the path
Repeatedly attempting the same known-failing sequence is not recovery. After a bounded number of failures, select the internal clock, confirmed image, safe-mode configuration, or immutable recovery service and publish why.
Synthesis capstone · complete boot contract
ControlNode trusted-startup proof sheet
Each row names an owner, pass condition, deadline, evidence, and fallback. Replace every lab value with measured target data before using this pattern in a product.
Gate
Owner
Pass condition
Lab deadline
Evidence
Fallback
Reset capture
Early boot
Raw flags and retained record copied before clear
0.2 ms
Boot record sequence
Mark evidence invalid, continue conservatively
Image selection
Bootloader
One policy-allowed slot has valid committed metadata
1.0 ms
Slot and metadata sequence
Immutable recovery
Image verification
Bootloader
Bounds, vectors, signature, compatibility, version pass
Whole-boot lab bound0.2 + 1.0 + 14 + 3 + 13 = 31.2 ms before application health monitoring begins
InvariantNo unbounded wait, destructive evidence read, unauthenticated jump, or trial confirmation occurs outside a named owner and recovery transition
BoundaryException priority, masking, ISR response, and DMA-completion mechanics remain in the companion interrupts guide
Checkpoint · 2 easy, 2 medium, 1 hard
Can you defend the complete startup path?
Easy What makes a useful boot breadcrumb?
It is written before entering a risky stage, survives the relevant reset, has a version and integrity check, and maps to one unambiguous operation. “Boot failed” is weak; “entered PLL-lock wait, sequence 42” narrows the next experiment.
What this tests: observability with minimal early dependencies.
Easy Why can a debugger make a reset-loop bug disappear?
Halting may freeze watchdogs, extend oscillator startup time, alter boot straps or reset type, preserve debug-domain state, and change timing. Reproduce autonomous power-on behavior with external probes and retained records before relying on a halted snapshot.
What this tests: measurement intrusion and reset conditions.
Medium The retained stage says “VERIFY_IMAGE,” but verification normally takes only 14 ms and the watchdog is 40 ms. What do you inspect next?
Confirm the watchdog’s actual clock and window, then instrument verification sub-stages: header bounds, hash progress, signature operation, trust-store access, and flash reads. Check whether an input length can exceed the slot, whether the crypto accelerator clock is enabled, and whether the measured path differs from the assumed image.
What this tests: turning a breadcrumb into discriminating measurements.
Medium How do you detect a boot loop without counting normal user resets as failures?
Combine reset cause, last committed boot stage, image/slot identity, and an application confirmation marker. Increment a bounded failure count only when reset occurs before the expected stage completes or while a trial remains unconfirmed. Clear or decay it after a confirmed healthy interval according to policy.
What this tests: classification and persistent state transitions.
Hard A production unit intermittently resets between clock switch and application confirmation. Give a highest-information experiment plan.
First preserve raw reset flags, stage sequence, selected slot, clock status, and watchdog timing in the boot record. Probe reset, a divided clock output, supply rail, and stage GPIO without halting. Compare cold/warm reset, internal/external clock, confirmed/recovery image, and minimum/maximum environmental conditions one variable at a time.
Rank hypotheses from evidence: rail droop, oscillator/PLL loss, invalid flash timing, watchdog expiry, runtime corruption, or application failure. Choose the cheapest measurement that separates the top candidates. After the fix, add an automated power-cycle and clock-fault regression that asserts bounded recovery and a valid final boot record.
What this tests: integrated evidence, hypothesis ranking, and regression closure.
Primary references
Verify architecture, toolchain, target, and update policy separately.