Sunday, February 22, 2026

Port MicroPython to PolarFire SoC

 We have built MicroPython for STM32F429DISC previously.  We'll use it as a guide to port MicroPython to MPFS Discovery Kit and Beagle-V Fire, featuring PolarFire SoC.  It is straightforward to compile for the minimal configuration.  We aim to achieve the same functionalities as STM32F429DISC.  In the hardware-wise comparison, the shortage of non-volatile memory can be overcome by using a micro-SD card.  PolarFire RISC V supports both single and double precision floating point; STM32F429 supports only single precision. 

STM32F429I-DISC1MPFS-DISCO-KITBeagle-V Fire
CPUCortex-M4FQuad RV64IMAFDCQuad RV64IMAFDC
Flash2M128K128K
SRAM256K1.8M1.8M
SDRAM8M1G2G
eMMC--16G
SPI Flash--16M
Debug/ProgST-LINKFlashProFlashPro
USB-UART133
Ext Headers2x 2x32RPi + MikroBusBeagle Cape 2x 2x23
ConnectorsMIPI CSIM.2 PCIe, MIPI CSI, QSH-40
ADC Channels24?-7
DAC Channels2--
User LEDs2812
Push Buttons222
Dip Switches-8-
SensorMEMS gyro--
USBOTG-OTG
Ethernet-1 GigE1 GigE
Display2.4" QVGA LCD touch--
Ext Storage-microSDmicroSD

The STM32F429DISC build includes the following modules.

>>> help('modules')
__main__          builtins          json              select
_asyncio          cmath             machine           socket
_onewire          collections       math              stm
array             deflate           micropython       struct
asyncio/__init__  dht               network           sys
asyncio/core      errno             onewire           time
asyncio/event     framebuf          os                uasyncio
asyncio/funcs     gc                platform          uctypes
asyncio/lock      hashlib           pyb               vfs
asyncio/stream    heapq             random
binascii          io                re
Plus any modules on the filesystem

The MicroPython does not seem have a good way to manage configurations.  The #define parameters that affect the build can come from a number of places, many include files and many makefiles.  And the dependencies are not always clear.

First MICROPY_CONFIG_ROM_LEVEL can range from minimum, core, basic, extra to full, which enables more feature.  We can change it from minimum to extra.  This enables the majority of features, including compiler, stack check, helper REPL, EMACS keys in REPL, scheduler, micropython module, etc.  

 Then we add the additional options enabled by STM32.  We include FROZEN_MANIFEST, which requies in the micropython-lib submodule.  We enable VFS, which turns out have some tricky dependencies.  We have to define MICROPY_VFS_FAT in the makefile in order to bring in the oofatfs.  This compiles to about 300K (text+data).  STM32 uses a portion (112K out of 2M) of its Flash to make a file system that stores initialization files like "boot.py" and "main.py". We will make use of the micro SD card.

 Next, we want to enable floating number.  We run it on one of the application cores that support floating number.  We set MICROPY_FLOAT_IMPL to double in the makefile.  This causes additional variable to be set.  This compiles to 360K and the math module is available.

We enable the machine module by defining MICRO_PY_MACHINE.  The machine module supports peripherals such as uart, spi, i2c, timer etc; board specific code has to be write to make them work. Additional python modules can be added to manifest.py.  We also include the network module.  Another useful module in STM32 is stm, which provides access to peripheral registers and we implement a similar module. The compiles to 430K.

At this point, we have the following modules,

>>> help('modules')
__main__          builtins          json              select
_asyncio          cmath             machine           socket
array             collections       math              struct
asyncio/__init__  deflate           micropython       sys
asyncio/core      errno             mpfs              time
asyncio/event     framebuf          network           uasyncio
asyncio/funcs     gc                os                uctypes
asyncio/lock      hashlib           platform          vfs
asyncio/stream    heapq             random
binascii          io                re

Now we have to write some actual code.  As a warm-up, we first add the LED class to the machine module.  Extra classes can be added by defining MACHINE_EXTRA_GLOBALS in the port modmachine.c, which is inserted into extmod/modmachine.c.  STM32 has a separate module, pyb, that contains the LED and other devices with some overlap with machine.  We can borrow code from STM32 and only change the GPIO code.  There are 8 user LEDs that can now be controlled by from Python.

Before we add the drivers for the other peripherals, GPIO, SPI, I2C, UART, Timers, we want to look into the Micropython compilation and assembly code for RISC-V by defining EMIT_RV32 and EMIT_INLINE_RV32.  MicroPython can emit native code instead of byte code.  The decorator @micropython.asm_rv32 seems to work; as example,

>>> @micropython.asm_rv32
... def mul(a0,a1):
...     mv(t0,a0)
...     mv(t1,a1)
...     mul(a0,t0,t1)
...
>>> mul(5,10)
50

But @micropython.native does not. It crashes when running the function with that decorator; the cause of the crash is illegal instruction.  Interestingly, it does not work on STM32 either.   EMIT_THUMB_ARMV7M is enabled by default, but disabling it does not solve the problem.  We'll have to investigate further.  In addition, we need to extend to support RV64.

 


 

No comments:

Post a Comment