BARE-METAL ATSAM3X8E CHIPS
05 OCTOBER 2024
This article is a step-by-step guide for programming bare-metal ATSAM3X8E chips
found on Arduino Due boards. It also includes notes on the chip’s memory layout
relevant for writing linker scripts. The steps described in this article were
tested on an OpenBSD workstation.
Toolchain
To interact directly with a bare-metal ATSAM3X8E chips, we must bypass the
embedded bootloader. To do that, we need a hardware programmer capable of
communicating with the chip over the Serial Wire Debug (SWD) protocol. Since
the workstation we upload the program from presumably doesn’t speak SWD, the
hardware programmer acts as a SWD-USB adapter. The ST-LINK/V2 programmer fits this
bill.
The OpenOCD on-chip debugger software supports
ATSAM3X8E chips. OpenOCD, on startup, runs a telnet server that we can connect to
to issue commands to the ATSAM3X8E chip. OpenOCD translates plain-text commands
into the binary sequences the chip understands, and sends them over the wire.
Finally, we need the ARM GNU Compiler
Toolchain to compile C programs for the chip. The ARM GNU compiler
toolchain and OpenOCD, as a consequence of being free software, are available
on every conceivable platform, including OpenBSD.
Electrical connections
The following photos illustrate the electrical connections between the Arduino
Due, PC, and the ST-LINK/V2 programmer required to transfer a compiled program
from a PC to the MCU.
Wiring
Arduino Due
Arduino Due exposes the ATSAM3X8E’s SWD interface via its DEBUG port. The
ST-LINK/v2 programmer connects to that to communicate with the chip.
Uploading the program
The source.tar.gz tarball at the end of this page contains a sample C program
(the classic LED blink program) with OpenOCD configuration and linker scripts.
First, use the following command to build it:
$ arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -T script.ld \
-nostartfiles \
-nostdlib \
-o a.elf main.c
Then, open a telnet session with OpenOCD and issue the following sequence of
commands to configure the chip and upload the compiled program to it:
$ openocd -f openocd-due.cfg
$ telnet localhost 4444
> halt
> at91sam3 gpnvm show
> at91sam3 gpnvm set 1
> at91sam3 gpnvm show
$ openocd -f openocd-due.cfg -c "program a.elf verify reset exit"
The first of the above commands starts OpenOCD. In the telnet session, the
first command halts the chip in preparation for receiving commands. Next, we
inspect the current GPNVM bit setting (more on this later). If the bit is unset
(the gpnvm show command returns 0), we set it to 1 and verify the update.
The final command, issued from outside the telnet session, uploads the program
to the chip. Those are the bare minimum set of commands required to program the
chip. The AT91SAM3 flash driver section of the OpenOCD manual lists all
available commands for the ATSAM3X8E chip.
GPNVM bits
By design, ARM chips boot into address 0x00000. ATSAM3X8E’s memory consists of
a ROM and a dual-banked flash (flash0 and flash1), residing in different
locations of the chip’s address space. The GPNVM bits control which of them
maps to 0x00000. When GPNVM1 is cleared (the default), the chip boots from the ROM,
which contains Atmel’s SAM-BA bootloader.
Conversely, when the GPNVM1 bit is 1 (and the GPNVM2 bit is 0), flash0 at
address 0x80000 maps to 0x00000. When both GPNVM bits are 0, flash1 maps to
0x00000. Since we place our program in flash0 in the linker script, we set the
GPNVM1 bit and leave the GPNVM2 bit unchanged to ensure the chip
executes our program instead of the embedded bootloader at startup.
Linker script
At a minimum, the linker script must place the vector table at the first
address of the flash. This is mandatory for ARM chips unless we relocate the
vector table using the VTOR register.
The first entry of the vector table must be the stack pointer. The stack
pointer must be initializes to the highest memory location available to
accommodate the ATSAM3X8E’s descending stack.
The second entry of the vector table must be the reset vector. In the reset
vector, we can perform tasks such as zeroing out memory and initializing
registers before passing control to the main program.
Files: source.tar.gz
This article is a step-by-step guide for programming bare-metal ATSAM3X8E chips found on Arduino Due boards. It also includes notes on the chip’s memory layout relevant for writing linker scripts. The steps described in this article were tested on an OpenBSD workstation.
Toolchain
To interact directly with a bare-metal ATSAM3X8E chips, we must bypass the embedded bootloader. To do that, we need a hardware programmer capable of communicating with the chip over the Serial Wire Debug (SWD) protocol. Since the workstation we upload the program from presumably doesn’t speak SWD, the hardware programmer acts as a SWD-USB adapter. The ST-LINK/V2 programmer fits this bill.
The OpenOCD on-chip debugger software supports ATSAM3X8E chips. OpenOCD, on startup, runs a telnet server that we can connect to to issue commands to the ATSAM3X8E chip. OpenOCD translates plain-text commands into the binary sequences the chip understands, and sends them over the wire.
Finally, we need the ARM GNU Compiler Toolchain to compile C programs for the chip. The ARM GNU compiler toolchain and OpenOCD, as a consequence of being free software, are available on every conceivable platform, including OpenBSD.
Electrical connections
The following photos illustrate the electrical connections between the Arduino Due, PC, and the ST-LINK/V2 programmer required to transfer a compiled program from a PC to the MCU.
Wiring |
Arduino Due |
Arduino Due exposes the ATSAM3X8E’s SWD interface via its DEBUG port. The ST-LINK/v2 programmer connects to that to communicate with the chip.
Uploading the program
The source.tar.gz tarball at the end of this page contains a sample C program (the classic LED blink program) with OpenOCD configuration and linker scripts. First, use the following command to build it:
$ arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -T script.ld \
-nostartfiles \
-nostdlib \
-o a.elf main.c
Then, open a telnet session with OpenOCD and issue the following sequence of commands to configure the chip and upload the compiled program to it:
$ openocd -f openocd-due.cfg
$ telnet localhost 4444
> halt
> at91sam3 gpnvm show
> at91sam3 gpnvm set 1
> at91sam3 gpnvm show
$ openocd -f openocd-due.cfg -c "program a.elf verify reset exit"
The first of the above commands starts OpenOCD. In the telnet session, the first command halts the chip in preparation for receiving commands. Next, we inspect the current GPNVM bit setting (more on this later). If the bit is unset (the gpnvm show command returns 0), we set it to 1 and verify the update.
The final command, issued from outside the telnet session, uploads the program to the chip. Those are the bare minimum set of commands required to program the chip. The AT91SAM3 flash driver section of the OpenOCD manual lists all available commands for the ATSAM3X8E chip.
GPNVM bits
By design, ARM chips boot into address 0x00000. ATSAM3X8E’s memory consists of a ROM and a dual-banked flash (flash0 and flash1), residing in different locations of the chip’s address space. The GPNVM bits control which of them maps to 0x00000. When GPNVM1 is cleared (the default), the chip boots from the ROM, which contains Atmel’s SAM-BA bootloader.
Conversely, when the GPNVM1 bit is 1 (and the GPNVM2 bit is 0), flash0 at address 0x80000 maps to 0x00000. When both GPNVM bits are 0, flash1 maps to 0x00000. Since we place our program in flash0 in the linker script, we set the GPNVM1 bit and leave the GPNVM2 bit unchanged to ensure the chip executes our program instead of the embedded bootloader at startup.
Linker script
At a minimum, the linker script must place the vector table at the first address of the flash. This is mandatory for ARM chips unless we relocate the vector table using the VTOR register.
The first entry of the vector table must be the stack pointer. The stack pointer must be initializes to the highest memory location available to accommodate the ATSAM3X8E’s descending stack.
The second entry of the vector table must be the reset vector. In the reset vector, we can perform tasks such as zeroing out memory and initializing registers before passing control to the main program.
Files: source.tar.gz