Skip to content

Commit

Permalink
qemu-arm: Merge RISC-V 32-bit support into qemu-arm port.
Browse files Browse the repository at this point in the history
Currently both the qemu-arm and qemu-riscv ports share a lot of code and
functionality.  This commit merges the qemu-riscv port into the qemu-arm
port.  The only real differences between the two are the toolchains used to
build the code, and the initialisation/startup framework.  Everything else
is pretty much the same, so this brings the following benefits:
- less code duplication
- less burden on maintenance
- generalised qemu port, could in the future support other architectures

A new board `VIRT_RV32` has been added to the qemu-arm port which is the
existing RISC-V board from the qemu-riscv port.  To build it:

    $ make BOARD=VIRT_RV32 repl

To cleanly separate the code for the different architectures, startup code
has been moved to ports/qemu-arm/mcu/<arch>/.

Signed-off-by: Damien George <[email protected]>
  • Loading branch information
dpgeorge committed Sep 4, 2024
1 parent f769b43 commit 0426934
Show file tree
Hide file tree
Showing 33 changed files with 212 additions and 602 deletions.
18 changes: 15 additions & 3 deletions .github/workflows/ports_qemu-arm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,26 @@ concurrency:
cancel-in-progress: true

jobs:
build_and_test:
build_and_test_arm:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install packages
run: source tools/ci.sh && ci_qemu_arm_setup
run: source tools/ci.sh && ci_qemu_setup_arm
- name: Build and run test suite
run: source tools/ci.sh && ci_qemu_arm_build
run: source tools/ci.sh && ci_qemu_build_arm
- name: Print failures
if: failure()
run: tests/run-tests.py --print-failures

build_and_test_rv32:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install packages
run: source tools/ci.sh && ci_qemu_setup_rv32
- name: Build and run test suite
run: source tools/ci.sh && ci_qemu_build_rv32
- name: Print failures
if: failure()
run: tests/run-tests.py --print-failures
33 changes: 0 additions & 33 deletions .github/workflows/ports_qemu-riscv.yml

This file was deleted.

105 changes: 92 additions & 13 deletions ports/qemu-arm/Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
################################################################################
# Initial setup of Makefile environment

BOARD ?= MPS2_AN385

# Make the build directory reflect the board.
Expand All @@ -14,16 +17,79 @@ QSTR_DEFS = qstrdefsport.h

# MicroPython feature configurations
MICROPY_ROM_TEXT_COMPRESSION ?= 1

ifeq ($(QEMU_ARCH),arm)
FROZEN_MANIFEST ?= "freeze('test-frzmpy')"
endif
ifeq ($(QEMU_ARCH),riscv32)
FROZEN_MANIFEST ?= "freeze('test-frzmpy', ('frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))"
endif

# include py core make definitions
include $(TOP)/py/py.mk
include $(TOP)/extmod/extmod.mk

################################################################################
# ARM specific settings

ifeq ($(QEMU_ARCH),arm)

CROSS_COMPILE ?= arm-none-eabi-

LDFLAGS += -nostdlib
LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)

SRC_C += \
mcu/arm/startup.c \
shared/runtime/semihosting_arm.c \

endif

################################################################################
# RISC-V 32-bit specific settings

ifeq ($(QEMU_ARCH),riscv32)

CROSS_COMPILE ?= riscv64-unknown-elf-

GCC_VERSION = $(word 1, $(subst ., , $(shell $(CC) -dumpversion)))

RV32_ABI = ilp32

QEMU_ARGS += -bios none

# GCC 10 and lower do not recognise the Zicsr extension in the architecture name.
ifeq ($(shell test $(GCC_VERSION) -le 10; echo $$?),0)
RV32_ARCH ?= rv32imac
else
# Recent GCC versions explicitly require to declare extensions.
RV32_ARCH ?= rv32imac_zicsr
endif

AFLAGS += -mabi=$(RV32_ABI) -march=$(RV32_ARCH)
CFLAGS += $(AFLAGS)
LDFLAGS += -mabi=$(RV32_ABI) -march=$(RV32_ARCH) -Wl,-EL

SRC_C += \
mcu/rv32/interrupts.c \
mcu/rv32/startup.c \

SRC_BOARD_O += mcu/rv32/entrypoint.o

endif

################################################################################
# Project specific settings and compiler/linker flags

QEMU_SYSTEM = qemu-system-$(QEMU_ARCH)
QEMU_ARGS += -machine $(QEMU_MACHINE) -nographic -monitor null -semihosting
QEMU_ARGS += $(QEMU_EXTRA)

# Specifying QEMU_DEBUG=1 will block qemu until a debugger is connected.
ifeq ($(QEMU_DEBUG),1)
QEMU_DEBUG_ARGS ?= -s
QEMU_ARGS += -S $(QEMU_DEBUG_ARGS) $(QEMU_DEBUG_EXTRA)
endif

INC += -I.
INC += -I$(TOP)
Expand All @@ -34,6 +100,8 @@ CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -We
-ffunction-sections -fdata-sections
CFLAGS += $(CFLAGS_EXTRA)

LDFLAGS += -T $(LDSCRIPT) -Wl,--gc-sections -Wl,-Map=$(@:.elf=.map)

# Debugging/Optimization
ifeq ($(DEBUG), 1)
CFLAGS += -g
Expand All @@ -42,25 +110,31 @@ else
COPT += -Os -DNDEBUG
endif

## With CoudeSourcery it's actually a little different, you just need `-T generic-m-hosted.ld`.
## Although for some reason `$(LD)` will not find that linker script, it works with `$(CC)`.
## It turns out that this is specific to CoudeSourcery, and ARM version of GCC ships something
## else instead and according to the following files, this is what we need to pass to `$(CC).
## - gcc-arm-none-eabi-4_8-2014q1/share/gcc-arm-none-eabi/samples/src/makefile.conf
## - gcc-arm-none-eabi-4_8-2014q1/share/gcc-arm-none-eabi/samples/src/qemu/Makefile
LDFLAGS= -T $(LDSCRIPT) --gc-sections -Map=$(@:.elf=.map)
LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
# If Picolibc is available then select it explicitly. Ubuntu 22.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 $(CC) --print-file-name=picolibc.specs)
ifeq ($(PICOLIBC_SPECS),picolibc.specs)
# Picolibc was not found.
else
$(info picolibc used $(PICOLIBC_SPECS))
SPECS_FRAGMENT = --specs=$(PICOLIBC_SPECS)
CFLAGS += $(SPECS_FRAGMENT)
LDFLAGS += $(SPECS_FRAGMENT)
endif

SRC_C = \
################################################################################
# Source files and libraries

SRC_C += \
main.c \
startup.c \
uart.c \
mphalport.c \
shared/libc/string0.c \
shared/readline/readline.c \
shared/runtime/interrupt_char.c \
shared/runtime/pyexec.c \
shared/runtime/semihosting_arm.c \
shared/runtime/stdout_helpers.c \
shared/runtime/sys_stdio_mphal.c \

Expand All @@ -75,6 +149,9 @@ OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o))
# List of sources for qstr extraction
SRC_QSTR += $(SRC_C) $(LIB_SRC_C)

################################################################################
# Main targets

all: $(BUILD)/firmware.elf

.PHONY: repl
Expand All @@ -91,9 +168,11 @@ test: $(BUILD)/firmware.elf
$(eval DIRNAME=ports/$(notdir $(CURDIR)))
cd $(TOP)/tests && ./run-tests.py --target qemu-arm --device execpty:"$(QEMU_SYSTEM) $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(RUN_TESTS_ARGS) $(RUN_TESTS_EXTRA)

## `$(LD)` doesn't seem to like `--specs` for some reason, but we can just use `$(CC)` here.
$(BUILD)/firmware.elf: $(LDSCRIPT) $(OBJ)
$(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
$(Q)$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
$(Q)$(SIZE) $@

################################################################################
# Remaining make rules

include $(TOP)/py/mkrules.mk
42 changes: 34 additions & 8 deletions ports/qemu-arm/README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
MicroPython port to qemu-arm
============================

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

The purposes of this port are to enable:

Expand All @@ -18,6 +18,25 @@ The purposes of this port are to enable:
- no need to use OpenOCD or anything else that might slow down the
process in terms of plugging things together, pressing buttons, etc.

Dependencies
------------

### ARM

For ARM-based boards the build requires a bare-metal ARM toolchain, such as
`arm-none-eabi-gcc`.

### RISC-V

For RISC-V-based boards the build requires a bare metal RISC-V toolchain with GCC 10
or later, either with multilib support or 32 bits specific (M, C, and Zicsr
extensions must be supported, along with ilp32 ABI). Both newlib and picolibc are
supported, with the latter having precedence if found.

Most pre-built toolchains should work out of the box, either coming from your
Linux distribution's package manager, or independently packaged ones like
[xPack](https://xpack.github.io/dev-tools/riscv-none-elf-gcc/).

Build instructions
------------------

Expand All @@ -36,12 +55,13 @@ different board pass the `BOARD` argument to `make`, for example:

Available boards are:

| Name for `BOARD=` | Corresponding qemu board |
| ----------------- | ------------------------ |
| `MICROBIT` | `microbit` |
| `MPS2_AN385` | `mps2-an385` |
| `NETDUINO2` | `netduino2` |
| `SABRELITE` | `sabrelite` |
| Name for `BOARD=` | Architecture | Corresponding qemu board |
| ----------------- | ------------ | ------------------------ |
| `MICROBIT` | `arm` | `microbit` |
| `MPS2_AN385` | `arm` | `mps2-an385` |
| `NETDUINO2` | `arm` | `netduino2` |
| `SABRELITE` | `arm` | `sabrelite` |
| `VIRT_RV32` | `riscv32` | `virt` |

Running
-------
Expand Down Expand Up @@ -84,3 +104,9 @@ The following options can be specified on the `make` command line:
- `CFLAGS_EXTRA`: pass in extra flags for the compiler.
- `RUN_TESTS_EXTRA`: pass in extra flags for `run-tests.py` when invoked via
`make test`.
- `QEMU_DEBUG=1`: when running qemu (via `repl`, `run` or `test` target), qemu
will block until a debugger is connected. By default it waits for a gdb connection
on TCP port 1234.
- `QEMU_DEBUG_ARGS`: defaults to `-s` (gdb on TCP port 1234), but can be overridden
with different qemu gdb arguments.
- `QEMU_DEBUG_EXTRA`: extra options to pass to qemu when `QEMU_DEBUG=1` is used.
2 changes: 1 addition & 1 deletion ports/qemu-arm/boards/MICROBIT.mk
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_NRF51
CFLAGS += -DMICROPY_HW_MCU_NAME='"nRF51"'

LDSCRIPT = nrf51.ld
LDSCRIPT = mcu/arm/nrf51.ld

SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb1.o

Expand Down
2 changes: 1 addition & 1 deletion ports/qemu-arm/boards/MPS2_AN385.mk
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_MPS2
CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-M3"'

LDSCRIPT = mps2.ld
LDSCRIPT = mcu/arm/mps2.ld

SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o

Expand Down
2 changes: 1 addition & 1 deletion ports/qemu-arm/boards/NETDUINO2.mk
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_STM32
CFLAGS += -DMICROPY_HW_MCU_NAME='"STM32"'

LDSCRIPT = stm32.ld
LDSCRIPT = mcu/arm/stm32.ld

SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o

Expand Down
2 changes: 1 addition & 1 deletion ports/qemu-arm/boards/SABRELITE.mk
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-A9"'
# Cortex-A9 should support unaligned-access, but qemu doesn't seem to.
CFLAGS += -mno-unaligned-access

LDSCRIPT = imx6.ld
LDSCRIPT = mcu/arm/imx6.ld

SRC_BOARD_O = shared/runtime/gchelper_generic.o

Expand Down
14 changes: 14 additions & 0 deletions ports/qemu-arm/boards/VIRT_RV32.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
QEMU_ARCH = riscv32
QEMU_MACHINE = virt

CFLAGS += -DQEMU_SOC_VIRT
CFLAGS += -DMICROPY_HW_MCU_NAME='"$(RV32_ARCH)"'

LDSCRIPT = mcu/rv32/virt.ld

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

MPY_CROSS_FLAGS += -march=rv32imc

# These Thumb tests don't run on RV32, so exclude them.
RUN_TESTS_ARGS = --exclude 'inlineasm|qemu-arm/asm_test'
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
6 changes: 6 additions & 0 deletions ports/qemu-arm/mpconfigport.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#define MICROPY_EMIT_THUMB (1)
#define MICROPY_EMIT_INLINE_THUMB (1)
#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1))
#elif defined(__riscv)
#define MICROPY_EMIT_RV32 (1)
#endif

#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1)
Expand All @@ -47,7 +49,11 @@
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_WARNINGS (1)
#define MICROPY_PY_IO_IOBASE (0)
#if defined(__ARM_ARCH)
#define MICROPY_PY_SYS_PLATFORM "qemu-arm"
#elif defined(__riscv)
#define MICROPY_PY_SYS_PLATFORM "qemu-riscv32"
#endif
#define MICROPY_PY_SYS_STDIO_BUFFER (0)
#define MICROPY_PY_SELECT (0)
#define MICROPY_PY_TIME (0)
Expand Down
1 change: 1 addition & 0 deletions ports/qemu-arm/mphalport.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,4 +24,5 @@
* THE SOFTWARE.
*/

#include <stddef.h>
#include "shared/runtime/interrupt_char.h"
Loading

0 comments on commit 0426934

Please sign in to comment.