As I was working on my RISCV clone compatibility layer, I ran my I²C test code. When running this code, I found it runs successfully on the CH32V103 however, it failed on the GD32VF103. Upon further investigation, I found it failed on the GD32F103 as well. I guess it is about time I run some tests comparing the clones. The battle of the clones.

Initial testing found that the APM32F103, CS322F103 and CH32F103 succeeded, and the GD32F103 and HK32F103 failed. I have some more clones ordered, and waiting for them to arrive. To look some more at the testing, I decided to have a look again at identifying them. Previously I’ve looked at the ROMTABLE. Using the ROMTABLE, the STM32F103 and the GD32F103 identify themselves with their JEP106 company identification codes. The HK32F103 has a non-standard identification in there, but it has a unique value. The APM32, CS32 and CH32 however, have just ARM Cortex-M3 in their ROMTABLE. So, using the ROMTABLE I can’t tell them apart.

Looking at the APM32 and CH32 datasheets, it seems these have extra peripherals compared to the STM32. I can look for the presence of these peripherals to detect them, or at least, guess them. The APM32 has a FPU peripheral, and CH32 has a second USB controller.

In am STM32-derived design, in order to use a peripheral, one must enable its clock. The peripherals we are interested in are connected to the AHB bus, and the clocks for peripherals connected to the AHB bus is controlled by the RCC->AHBENR register. Setting a bit enables a clock. The bits for the extra peripherals are reserved on the STM32. Trying to set a reserved bit leaves it cleared. The FPU unit on the APM32 has bit 3, and the second USB controller on the CH32 uses bit 12.

uint32_t initial_val = RCC->AHBENR;

RCC->AHBENR |= (1<<3);
if ((1<<3)&RCC->AHBENR) {
	// The bit sticks
	prob = "APM32";

RCC->AHBENR |= (1<<12);
if ((1<<12)&RCC->AHBENR) {
	// The bit sticks
	prob = "CH32";

Another approach to detect what microcontroller we are running on is looking at the System ROM. The System ROM contains a built-in UART bootloader. The System ROM starts at 0x1FFFF000. The flash size is stored at 0x1FFFF7E0, and the serial number is at 0x1FFFF7E8.

For a look at the System ROM at the PC, I’ve made a bash script that runs OpenOCD to dump it. It is dumped as a binary file, and converted into a hex file for calling diff on. It will look at the location of the serial number, and call the files after the serial number.


openocd -f interface/jlink.cfg -c "transport select swd" -f target/stm32f1x.cfg \
-c "
reset halt;
dump_image dump.bin 0x1ffff000 0x800;

SERIAL=`xxd -l 12 -s 0x7E8 -ps dump.bin`
echo $SERIAL
mv dump.bin $SERIAL.bin
xxd $SERIAL.bin $SERIAL.txt

Capturing the System ROM on a number of STM32F103CBT6 chips reveals they differ at the location of the serial numbers, but otherwise they are the same. This is what is expected. Dumping the System ROMs for the GD32, HK32 and CH32 shows a completely different content. That is also to be expected. One thing to notice is that the dump for the CH32 showed all 0xFFs where the serial number is supposed to be. The datasheets do mention the serial number at the said address, so where is it? Is my chip bad or does this happen to all of them?

However, it looks like the APM32 and CS32 appear to have a copy of STM’s System ROM. So, they’ve pirated the bootloader. The CS32 System ROM is the same except, of course the serial number. On the APM32 we see one more difference, just before the Flash size and serial number, at address 0x1ffff7d0. On the APM32 there is some more data, where it is all blank on the STM32. I’m not sure what this data is, or how it’s even used, but I suppose its usable for identification.

APM32 000007d0: ff00 f30c 926d ea15 ffff ffff ff00 ff00 .....m..........
STM32 000007d0: ffff ffff ffff ffff ffff ffff ffff ffff ................

On the microcontroller, I will calculate the CRC over the first kilobyte of the System ROM, and use those values as identification.

Anyhow, at this point I can tell all the chips I currently own apart. I’m expecting some more chips, FCM32F103CBT6, MG32F103CBT6, and RX32F103CBT6, to arrive next week. Furthermore some more APM32. The chips I own are back from 2019, when they were ApexMic branded. These days they’re Geehy branded. I expect them to be the same part, but who knows? I will analyse them and try to find ways to tell them apart.

Being able to tell the part apart at runtime is convenient for creating some output for testing. I’ve already established there are some I²C issues. I will extend my test-cases, and also test with single devices rather them throwing them all on the bus. That might be part of the problem, as the Chinese break-out boards have all their own pull-up resistors, so the final pull-up resistance might be on the low side. However, even if I violate the I²C specifications, this proofs clones are not always a drop-in replacement. The details will require further investigation.