baremetal-startup
Writes startup code that runs before an operating system, setting up memory and the processor from scratch.
Installation
Paste this into Claude Code, Cursor, or any agent that can run commands.
SKILL.mdShow the author's original SKILL.md
---
name: baremetal-startup
description: Bare-metal startup skill for reset-to-main bring-up. Use when writing startup code, vector tables, .data/.bss init, stack setup, or crt0 for Cortex-M/RISC-V. Activates on queries about reset vector, VTOR, startup.s, bss init, or bare-metal entry point.
---
# Bare-Metal Startup
## Purpose
Guide agents through bare-metal startup from reset to `main()`: reset and exception vectors, `.data`/`.bss` initialization, stack and heap setup, C runtime integration with linker scripts, and common Cortex-M and RISC-V bring-up bugs.
## When to Use
- Firmware hangs before reaching `main()`
- Porting to a new MCU without vendor HAL
- Writing custom `startup.s` or `Reset_Handler`
- Integrating with `skills/embedded/linker-scripts`
- Debugging uninitialized globals or stack overflows at boot
## Workflow
### 1. Boot sequence mental model
```
Power-on / reset
├── CPU loads SP from vector[0]
├── CPU branches to Reset_Handler from vector[1]
├── Reset_Handler: init clocks (optional), copy .data, zero .bss
├── Configure stack pointer (if not already from vector)
├── Call SystemInit() / platform_init()
└── Call main() — must not return (loop or WFI)
```
### 2. Cortex-M vector table
On Cortex-M, the vector table starts at address 0 or a relocatable base (VTOR):
| Index | Entry |
|-------|-------|
| 0 | Initial stack pointer (`_estack`) |
| 1 | `Reset_Handler` |
| 2 | `NMI_Handler` |
| 3 | `HardFault_Handler` |
| 4+ | IRQ handlers per NVIC |
```asm
/* startup.s — minimal Cortex-M */
.syntax unified
.thumb
.section .isr_vector,"a",%progbits
.global g_pfnVectors
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
/* ... device IRQ vectors from vendor CMSIS ... */
.section .text.Reset_Handler
.thumb_func
.global Reset_Handler
Reset_Handler:
ldr r0, =_sidata /* .data load address (LMA) */
ldr r1, =_sdata /* .data VMA start */
ldr r2, =_edata
b LoopCopyDataInit
CopyDataInit:
ldr r3, [r0], #4
str r3, [r1], #4
LoopCopyDataInit:
cmp r1, r2
blt CopyDataInit
ldr r2, =_sbss
ldr r4, =_ebss
movs r3, #0
b LoopFillZerobss
FillZerobss:
str r3, [r2], #4
LoopFillZerobss:
cmp r2, r4
blt FillZerobss
bl SystemInit
bl main
b .
```
```c
/* Linker symbols — declared extern in startup */
extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss;
```
### 3. VTOR relocation (Cortex-M)
```c
#include "core_cm4.h" /* CMSIS */
void relocate_vector_table(uint32_t base) {
SCB->VTOR = base & 0xFFFFFF80U; /* must be aligned per ARM ref manual */
__DSB();
__ISB();
}
```
Bootloader jumping to app often requires setting VTOR to the application's vector table address (e.g., `0x08020000` on STM32 flash).
### 4. RISC-V startup (overview)
```asm
/* entry.S — simplified */
.section .text.entry
.global _start
_start:
la sp, _stack_top
la t0, _bss_start
la t1, _bss_end
clear_bss:
beq t0, t1, bss_done
sw zero, 0(t0)
addi t0, t0, 4
j clear_bss
bss_done:
call main
j .
```
Trap vector: `mtvec` for M-mode; see `skills/platform/riscv-privileged`.
### 5. crt0 responsibilities
| Task | Who does it |
|------|-------------|
| Copy `.data` LMA→VMA | `Reset_Handler` / crt0 |
| Zero `.bss` | `Reset_Handler` |
| Init stack | Vector[0] or explicit `sp` |
| Heap (`_sbrk`) | Optional; libc or custom |
| C++ constructors | `__libc_init_array()` if using full libc |
| FPU enable | `SystemInit` on Cortex-M4F/M7 |
### 6. Linker script integration
```ld
_estack = ORIGIN(RAM) + LENGTH(RAM);
_sidata = LOADADDR(.data);
```
Cross-link `skills/embedded/linker-scripts` for MEMORY/SECTIONS layout.
### 7. Agent usage examples
```
/baremetal-startup My STM32 never reaches main — what should Reset_Handler do?
/baremetal-startup How do I relocate the vector table after bootloader jump?
```
## Common Problems
| Symptom | Cause | Fix |
|---------|-------|-----|
| HardFault immediately | Stack pointer invalid | Set `_estack` to valid RAM top |
| Globals wrong at boot | `.data` not copied | Copy LMA→VMA in Reset_Handler |
| BSS non-zero | `.bss` not zeroed | Add bss clear loop |
| IRQs don't fire | VTOR points to wrong table | `SCB->VTOR = app_vector_addr` |
| `main` returns into garbage | No loop after main | `while(1)` or `WFI` loop |
| C++ static init crash | Constructors before clocks | Call `SystemInit` first |
## Related Skills
- `skills/embedded/linker-scripts` — `_estack`, LMA/VMA, section placement
- `skills/baremetal/interrupts-and-exceptions-baremetal` — vector table IRQ entries
- `skills/baremetal/bootloaders-embedded` — VTOR relocation on app jump
- `skills/embedded/openocd-jtag` — halt at `Reset_Handler` with GDB
- `skills/low-level-programming/assembly-arm` — Thumb-2 syntax
- `skills/platform/riscv-privileged` — RISC-V trap setupMirrored from the author's public source. Install counts from the open skills registry.