ULWOS2 on GD32V: multithreading examples

In our previous article I introduced the GD32V and the Longan Nano development board. In this article I show the porting of my cooperative multithreading OS (ULWOS2) to the GD32V. We also present some examples running on the Longan Nano, which demonstrate CDC ACM and a simple CDC terminal using the onboard LCD. All examples make use of PlatformIO.

Porting ULWOS2

Adapting ULWOS2 to run on the GD32V wasn’t difficult. RISC-V cores include an internal 64-bit free running counter that can be used for this task. All we have to do is to declare a new ULWOS2_getMilliseconds function that retrieves and calculates the elapsed milliseconds based on that counter:

We also have to create the new target symbol (ULWOS2_TARGET_GD32V) on ULWOS2_HAL.h, which is pretty trivial.

Since ULWOS2 is does not use any Assembly and we are using GCC (which is mandatory), no further modification is needed!

Note that our examples make use of at least one extra file (system_gd32vf103.c). It is necessary for configuring the clock sources within the MCU.

ULWOS2 as a PlatformIO library

Before we proceed, I would like to share the news that since version 1.1.1, ULWOS2 is also available as a library within PlatformIO registry and it will automatically download and install the library since it is a required library for all these examples!An image showing ULWOS2 library available on PlatformIO registry

First ULWOS2 example on GD32V

As usual, the first thing we do when starting on a new MCU or development board is to blink an LED. The Longan Nano includes an RGB LED connected to the following I/Os:

Red PC13
Green PA1
Blue PA2

A header file (gd32v_pjt_include.h) defines a set of macros that can one can use for setting, resetting or toggling LED state.

The code for blinking the red LED is super simple:

I have included other examples such as a breathing RGB LED (which uses software PWM as PC13 is not connected to any timer output), a USB CDC device which echoes all data received and also sends a fixed message every 10 seconds and a fancier version of the CDC example which prints received data on the LCD (using signals).

CDC driver

GigaDevices provide several firmware examples for the GD32V. Unfortunately these examples are not very well documented and are not designed for use with PlatformIO. The API is also not very well designed. This is why I ended up modifying their driver to better suit my needs.

Basically, this is what I have modified from the original driver:

  1. The library stores the USB device structure locally instead of exposing it to the application. Application code can read USB connection state by using a getter;
  2. Single function to perform CDC communication maintenance;
  3. Use core tick for timing (instead of timer 2).
  4. Included wchar configuration (in order to example correct USB descriptor strings) and modified interface protocol to “No class specific protocol required” (see table 17 of USB CDC spec v1.1). Thanks to this Github repository for the useful information!

While working on the driver I’ve found that it is using a lot more RAM than it is necessary. I didn’t have time to investigate it but apparently the USB device structure that GigaDevice is using creates a static reference to I/O registers that is later (during init) mapped to the actual registers. The solution would be to reference the structure instead of declaring it statically which forces unnecessary memory allocation. It would be a good idea to improve that, maybe something for the future?

The basic CDC example has only two threads, one that sends a string every 10 seconds and another that echoes back all received characters:

CDC terminal with LCD

This example shows how to use ULWOS2’s signals in order to display received data on the LCD. It makes use of a signal for synchronizing the terminal thread and CDC thread.

A Longan Nano development board running ULWOS2 and showing received data on its display

This is how it works: terminal thread will always block and wait for SIGNAL_NEW_CHARACTER. Once this signal arrives the thread runs and takes the newCharacter and displays it (if it is printable) or performs another action (if it is a known control character). Currently known control characters are: RETURN, BACKSPACE and CTRL+D (clear the screen).

We also had to modify CDC_thread to send the signal when a single character is received:


In this article we have seen some examples demonstrating how easy it is to use ULWOS2 to enable cooperative multithreading on the RISC-V/GD32V. I hope you like it and the examples can be useful for anyone playing with the GD32V or Longan Nano development board.

As usual, all the source code is available on my Github repository.


Leave a Reply

Your email address will not be published. Required fields are marked *

3 × 5 =