Last time, we’ve configured the interrupt controller so we can boot up to main, there is one little more detail needed to get interrupt working, and that’s adding __attribute__ ((interrupt)) to your interrupt handlers, otherwise, the interrupt will trigger once, and then no more interrupt will be coming. Adding this will not break compiling on Cortex-M. It will not do anything on the Cortex-M platform, it was a thing on earlier ARM versions. With that, the interrupt part has been handled so far.

So, let’s have a look at where we are. The goal was, to have the stm32f1 HAL working on RISC-V microcontrollers that have compatible peripherals. As, in theory, the peripherals are independent of the core, and should be written in plain C, thus they should be able to recompile for a different architecture without a problem.

And this hold for most of them, but one thing to note is that the stm32 hal also uses the SysTick, which is part of the Cortex-M specs, thus won’t be present on a RISC-V core. On a RISC-V core, we have a system timer, which could be used to write a replacement for the code which uses the SysTick. Now, unlike the ARM Cortex-M cores, on the RISC-V architecture, we have different cores designed by different companies. While a RISC-V System Timer has been specified, they reside on different addresses on the two chips I am targetting. Furthermore, they’re running at different speeds. Therefore I’ll have to detect the core to detect the addresses to use, and the amount of ticks relative to the clock speed. For this part, we will have to provide a replacement implementation for the functions defined in stm32f1xx_hal.c. Most notably, the HAL_GetTick() function, which is used for timeouts in the HAL library.

The clock speed, is the next thing, right? After we boot, we wish to start our crystal oscillator, and run our clocks 72 MHz. And looking at the datasheets, the registers look compatible, however, the clock fails to start. Looking through the code, it looks like the macro __HAL_RCC_PLL_ENABLE is failing. Its implementation looks like (*(__IO uint32_t *) RCC_CR_PLLON_BB = ENABLE). What is going on here? It is supposed to get the PLLON bit in the RCC->CR register, yet it appears to be accessing a 32-bit word somewhere in memory. What is going on here is called bit-banding. Writing a word to a bit-banding address equals to a read-modify-write operation on a specific bit to another address. The PLLON bit is written using such a bit-banding address. This is a Cortex-M feature not present on the RISC-V cores we are targetting. After rewriting the macros to do a regular read-modify-write operation on the said register, the clock starts. We can override the included header where the macro resides by using the same file name, and specifying the directory before the HAL includes. Such way, the HAL itself remains unmodified, and the build system handles everything.

I will have to look for more replacements for bit banding access to certain bits in certain registers, but for now, the clock starts at the requested speed. When this runs, it’s time to try the I²C demo code I wrote for the STM32F103. If everything works as intended, this demo will run without further modification. As the code starts, the display initialises, and scans the I²C bus.

For the CH32V103, the binary version of OpenOCD is crashing. However, there has been a source release for the OpenOCD version required for the CH32V103. Newbrain on github made a source code request at WCH, as OpenOCD is GPL licensed, they should give the code, and they did. Building the code from this repo gives a working OpenOCD.
The I²C Demo runs fine on the CH32V103, but has some issues on the GD32VF103. After testing on the GD32F103, (the ARM version) it has the same issues, so it seems I’ve found an issue with GigaDevices I²C Peripheral.