Skip to content

Commit 46b10ee

Browse files
committed
ports/qemu: Update port.
Signed-off-by: iabdalkader <[email protected]>
1 parent f66a66d commit 46b10ee

File tree

21 files changed

+998
-32
lines changed

21 files changed

+998
-32
lines changed

ports/qemu/Dockerfile.riscv

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# This file is part of the MicroPython project, https://micropython.org/
2+
#
3+
# The MIT License (MIT)
4+
#
5+
# Copyright (c) 2025 Alessandro Gatti
6+
#
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy
8+
# of this software and associated documentation files (the "Software"), to deal
9+
# in the Software without restriction, including without limitation the rights
10+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
# copies of the Software, and to permit persons to whom the Software is
12+
# furnished to do so, subject to the following conditions:
13+
#
14+
# The above copyright notice and this permission notice shall be included in
15+
# all copies or substantial portions of the Software.
16+
#
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
# THE SOFTWARE.
24+
25+
FROM ubuntu:24.04
26+
27+
RUN apt-get update && \
28+
apt-get install -y \
29+
gcc-riscv64-unknown-elf \
30+
picolibc-riscv64-unknown-elf \
31+
python3 \
32+
python3-pip \
33+
python3-platformdirs \
34+
python3-pyelftools \
35+
python3-serial \
36+
qemu-system && \
37+
pip3 install ar --break-system-packages
38+
39+
WORKDIR /micropython

ports/qemu/Makefile

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,12 @@ ifeq ($(QEMU_ARCH),riscv32)
3535
MICROPY_HEAP_SIZE ?= 143360
3636
FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_asm_rv32.py', 'frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))"
3737
endif
38+
ifeq ($(QEMU_ARCH),riscv64)
39+
MICROPY_HEAP_SIZE ?= 204800
40+
FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_const.py',))"
41+
endif
42+
43+
MICROPY_FLOAT_IMPL ?= float
3844

3945
# include py core make definitions
4046
include $(TOP)/py/py.mk
@@ -44,6 +50,20 @@ GIT_SUBMODULES += lib/berkeley-db-1.xx
4450

4551
CFLAGS += -DMICROPY_HEAP_SIZE=$(MICROPY_HEAP_SIZE)
4652

53+
ifeq ($(MICROPY_FLOAT_IMPL),double)
54+
CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_DOUBLE
55+
else ifeq ($(MICROPY_FLOAT_IMPL),float)
56+
CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_FLOAT -fsingle-precision-constant
57+
else
58+
CFLAGS += -DMICROPY_FLOAT_IMPL=MICROPY_FLOAT_IMPL_NONE
59+
endif
60+
61+
ifeq ($(SUPPORTS_HARDWARE_FP_SINGLE),1)
62+
CFLAGS += -DMICROPY_HW_FPU=1
63+
else ifeq ($(SUPPORTS_HARDWARE_FP_DOUBLE),1)
64+
CFLAGS += -DMICROPY_HW_FPU=1
65+
endif
66+
4767
################################################################################
4868
# ARM specific settings
4969

@@ -57,6 +77,7 @@ LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
5777
SRC_C += \
5878
mcu/arm/errorhandler.c \
5979
mcu/arm/startup.c \
80+
mcu/arm/systick.c \
6081
shared/runtime/semihosting_arm.c \
6182

6283
endif
@@ -94,6 +115,39 @@ SRC_BOARD_O += mcu/rv32/entrypoint.o
94115

95116
endif
96117

118+
################################################################################
119+
# RISC-V 64-bit specific settings
120+
121+
ifeq ($(QEMU_ARCH),riscv64)
122+
123+
CROSS_COMPILE ?= riscv64-unknown-elf-
124+
125+
GCC_VERSION = $(word 1, $(subst ., , $(shell $(CC) -dumpversion)))
126+
127+
RV64_ABI = lp64
128+
129+
QEMU_ARGS += -bios none
130+
131+
# GCC 10 and lower do not recognise the Zicsr extension in the architecture name.
132+
ifeq ($(shell test $(GCC_VERSION) -le 10; echo $$?),0)
133+
RV64_ARCH ?= rv64imac
134+
else
135+
# Recent GCC versions explicitly require to declare extensions.
136+
RV64_ARCH ?= rv64imac_zicsr
137+
endif
138+
139+
AFLAGS += -mabi=$(RV64_ABI) -march=$(RV64_ARCH)
140+
CFLAGS += $(AFLAGS) -mcmodel=medany
141+
LDFLAGS += -mabi=$(RV64_ABI) -march=$(RV64_ARCH) -Wl,-EL -mcmodel=medany
142+
143+
SRC_C += \
144+
mcu/rv64/interrupts.c \
145+
mcu/rv64/startup.c \
146+
147+
SRC_BOARD_O += mcu/rv64/entrypoint.o
148+
149+
endif
150+
97151
################################################################################
98152
# Project specific settings and compiler/linker flags
99153

@@ -140,7 +194,7 @@ CFLAGS += $(SPECS_FRAGMENT)
140194
LDFLAGS += $(SPECS_FRAGMENT)
141195
endif
142196

143-
RUN_TESTS_FULL_ARGS = -t execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../ports/qemu/$<" $(RUN_TESTS_ARGS)
197+
RUN_TESTS_FULL_ARGS = -t execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../ports/qemu/$<" --mpy-cross-flags='$(MPY_CROSS_FLAGS)' $(RUN_TESTS_ARGS)
144198

145199
################################################################################
146200
# Source files and libraries
@@ -156,21 +210,34 @@ SRC_C += \
156210
shared/runtime/stdout_helpers.c \
157211
shared/runtime/sys_stdio_mphal.c \
158212

159-
LIB_SRC_C += $(SRC_LIB_LIBM_C)
160-
LIB_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C)
213+
ifeq ($(MICROPY_FLOAT_IMPL),double)
214+
LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_C)
215+
ifeq ($(SUPPORTS_HARDWARE_FP_DOUBLE),1)
216+
LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_SQRT_HW_C)
217+
else
218+
LIBM_SRC_C += $(SRC_LIB_LIBM_DBL_SQRT_SW_C)
219+
endif
220+
else
221+
LIBM_SRC_C += $(SRC_LIB_LIBM_C)
222+
ifeq ($(SUPPORTS_HARDWARE_FP_SINGLE),1)
223+
LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_HW_C)
224+
else
225+
LIBM_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C)
226+
endif
227+
endif
161228

162229
OBJ += $(PY_O)
163230
OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o))
164231
OBJ += $(addprefix $(BUILD)/, $(SRC_BOARD_O))
165-
OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o))
232+
OBJ += $(addprefix $(BUILD)/, $(LIBM_SRC_C:.c=.o))
166233

167234
# List of sources for qstr extraction
168-
SRC_QSTR += $(SRC_C) $(LIB_SRC_C)
235+
SRC_QSTR += $(SRC_C) $(LIBM_SRC_C)
169236

170237
################################################################################
171238
# Main targets
172239

173-
all: $(BUILD)/firmware.elf
240+
all: $(OBJ)
174241

175242
.PHONY: repl
176243
repl: $(BUILD)/firmware.elf
@@ -196,7 +263,7 @@ test_natmod: $(BUILD)/firmware.elf
196263
$(eval DIRNAME=ports/$(notdir $(CURDIR)))
197264
cd $(TOP)/tests && \
198265
for natmod in btree deflate framebuf heapq random_basic re; do \
199-
./run-natmodtests.py -p -d execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_EXTRA) extmod/$$natmod*.py; \
266+
./run-natmodtests.py -t execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_EXTRA) extmod/$$natmod*.py; \
200267
done
201268

202269
$(BUILD)/firmware.elf: $(LDSCRIPT) $(OBJ)

ports/qemu/README.md

Lines changed: 49 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
MicroPython port to qemu
22
========================
33

4-
This is experimental, community-supported port for Cortex-M and RISC-V RV32IMC
5-
emulation as provided by QEMU (http://qemu.org).
4+
This is experimental, community-supported port for Cortex-M and RISC-V
5+
RV32IMC/RV64IMC emulation as provided by QEMU (http://qemu.org).
66

77
The purposes of this port are to enable:
88

@@ -26,9 +26,9 @@ Dependencies
2626
For ARM-based boards the build requires a bare-metal ARM toolchain, such as
2727
`arm-none-eabi-gcc`.
2828

29-
### RISC-V
29+
### RISC-V 32
3030

31-
For RISC-V-based boards the build requires a bare metal RISC-V toolchain with GCC 10
31+
For RV32-based boards the build requires a bare metal RISC-V toolchain with GCC 10
3232
or later, either with multilib support or 32 bits specific (M, C, and Zicsr
3333
extensions must be supported, along with ilp32 ABI). Both newlib and picolibc are
3434
supported, with the latter having precedence if found.
@@ -37,6 +37,47 @@ Most pre-built toolchains should work out of the box, either coming from your
3737
Linux distribution's package manager, or independently packaged ones like
3838
[xPack](https://xpack.github.io/dev-tools/riscv-none-elf-gcc/).
3939

40+
### RISC-V 64
41+
42+
For RV64-based boards the build requires a bare metal RISC-V toolchain with GCC 10
43+
or later, either with multilib support or 64 bits specific (M, C, and Zicsr
44+
extensions must be supported, along with lp64 ABI). In other words, if your
45+
compiler can build a binary that's compliant to any ratified RVA profiles,
46+
you *might* be good to go: if you run your cross-compiler `gcc` binary with `-v` as
47+
its only argument and you don't see mentions of `-mcmodel=medany`, your
48+
toolchain is probably not going to work for this situation (more later). Both newlib
49+
and picolibc are supported, with the latter having precedence if found.
50+
51+
RISC-V 64 toolchains are a bit more temperamental. Some toolchains have their `libc`,
52+
`libm`, and `libgcc` libraries built in a way that they cannot be used with code
53+
placed at an offset beyond 0x7FFFF7FF. QEMU's `VIRT_RV64` board code address space
54+
starts at 0x80000000, making those toolchains not viable if code from those libraries
55+
is brought in by the build process. A lot of readily available RISC-V toolchains are
56+
unfortunately broken from this point of view.
57+
58+
However, to work around this, a Docker container file is provided to be able to run
59+
tests using a known working compiler (the same one running MicroPython's RV32 and RV64
60+
CI jobs).
61+
62+
To build the provided toolchain image you can use the following commands:
63+
64+
```bash
65+
cd $MICROPYTHON_SOURCE_ROOT/ports/qemu
66+
docker build -t micropython/mpy-qemu-riscv -f Dockerfile.riscv .
67+
```
68+
69+
and then tests can be run with:
70+
71+
```bash
72+
cd $MICROPYTHON_SOURCE_ROOT
73+
docker container run -v .:/micropython --rm -it micropython/mpy-qemu-riscv make -C ports/qemu -- BOARD=VIRT_RV64 test
74+
```
75+
76+
(If you use Podman, you can replace `docker` with `podman` in the commands above).
77+
78+
Also, if you replace `VIRT_RV64` with `VIRT_RV32` you should be able to run
79+
32-bits tests as well!
80+
4081
Build instructions
4182
------------------
4283

@@ -59,9 +100,11 @@ Available boards are:
59100
| ----------------- | ------------ | ------------------------ |
60101
| `MICROBIT` | `arm` | `microbit` |
61102
| `MPS2_AN385` | `arm` | `mps2-an385` |
103+
| `MPS2_AN500` | `arm` | `mps2-an500` |
62104
| `NETDUINO2` | `arm` | `netduino2` |
63105
| `SABRELITE` | `arm` | `sabrelite` |
64106
| `VIRT_RV32` | `riscv32` | `virt` |
107+
| `VIRT_RV64` | `riscv64` | `virt` |
65108

66109
Running
67110
-------
@@ -99,8 +142,8 @@ tests against the serial device, for example:
99142
$ ./run-tests.py -t /dev/pts/1
100143

101144
Selected native modules that come as examples with the MicroPython source tree
102-
can also be tested with this command (this is currently supported only for the
103-
`VIRT_RV32` board):
145+
can also be tested with this command (this is currently not supported for the
146+
`VIRT_RV64` board):
104147

105148
$ make test_natmod
106149

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
QEMU_ARCH = arm
2+
QEMU_MACHINE = mps2-an500
3+
4+
CFLAGS += -mthumb -mcpu=cortex-m7 -mfloat-abi=hard -mfpu=fpv5-d16
5+
CFLAGS += -DQEMU_SOC_MPS2
6+
CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-M7"'
7+
CFLAGS += -DCPU_FREQ_HZ=25000000
8+
9+
LDSCRIPT = mcu/arm/mps2.ld
10+
11+
SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o
12+
13+
MPY_CROSS_FLAGS += -march=armv7emdp
14+
15+
MICROPY_FLOAT_IMPL = double
16+
SUPPORTS_HARDWARE_FP_DOUBLE = 1
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
QEMU_ARCH = arm
2+
QEMU_MACHINE = mps3-an547
3+
4+
CFLAGS += -mthumb -mcpu=cortex-m55 -mfloat-abi=hard -mfpu=fpv5-d16
5+
CFLAGS += -DQEMU_SOC_MPS3
6+
CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-M55"'
7+
CFLAGS += -DCPU_FREQ_HZ=32000000
8+
9+
LDSCRIPT = mcu/arm/mps3.ld
10+
11+
SRC_BOARD_O = shared/runtime/gchelper_generic.o
12+
13+
MPY_CROSS_FLAGS += -march=armv7emdp
14+
15+
MICROPY_FLOAT_IMPL ?= double
16+
SUPPORTS_HARDWARE_FP_DOUBLE ?= 1

ports/qemu/boards/VIRT_RV32/mpconfigboard.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,4 @@ LDSCRIPT = mcu/rv32/virt.ld
88

99
SRC_BOARD_O += shared/runtime/gchelper_native.o shared/runtime/gchelper_rv32i.o
1010

11-
MPY_CROSS_FLAGS += -march=rv32imc
11+
MPY_CROSS_FLAGS += -march=rv32imc -march-flags=zba
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
QEMU_ARCH = riscv64
2+
QEMU_MACHINE = virt
3+
4+
CFLAGS += -DQEMU_SOC_VIRT
5+
CFLAGS += -DMICROPY_HW_MCU_NAME='"$(RV64_ARCH)"'
6+
7+
LDSCRIPT = mcu/rv64/virt.ld
8+
9+
SRC_BOARD_O += shared/runtime/gchelper_native.o shared/runtime/gchelper_rv64i.o
10+
11+
MPY_CROSS_FLAGS += -march=rv64imc

ports/qemu/main.c

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828

2929
#include "py/compile.h"
3030
#include "py/runtime.h"
31-
#include "py/stackctrl.h"
3231
#include "py/gc.h"
3332
#include "py/mperrno.h"
3433
#include "shared/runtime/gchelper.h"
@@ -41,8 +40,7 @@
4140
static uint32_t gc_heap[MICROPY_HEAP_SIZE / sizeof(uint32_t)];
4241

4342
int main(int argc, char **argv) {
44-
mp_stack_ctrl_init();
45-
mp_stack_set_limit(10240);
43+
mp_cstack_init_with_sp_here(10240);
4644
gc_init(gc_heap, (char *)gc_heap + MICROPY_HEAP_SIZE);
4745

4846
for (;;) {

ports/qemu/mcu/arm/errorhandler.c

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ static const char *EXCEPTION_NAMES_TABLE[] = {
6363
// R0-R15, PSR, Kind
6464
uintptr_t registers_copy[18] = { 0 };
6565

66+
#define BIT(v, b) (((v) & (1U << (b))) != 0)
67+
6668
__attribute__((naked)) MP_NORETURN void exception_handler(uintptr_t kind) {
6769
// Save registers
6870
__asm volatile (
@@ -127,13 +129,33 @@ __attribute__((naked)) MP_NORETURN void exception_handler(uintptr_t kind) {
127129
}
128130
break;
129131
}
130-
printf(" exception caught:\n");
132+
printf(" exception caught:\n\n");
133+
printf("CPU registers:\n");
131134
printf("R0: %08X R1: %08X R2: %08X R3: %08X\n", registers_copy[0], registers_copy[1], registers_copy[2], registers_copy[3]);
132135
printf("R4: %08X R5: %08X R6: %08X R7: %08X\n", registers_copy[4], registers_copy[5], registers_copy[6], registers_copy[7]);
133136
printf("R8: %08X R9: %08X R10: %08X R11: %08X\n", registers_copy[8], registers_copy[9], registers_copy[10], registers_copy[11]);
134137
printf("R12: %08X R13: %08X R14: %08X R15: %08X\n", registers_copy[12], registers_copy[13], registers_copy[14], registers_copy[15]);
135138
printf("xPSR: %08X\n", registers_copy[16]);
136139

140+
#if __ARM_ARCH == 7
141+
uint32_t cfsr = *(volatile uint32_t *)0xE000ED28;
142+
uint32_t hfsr = *(volatile uint32_t *)0xE000ED2C;
143+
uint32_t dfsr = *(volatile uint32_t *)0xE000ED30;
144+
uint32_t mmfar = *(volatile uint32_t *)0xE000ED34;
145+
uint32_t bfar = *(volatile uint32_t *)0xE000ED38;
146+
147+
printf("\nDebug registers:\n");
148+
printf("CFSR: %08lX HFSR: %08lX DFSR: %08lX MMFAR: %08lX BFAR: %08lX\n", cfsr, hfsr, dfsr, mmfar, bfar);
149+
printf("CFSR.IACCVIOL: %d CFSR.DACCVIOL: %d CFSR.MUNSTKERR: %d CFSR.MSTKERR: %d\n", BIT(cfsr, 0), BIT(cfsr, 1), BIT(cfsr, 3), BIT(cfsr, 4));
150+
printf("CFSR.MLSPERR: %d CFSR.MMARVALID: %d CFSR.IBUSERR: %d CFSR.PRECISERR: %d\n", BIT(cfsr, 5), BIT(cfsr, 7), BIT(cfsr, 8), BIT(cfsr, 9));
151+
printf("CFSR.IMPRECISERR: %d CFSR.UNSTKERR: %d CFSR.STKERR: %d CFSR.LSPERR: %d\n", BIT(cfsr, 10), BIT(cfsr, 11), BIT(cfsr, 12), BIT(cfsr, 13));
152+
printf("CFSR.BFARVALID: %d CFSR.UNDEFINSTR: %d CFSR.INVSTATE: %d CFSR.INVPC: %d\n", BIT(cfsr, 15), BIT(cfsr, 16), BIT(cfsr, 17), BIT(cfsr, 18));
153+
printf("CFSR.NOCP: %d CFSR.UNALIGNED: %d CFSR.DIVBYZERO: %d\n", BIT(cfsr, 19), BIT(cfsr, 24), BIT(cfsr, 25));
154+
printf("HFSR.VECTTBL: %d HFSR.FORCED: %d HFSR.DEBUGEVT: %d\n", BIT(hfsr, 1), BIT(hfsr, 30), BIT(hfsr, 31));
155+
printf("DFSR.HALTED: %d DFSR.BKPT: %d DFSR.DWTTRAP: %d DFSR.VCATCH: %d\n", BIT(dfsr, 0), BIT(dfsr, 1), BIT(dfsr, 2), BIT(dfsr, 3));
156+
printf("DFSR.EXTERNAL: %d\n", BIT(dfsr, 4));
157+
#endif
158+
137159
for (;;) {}
138160
}
139161

@@ -169,7 +191,7 @@ __attribute__((naked)) MP_NORETURN void PendSV_Handler(void) {
169191
exception_handler(PENDING_SV);
170192
}
171193

172-
__attribute__((naked)) MP_NORETURN void SysTick_Handler(void) {
194+
__attribute__((naked, weak)) MP_NORETURN void SysTick_Handler(void) {
173195
exception_handler(SYSTEM_TICK);
174196
}
175197

0 commit comments

Comments
 (0)