BARE-METAL ARM CORTEX M3 CHIPS

05 OCTOBER 2024

This post is about programming bare metal SAM3X8E Arm Cortex M3 chips found on Arduino Due boards. I had to learn how to do this because none of the high-level tools for programming Arduino Dues are available for OpenBSD, which I use for much of my personal computing.

Toolchain

Since we will not be using pre-packaged development tools, we need to assemble our own toolchain. As usual, we need a compiler toolchain to build programs for the target chip. As we will be bypassing the embedded bootloader, we will also need a hardware programmer and an on-chip debugger to flash programs to the chip. I used the following toolchain.

Electrical connections

The following diagram outlines the electrical connections between the different components necessary to move a compiled program from a PC to the MCU.

Pinout

Wiring

Circuit

Arduino Due

Arduino Due exposes the SAM3X8E’s Serial Wire Debug (SWD) interface via its DEBUG port. The ST-LINK/v2 programmer uses the SWD protocol to communicate with the chip.

Uploading the program

Follow the steps below to upload a program to the SAM3X8E chip. The source.tar.gz tarball at the end of the page contains a sample program with a OpenOCD config file and a linker script.

  1. Start OpenOCD:
    $ openocd -f openocd-due.cfg
    
  2. Open a telnet session and set the GPNVM1 bit to 1:
    $ telnet localhost 4444
      > halt
      > at91sam3 gpnvm show
      > at91sam3 gpnvm set 1
      > at91sam3 gpnvm show
    
  3. Build the program using the custom linker script.
    $ arm-none-eabi-gcc -mcpu=cortex-m3 -mthumb -T script.ld \
        -nostartfiles \
        -nostdlib \
        -o a.elf main.c
    
  4. Upload the program using OpenOCD:
    $ openocd -f openocd-due.cfg -c "program a.elf verify reset exit"
    

Refer to the OpenOCD manual (AT91SAM3 flash driver section) for a complete list of commands supported for the ATSAM3X8E.

GPNVM bits and the linker script

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 (default), the chip boots from the ROM, which contains Atmel’s SAM-BA bootloader. So, the chip runs the embedded bootloader instead of our program.

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 using the linker script, we set the GPNVM1 bit and leave the GPNVM2 bit as it is.

The linker script places the vector table at the first address of the flash. ARM chips expect this unless we relocate the vector table using the VTOR register. The first entry of the vector table must be the stack pointer, and the second must be the reset vector.

Finally, the ATSAM3X8E uses a descending stack. So, in the linker script, we initialize the stack pointer to the highest memory location available. In the reset vector, we zero out memory, initialize registers, and perform other tasks before passing control to the main program.

Files: source.tar.gz