Tuesday, January 19, 2021

GD32E230: a better STM32F0?


On my last LCSC order, I bought a few GD32E230 chips, specifically the GD32E230K8T6.  I chose the LQFP parts since I have lots of QFP32 breakout boards that I've used for other QFP32 parts.  Gigadevice is much better than many other Chinese MCU manufacturers when it comes to providing English documents.  After my past endeavors trying to understand datasheets from WCH and CHK, going through the Gigadevice documentation was rather pleasant.

Although Gigadevice makes no mention of any STM32 compatibility, but the first clue is the matching pinouts of the STM32F030 and GD32E230.  To prepare for testing, I tinned the pads on a couple of breakout boards, applied some flux, and laid the chips on the pads.  I laid the modules on a cast-iron skillet, and heated it up to about 240C.  The solder reflowed well, however I noticed some browning of the white silkscreen.  Next time I'll limit the temperature to 220C.  After testing for continuity and fixing a solder bridge, I was ready to try SWD.  I connected 3.3V power and the SWD lines, and ran "pyocd cmd -v":

0000710:INFO:board:Target type is cortex_m
0000734:INFO:dap:DP IDR = 0x0bf11477 (v1 MINDP rev0)
0000759:INFO:ap:AHB5-AP#0 IDR = 0x04770025 (AHB5-AP var2 rev0)
0000799:INFO:rom_table:AHB5-AP#0 Class 0x1 ROM table #0 @ 0xe00ff000 (designer=4 3b part=4cb)
0000812:INFO:rom_table:[0]<e000e000:SCS-M23 class=9 designer=43b part=d20 devtyp e=00 archid=2a04 devid=0:0:0>
0000823:INFO:rom_table:[1]<e0001000:DWT class=9 designer=43b part=d20 devtype=00 archid=1a02 devid=0:0:0>
0000841:INFO:rom_table:[2]<e0002000:BPU class=9 designer=43b part=d20 devtype=00 archid=1a03 devid=0:0:0>
0000848:INFO:cortex_m_v8m:CPU core #0 is Cortex-M23 r1p0
0000859:INFO:dwt:2 hardware watchpoints
0000866:INFO:fpb:4 hardware breakpoints, 0 literal comparators

I did little probing around the chip memory.  The GD32E23x user manual shows SRAM at 0x20000000, like STM32 parts.  The contents looked like random values, which I could overwrite using the pyocd "ww' command.  Writing to 0x20002000 resulted in a memory fault, indicating the part does not have any "bonus" RAM beyond 8kB.

Next, I tried using the built-in serial bootloader.  After connecting BOOT0 to VDD and connecting power, PA9 and PA10 were pulled high, indicative of the UART being activated.  However my first attempt at using stm32flash was not successful:

After attaching my oscilloscope, and writing a small bootloader protocol test program, I was able to determine that the responses did seem to conform to the STM32 bootloader protocol.  I did notice that the baud rate from the GD32E230 was only 110kbps, so it wasn't perfectly matching the 115.2kbps speed of the 0x7F byte sent for baud rate detection.  To avoid the potential for data corruption, I switched to 57.6kbps.  Before resorting to debugging the source for stm32flash, my test of stm32loader gave better results:
$ stm32loader -V -p com39
Open port com39, baud 115200
Activating bootloader (select UART)
*** Command: Get
    Bootloader version: 0x10
    Available commands: 0x0, 0x2, 0x11, 0x21, 0x31, 0x43, 0x63, 0x73, 0x82, 0x92, 0x6
Bootloader version: 0x10
*** Command: Get ID
Chip id: 0x440 (STM32F030x8)
Supply -f [family] to see flash size and device UID, e.g: -f F1

Next, I was ready to try flashing a basic program.  I first checked for GD32E support in libopencm3.  No luck.  Then as I read through the user manual, I noticed GPIOA starts at 0x4800 0000 on AHB2, the same as STM32F0 devices.  The register names didn't match the STM32, but the function and offsets were the same.  For example on the GD32E, the register to clear individual GPIOA bits is called GPIOA_BC, rather than GPIOA_BRR as it is called on the STM32.  The clock control registers, called RCU on the GD32E, also matched the STM32 RCC registers.  Since it was looking STM32F0 compatible, I tried flashing my blink example with stm32loader, and it worked!

The LED was flashing faster than it did with the STM32F030.  A little searching revealed that the ARM Cortex-M23, like the M0+, has a 2-stage pipeline.  The STM32F030 with it's M0 core has a 3-stage pipeline.  My delay busy loop needs to be four cycles per iteration, and on the M23, the bne instruction only takes two cycles.  My solution is adding a nop instruction based on an optional compile flag.

One problem I have yet to resolve with the GD32E is support for the bootloader Go/0x21 command.  With the STM32F0, I left BOOT0 high, and used DTR to toggle nRST before uploading new code.  The stm32flash "-g 0" option made the target run the uploaded code after flashing was complete.  I went back to debugging stm32flash, and discovered that it is hard-coded to use the "Get Version"/0x01 command, and silently fails if the bootloader responds with a NAK.  After a few mods to the source, I was able to build a version that works with the GD32E230, however the Go command still doesn't work.  Perhaps a task for a later date will be to hook up a debug probe to see what the E230 is doing when it gets the Go command.

Overall, I'm quite happy with the GD32E230K8T6.  They cost less than half the equivalent STM32 parts, and are even cheaper than other Chinese STM32 clones I've seen.  They are lower power and their maximum clock speed is 50% faster than the STM32F0.  In addition to the shorter 2-stage pipeline, the GD32E devices support single-cycle IO, making them faster for bit-banged communications than the STM32F0 which takes 2 cycles to write to a GPIO pin.  The GD32E230 also has some new features, which might be worth discussing in a future blog post.

1 comment:

  1. Can you please check if you get your device to reset with the SYSRESETREQ bit? After observing some strange behavior (like peripherals not reset at the begin of a debug session with J-Link and Eclipse) and the inability to use the Pre-run reset.

    Then I tried to add a monitor command to directly write 0x5fa0004 into SCB->AIRCR and it had zero effect. Next I tried to do it from within my own code (to provoke a reset loop when it works) but the write is just ignored. Calling nvic_system_reset() will not work either, it will be stuck in an infinite loop, waiting for a reset that will never happen, it works neither during debug nor when free running.