Posts

Showing posts with the label CH32V307

RISCV memory access

 This is a small summary of why accessing memory when debugging a riscv cpu is complicated. It is work in progress, the current code is available on swindle's github. Introduction As usual, as it is RISCV, a lot is optional with important details left to implementation So let's see what is possible (definition from Gemini): Recommended : Program Buffer Execution (ProgBuf): Memory is accessed by halting the CPU and executing standard load or store instructions placed into a dedicated buffer. This uses the core's native pipeline, meaning all MMU translations, Physical Memory Protection (PMP), and cache hierarchy rules apply automatically. Optional: System Bus Access (SysBus): The Debug Module acts as an independent master directly on the system interconnect, bypassing the CPU entirely. This allows reading or writing physical memory while the core is actively running, though it does not go through the CPU's MMU or local L1/L2 caches. (NB this is more or less what ARM doe...

Swindle : Downloading code to the CH32VXX faster

 Now that the new bootloader has shown that it can be fast to write to the CH32v3xx flash, time to revisit the swindle way of loading code. (Gemini was really helpful again. But as a smart assistant, no vibe coding here). Starting point: So we started around average speed of 3kB/S (including erase, download and write) Optim 1: use bigger blocks Before, we were configuring the block size/erase size to be 256 bytes (same as the small hardware pages). Reconfiguring the description to be 4kB gave a small speed bump => 5 kB (internally it will call 256 bytes multiple times, it just reduces the overhead). Optim 2: Faster writing to RAM The dominant slowdown is the amount of time needed to write to the CH32V3xx ram (then we call the flashstub to actually write it, that's pretty fast). Just the writing caps the overall speed to 9 kB/s , without even erasing or writing. So the optimisation is to use a fast_write_ram function there that is using abstract commands with auto exec to pipelin...

Revisiting the CH32V303 bootloader (with gemini), it is now FAST

 The DFU bootloader i wrote a while back for the swindle running on the CH32V303/CH32V307 was a quick job. It contained a cut down version of Esprit (the framework) with FreeRTOS and everything. It was about 14kB big. Since i'm playing with antigravity/gemini, it was a good sample task to run it. (On the antigravity front : it does a good job. But you really want to give small tasks, with clear instructions and  check whatever it wants to do. It tends to circumcumvent/hide problems rather than solving them.) Now back to the bootloader. The previous scheme was to use the native 256 bytes for erase and write. Write is actually fast, but erasing the whole 240kB was slow. It happens than the CH32v3xx chips have 2 extra modes compared the the bluepill family  : 32 kB and 64 kB erase. So i switched to a slightly incorrect algorithm (basically if asked to erase 4Kb at the beginning of a 32kB block address, erase the full 32 kB, and dont erase already erased blocks). And now it's...

Swindle Preview 6

New year version  Preview 6 https://github.com/mean00/swindle/releases Ethernet version for the CH32V307 eval board RTT & serial fixes Updated blackmagic engine Breakpoints in Flash for CH32V1/2 

Ethernet Swindle and gcc vs clang

Image
 There will be soon a new flavor of the swindle : The ethernet swindle It's starting to work nicely, utltimately the goal is to use the CH32V208 that is available for ~ 4$ on aliexpress. It's basically a CH32V307 without HS usb and without fpu,  same flash, same ram (more on that later).  LWIP is pretty big and i'm running short on both flash & ram. While looking into it, i discovered a nice option that gcc has and clang does not "-msave-restore" Since the Riscv does not have stmia/ldmia style instruction, it must push and pop all registers one at a time when entering/exiting a function. That is consuming a lot of code space for short functions. The -msave-restore creates function to save/restore , all possible variants and call them instead of manually pushing/popping registers. Let's give it a try, baseline is clang + hw FPU: Clang + HW FPU    255 508   (+0kB) Clang + no FPU       257 632  (+2KB) Clang + FPU+LTO  232...

SW breakpoint for the CH32V203 (and similar)

  The WCH doc clearly  states the QingKev4 b does not have hardware breakpoints. We saw that a bit earlier. The CH32V203 among others is a QingKeV4 b . I've added support for flash software breakpoint for the CH32V[2/3]xx to swindle. It is still a bit experimental. When such software breakpoints are used, under the hood the following happens : - Identify the page where the breakpoint is ( 256 bytes page for the CH32V[2/3]xx - Read the page - Store the 16 bits where the breakpoint is - Replace those 16 bits by a "ebreak" opcode - Erase the page - Write the modified page When removing the breakpoint the same thing happens except we put back the original opcode. It is a bit slow, but usable. The main drawback is that it will speed up flash wearing a lot. The framework is in place, if and when need it will be easy to add such functions to other chips. The main difference with other flashing operations are : - we want to use the smaller size possible. Usually we aim a bit hig...

CH32V3, debug in ram with vscode continued

Summary of previous episodes (CH32v3): 1- Reallocate shadow ram to have 128k of RAM 2- In flash, put a basic harmless loop 3- Tweak the linker script to put everything in ram 4- Load the code (to ram), change the PC  to the code in ram 5- You now upload much faster with infinite software breakpoints Vscode You can use the cortexm extension with riscv, it works fine with one caveat : you cannot "attach" You can only "launch" (this is specific to riscv) and that's the root of the problem. The init script looks like this :     {              "name" : "riscv GCC (pico)" , "type" : "gdb" , "request" : "launch" , "cwd" : "${workspaceRoot}" , "target" : "${workspaceRoot}/build/swindle_bootloader_ch32v3x_GCC_DEBUG.elf" , "gdbpath" : "${config:riscv_gdb}" , "breakAfterReset...

CH32V3xx Faster development by doing everything ram

Image
  Debugging the CH32V3xx (and V2xx) The CH32V3xx chips are rather good, they are pretty fast, cheap with tons of peripherals. Swindle supports the CH32V3xx chips (CH32V2XX as well but they are less interesting imho). Debugging with them is not so great : - They completely lack watchpoint (some revisions of WCH riscv cores do have them, but not the CH32V3x  it seems) - Only 4 breakpoints - Writing to flash is really slow compared to other similar chips ( from STM or Gigadevice) Shadow Flash ? The chip has 'shadow flash' i think.  It is using slow flash ( or RRAM/MRAM)  and shadowing them with  RAM.  At reset , it copies the flash to ram and execute from there. That way you get low cost and fast execution time. The option bytes  allocates the physical ram between shadow flash area and ram area.  But the total cannot exceed the physical amount of ram, ~ 256+64 in my case. Debugging in bigger RAM Similarly to what was done with the RP2040, we can debu...

swindle (lnBMP) v0.3

 A small release of swindle, a blackmagic derivative with rust in it : Changelog (short): - Better CH32v3xx support (host and target) - Rewrote ADiv logic so that we can ... - Use RP2040 PIO hardware to drive SWD - Update to latest blackmagic and still the M ain features : - Run on GD32F303, CH32V303, RP2040 - Support ARM devices and WCH riscv devices (CH32V2xx and CH32V3xx) - Soft breakpoint to debug code in ram (Arm only for now) - Built in FreeRTOS support through  "mon fos M0|M4|RV"

CH32V203C8xx : no breakpoint ? :(

 A couple of months back i bought some CH32V203 on ebay.  Why ? They were cheap, they are pin to pin compatible with STM32F103Cxxxx, i can put them on bluepill board . They are fast riscv.  Why not. NB: by mistake i bought the ones with 64kB of flash :( But there is a BIG showstopper, there seems to be no hw breakpoints, only sw breakpoints.  If you have a lot of ram (like the RP2040) you can put the code in ram until it works fine and then put it in flash. That's not the case here. Having the debugger read/modify/write all the time to but sw breakpoint in flash is really a pain. In theory, the "flash" of the CH32 is actually flash copied to ram, so if there was a way to use the fake flash ram to put SW breakpoint in it, we would be good. For reference, the CH32V303 has only 4 breakpoints, and no watchpoint (i.e. triggers with the right capabilities) which is already a bit of a pain in the neck. I'll put those aside for the moment.

lnBMP : CH32V3xx support

Image
 It is painful, but it is beginning to work : On the picture a RP2040-pico zero running lnBMP and happily debugging a ch32v307 Riscv chip (of course it can still debug Arm chips) Perigoso did a lot of work there, only the communication protocol /implementation was missing. It is still a bit slow though :(

lnBMP + CH32V303!

Image
 Finally got the PCB (that was my first one) As expected a couple of mistakes (selected footprints too small etc..) but it works! Since the CH32V303 is running at 140 Mhz, it is faster than the bluepill based one. For reference, i did the same PCB for a GD32F303, and it was more complicated. The ch32v303 version is much simpler, no need for extra glue. The chip, a 8mhz crystal, couple of resistors and caps and that's it. I've also ran into a false problem. The normal BMP  just reboots when it asserts, so you dont see that happening. The lnBMP halts when it asserts, took me a bit of time to realize that was not due to a problem i introduced.

lnBMP+CH32V3 : Slowly getting there...

Image
 It is taking a bit of time, but getting there. What has changed since the last time : A tinyUSB based DFU bootloader  for the CH32V3x, a bit quirky but good enough for now and a small PCB (basically my first one) to host the CH32V303 and provides the pin out i usually use. Also, i fixed the hosted mode of lnBMP so it behaves like a genuine black magic, i.e. you can have the code running on your PC and use the BMP/lnBMP just as a hardware interface to your board.

lnBMP running on a CH32V303

Image
The lnBMP is a project using the blackmagic probe core + a gdb remote protocol parser written in rust, on top of the lnArduino framework. As it is running on lnArduino, i'm making sure it works also on the CH32v3x chips You can see from left to right  : 1- The bluepill board under debug 2-  lnBlackmagic running on a homeboard based on  CH32v303 3- The WCH debugger to debug the CH32V303 I ported a basic tinyusb driver for the ch32v3x so now it works to some extent! That WCH chip is pretty good, plenty of flash, plenty of ram, 144 Mhz, FPU, plenty of IO and compatible largely with the good ol' STM32F103.

Rust + CH32V307 + ILI9341 : Small demo video

Image
The mandatory youtube-video-or-it-didnt-happen (This is the screen test for my power supply)

CH32V307 : tinyUsb

Image
One of the last big chunk of support for the CH32V3x is now working : Usb TinyUSB  has just received a driver for the CH32V307, which was merged in lnArduino I modified the driver a bit so it fits better within lnArduino. Within lnArduino both high speed and full-speed work, while inside tinyUsb only high speed is working?

CH32V307 : FPU + FreeRTOS

Toolchain/Clang  Time to enable the FPU! I've modified the   LLVM-embedded-toolchain-for-rv32  so that it builds both soft-ftp and hard-fp clang runtimes (it is a bit clunky but works). FreeRTOS WCH implementation The FreeRTOS implementation saves this on the task stack :  MEPC [FPU] Normal regs MStatus [FPU] depends on a compilation switch.  It contains either all the FPU registers (the 32 of them) or nothing. Always. This is problematic. Saving all those registers is hitting the stack hard and it takes time. Furthermore, that happens for all task switches  even if the FPU is not used. Conversely, if you dont save the FPU registers and use the FPU in 2 different tasks, you'll get data corruption. Naive improvement I implemented a very naive change using the FS bits in mstatus. These FS bits can have 2 interesting values :  FPU never used (Off, original, clean) FPU dirty (i.e. used at some point). So, when the FS bits are "not used", you can skip ...

CH32V307 : Rust + I2C + Clang

Image
 I2C is still  a bit unstable but here we go : CH32V307+SSD1306 + rust + clang ! This is a pic of the simplerSSD1306 rust driver running on the CH32V307 + rnArduino NB: The code seems to be significantly larger than the exact same thing built for bluepill / Arm cortex m3 though. NB: Seems the rust target riscv32-imafc does not exist as of today. So no FPU with rust!

CH32V307 : SPI & LCD

Image
After tackling some riscv/clang issues, i can now say that the compatibility of the CH32V307 with the good old STM32F103 is pretty good.  lnArduino works quite well so far,  the associated sample code is running okay-ish. There are still some stuff to work on though : - i2c is unstable - the init code/linker script does not seem to be 100% correct - rust binaries are really huge (they should be ~ 60 kB, they end up being 300kB, like if the clang LTO was not working). - so called hardware fast interrupt seems to be doing nothing at all - i still have to figure out how to enable the fpu without hammering the stack on interrupt/context switch Overall, the CH323V307 seems to be a valid replacement for the GD32VF103 (which is going into oblivion or so it seems). Mandatory pic-or-it-didnt-happen (the exact same driver/code is running also on stm32f103):  

CH32V307 : Full llvm/clang toolchain

 Clang /LLVM can be a bit complicated to build especially if you want to use it with riscv32+picolibc. There is an   arm toolchain build project  to build it for arm cores,. Fortunately,  it is easy to patch to build for rv32imac. I posted the patched version on  github Checkout the project then ln -s $PWD/build/_deps/llvmproject-src/llvm/lib lib then mkdir build && cd build &&  cmake  -G Ninja .. && ninja &&  ninja package-llvm-toolchain You end up with a full blown LLVM C/C++ toolchain in LLVM.....tar.gz