summaryrefslogtreecommitdiffstats
path: root/_archive/arduino-due.md
blob: 055137686f7185e3baf52a987b431bb55ae8478f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
---
title: Bare-metal ARM Cortex M3 chips
date: 2024-10-05
author: W. D. Sadeep Madurange
layout: post
---

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.

- <a href="https://developer.arm.com/Tools%20and%20Software/GNU%20Toolchain"
  class="external" target="_blank" rel="noopener noreferrer">Arm GNU compiler 
  toolchain</a>.
- <a href="https://openocd.org/" class="external" target="_blank"
  rel="noopener noreferrer">OpenOCD</a> on-chip debugger.
- <a href="https://www.st.com/en/development-tools/st-link-v2.html"
  class="external" target="_blank" rel="noopener noreferrer">ST-LINK/V2</a>
  programmer.

## 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.

<table style="border: none; width: 100%;">
  <tr style="border: none;">
    <td style="border: none; width: 50%; vertical-align: top; background-color: transparent;">
      <img src="schematic.png" alt="Pinout" style="width: 100%">
      <p style="text-align: center;">Wiring</p>
    </td>
    <td style="border: none; width: 50%; vertical-align: top; background-color: transparent;">
      <img src="connections.jpeg" alt="Circuit" style="width: 100%">
      <p style="text-align: center;">Arduino Due</p>
    </td>
  </tr>
</table>

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 check that the GPNVM1 bit is set. Otherwise
     set it 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](source.tar.gz)