From e939d3ec76eba180deb36696a69cf5ea34c9876d Mon Sep 17 00:00:00 2001 From: Alessandro Gatti Date: Mon, 17 Nov 2025 03:42:06 +0100 Subject: [PATCH] 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 --- docs/develop/natmod.rst | 11 ++++++----- examples/natmod/btree/Makefile | 2 +- examples/natmod/deflate/Makefile | 2 +- examples/natmod/features0/Makefile | 2 +- examples/natmod/features1/Makefile | 2 +- examples/natmod/features2/Makefile | 2 +- examples/natmod/features3/Makefile | 2 +- examples/natmod/features4/Makefile | 2 +- examples/natmod/framebuf/Makefile | 2 +- examples/natmod/heapq/Makefile | 2 +- examples/natmod/random/Makefile | 2 +- examples/natmod/re/Makefile | 2 +- py/dynruntime.mk | 21 ++++++++++++++++++++- tests/run-natmodtests.py | 1 + tools/ci.sh | 9 +++++++-- tools/mpy_ld.py | 15 ++++++++++++--- 16 files changed, 57 insertions(+), 22 deletions(-) diff --git a/docs/develop/natmod.rst b/docs/develop/natmod.rst index 072d78b207..142e475366 100644 --- a/docs/develop/natmod.rst +++ b/docs/develop/natmod.rst @@ -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 `_ 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. diff --git a/examples/natmod/btree/Makefile b/examples/natmod/btree/Makefile index 1910c67c1f..4ded62bafd 100644 --- a/examples/natmod/btree/Makefile +++ b/examples/natmod/btree/Makefile @@ -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 diff --git a/examples/natmod/deflate/Makefile b/examples/natmod/deflate/Makefile index 5823aa4d45..0574bbaf41 100644 --- a/examples/natmod/deflate/Makefile +++ b/examples/natmod/deflate/Makefile @@ -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) diff --git a/examples/natmod/features0/Makefile b/examples/natmod/features0/Makefile index fb01b8d031..788d035eb8 100644 --- a/examples/natmod/features0/Makefile +++ b/examples/natmod/features0/Makefile @@ -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 diff --git a/examples/natmod/features1/Makefile b/examples/natmod/features1/Makefile index 4904051102..47deeed8f2 100644 --- a/examples/natmod/features1/Makefile +++ b/examples/natmod/features1/Makefile @@ -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 diff --git a/examples/natmod/features2/Makefile b/examples/natmod/features2/Makefile index 5ddb74087b..efd096c4ed 100644 --- a/examples/natmod/features2/Makefile +++ b/examples/natmod/features2/Makefile @@ -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 diff --git a/examples/natmod/features3/Makefile b/examples/natmod/features3/Makefile index 3573f41cac..85d1a13421 100644 --- a/examples/natmod/features3/Makefile +++ b/examples/natmod/features3/Makefile @@ -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 diff --git a/examples/natmod/features4/Makefile b/examples/natmod/features4/Makefile index 34fc3a7ef7..4eb657b722 100644 --- a/examples/natmod/features4/Makefile +++ b/examples/natmod/features4/Makefile @@ -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 diff --git a/examples/natmod/framebuf/Makefile b/examples/natmod/framebuf/Makefile index 35453c0bb4..a86efef41f 100644 --- a/examples/natmod/framebuf/Makefile +++ b/examples/natmod/framebuf/Makefile @@ -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) diff --git a/examples/natmod/heapq/Makefile b/examples/natmod/heapq/Makefile index 61e2fc8fcc..345359abb3 100644 --- a/examples/natmod/heapq/Makefile +++ b/examples/natmod/heapq/Makefile @@ -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 diff --git a/examples/natmod/random/Makefile b/examples/natmod/random/Makefile index bffbb32d60..27d8ec935f 100644 --- a/examples/natmod/random/Makefile +++ b/examples/natmod/random/Makefile @@ -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) diff --git a/examples/natmod/re/Makefile b/examples/natmod/re/Makefile index 6535693dcb..c5f05e64ab 100644 --- a/examples/natmod/re/Makefile +++ b/examples/natmod/re/Makefile @@ -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) diff --git a/py/dynruntime.mk b/py/dynruntime.mk index 030728cfc9..866d5fae7b 100644 --- a/py/dynruntime.mk +++ b/py/dynruntime.mk @@ -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 diff --git a/tests/run-natmodtests.py b/tests/run-natmodtests.py index e35e580dc1..cd6c643bf9 100755 --- a/tests/run-natmodtests.py +++ b/tests/run-natmodtests.py @@ -40,6 +40,7 @@ AVAILABLE_ARCHS = ( "xtensa", "xtensawin", "rv32imc", + "rv64imc", ) ARCH_MAPPINGS = {"armv7em": "armv7m"} diff --git a/tools/ci.sh b/tools/ci.sh index 60e870ce65..4ade31160b 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -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 diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index b3b28e453d..b363c6a25f 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -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("> 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: