This is part 2 of the look at the GD32VF103 and the CH32V103. Microcontrollers with a RISC-V RV32IMAC core and STM32F103 compatible peripherals. As stated in the pervious article, their main difference is how the interrupts are handled. Of course, both can do interrupts as per RISC-V standard, the main issue is the fact the numbering is different. Nevertheless, on the GD32VF103 we also have a CLIC mode for interrupt handling, which I’ll use as we have to handle them separately anyways, due to the numbering issue. So, upon startup, we’ll have to detect what mcu we are running on and initialise it accordingly.

How will we tell them apart? The RISC-V standard described marchid and mvendorid registers that allow to identify the core we are running on.
According to the specification, the combination of mvendorid and marchid should uniquely identify the type of hart microarchitecture that is implemented.
The GD32VF103 uses a BumbleBee core by Nuclei, while the CH32V103 uses a core, which I believe is developed by WinChipHead themselves. Looking at the marchid for the GD32, we see 0x80000022. Notice the high bit is set. According to the RISC-V specifications, this indicates a commercial implementation, and the rest of the value is defined by the vendor, which is indicated by the mvendorid register. When the high bit of the marchid is not set, a non-zero value indicates an open source design registered with RISC-V International. When we look at the mvendorid register, we see value 0x31e. This register contains the JEDEC id of the vendor. The 7 least significant bits encode the entry, and the rest the page. Thus 0x31e >> 7 = 6, and 0x31e & 0x7f = 0x1e = 30. Looking at the JP106 table, this indicates Andes Technology Corporation. As the BumbleBee core was made by them in collaboration with Nuclei, this value is to be expected.

As for the CH32, we see this field is zero, and the vendor id has been set to 0x01020304. This is obviously not a valid JP106 id, and thus it appears this value violates the specifications. When I looked at STM32F103 clones with Cortex-M3 core, we’ve seen most clones have ARM in the ROMTABLE, rather then their own ID. The only exception was GigaDevice. It seems most manufacturers don’t bother registering with JEDEC. According to the RISC-V specifications, at the time of writing (of the specs) registering would be a one-time fee of US$500. Hardly a reason not to register for a chip manufacturer I would say. Nevertheless, it seems many Chinese MCU makers don’t.
An empty marchid would be valid according to the specification, indicating the field has not been implemented. While the values returned by the CH32V103 might not fully identify the core, the values for the GD32 do. Either way, since we are only interested in telling these two MCUs apart, we have enough information to do that. So, let’s have a look at some RISC-V assembly to detect what core we are running on

.equ GD32_MARCHID,    0x80000022        # Identifiers for GD32VF103
.equ GD32_MVENDORID,  0x0000031E        # Identifiers for GD32VF103

.equ CH32_MARCHID,    0x00000000        # Identifiers for CH32V103
.equ CH32_MVENDORID,  0x01020304        # Identifiers for CH32V103

.extern _init_gd32
.extern _init_ch32

identify_chip:
    csrr    a0, marchid             # load the marchid into register a0        
    csrr    a1, mvendorid           # load the mvendorid into register a1

try_gd32:                           
    li      a2, GD32_MARCHID        # load GD32_MARCHID into register a2
    bne     a0, a2, try_ch32        # branch to try_ch32 if a0 != a2
    li      a2, GD32_MVENDORID      # load GD32_MVENDORID into register a2 
    bne     a1, a2, not_supported   # branch to not_supported if a0 != a2
    j      _init_gd32               # GD32VF103 identified, jump to _init_gd32

try_ch32:                           # Check if we are running on a CH32V103
    li      a2, CH32_MARCHID         
    bne     a0, a2, not_supported
    li      a2, CH32_MVENDORID
    bne     a1, a2, not_supported
    j       _init_ch32              # CH32VF103 identified,


not_supported:
    j not_supported                 # Endless loop for unknown chip

This way we can identify the mcu we are running and and initialise it accordingly.