tools/mpy_ld.py: Add RV64 natmod support.

This commit adds the ability to compile native modules for the RV64
platform, using "rv64imc" as its architecture name (eg.
"make ARCH=rv64imc" should build a RV64 natmod).

The rest of 64-bits relocations needed to build a native module are now
implemented, and all sample native modules build without errors or
warnings.  The same Picolibc caveats on RV32 also apply on RV64, thus
the documentation was updated accordingly.

RV64 native modules are also built as part of the CI process, but not
yet executed as the QEMU port is not yet able to load and run them.

Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
This commit is contained in:
Alessandro Gatti
2025-11-17 03:42:06 +01:00
parent 1f67289a9e
commit e939d3ec76
16 changed files with 57 additions and 22 deletions

View File

@@ -41,6 +41,7 @@ options for the ``ARCH`` variable, see below):
* ``xtensa`` (non-windowed, eg ESP8266)
* ``xtensawin`` (windowed with window size 8, eg ESP32, ESP32S3)
* ``rv32imc`` (RISC-V 32 bits with compressed instructions, eg ESP32C3, ESP32C6)
* ``rv64imc`` (RISC-V 64 bits with compressed instructions)
When compiling and linking the native .mpy file the architecture must be chosen
and the corresponding file can only be imported on that architecture. For more
@@ -190,7 +191,7 @@ The file ``Makefile`` contains:
# Source files (.c or .py)
SRC = factorial.c
# Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin, rv32imc)
# Architecture to build for (x86, x64, armv6m, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH = x64
# Include to get the rules for compiling and linking the module
@@ -232,15 +233,15 @@ Using Picolibc when building modules
------------------------------------
Using `Picolibc <https://github.com/picolibc/picolibc>`_ as your C standard
library is not only supported, but in fact it is the default for the rv32imc
platform. However, there are a couple of things worth mentioning to make sure
you don't run into problems later when building code.
library is not only supported, but in fact it is the default for the rv32imc and
rv64imc platforms. However, there are a couple of things worth mentioning to make
sure you don't run into problems later when building code.
Some pre-built Picolibc versions (for example, those provided by Ubuntu Linux
as the ``picolibc-arm-none-eabi``, ``picolibc-riscv64-unknown-elf``, and
``picolibc-xtensa-lx106-elf`` packages) assume thread-local storage (TLS) is
available at runtime, but unfortunately MicroPython modules do not support that
on some architectures (namely ``rv32imc``). This means that some
on some architectures (namely ``rv32imc`` and ``rv64imc``). This means that some
functionalities provided by Picolibc will default to use TLS, returning an
error either during compilation or during linking.

View File

@@ -7,7 +7,7 @@ MOD = btree_$(ARCH)
# Source files (.c or .py)
SRC = btree_c.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH ?= x64
BTREE_DIR = $(MPY_DIR)/lib/berkeley-db-1.xx

View File

@@ -7,7 +7,7 @@ MOD = deflate_$(ARCH)
# Source files (.c or .py)
SRC = deflate.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH ?= x64
ifeq ($(ARCH),armv6m)

View File

@@ -7,7 +7,7 @@ MOD = features0
# Source files (.c or .py)
SRC = features0.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH = x64
# Include to get the rules for compiling and linking the module

View File

@@ -7,7 +7,7 @@ MOD = features1
# Source files (.c or .py)
SRC = features1.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH = x64
# Include to get the rules for compiling and linking the module

View File

@@ -7,7 +7,7 @@ MOD = features2
# Source files (.c or .py)
SRC = main.c prod.c test.py
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH = x64
# Link with libm.a and libgcc.a from the toolchain

View File

@@ -7,7 +7,7 @@ MOD = features3
# Source files (.c or .py)
SRC = features3.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH = x64
# Include to get the rules for compiling and linking the module

View File

@@ -7,7 +7,7 @@ MOD = features4
# Source files (.c or .py)
SRC = features4.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH = x64
# Include to get the rules for compiling and linking the module

View File

@@ -7,7 +7,7 @@ MOD = framebuf_$(ARCH)
# Source files (.c or .py)
SRC = framebuf.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin)
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH ?= x64
ifeq ($(ARCH),armv6m)

View File

@@ -7,7 +7,7 @@ MOD = heapq_$(ARCH)
# Source files (.c or .py)
SRC = heapq.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH = x64
include $(MPY_DIR)/py/dynruntime.mk

View File

@@ -7,7 +7,7 @@ MOD = random_$(ARCH)
# Source files (.c or .py)
SRC = random.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH ?= x64
ifeq ($(ARCH),xtensa)

View File

@@ -7,7 +7,7 @@ MOD = re_$(ARCH)
# Source files (.c or .py)
SRC = re.c
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc)
# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin, rv32imc, rv64imc)
ARCH = x64
ifeq ($(ARCH),armv6m)

View File

@@ -106,7 +106,7 @@ else ifeq ($(ARCH),rv32imc)
# rv32imc
CROSS = riscv64-unknown-elf-
CFLAGS_ARCH += -march=rv32imac -mabi=ilp32 -mno-relax
# If Picolibc is available then select it explicitly. Ubuntu 22.04 ships its
# If Picolibc is available then select it explicitly. Ubuntu 24.04 ships its
# bare metal RISC-V toolchain with Picolibc rather than Newlib, and the default
# is "nosys" so a value must be provided. To avoid having per-distro
# workarounds, always select Picolibc if available.
@@ -120,6 +120,25 @@ endif
MICROPY_FLOAT_IMPL ?= none
else ifeq ($(ARCH),rv64imc)
# rv64imc
CROSS = riscv64-unknown-elf-
CFLAGS_ARCH += -march=rv64imac -mabi=lp64 -mno-relax
# If Picolibc is available then select it explicitly. Ubuntu 24.04 ships its
# bare metal RISC-V toolchain with Picolibc rather than Newlib, and the default
# is "nosys" so a value must be provided. To avoid having per-distro
# workarounds, always select Picolibc if available.
PICOLIBC_SPECS := $(shell $(CROSS)gcc --print-file-name=picolibc.specs)
ifneq ($(PICOLIBC_SPECS),picolibc.specs)
CFLAGS_ARCH += -specs=$(PICOLIBC_SPECS)
USE_PICOLIBC := 1
PICOLIBC_ARCH := rv64imac
PICOLIBC_ABI := lp64
endif
MICROPY_FLOAT_IMPL ?= none
else
$(error architecture '$(ARCH)' not supported)
endif

View File

@@ -40,6 +40,7 @@ AVAILABLE_ARCHS = (
"xtensa",
"xtensawin",
"rv32imc",
"rv64imc",
)
ARCH_MAPPINGS = {"armv7em": "armv7m"}

View File

@@ -378,6 +378,8 @@ function ci_qemu_setup_rv64 {
ci_gcc_riscv_setup
sudo apt-get update
sudo apt-get install qemu-system
sudo pip3 install pyelftools
sudo pip3 install ar
qemu-system-riscv64 --version
}
@@ -436,6 +438,9 @@ function ci_qemu_build_rv64 {
make ${MAKEOPTS} -C mpy-cross
make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV64 submodules
make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV64 test
# Test building native .mpy with rv64imc architecture.
ci_native_mpy_modules_build rv64imc
}
########################################################################################
@@ -669,9 +674,9 @@ function ci_native_mpy_modules_build {
make -C examples/natmod/$natmod ARCH=$arch
done
# features2 requires soft-float on rv32imc and xtensa.
# features2 requires soft-float on rv32imc, rv64imc, and xtensa.
make -C examples/natmod/features2 ARCH=$arch clean
if [ $arch = "rv32imc" ] || [ $arch = "xtensa" ]; then
if [ $arch = "rv32imc" ] || [ $arch = "rv64imc" ] || [ $arch = "xtensa" ]; then
make -C examples/natmod/features2 ARCH=$arch MICROPY_FLOAT_IMPL=float
else
make -C examples/natmod/features2 ARCH=$arch

View File

@@ -49,6 +49,7 @@ MP_NATIVE_ARCH_ARMV7EMDP = 8
MP_NATIVE_ARCH_XTENSA = 9
MP_NATIVE_ARCH_XTENSAWIN = 10
MP_NATIVE_ARCH_RV32IMC = 11
MP_NATIVE_ARCH_RV64IMC = 12
MP_PERSISTENT_OBJ_STR = 5
MP_SCOPE_FLAG_VIPERRELOC = 0x10
MP_SCOPE_FLAG_VIPERRODATA = 0x20
@@ -62,6 +63,7 @@ R_RISCV_32 = 1
R_X86_64_64 = 1
R_XTENSA_32 = 1
R_386_PC32 = 2
R_RISCV_64 = 2
R_X86_64_PC32 = 2
R_ARM_ABS32 = 2
R_386_GOT32 = 3
@@ -175,7 +177,7 @@ def asm_jump_xtensa(entry):
return struct.pack("<BH", jump_op & 0xFF, jump_op >> 8)
def asm_jump_rv32(entry):
def asm_jump_riscv(entry):
# This could be 6 bytes shorter, but the code currently cannot
# support a trampoline with varying length depending on the offset.
@@ -261,7 +263,14 @@ ARCH_DATA = {
MP_NATIVE_ARCH_RV32IMC << 2,
4,
(R_RISCV_32, R_RISCV_GOT_HI20, R_RISCV_GOT32_PCREL),
asm_jump_rv32,
asm_jump_riscv,
),
"rv64imc": ArchData(
"EM_RISCV",
MP_NATIVE_ARCH_RV64IMC << 2,
8,
(R_RISCV_64, R_RISCV_GOT_HI20, R_RISCV_GOT32_PCREL),
asm_jump_riscv,
),
}
@@ -779,7 +788,7 @@ def do_relocation_data(env, text_addr, r):
or env.arch.name == "EM_XTENSA"
and r_info_type == R_XTENSA_32
or env.arch.name == "EM_RISCV"
and r_info_type == R_RISCV_32
and r_info_type in (R_RISCV_32, R_RISCV_64)
):
# Relocation in data.rel.ro to internal/external symbol
if env.arch.word_size == 4: