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.

Pinout

Wiring

Circuit

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