The very first code a chip ever runs lives in immutable silicon. Step through a CPU booting from ROM — instruction by instruction — then see the real ARM vector table and the secure-boot chain of trust.
When a chip powers on, everything is garbage — registers, RAM, no operating system. A CPU can do exactly one thing: fetch the instruction at an address and execute it. So designers hard-wire one question into the silicon: "At reset, which address do I fetch my first instruction from?"
That fixed address is the reset vector, and the code there is the Boot ROM — a small, read-only, immutable memory (a mask ROM patterned into the chip at the foundry). It's the first code that ever runs, and because it can't be changed, it's the trusted starting point for the whole system.
Here's a toy 8-bit CPU. At reset, the silicon forces PC = 0x00 — that's the reset vector. The Boot ROM at 0x00 sets up the stack, prints two characters over UART, then jumps to RAM (the "loaded app"). Press Step to advance one instruction, or Run to watch it boot.
| Addr | Bytes | Instruction | Meaning |
|---|
PC = 0x00 and fetches its first instruction from the Boot ROM.In a real chip, the last step wouldn't just print “HI” — it would copy a bootloader from flash/SD into RAM and jump to it. Same idea, bigger payload.
A Cortex-M makes this even cleaner. At address 0x0000_0000 it doesn't place an instruction — it places a vector table. The hardware automatically loads the first two entries on reset, so the stack and entry point are set before a single line of your code runs:
So on a Cortex-M the boot sequence's first two steps are literally done by the silicon: SP ← [0x0], then PC ← [0x4] and off it runs.
A real SoC boot-ROM reset handler then does roughly this:
void Reset_Handler(void) { init_clocks(); // PLL, set core frequency copy_data_section(); // init .data from flash → RAM zero_bss(); // clear .bss sec = read_boot_pins(); // boot source? SD / eMMC / UART load_next_stage(sec, 0x20000000); // DMA bootloader into RAM verify_signature(0x20000000); // secure boot: is it authentic? jump(0x20000000); // hand control to the bootloader }
Each stage is bigger and more capable than the last — but only the Boot ROM is immutable, so the chain of trust is anchored there:
Silicon, immutable, trusted root. Verifies stage 1.
Flash → RAM (e.g. U-Boot). Verifies the kernel.
Linux or an RTOS, running in RAM.
Your software.
Why ROM, not flash or RAM, for stage 0? It's immutable (can't be corrupted → safe root of trust), it's guaranteed present at reset (flash and DRAM controllers aren't initialised yet), and mask ROM is cheap and dense in silicon — one transistor presence/absence per bit. Secure boot begins here: the ROM refuses to run a bootloader that isn't cryptographically signed.
A small, read-only, immutable mask ROM holding the first code a CPU runs after reset. It sets up the chip, loads the next-stage bootloader into RAM, and jumps to it.
The fixed, silicon-wired address the CPU fetches its first instruction from at power-on. On many CPUs it's address 0; on Cortex-M the CPU loads SP from 0x0 and PC from 0x4.
It's the root of trust — it can't be corrupted, it's always present before flash/DRAM are ready, and it anchors secure boot by verifying the next stage's signature.
Boot ROM → bootloader → kernel → apps, where each stage authenticates the next, all anchored to the immutable ROM.
Related: Cache Simulator · FSM Designer · Digital Electronics · Transistor Evolution