Skip to content

Memory Layout

Understanding the ARM Cortex-M memory map is essential for working with mcjtag safely. The memory map explains which addresses are safe to read and write, and why mcjtag restricts write operations to SRAM by default.

ARM defines a fixed memory map for all Cortex-M processors. The 4 GB address space is divided into regions with specific purposes:

RegionAddress RangePurpose
Code0x000000000x1FFFFFFFFlash memory, vector table, boot ROM
SRAM0x200000000x3FFFFFFFRAM — stack, heap, global variables
Peripherals0x400000000x5FFFFFFFMemory-mapped I/O registers
External RAM0x600000000x9FFFFFFFExternal memory interfaces (FSMC, FMC)
External Device0xA00000000xDFFFFFFFExternal peripheral bus
System0xE00000000xFFFFFFFFDebug, SysTick, NVIC, CoreSight

Not all of this space is populated on any given chip. An STM32F103C8 (Blue Pill), for example, has:

  • 64 KB flash at 0x080000000x0800FFFF
  • 20 KB SRAM at 0x200000000x20004FFF
  • Peripherals at 0x40000000+

Reading from an unpopulated address typically returns a bus fault. Writing to an unpopulated address does the same.


Flash is non-volatile storage where firmware lives. On STM32, flash is mapped at 0x08000000 and aliased to 0x00000000 at boot.

Flash has important properties that make it dangerous to write carelessly:

  • Erase-before-write: Flash cells must be erased (set to 0xFF) before they can be programmed. Writing without erasing produces corrupted data.
  • Sector granularity: Erase operations work on entire sectors (typically 1—128 KB), not individual bytes.
  • Limited endurance: Flash cells wear out after 10,000—100,000 erase cycles.
  • Bricking risk: Corrupting the vector table or bootloader can make the chip unbootable without a full erase.

For these reasons, mcjtag provides flash_program as the only sanctioned way to write flash. It handles erase, write, and verify as an atomic operation.


SRAM occupies 0x200000000x3FFFFFFF in the memory map. On a real chip, only a fraction of this range is populated (e.g., 20 KB on STM32F103, 128 KB on STM32F407).

SRAM is the default safe write zone because:

  • Volatile: Contents are lost on power cycle. The worst case for a bad write is corrupted variables that disappear on reset.
  • No erase required: Any address can be written at any time without a preceding erase step.
  • No wear: SRAM can be written an unlimited number of times.
  • No bricking risk: Corrupted SRAM cannot prevent the chip from booting.

mcjtag defaults MCJTAG_SAFE_WRITE_RANGES to 0x20000000-0x20100000 (1 MB of address space), which covers the SRAM of virtually all Cortex-M chips.

Some chips have additional RAM regions:

Terminal window
# STM32F4: Main SRAM + CCM RAM (core-coupled memory)
export MCJTAG_SAFE_WRITE_RANGES="0x20000000-0x20020000,0x10000000-0x10010000"
# STM32H7: Multiple SRAM banks
export MCJTAG_SAFE_WRITE_RANGES="0x20000000-0x20020000,0x24000000-0x24080000,0x30000000-0x30048000"
# With external SDRAM (FMC)
export MCJTAG_SAFE_WRITE_RANGES="0x20000000-0x20100000,0xD0000000-0xD1000000"

For advanced use (register poking, peripheral testing, etc.), write restrictions can be disabled entirely:

Terminal window
export MCJTAG_SAFE_WRITE_RANGES="none"

This allows writing to any address, including peripheral registers and system space. Use with extreme caution on safety-critical hardware.


The peripheral region (0x400000000x5FFFFFFF) contains memory-mapped I/O registers. Writing to a peripheral register does not store data — it changes hardware behavior.

Examples of what peripheral writes can do:

Write targetEffect
GPIO output registerToggle physical pins (could drive motors, relays, LEDs)
USART data registerTransmit a byte over serial
Timer control registerStart or stop a hardware timer
ADC control registerBegin an analog-to-digital conversion
RCC clock registerChange the system clock (can crash firmware)
Flash control registerErase or program flash sectors
Watchdog registerReset the system

Because peripheral writes have real-world consequences, they are excluded from the default safe write ranges. Use svd_inspect to read and understand peripheral registers before deciding to modify them.


The system region (0xE00000000xFFFFFFFF) contains ARM’s CoreSight debug infrastructure:

AddressBlockPurpose
0xE000E000System Control Block (SCB)Fault status, vector table offset, system reset
0xE000E010SysTickSystem tick timer
0xE000E100NVICNested Vectored Interrupt Controller
0xE000ED00CPUIDProcessor identification (read by probe_diagnostics)
0xE000EDF0Debug ControlHalt, step, breakpoint control
0xE0040000TPIUTrace output
0xE0041000ETMEmbedded Trace Macrocell

Writing to debug control registers can lock out the debugger itself, requiring a power cycle to recover. These addresses are excluded from default write ranges.


The vector table is the first structure in flash, at the base of the code region. On STM32, this is at 0x08000000 (aliased to 0x00000000).

OffsetWordContent
0x000Initial stack pointer (top of SRAM)
0x041Reset handler address
0x082NMI handler
0x0C3HardFault handler
0x104MemManage handler
0x145BusFault handler
0x186UsageFault handler
0x1C0x287—10Reserved
0x2C11SVCall handler
0x3012Debug monitor
0x3413Reserved
0x3814PendSV handler
0x3C15SysTick handler
0x40+16+IRQ handlers (device-specific)

The vector table is a goldmine of information:

  • Word 0 (initial SP) tells you where SRAM ends. If it reads 0x20005000, the chip has 20 KB of SRAM (0x200000000x20004FFF, SP points one past the end).
  • Word 1 (reset vector) tells you where code execution begins. This is typically the start of the firmware’s main() or startup code.
  • Fault handler addresses reveal whether the firmware has custom fault handlers or uses the default infinite-loop handler.

Read the vector table with:

# Read first 8 vectors (32 bytes)
read_memory(address="0x08000000", count=8, width=32)

The memory_map prompt automates this analysis. See Prompts for the full workflow.


Reading memory through read_memory is non-destructive for most addresses. The exceptions are hardware registers with read-clear semantics — reading them changes their state. Common examples:

  • USART status registers (reading clears flags)
  • Interrupt pending registers
  • DMA transfer complete flags

mcjtag builds hexdumps from already-read values (it does not re-read for the formatted view) specifically to avoid double-reading registers with side effects.

Before writing to any address outside SRAM, consider:

  1. What lives at this address? Use svd_inspect to decode it.
  2. What will the write do? Peripheral writes change hardware state.
  3. Is the firmware expecting this? Firmware may be actively using the peripheral.
  4. Can you recover? Flash writes can brick the device. Peripheral writes may require a reset.

The safe write range default is deliberately conservative. Expand it only for addresses you understand.