Some notes on RISCV bare metal toolchains, regarding building and debugging code on GigaDevice GD32VF103 and WinChipHead CH32V103 microcontrollers. In the past I’ve made some start-up code and linker files that allow building firmwares abstracting the differences between these two chips, allowing one unified firmware image for both of the MCUs.

I’ll look at three toolchains (either packages from ArchLinux repo or AUR):



This is the compiler for RISCV bare metal programming on the Arch repositories. While the name says riscv64, it also can build for 32-bit riscv. This compiler identifies itself as riscv64-elf-gcc (Arch Linux Repositories) 12.2.0


Binary distributed toolchain, as used by the MounRiver Studio IDE. The compiler identifies itself as riscv-none-embed-gcc (xPack GNU RISC-V Embedded GCC, 64-bit) 8.2.0.


Binary distributed toolchain, this is an “xPack” distribution. This compiler identifies itself as riscv-none-elf-gcc (xPack GNU RISC-V Embedded GCC x86_64) 12.2.0

Further notes

The MounRiver toolchain is a GCC 8, while the other toolchains are GCC 12. I would rather move to GCC 12, but I am running into an issue when debugging. I think I’ve mentioned this before when I was working on my unified “RV32F103” project. From what I can tell, GDB has trouble keeping track of the stack when a newer compiler is used. What I saw is the backtrace gets lost and local variables are at the wrong address. However, the code appears to function as intended. This will require some more investigation.

Nevertheless, let’s look at building firmwares first. There are a few modifications required. Some compiler flags have changed, and there have been new ABI versions.
One thing to consider is the fact the the zicsr was split from “i” extension..

This issue results in errors like, (on either of the gcc 12 toolchains)

Error: unrecognized opcode `csrs 0x300,ra'

This can be solved by either specifying ISA version 2.2 -misa-spec=2.2 or by replacing -march=rv32imac with -march=rv32imac_zicsr in the compiler flags.

The former solution works for both gcc 12 compilers, the latter gives issues with the riscv64-elf-gcc compiler. Here the linker does not recognise rv32imac_zicsr and attempts to link 64-bit versions of libgcc. It links fine if I pass rv32imac to the linker.

However, the debugging issues. I’m still trying to figure out what is causing them. I have made test cases that work fine built with GCC 12, and then, some, not specifically made to test this issue, that fail. Could it be size related or something? I’m not sure.

More details follow in a later post.