Debugging the SAMD21 with GDB

The SAMD21 is a microcontroller developed by Atmel which runs at 48mhz with a Cortex M0+ core. It’s used in the Arduino Zero, Arduino M0 Pro and Feather M0s. Many of our Arduino and MicroPython libraries use C to interface directly with the hardware and when things go wrong it can be hard to figure out why.

Unlike more common web or server software development the code isn’t being run on the same machine as its written on. When code is compiled for a different architecture, such as ARM for the SAMD21, than the architecture the compiler is running on, usually x86, its called cross compiling. There are many different ways to organize how your code compiles we can cover in another guide. What you need to know is that there are a few different toolchains that convert human readable code to machine code. Today we’ll focus on the GNU toolchain that is made up of the GNU Compiler Collection (better known as gcc), the GNU Debugger (gdb) and number of other programs. Since the GNU toolchain is open source, ARM, the designer of the Cortex M0+ core, is able to ensure it produces well working code for their cores. So, we’ll be using the ARM version of the GNU toolchain to compile and debug the C code.

While I won’t cover compiling the code, I’ll be using the Adafruit version of MicroPython in the examples. Its source is here and it uses make to run the compiler. I also use an Arduino Zero for all of my debugging because it has a builtin debug chip which converts USB commands to commands the Cortex M0+ core understands.

There are a couple aspects of debugging we’ll talk about. First we’ll cover a few ways to stop the program at an interesting spot. Second, we’ll cover how to inspect the current program state using backtrace. Lastly, using the Micro Trace Buffer, we’ll inspect the history of the program execution. Lets get setup.

Software Installation

There are two pieces of software we need to install in order to get debugging. OpenOCD is a tool to communicate with debug hardware tools such as the EDBG chip on the Arduino Zero or a JLink. GDB is the GNU Debugger which talks with OpenOCD to control and inspect the raw state of the microcontroller and, using the binary symbols, translate that info back into the source code realm.


You can get OpenOCD from here. Below are quick start instructions.


Download and install the latest binary from here.


Installation on Mac OSX is easiest using Homebrew. (If you don’t have brew installed see here.)

brew install open-ocd


Its best to use your package manager to install openocd. Exactly how you do that will vary with your distribution. Here are some examples.

# Ubuntu, Debian, Raspbian, Mint
sudo apt-get install openocd #
# Fedora
su -c 'yum install openocd' #
# Arch
pacman -S openocd #


GDB is part of the larger ARM toolchain. So don’t be surprised to see a number of arm-none-eabi-* binaries installed.


Download install the win32 executable from here.

Also make sure that you have 32-bit (x86 not x86-64) Python 2.7x installed from here. We’ll be using Python to interpret the Micro Trace Buffer later.


Installation on Mac OSX is easiest using Homebrew. (If you don’t have brew installed see here.)

brew install arm-none-eabi-gcc


Its best to use your package manager to install the arm gcc toolchain. Exactly how you do that will vary with your distribution. Here are some examples.

# Ubuntu, Debian, Raspbian, Mint
sudo apt-get install gdb-arm-none-eabi
# Fedora
su -c 'yum install arm-none-eabi-gdb' #
# Arch
pacman -S arm-none-eabi-gdb #


This setup is for the Arduino Zero because it has an easy, on-board debug chip. Debugging other boards including Feathers through J-Link is a slightly different process not covered here.

Before we can get into the nitty gritty of debugging we need to first get everything running.


First, we need to get OpenOCD going to bridge from our computer to the hardware debugger. Its easy with the Arduino Zero.

Arduino Zero

Connect a USB cable from your computer to the DEBUG USB connector on the Arduino Zero. Now, make sure you have the Arduino Zero config file for OpenOCD available here.

Now run OpenOCD in a terminal. It will stay running while we debug.

openocd -f arduino_zero.cfg

You should see that it found the Arduino Zero with output similar to this:

Info : CMSIS-DAP: SWD  Supported
Info : CMSIS-DAP: Interface Initialised (SWD)
Info : CMSIS-DAP: FW Version = 02.01.0157
Info : SWCLK/TCK = 1 SWDIO/TMS = 1 TDI = 1 TDO = 1 nTRST = 0 nRESET = 1
Info : CMSIS-DAP: Interface ready
Info : clock speed 500 kHz
Info : SWD IDCODE 0x0bc11477
Info : at91samd21g18.cpu: hardware has 4 breakpoints, 2 watchpoints


GDB is similarly straightforward. The most important thing is that your current directory is near your binary. With Adafruit’s MicroPython I like to be in the atmel-samd directory where our binary is build-arduino_zero/firmware.elf. (If you are following along with MicroPython you can compile it with make BOARD=arduino_zero DEBUG=1.)

arm-none-eabi-gdb-py build-arduino_zero/firmware.elf

Now you should see some version information and a prompt that start with (gdb). All examples that start with (gdb) should be run in gdb and you do not need to type (gdb) in.

Now we need to tell GDB to debug through OpenOCD rather than on this computer.

(gdb) target extended-remote :3333

Loading, Resetting and Running

Loading, resetting and running the currently running program on the microcontroller is critical to the debugging process. To load a new version of the program after you’ve compiled outside of gdb do:

(gdb) load
Loading section .text, size 0x2bb84 lma 0x0
Loading section .data, size 0x5a4 lma 0x2bb84
Start address 0x0, load size 180520
Transfer rate: 5 KB/sec, 13886 bytes/write.

To reset the microcontroller to the start of the new program you need to ask OpenOCD via monitor to reset to the initialization state.

(gdb) monitor reset init
target state: halted
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x00018dd0 msp: 0x20008000

Finally, to make the program run type continue or c and hit enter. The prompt won’t return until your program finishes, hits a breakpoint or you type ctrl-c.


Its great if your program runs as expected but what if it doesn’t? It may crash or return an error when you least expect. Breakpoints are a great way to stop the execution of your code so you can inspect how you got a certain place in your code and with what state. There are two primary ways I use breakpoints:

  1. to stop when a function is called.
  2. to stop at a particular line of code.

Once stopped, we can use backtrace and the Micro Trace Buffer to see our current state and how we got there.

Adafruit Metro M0 Express – Designed for CircuitPython

Metro is our series of microcontroller boards for use with the Arduino IDE. This new Metro board looks a whole lot like our original Metro 328, but with a huge upgrade. Instead of the ATmega328, this Metro features a ATSAMD21G18 chip, an ARM Cortex M0+. It’s our first Metro that is designed for use with CircuitPython! CircuitPython is our beginner-oriented flavor of MicroPython – and as the name hints at, its a small but full-featured version of the popular Python programming language specifically for use with circuitry and electronics.

Not only can you use CircuitPython, but the Metro M0 is also usable in the Arduino IDE.

At the Metro M0’s heart is an ATSAMD21G18 ARM Cortex M0 processor, clocked at 48 MHz and at 3.3V logic, the same one used in the new?Arduino Zero. This chip has a whopping 256K of FLASH (8x more than the Atmega328) and 32K of RAM (16x as much)! This chip comes with built in USB so it has USB-to-Serial program & debug capability built in with no need for an FTDI-like chip.

  • Power the METRO with 7-9V polarity protected DC or the micro USB connector to any 5V USB source. The 2.1mm DC jack has an on/off switch next to it so you can turn off your setup easily. The METRO will automagically switch between USB and DC.
  • METRO has 25 GPIO pins, 12 of which are analog in, and one of which is a true analog out. There’s a hardware SPI port, hardware I2C port and hardware UART. Logic level is 3.3V
  • Native USB, there’s no need for a hardware USB to Serial converter as the Metro M0 has built in USB support. When used to act like a serial device, the USB interface can be used by any computer to listen/send data to the METRO, and can also be used to launch and update code via the bootloader. It can also act like a keyboard, mouse or MIDI device as well.
  • Four indicator LEDs and one NeoPixel, on the front edge of the PCB, for easy debugging. One green power LED, two RX/TX LEDs for data being sent over USB, and a red LED connected. Next to the reset button there is an RGB NeoPixel that can be used for any purpose.
  • 2 MB SPI Flash storage chip is included on board. You can use the SPI Flash storage like a very tiny hard drive. When used in Circuit Python, the 2 MB flash acts as storage for all your scripts, libraries and files. When used in Arduino, you can read/write files to it, like a little datalogger or SD card, and then with our helper program, access the files over USB.
  • Easy reprogramming, comes pre-loaded with the UF2 bootloader, which looks like a USB key. Simply drag firmware on to program, no special tools or drivers needed! It can be used by MakeCode or Arduino IDE (in bossa compatibility)

Here’s some handy specs!

  • Measures 2.8″ x 2.1″ x 0.28″
  • ATSAMD21G18 @ 48MHz with 3.3V logic/power
  • 256KB of FLASH + 32KB of RAM
  • 4 MB SPI Flash chip
  • 32.768 KHz crystal for clock generation & RTC
  • 3.3V regulator with 500mA peak current output
  • USB native support, comes with USB bootloader and serial port debugging
  • You also get tons of pins – 25 GPIO pins, 5 more than the Metro 328
  • Hardware Serial, hardware I2C, hardware SPI support
  • PWM outputs on almost all pins
  • 6 x 12-bit analog inputs
  • 1 x 10-bit analog output (DAC)
  • Built in NeoPixel on pin #40
  • Pin #13 red LED for general purpose blinking
  • Power on/off switch
  • 4 mounting holes
  • We also include 4 rubber bumpers to keep it from slipping off your desk
  • Reset button

Please note, CircuitPython is still in beta and we’re working hard to make it awesome! Please pick up one of these Metro M0 Expresses if you want to try it out – maybe even help us find bugs and make improvements!


The Metro M0 is chock-full of microcontroller goodness. There’s also a lot of pins and ports. We’ll take you a tour of them now!

Power Connections

There’s a lot of ways to power the Metro M0 Express, and a lot of ways to get power out as well.

There are two primary ways to power the Metro:

  • Through the Micro USB port up at the top left
  • Through the DC jack at the bottom left

The MicroUSB jack provides 5V at 500mA or so, there is a fuse that will shut off temporarily when more than 1000mA is drawn, this is to protect a computer USB port. You can plug this into any computer or USB charger with a USB cable. You can draw? up to 500mA between the Vin, 5V and 3.3V supplies (combined).

The DC Jack is a 5.5mm/2.1mm center-positive DC connector, which is the most common available. Provide about 6V-12V here to power the Metro. There is no fuse on this connection so you can draw more current, up to 800mA between the 5V and 3.3V supplies, and 2A from Vin.

Onboard regulators take the USB or DC power and linearly convert it to 3.3V and 5V:

  • 3V – this is the output from the 3.3V regulator, it can supply 500mA peak
  • 5V – this is the output from the 5V regulator (when DC jack is used), or from USB. It can supply ~500mA peak from USB and ~800mA peak from DC
  • GND - this is the common ground for all power and logic
  • Vin – this is the higher of the DC jack or USB voltage. So if the DC jack is plugged in and 9V, Vin is 9V. If only USB connected, this will be 5V.

There is also an on/off switch. This switch is only for the DC jack and does not affect powering via USB

Logic pins

This is the general purpose I/O pin set for the microcontroller.
All logic is 3.3V
Most pins can do PWM output
All pins can be interrupt inputs

Top Row

  • #0 / RX – GPIO #0, also receive (input) pin for Serial1 (hardware UART)
  • #1 / TX – GPIO #1, also transmit (output) pin for Serial1
  • #2 through #12 – These are general purpose GPIO. If there’s a dot next to the pad it can act as a PWM output.
  • #13 – GPIO #13 and is connected to the red LED marked L next to the USB jack
  • SDA?- the I2C (Wire) data pin. There’s no pull up on this pin by default so when using with I2C, you may need a 2.2K-10K pullup.
  • SCL - the I2C (Wire) clock pin. There’s no pull up on this pin by default so when using with I2C, you may need a 2.2K-10K pullup.

Bottom Row

  • A0 – This pin is analog input A0 but is also an analog output due to having a DAC (digital-to-analog converter). You can set the raw voltage to anything from 0 to 3.3V, unlike PWM outputs this is a true analog output
  • A1 thru A5 – These are each analog input as well as digital I/O pins.

Right side

  • SCK/MOSI/MISO – These are the hardware SPI pins, are are connected to the 2×3 header on the right hand side. you can use them as everyday GPIO pins (but recommend keeping them free as they are best used for hardware SPI connections for high speed.)

Additional analog inputs

In addition to the A0-A5 pins, there are extra analog inputs available

  • Digital #0 is also A6
  • Digital #1 is also A7
  • Digital #4 is also A8
  • Digital #5 is also A9
  • Digital #8 is also A10
  • Digital #9 is also A11

These pins are available in CircuitPython under the board module. Names that start with # are prefixed with D and other names are as is. So #0 / RX above is available as board.D0 and board.RX for example.

SPI Flash and NeoPixel

As part of the ‘Express’ series of boards, the Metro M0 Express is designed for use with CircuitPython. To make that easy, we have added two extra parts to this Metro M0: a mini NeoPixel (RGB LED) and a 2 MB SPI Flash chip

The NeoPixel is connected to pin #40 in Arduino, so just use our NeoPixel library and set it up as a single-LED strand on pin 8. In CircuitPython, the NeoPixel is board.NEOPIXEL and the library for it is here and in the bundle. The NeoPixel is powered by the 3.3V power supply but that hasn’t shown to make a big difference in brightness or color. The NeoPixel is also used by the bootloader to let you know if the device has enumerated correctly (green) or USB failure (red). In CircuitPython, the LED is used to indicate the runtime status.

The SPI Flash is connected to 4 pins that are not brought out on the GPIO pads. This way you don’t have to worry about the SPI flash colliding with other devices on the main SPI connection. Under Arduino, the FLASH SCK pin is #38, MISO is #36,? MOSI is #37, and CS is #39. If you use Metro M0 Express as your board type, you’ll be able to access the Flash SPI port under SPI1 – this is a fully new hardware SPI device separate from the GPIO pins on the outside edge of the Feather. In CircuitPython, the SPI flash is used natively by the interpretter and is read-only to user code, instead the Flash just shows up as the writeable disk drive!