--- title: Bare-metal ATSAM3X8E (Arduino Due) date: 2024-09-16 layout: post --- Three on-chip memories: ROM, flash0, flash1. ARM Cortex-M chips boot into 0x00000. GPNVM bits map one of them to 0x00000: - GPNVM1=0 → ROM (default). - GPNVM1=1 and GPNVM2=0 → flash0. - GPNVM1=1 and GPNVM2=1 → flash1. On power-up, control jumps to ROM. Without a user program, Atmel's SAM-BA bootloader traps execution in the ROM. Must remap memory via SWD to force chip to boot from flash, bypassing factory bootloader. Toolchain: ST-LINK/V2 programmer, OpenOCD, ARM GNU Compiler Toolchain. Connect ST-LINK/v2 to Arduino Due's DEBUG port:
Pinout

Wiring

Circuit

Arduino Due

Remap memory using OpenOCD: ``` $ openocd -f openocd-due.cfg $ telnet localhost 4444 > halt > at91sam3 gpnvm show > at91sam3 gpnvm set 1 > at91sam3 gpnvm show ``` Full command list in OpenOCD manual AT91SAM3 (flash driver section). Initialize vector table with the stack pointer and the reset vector: ``` __attribute__((noreturn)) void _reset(void) { unsigned long *dst, *src; extern unsigned long _sbss, _ebss; extern unsigned long _sdata, _edata, _sidata; for (dst = &_sbss; dst < &_ebss; dst++) *dst = 0; for (dst = &_sdata, src = &_sidata; dst < &_edata;) *dst++ = *src++; main(); } extern const unsigned int _sp; __attribute__ ((section(".vtor"))) const void* _tab[] = { &_sp, _reset }; ``` Linker script: Place the vector table at the start of flash0 and initialize the stack pointer to the top of the RAM: ``` MEMORY { rom (rx) : ORIGIN = 0x00080000, LENGTH = 512K ram (rwx) : ORIGIN = 0x20000000, LENGTH = 96K } SECTIONS { .text : { KEEP(*(.vtor)) *(.text*) *(.rodata*) } > rom } _sp = ORIGIN(ram) + LENGTH(ram); ``` Build and upload the program: ``` $ arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -T script.ld \ -nostartfiles \ -nostdlib \ -o a.elf main.c $ openocd -f openocd-due.cfg -c "program a.elf verify reset exit" ``` Commit: 3184969.