PineTime Smart Watch running bosmoment PineTime-app Firmware based on RIOT-OS

Debug RIOT-OS on PineTime with VSCode

Lup Yuen Lee 李立源
Feb 2 · 8 min read

Tracking down a firmware bug in PineTime Smart Watch? Or trying to understand somebody else’s firmware code?

VSCode gives you visual insight as the firmware runs on PineTime… And any gadget based on Nordic nRF52 Microcontroller. Today we’ll learn to configure VSCode to debug RIOT-OS firmware for PineTime… Though the steps here will work for any PineTime firmware: FreeRTOS, Zephyr, TinyGo, Rust Embedded, Bare Metal, …

The steps here are based on the VSCode debugging configuration for Rust+Mynewt OS firmware on PineTime. We’ll be debugging the bosmoment PineTime-apps firmware based on RIOT-OS…

VSCode debugger works with the ST-Link USB Programmer on Windows and macOS. VSCode debugging on Raspberry Pi is also supported, check this article for the setup instructions.

🛈 What is VSCode? Is it related to Visual Studio? How is Microsoft involved? Read this


For macOS: Update GNU Make

If you’re using macOS, we’ll need to update GNU Make to the latest version, because the RIOT-OS build will fail without it.

Download the latest version of GNU Make source code from the GNU Make website

ftp.gnu.org/gnu/make/make-4.3.tar.gz

Expand the archive and run…

This installs the latest GNU Make into /usr/local/bin/make. We’ll use this in a while.


Build RIOT-OS Firmware

Download PineTime-apps and build it…

On macOS we use the updated version of GNU make like this…

We should see this…

For easier debugging, let’s dump the Arm Assembly Code from the ELF file…

To see the Assembly Code from my build, download PineTime-new.S from github.com/lupyuen/pinetime-rust-mynewt/releases/tag/bosmoment

If you look inside the Assembly Code, it’s not that hard to read… The original C code is interspersed with the Assembly Code, so we can still understand what the code is doing. This is useful for looking up a memory address, and figuring out what this address in RAM or ROM is for.

Address Tip: ROM addresses on nRF52 look like 0x0000????, RAM addresses look like 0x2000????

Reset Handler code in PineTime-new.S. Arm Assembly Code interspersed with the original C code. From https://github.com/lupyuen/pinetime-rust-mynewt/releases/tag/bosmoment

PineTime connected to ST-Link via SWD Port

Connect ST-Link to PineTime

Let’s connect PineTime’s SWD port to ST-Link…

Note that we’re using only the bottom row of pins on ST-Link. If you’re using the top row… You’re probably doing it wrong!

For Windows:

Download the ST-Link USB driver from ST-Link Driver Website (email registration required)…
https://www.st.com/en/development-tools/stsw-link009.html

Click Get Software
Unzip the downloaded file. Double-click the driver installer:
dpinst_amd64.exe

The driver should be installed before plugging ST-Link into the USB port.

For Linux:

Install the Linux driver for ST-Link like this…

For macOS: No driver is needed.


Configure VSCode Debugger

1️⃣ Download pinetime-rust-mynewt according to the instructions in the article Build and Flash Rust+Mynewt Firmware for PineTime Smart Watchunder the sections (Windows or macOS)…

“[Option 1] Install PineTime Build Tools on Windows”, or

“[Option 2] Install PineTime Build Tools on macOS”

2️⃣ Install and launch VSCode. For Raspberry Pi, use the Community Build of VSCode.

Click File → Open Workspace

Select the workspace.code-workspace file inside pinetime-rust-mynewt

Install all recommended extensions when prompted. This installs the Cortex Debug VSCode Extension (which is defined in .vscode/extensions.json)

3️⃣ We’ll tell VSCode where to find our source files during debugging…

Click File → Add Folder To Workspace

Select the source folder for firmware to be debugged: PineTime-apps

4️⃣ Edit the debugger configuration file .vscode/launch.json

For ST-Link: Copy the contents of .vscode/launch-nrf52.json to .vscode/launch.json

For Raspberry Pi: Copy the contents of .vscode/launch-nrf52-pi.json to .vscode/launch.json

This selects the debugging interface that we’ll be using with OpenOCD: ST-Link or Raspberry Pi (SWD over SPI).

Note: The launch.json file is overwritten whenever we run pinetime-rust-mynewt/scripts/build-app.sh. So be careful not to build the Rust+Mynewt firmware, and always back up launch.json!

5️⃣ In .vscode/launch.json, set the ELF executable to be debugged…

Copy the path from the output of the RIOT-OS build earlier.

Why is the ELF file needed? The debugger (GDB) will flash the ELF executable into the flash ROM. It also reads the debug symbols from the ELF file.

6️⃣ In .vscode/launch.json, set additional breakpoints if needed.

Comment out the break lines as shown below…

By default, the debugger will always break at first line of code in the Reset Handler, which is the first function that will be executed when PineTime powers up.

If you wish to break at the first line of the main() function, uncomment the line "break main"

The break for assert_func will brake execution whenever an assertion fails on Mynewt OS. Good for checking the stack and understanding why it failed. If there is a similar assertion function for your firmware, uncomment this line and replace __assert_func by the function name.

The break for os_default_irq is for unhandled interrupts. If you know the function that handles unhandled interrupts on your firmware, uncomment the line and replace os_default_irq by the function name.

Here’s the .vscode/launch.json debugger configuration I used for debugging RIOT-OS on PineTime…

7️⃣ Set the OpenOCD path in .vscode/settings.json

Where OpenOCD is installed. From https://github.com/lupyuen/pinetime-rust-mynewt/blob/master/.vscode/settings.json

Debug RIOT-OS Firmware

Setting a breakpoint

In VSCode, set a breakpoint in the firmware, like in the main() function.

Click the empty space at the left of the line of code that you wish to pause execution.

You should see a red dot.

Click Debug → Start Debugging or press F5

The first break is at the Reset Handler, as expected…

Breaking at Reset Handler

When hitting the breakpoint, we may explore the Globals in the Variables panel at top left to examine the RIOT-OS state… Looks interesting!

We may now click Continue or press F5 to continue execution. Or step through the code. Here are the buttons in the Debugging Toolbar…

Debugging Toolbar
Breaking at function _gui_screen_on() to step through the display updating

In the video below I set a breakpoint at function _gui_screen_on() in PineTime-apps/modules/gui/gui.c. Pressing the button on PineTime will trigger this breakpoint… So that we may step through the code to understand how it responds to the button press and how it refreshes the screen.

To dump out the raw contents of the stack (for troubleshooting weird stack issues like in the video below), we will need to enter GDB commands (like x/-10x 0x20004b88) into the Debug Console panel (at the right of the screen above). Here’s the GDB Cheat Sheet

GDB Tip: x/-10x 0x20004b88 means dump the memory contents of the 10 words before RAM address 0x20004b88.

Why this address? We’ll find it in the Registers panel near the lower left corner of the screen above… SP = 0x20004b88. This is the Arm CPU’s stack Pointer, and the stack grows downwards, so we dump out the words before this address (not after this address).

Check the article “Debug Rust+Mynewt Firmware for PineTime on Raspberry Pi” for more details of the VSCode Debugger


Build and Debug RIOT-OS Livestream

Here’s the recording of my live-streamed build and debug session for RIOT-OS on PineTime. That was first time I used RIOT-OS. (My only experience with RTOS has been with Mynewt OS)

In this video we’ll see all the concepts covered in this article…

  1. Build RIOT-OS firmware
  2. Set VSCode Debug Configuration (.vscode/launch.json)
  3. Debug firmware with breakpoints
  4. Walk through the stack
  5. Inspect the C variables
Build and Debug RIOT-OS Livestream

Configure Your Own Workspace

Why did we start with pinetime-rust-mynewt when debugging our RIOT-OS firmware? Because the VSCode debugging configuration was already all set up, so it’s easier to adapt from a working configuration.

To set up debugging on your own VSCode workspace for any firmware (RIOT-OS, FreeRTOS, Zephyr, TinyGo, Rust Embedded, Bare Metal, …), here’s what we’ll need…

.vscode/extensions.json: Installs the Cortex Debug VSCode Extension automatically when the workspace is opened in VSCode. This extension contains the debugger that we’re using. Refer to this sample

.vscode/launch.json: Configuration for the VSCode debugger. Update the settings that we have covered in the section “Configure VSCode Debugger” above. Also update the paths for debug.ocd and nrf52.svd (see below). Refer to this sample

.vscode/settings.json: Configures the path of OpenOCD. Refer to this sample

debug.ocd: Sets up Arm Semihosting for showing debugging messages. Refer to this sample

nrf52.svd: This is the Arm CMSIS System View Description (SVD) file. It describes the peripherals on our microcontroller, like GPIO, I2C, SPI, … This is needed to allow browsing of the Peripherals panel at the left side of the debugger screen. Get it here


What’s Next?

Everything you see in this tutorial was set up within an hour… And the live debug video is proof of that!

I have never experienced RIOT-OS before that livestream session. I was pleasantly surprised that RIOT-OS was so well designed for a simple, modular embedded OS. Comparable to Apache Mynewt actually!

I’m happy to help more RIOT-OS developers get on board PineTime development…

  1. Set up a proper VSCode Workspace for coding, building, flashing and debugging PineTime firmware. On Windows, macOS, Linux AND Raspberry Pi, similar to the article Debug Rust+Mynewt Firmware for PineTime on Raspberry Pi
  2. Set up Rust on RIOT-OS, so that people can build Watch Apps and Watch Faces in Rust, without worrying about crashing pointers (like in C)
  3. And for non-coders, we could possibly introduce visual programming, like Visual Embedded Rust
  4. We have some really interesting technical challenges on PineTime… Could RIOT-OS be the solution? Check out the article Debug Rust+Mynewt Firmware for PineTime on Raspberry Pi, under the section “Your IoT Learning Journey with PineTime”. RIOT-OS could save lives someday!

Drop me a note if you’re keen to get involved!

Lup Yuen Lee 李立源

Written by

Techie and Educator in IoT 物聯網教師

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade