Status Update: Initial Experiments with the GD32 chip

tomli at tomli at
Sat Feb 24 11:18:20 CET 2018

Hello Niibe.

I got a new working ST-Link programmer and did some experiments on this GD32
chip in this week.

But the USB was not working, presumably due to timings and clock differences
between the two chips. I'm going to purchase an official development board
to help with my continued experiments, otherwise it's difficult to trace
the program dlown.

Here's my progress:

1. Because the property of the underlying flash technology by GigaDevice,
the first 32 KiB of the flash is zero-wait for reading, but the writing and
erasing of the chip is significantly much more longer than STM32, as a
result, the programming software needs some modifications.

The official application notes recommends:

    i)  increase the erase timeout per page to 300ms,
        increase the mass erase timeout to 3s.

    ii) increase the program time per word to 2ms,
        increase the program time per page to 300ms.

In addition, the access time for data behind of first 32 KiB is slow,
one should put timing-crtitial data at the beginning of the flash chip.

Gnuk also needs to adjust the timeout values for erasing and writing to
the flash.

2. The GigaDevice flash doesn't support "block write" mode, which is the
default mode to program the flash on OpenOCD, further, OpenOCD will fall
back to single halfword (16-bit) accesses when block write is not available,
however, it's also doesn't work on this flash. The only known working mode
on this chip is the 32-bit fullword access.

I've implemented a patch to hack OpenOCD quickly and dirtily and make it work
with GD32, but it will break STM32 chips, the longterm solution is adding
additional code and merge it to the upstream.

3. GD32 implemented extra readout protection modes, custom instructions is
needed to remove the protections for programming and testing. The protections
are observed to switch on automatically after __every power cycle__. I believe
it's a proactive protection againist physical tampering.

4. Because it only has a USB prescaler of 1, 1.5, 2 and 2.5, it is not possible to
get the 48 MHz USB clock on the standard maximum frequence, 105 MHz. The maximum
frequency with a working USB is 96 MHz, or a overclocked, out-of-spec 120 MHz.
Nevertheless, 96 MHz is still a huge improvement.

5. Even on the same frequency, the application notes claimed some instructions may
run faster than its STM32 counterpart. According to the application notes,

    void delay(void)
        uint8_t i;
        for(i = 0; i < 75; i++);

7.4 us on STM32,
5.4 us on GD32.

By the way, the HSE clock needs a longer time to start and settle, but it's
irrelevant to Gnuk since chopstx uses a infinite loop to wait for HSE.

6. I still have problem with USB enumeration and the chip cannot be recognized
by the computer. Sometimes the USB CDC demo in chopstx works, sometimes it doesn't,
Gnuk simply hangs.

This is probably a problem caused by the differences of the clocks and timings
of the two chips, perhaps a software thing, perhaps a hardware thing.

I'm going to order and wait for the official development board and uses it as the
reference platform for further testing, otherwise it's going to be clueless.

I think I'd better not to send my chips to you for now, or it will be very confusing
and frustrating with the chip. Meanwhile, you could see if there are avaliable development
board for sale on AliExpress, once the program runs flawlessly on the development board,
I can send a bunch of the QFN chips to you for the new FST-01.

7. The hacky patch is attached at the bottom of this email. After applying the patch,
to program a GD32 chip, one needs the following:

# Disable Readout Protection 
openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c init -c 'reset halt' \
        -c 'mdb 0x1ffff800 12'         -c 'sleep 100' \
        -c 'mww 0x40022004 0x45670123' -c 'sleep 100' \
        -c 'mww 0x40022004 0xcdef89ab' -c 'sleep 100' \
        -c 'mww 0x40022008 0x45670123' -c 'sleep 100' \
        -c 'mww 0x40022008 0xcdef89ab' -c 'sleep 100' \
        -c 'mww 0x40022010 0x00000260' -c 'sleep 100' \
        -c 'mww 0x40022010 0x00000210' -c 'sleep 100' \
        -c 'mwh 0x1ffff800 0x5aa5'     -c 'sleep 100' \
        -c 'shutdown'

# Disable Page Write Protection
# TODO: What is this exactly? What code is needed to change for Gnuk?
openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c init -c 'reset halt' \
        -c 'mdb 0x1ffff800 12'         -c 'sleep 100' \
        -c 'mww 0x40022004 0x45670123' -c 'sleep 100' \
        -c 'mww 0x40022004 0xcdef89ab' -c 'sleep 100' \    
        -c 'mww 0x40022008 0x45670123' -c 'sleep 100' \
        -c 'mww 0x40022008 0xcdef89ab' -c 'sleep 100' \
        -c 'mww 0x40022010 0x00000210' -c 'sleep 100' \        
        -c 'mwh 0x1ffff802 0xff00'     -c 'sleep 100' \    
        -c 'mwh 0x1ffff804 0xff00'     -c 'sleep 100' \
        -c 'mwh 0x1ffff806 0xff00'     -c 'sleep 100' \
        -c 'mwh 0x1ffff808 0x00ff'     -c 'sleep 100' \
        -c 'mwh 0x1ffff80a 0x00ff'     -c 'sleep 100' \
        -c 'mww 0x40022010 0x00000080' -c 'sleep 100' \
        -c 'mdb 0x1ffff800 12'         -c 'mdw 0x4002201c 1' \
        -c 'shutdown'

# Mass Erase
openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c init -c 'reset halt' \
        -c 'flash erase_sector 0 0 31' \
        -c 'shutdown'

# Program a .bin to 0x08000000, ELF should be okay too
openocd -f interface/stlink-v2.cfg -f target/stm32f1x.cfg -c init -c 'reset halt' \
        -c 'flash write_image gnuk-vidpid.bin 0x8000000 bin'
        -c 'shutdown'

Using other commands such as "mass_erase" or "program" may not work, because of the
differences mentioned above.

Happy Hacking,
Tom Li

diff -uprN openocd-0.10.0/src/flash/nor/stm32f1x.c openocd-0.10.0.hack/src/flash/nor/stm32f1x.c
--- openocd-0.10.0/src/flash/nor/stm32f1x.c	2016-12-25 22:12:54.000000000 +0800
+++ openocd-0.10.0/src/flash/nor/stm32f1x.c	2018-02-19 07:26:57.097567934 +0800
@@ -102,8 +102,8 @@
 /* timeout values */
 struct stm32x_options {
 	uint16_t RDP;
@@ -163,7 +163,7 @@ static inline int stm32x_get_flash_statu
 	return target_read_u32(target, stm32x_get_flash_reg(bank, STM32_FLASH_SR), status);
-static int stm32x_wait_status_busy(struct flash_bank *bank, int timeout)
+static int stm32x_wait_status_busy(struct flash_bank *bank, unsigned int timeout)
 	struct target *target = bank->target;
 	uint32_t status;
@@ -736,30 +736,21 @@ static int stm32x_write(struct flash_ban
 	if (retval != ERROR_OK)
 		goto cleanup;
-	/* try using a block write */
-	retval = stm32x_write_block(bank, buffer, offset, words_remaining);
+	while (words_remaining > 0) {
+		uint32_t value;
+		memcpy(&value, buffer, sizeof(uint32_t));
-		/* if block write failed (no sufficient working area),
-		 * we use normal (slow) single halfword accesses */
-		LOG_WARNING("couldn't use block writes, falling back to single memory accesses");
-		while (words_remaining > 0) {
-			uint16_t value;
-			memcpy(&value, buffer, sizeof(uint16_t));
-			retval = target_write_u16(target, bank->base + offset, value);
-			if (retval != ERROR_OK)
-				goto reset_pg_and_lock;
-			retval = stm32x_wait_status_busy(bank, 5);
-			if (retval != ERROR_OK)
-				goto reset_pg_and_lock;
-			words_remaining--;
-			buffer += 2;
-			offset += 2;
-		}
+		retval = target_write_u32(target, bank->base + offset, value);
+		if (retval != ERROR_OK)
+			goto reset_pg_and_lock;
+		retval = stm32x_wait_status_busy(bank, FLASH_ERASE_TIMEOUT);
+		if (retval != ERROR_OK)
+			goto reset_pg_and_lock;
+		words_remaining -= 2;
+		buffer += 4;
+		offset += 4;
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 851 bytes
Desc: Digital signature
URL: <>

More information about the Gnuk-users mailing list