In this lesson:
- what a memory map is and why it's fixed by the silicon
- the canonical Cortex-M address layout (flash, SRAM, peripherals, system)
- how stack, heap, and static data share the SRAM
- why a stack/heap collision is silent and deadly without an MMU
- the boot alias at address 0 and accessing unmapped memory
This is the MCU-specific view of the layout sketched in the C stack-vs-heap lesson.
What a memory map is
The CPU has one flat address space (4 GB on a 32-bit Cortex-M: 0x0000_0000-0xFFFF_FFFF). The memory map is the fixed assignment of which address range hits what: this band is flash, that band is SRAM, that band is peripheral registers. It's defined by the chip designer and burned into the silicon, your linker script must match it or nothing runs.
The canonical Cortex-M map
ARM standardizes the broad regions; vendors place their flash/SRAM/peripherals within them. A typical STM32 layout:
0xFFFF_FFFF βββββββββββββββββββββββββββββββ
β System (PPB): NVIC, SysTick,β 0xE000_0000 β core peripherals
β SCB β the "private periph" β
βββββββββββββββββββββββββββββββ€
β Peripherals: GPIO, UART, β 0x4000_0000 β memory-mapped registers
β timers, ADC, ... (APB/AHB) β
βββββββββββββββββββββββββββββββ€
β SRAM (data, stack, heap) β 0x2000_0000
βββββββββββββββββββββββββββββββ€
β Flash (program + const) β 0x0800_0000 β STM32 flash base
βββββββββββββββββββββββββββββββ€
β Boot alias (β flash/sys/RAM β 0x0000_0000 β what the CPU sees at reset
β per BOOT pins) β
0x0000_0000 βββββββββββββββββββββββββββββββThe four bands you care about:
| Region | Typical base | Holds |
|---|---|---|
| Flash | 0x0800_0000 |
program code, const, the initial vector table |
| SRAM | 0x2000_0000 |
.data, .bss, heap, stack |
| Peripherals | 0x4000_0000 |
memory-mapped registers (GPIO, timers, β¦) |
| System (PPB) | 0xE000_0000 |
NVIC, SysTick, SCB, inside the core |
(Exact bases are vendor-specific, always check your chip's reference manual. The bands are ARM-standard; the numbers within differ.)
How SRAM is divided
The SRAM holds several things that the linker lays out from the bottom up, with the stack at the very top growing down toward them:
high addr ββββββββββββββββββββββββ β top of SRAM (initial stack pointer)
β stack β grows DOWN β (locals, call frames)
β β β
β βΌ β
β (free SRAM gap) β β the danger zone: stack and heap
β β² β grow toward each other here
β β β
β heap β grows UP β (malloc, if used)
ββββββββββββββββββββββββ€
β .bss (zero-init) β uninitialized globals/statics
ββββββββββββββββββββββββ€
β .data (init values) β initialized globals/statics
low addr ββββββββββββββββββββββββ β 0x2000_0000.data, initialized globals/statics. Their initial values live in flash and are copied into SRAM at boot (boot lesson)..bss, globals/statics that start at zero; the startup code zeroes this region.- heap, grows up from above
.bssif you usemalloc(often avoided, see the allocation lessons). - stack, starts at the top of SRAM and grows down. The initial stack pointer value is the very first word of the vector table.
The collision danger: no MMU to referee
On a desktop, the MMU and guard pages catch a stack that grows too far. A typical Cortex-M MCU has no MMU, nothing referees the gap. If the stack grows down far enough to meet the heap (or .bss) growing up, they silently overwrite each other. The symptom is corrupted variables or a wild crash far from the actual cause (the stack-overflow point from the C module, made concrete here).
Defenses: size the stack from worst-case call depth + ISR nesting, avoid deep recursion and large local arrays, paint SRAM with a known pattern at boot to measure the stack high-water mark, and optionally use the MPU (memory protection unit) to mark a guard region that faults on access.
The boot alias at address 0, and unmapped memory
At reset the Cortex-M fetches two words from address 0x0000_0000: the initial stack pointer and the initial program counter (reset handler). On many MCUs 0x0000_0000 is an alias that the BOOT configuration pins map to flash, system memory (the built-in bootloader), or SRAM, which is how you select "boot from flash" vs "boot from the ROM bootloader." Normally it aliases flash, so the vector table at 0x0800_0000 is what the core reads.
Accessing an address in an unmapped gap (no memory or peripheral there) triggers a BusFault/HardFault, the MCU's version of a segfault. A null-pointer write (*(int*)0 = 1) often lands in the flash/alias region and faults or silently misbehaves, which is why null derefs are dangerous but not always a clean crash on bare metal.
Gotchas
- Flash base is vendor-specific. STM32 flash is at
0x0800_0000; other families differ. Your linker script'sORIGINmust match the silicon, or the image won't boot. - Stack/heap collision is silent. No MMU means no fault at the moment of overflow, just corrupted neighbors and a delayed, confusing failure. Size the stack deliberately and monitor high-water marks.
.datais copied,.bssis zeroed at boot. A global isn't magically initialized, startup code does it (boot lesson). Reading a global beforemain(e.g., from a constructor) can see uninitialized memory.- Unmapped access faults. Reading/writing an address with nothing behind it raises a BusFault. Stray pointers and bad peripheral base addresses manifest as HardFaults, not silent reads.
- The first vector-table entry is the stack pointer, not code. It's a data word (initial SP), followed by the reset-handler address. Mislaying the vector table breaks boot immediately.
TL;DR
- A memory map is the silicon-fixed assignment of address ranges to flash, SRAM, peripherals, and system blocks; your linker script must match it.
- Cortex-M canonical bands: flash (~
0x0800_0000, code/const), SRAM (0x2000_0000, data/stack/heap), peripherals (0x4000_0000, registers), system/PPB (0xE000_0000, NVIC/SysTick/SCB). - SRAM holds
.data(copied from flash at boot),.bss(zeroed at boot), the heap (grows up), and the stack (grows down from the top). - With no MMU, a stack/heap collision silently corrupts memory, size the stack carefully, avoid deep recursion/big locals, and consider an MPU guard region.
- At reset the core reads the initial stack pointer and reset vector from address 0 (often an alias to flash set by BOOT pins); accessing unmapped addresses raises a HardFault.