Status of GD32 ARM support?

Hi, I saw a thread about using GD32 with PlatformIO from about a year ago, and it hinted that there might be GD32 support coming to PlatformIO. I just wanted to follow up and see if that is planned?

Note: I’m specifically asking about the GD32 ARM chips that start with “GD32F” and “GD32E” and are kinda-sorta STM32 compatible, not the RISC-V chips that start with “GD32V” and are already supported by PlatformIO.


Tracked in Add Support for GigaDevice GD32F and GD32E chips · Issue #3927 · platformio/platformio-core · GitHub now. I’m wondering why noone else opened that issue before me…


Thanks! If I wanted to do for GD32F350CB what you’ve already done for GD32F130C6 and GD32F103RC, how would I go about doing that?

Specifically, once I download the firmware library, how I do I get it into a form where it can be specified in platform_packages like you do for the other two chips:

; globally override framework-spl for all environments.
platform_packages = 

(I’m new to PlatformIO, so I don’t yet understand how all the pieces fit together.)



There’s no short answer to that, so I just did some experimenting and got something that is compilable. I have no board so I’d be pleased if you can test it (and possible adopt it to get it there):

I’ve also updated the modified framework-spl library at

Basically to understand what’s going on here you need to understand:

Roughly what happened here is:

  • download GigaDevice’s SPL pacakge
  • copy header files from GD32F3x0_Firmware_Library\Firmware\CMSIS in framework-spl\gd32\cmsis\cores\gd32
  • copy device header and system header files from GD32F3x0_Firmware_Library\Firmware\CMSIS\GD\GD32F3x0\Include into framework-spl\gd32\cmsis\variants\gd32f35
  • copy system_gd32f3x0.c from GD32F3x0_Firmware_Library\Firmware\CMSIS\GD\GD32F3x0\Source into the above mentioned target path too
  • find CMSIS\GD\GD32F3x0\Source\ARM\startup_gd32f3x0.s as the startup assembly code meant for the ARM compiler / assembler
    • the content in there is the Reset_Handler entry point, initialization and startup code up to the piont where main() is called and all interrupt vectors
    • here is where it get’s tricky: PlatformIO uses GCC. GigaDevices only provides the startup script for IAR + ARM assemblers/compilers. GCC will not accept assembly file in the ARM compiler syntax. The file has to be converted into proper arm-gcc assembly. using a template like startup_stm32f30x.s together with the interrupt vector names from the original file and some background knowledge in assembly allows one to create a fixed startup_gd32f3x0.s file which will correctly assembler in GCC.
  • Copy all files from GD32F3x0_Firmware_Library\Firmware\GD32F3x0_standard_peripheral\Include to framework-spl\gd32\spl\variants\gd32f35\inc (SPL code header files)
  • Copy all files from GD32F3x0_Firmware_Library\Firmware\GD32F3x0_standard_peripheral\Source to framework-spl\gd32\spl\variants\gd32f35\src (SPL code implementation files)

So all in all it’s knowing which paths PlatformIO’s SPL builder scripts expects the different type of files to be and copying there, and doing one conversion for the startup assembly.

After that it’s down to knowing how to

  • create board definition JSON files (documentation linked above, and existing stm32 definitions can be used as template)
    • just contains some names, OpenOCD config file names, amount of flash & RAM, clockspeed, and activated macros (which can in turn be read from the SPL framework code to find out which are needed for a specific chip, e.g. GD32F350 or GD32F350)
  • make PlatformIO use the new SPL files (that is, exchanging the source of framework-spl with platform_packages
  • make PlatformIO use a custom board definition file (that is, create a folder boards/ in the project and put in the abc.json board definition file, then say board = abc in the platformio.ini, as documented)

Let me know if the example above is flashable + runs.

1 Like

I tried flashing the GD32F350 using the ST-Link probe, and it failed with the error:

Error: init mode failed (unable to connect to the target)

This is the same error I get when trying to program a Blue Pill. So either there’s something wrong with both the GD32F350 and the Blue Pill, or else I’m doing something wrong. (Although somehow my bad luck did not extend to the Discovery F411, which worked okay.)

Since I’m having some trouble with my debug probes, I decided to try uploading to the F350 using the serial protocol.

The serial upload worked successfully, in that there were no errors. However, the program does not seem to be running correctly, because the LED is not blinking.

My changes are at ppelleti/pio-gd32f350cb.

Not that it makes a difference, but can you uncomment this line

and retry? Also the board isn’t still in bootloader mode after the upload due to the Boot0/1 pins, and you’ve tried a reset?

If it’s still not working you should try and connnect the built-in stlink of the Nucleo or discovery board to the GD32 chip. See chapters 6.2.3 and 6.2.4. Then you should be able to upload & debug normally.

I tried uncommenting the line, but it didn’t make a difference.

Yes. (The BOOT0 is a button to press at reset, so it can’t be left on like the Blue Pill. And I don’t think the F350 has a BOOT1 pin, just a BOOT1 bit in some configuration register?)

I tried the reset button, and I also tried completely unplugging the board from power and then plugging it back in.

Thanks! I’ll read up on that and give it a try.

I tried that with my Discovery board, and it worked successfully to upload the program. I then tried debugging, and I hit ^C to break into the debugger and see where the program was at:

Program received signal SIGINT, Interrupt.
WWDGT_IRQHandler ()
    at /home/ppelleti/.platformio/packages/framework-spl/gd32/cmsis/variants/gd32f35/startup_gd32f3x0.S:71
71	  b  Infinite_Loop
(gdb) bt
#0  WWDGT_IRQHandler ()
    at /home/ppelleti/.platformio/packages/framework-spl/gd32/cmsis/variants/gd32f35/startup_gd32f3x0.S:71

So it appears that it got an unexpected interrupt:

 * @brief  This is the code that gets called when the processor receives an
 *         unexpected interrupt.  This simply enters an infinite loop, preserving
 *         the system state for examination by a debugger.
 * @param  None
 * @retval None
    .section  .text.Default_Handler,"ax",%progbits
  b  Infinite_Loop
  .size  Default_Handler, .-Default_Handler

Hmm I see.

Can you please open the file ~/.platformio/packages/framework-spl/gd32/cmsis/variants/gd32f35/startup_gd32f3x0.S and find and delete the lines

.weak _estack
.thumb_set _estack,Default_Handler

.weak Reset_Handler
.thumb_set Reset_Handler,Default_Handler

then clean, recompile and reupload.

It might have remapped the reset ISR to the infinite loop by accident (also the _estack makes no sense).

If that still doesn’t help, please add

debug_init_break = break Reset_Handler

in the platformio.ini (docs), so that it directly halts at the first possible instruction. Try and follow the execution flow until it’s stuck again.

I did that, and did clean, recompile, and reupload, but still no blinking LED.

I did that, and here’s what happened:

Reading symbols from /home/ppelleti/src/pio-gd32f350cb/.pio/build/gd32f350cbt6/firmware.elf...done.
PlatformIO Unified Debugger ->
PlatformIO: debug_tool = stlink
PlatformIO: Initializing remote target...
xPack OpenOCD, x86_64 Open On-Chip Debugger 0.10.0+dev-00378-ge5be992df (2020-06-26-09:27)
Licensed under GNU GPL v2
For bug reports, read
Info : The selected transport took over low-level target control. The results might differ compared to plain JTAG/SWD
Info : tcl server disabled
Info : telnet server disabled
Info : clock speed 1000 kHz
Info : STLINK V2J17S0 (API v2) VID:PID 0483:3748
Info : Target voltage: 2.891070
Info : stm32f3x.cpu: hardware has 6 breakpoints, 4 watchpoints
Info : starting gdb server for stm32f3x.cpu on pipe
Info : accepting 'gdb' connection from pipe
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080001b8 msp: 0x20004000
Info : device id = 0x17040410
Warn : STM32 flash size failed, probe inaccurate - assuming 128k flash
Info : flash size = 128kbytes
0x080001b8 in delay_decrement () at src/systick.c:84
84	}
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080001b8 msp: 0x20004000
Loading section .isr_vector, size 0x150 lma 0x8000000
Loading section .text, size 0x88 lma 0x8000150
Loading section .init_array, size 0x4 lma 0x80001d8
Loading section .fini_array, size 0x4 lma 0x80001dc
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080001bc msp: 0x20004000
Start address 0x80001bc, load size 480
Transfer rate: 273 bytes/sec, 120 bytes/write.
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080001bc msp: 0x20004000
target halted due to debug-request, current mode: Thread
xPSR: 0x01000000 pc: 0x080001bc msp: 0x20004000
Breakpoint 1 at 0x80001bc: file /home/ppelleti/.platformio/packages/framework-spl/gd32/cmsis/variants/gd32f35/startup_gd32f3x0.S, line 71.
PlatformIO: Initialization completed
(gdb) PlatformIO: Resume the execution to `debug_init_break = break Reset_Handler`
PlatformIO: More configuration options ->
Note: automatically using hardware breakpoints for read-only addresses.

Breakpoint 1, WWDGT_IRQHandler ()
    at /home/ppelleti/.platformio/packages/framework-spl/gd32/cmsis/variants/gd32f35/startup_gd32f3x0.S:71
71	  b  Infinite_Loop

So it looks like the reset handler is still mapped to the infinite loop, even though I commented out those lines and rebuilt.

Mhmm it seems like it didn’t hit hit the breakpoint though, just printing that it set the breakpoint there?

When you press the ‘pause’ button it definitely halts in that infinite loop? Can you type backtrace in the debug / gdb console to get more info? Or better, p/x 0xE000ED04 for the ICSR register?

Does it make a difference if you add

lib_archive = no 

in the platformio.ini?

I definitely see the difference when I remove the 4 wrong lines in the analysis of the .elf file. First the reset vector would have the second entry (Reset_Handler) jump to the infinite loop, after the correction it jumps to the correct function.

Here’s what I get:

Breakpoint 1 at 0x80001bc: file /home/ppelleti/.platformio/packages/framework-spl/gd32/cmsis/variants/gd32f35/startup_gd32f3x0.S, line 71.
PlatformIO: Initialization completed
(gdb) PlatformIO: Resume the execution to `debug_init_break = break Reset_Handler`
PlatformIO: More configuration options ->
Info : halted: PC: 0x080001bc
halted: PC: 0x080001bc

Breakpoint 1, WWDGT_IRQHandler ()
    at /home/ppelleti/.platformio/packages/framework-spl/gd32/cmsis/variants/gd32f35/startup_gd32f3x0.S:71
71	  b  Infinite_Loop
(gdb) c
Info : halted: PC: 0x080001bc
halted: PC: 0x080001bc

Breakpoint 1, WWDGT_IRQHandler ()
    at /home/ppelleti/.platformio/packages/framework-spl/gd32/cmsis/variants/gd32f35/startup_gd32f3x0.S:71
71	  b  Infinite_Loop
(gdb) bt
#0  WWDGT_IRQHandler ()
    at /home/ppelleti/.platformio/packages/framework-spl/gd32/cmsis/variants/gd32f35/startup_gd32f3x0.S:71
(gdb) p/x 0xE000ED04
$1 = 0xe000ed04
(gdb) p/x *0xE000ED04
$2 = 0x0

No. I added that and rebuilt, and it still behaves the same.

I’m doing:

pio run -t clean

to clean. Is that correct, or is there some sort of “super extra deep clean” that I should be doing?

Aha! It looks like semicolon is not a comment character in ARM assembly?

I had commented out the lines you specified. But now I tried actually deleting them instead and it started working. The LED is now blinking! Thanks for all your help!

1 Like

The way the .S file is handled is that it’s run through gcc with the -X assembler-with-cpp option and it accepts /* */ as comments it seems.

Glad to hear it’s running! Then SPL as a whole should probably work. Did you test some other stuff like UART output and GPIO input?

Yes, the UART output and GPIO input work.

1 Like


I’d like to revive this thread, as I’m looking for someone to support a very similar chip, the GD32F350G8U6. I hope that is sufficiently “related” in order not to require a new thread. I have a few of the corresponding GD32350G-START boards here, and I’m willing to donate one of them, along with an appropriate money donation for anyone who is willing to add support for this into PlatformIO.

My ultimate goal is to compile and run both the USB Host HID example and the USB Device HID example that Gigadevice supplies.

I’m a fresh newbie to PlatformIO, but I already saw a “Guru meditation” error in another thread of this forum. Kinda makes me feel at home, as I’m a long-term Amiga guy.

I’m located in Germany, but will ship the board worldwide if required.

Me and a group of people have recently started a massive initiative to support GD32 chips in PlatformIO.


  • new Arduino Core
  • SPL support
  • all GD32 ARM type chips to be supported
    • currently lots GD32F3, F1, F4, E1, E2 chips supported.

And we are also looking for contributors and testers, Discord link is in the repo.

See the repos at

My smaller per-chip repos are considered outdated with the new repos above.

1 Like