Commit 04269349 authored by Damien George's avatar Damien George

qemu-arm: Merge RISC-V 32-bit support into qemu-arm port.

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: default avatarDamien George <damien@micropython.org>
parent f769b432
...@@ -19,14 +19,26 @@ concurrency: ...@@ -19,14 +19,26 @@ concurrency:
cancel-in-progress: true cancel-in-progress: true
jobs: jobs:
build_and_test: build_and_test_arm:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install packages - 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 - 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 - name: Print failures
if: failure() if: failure()
run: tests/run-tests.py --print-failures run: tests/run-tests.py --print-failures
name: qemu-riscv port
on:
push:
pull_request:
paths:
- '.github/workflows/*.yml'
- 'tools/**'
- 'py/**'
- 'extmod/**'
- 'shared/**'
- 'lib/**'
- 'drivers/**'
- 'ports/qemu-arm/main.c'
- 'ports/qemu-riscv/**'
- 'tests/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build_and_test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install packages
run: source tools/ci.sh && ci_qemu_riscv_setup
- name: Build and run test suite
run: source tools/ci.sh && ci_qemu_riscv_build
- name: Print failures
if: failure()
run: grep --before-context=100 --text "FAIL" ports/qemu-riscv/build/console.out
################################################################################
# Initial setup of Makefile environment
BOARD ?= MPS2_AN385 BOARD ?= MPS2_AN385
# Make the build directory reflect the board. # Make the build directory reflect the board.
...@@ -14,16 +17,79 @@ QSTR_DEFS = qstrdefsport.h ...@@ -14,16 +17,79 @@ QSTR_DEFS = qstrdefsport.h
# MicroPython feature configurations # MicroPython feature configurations
MICROPY_ROM_TEXT_COMPRESSION ?= 1 MICROPY_ROM_TEXT_COMPRESSION ?= 1
ifeq ($(QEMU_ARCH),arm)
FROZEN_MANIFEST ?= "freeze('test-frzmpy')" 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 py core make definitions
include $(TOP)/py/py.mk include $(TOP)/py/py.mk
include $(TOP)/extmod/extmod.mk include $(TOP)/extmod/extmod.mk
################################################################################
# ARM specific settings
ifeq ($(QEMU_ARCH),arm)
CROSS_COMPILE ?= arm-none-eabi- 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_SYSTEM = qemu-system-$(QEMU_ARCH)
QEMU_ARGS += -machine $(QEMU_MACHINE) -nographic -monitor null -semihosting 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.
INC += -I$(TOP) INC += -I$(TOP)
...@@ -34,6 +100,8 @@ CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -We ...@@ -34,6 +100,8 @@ CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -We
-ffunction-sections -fdata-sections -ffunction-sections -fdata-sections
CFLAGS += $(CFLAGS_EXTRA) CFLAGS += $(CFLAGS_EXTRA)
LDFLAGS += -T $(LDSCRIPT) -Wl,--gc-sections -Wl,-Map=$(@:.elf=.map)
# Debugging/Optimization # Debugging/Optimization
ifeq ($(DEBUG), 1) ifeq ($(DEBUG), 1)
CFLAGS += -g CFLAGS += -g
...@@ -42,25 +110,31 @@ else ...@@ -42,25 +110,31 @@ else
COPT += -Os -DNDEBUG COPT += -Os -DNDEBUG
endif endif
## With CoudeSourcery it's actually a little different, you just need `-T generic-m-hosted.ld`. # If Picolibc is available then select it explicitly. Ubuntu 22.04 ships its
## Although for some reason `$(LD)` will not find that linker script, it works with `$(CC)`. # bare metal RISC-V toolchain with Picolibc rather than Newlib, and the default
## It turns out that this is specific to CoudeSourcery, and ARM version of GCC ships something # is "nosys" so a value must be provided. To avoid having per-distro
## else instead and according to the following files, this is what we need to pass to `$(CC). # workarounds, always select Picolibc if available.
## - gcc-arm-none-eabi-4_8-2014q1/share/gcc-arm-none-eabi/samples/src/makefile.conf PICOLIBC_SPECS = $(shell $(CC) --print-file-name=picolibc.specs)
## - gcc-arm-none-eabi-4_8-2014q1/share/gcc-arm-none-eabi/samples/src/qemu/Makefile ifeq ($(PICOLIBC_SPECS),picolibc.specs)
LDFLAGS= -T $(LDSCRIPT) --gc-sections -Map=$(@:.elf=.map) # Picolibc was not found.
LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) 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 \ main.c \
startup.c \
uart.c \ uart.c \
mphalport.c \ mphalport.c \
shared/libc/string0.c \ shared/libc/string0.c \
shared/readline/readline.c \ shared/readline/readline.c \
shared/runtime/interrupt_char.c \ shared/runtime/interrupt_char.c \
shared/runtime/pyexec.c \ shared/runtime/pyexec.c \
shared/runtime/semihosting_arm.c \
shared/runtime/stdout_helpers.c \ shared/runtime/stdout_helpers.c \
shared/runtime/sys_stdio_mphal.c \ shared/runtime/sys_stdio_mphal.c \
...@@ -75,6 +149,9 @@ OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o)) ...@@ -75,6 +149,9 @@ OBJ += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o))
# List of sources for qstr extraction # List of sources for qstr extraction
SRC_QSTR += $(SRC_C) $(LIB_SRC_C) SRC_QSTR += $(SRC_C) $(LIB_SRC_C)
################################################################################
# Main targets
all: $(BUILD)/firmware.elf all: $(BUILD)/firmware.elf
.PHONY: repl .PHONY: repl
...@@ -91,9 +168,11 @@ test: $(BUILD)/firmware.elf ...@@ -91,9 +168,11 @@ test: $(BUILD)/firmware.elf
$(eval DIRNAME=ports/$(notdir $(CURDIR))) $(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) 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) $(BUILD)/firmware.elf: $(LDSCRIPT) $(OBJ)
$(Q)$(LD) $(LDFLAGS) -o $@ $(OBJ) $(LIBS) $(Q)$(CC) $(LDFLAGS) -o $@ $(OBJ) $(LIBS)
$(Q)$(SIZE) $@ $(Q)$(SIZE) $@
################################################################################
# Remaining make rules
include $(TOP)/py/mkrules.mk include $(TOP)/py/mkrules.mk
MicroPython port to qemu-arm MicroPython port to qemu-arm
============================ ============================
This is experimental, community-supported port for Cortex-M emulation as This is experimental, community-supported port for Cortex-M and RISC-V RV32IMC
provided by QEMU (http://qemu.org). emulation as provided by QEMU (http://qemu.org).
The purposes of this port are to enable: The purposes of this port are to enable:
...@@ -18,6 +18,25 @@ The purposes of this port are to enable: ...@@ -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 - no need to use OpenOCD or anything else that might slow down the
process in terms of plugging things together, pressing buttons, etc. 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 Build instructions
------------------ ------------------
...@@ -36,12 +55,13 @@ different board pass the `BOARD` argument to `make`, for example: ...@@ -36,12 +55,13 @@ different board pass the `BOARD` argument to `make`, for example:
Available boards are: Available boards are:
| Name for `BOARD=` | Corresponding qemu board | | Name for `BOARD=` | Architecture | Corresponding qemu board |
| ----------------- | ------------------------ | | ----------------- | ------------ | ------------------------ |
| `MICROBIT` | `microbit` | | `MICROBIT` | `arm` | `microbit` |
| `MPS2_AN385` | `mps2-an385` | | `MPS2_AN385` | `arm` | `mps2-an385` |
| `NETDUINO2` | `netduino2` | | `NETDUINO2` | `arm` | `netduino2` |
| `SABRELITE` | `sabrelite` | | `SABRELITE` | `arm` | `sabrelite` |
| `VIRT_RV32` | `riscv32` | `virt` |
Running Running
------- -------
...@@ -84,3 +104,9 @@ The following options can be specified on the `make` command line: ...@@ -84,3 +104,9 @@ The following options can be specified on the `make` command line:
- `CFLAGS_EXTRA`: pass in extra flags for the compiler. - `CFLAGS_EXTRA`: pass in extra flags for the compiler.
- `RUN_TESTS_EXTRA`: pass in extra flags for `run-tests.py` when invoked via - `RUN_TESTS_EXTRA`: pass in extra flags for `run-tests.py` when invoked via
`make test`. `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.
...@@ -6,7 +6,7 @@ CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft ...@@ -6,7 +6,7 @@ CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_NRF51 CFLAGS += -DQEMU_SOC_NRF51
CFLAGS += -DMICROPY_HW_MCU_NAME='"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 SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb1.o
......
...@@ -5,7 +5,7 @@ CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft ...@@ -5,7 +5,7 @@ CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_MPS2 CFLAGS += -DQEMU_SOC_MPS2
CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-M3"' 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 SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o
......
...@@ -5,7 +5,7 @@ CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft ...@@ -5,7 +5,7 @@ CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_STM32 CFLAGS += -DQEMU_SOC_STM32
CFLAGS += -DMICROPY_HW_MCU_NAME='"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 SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o
......
...@@ -8,7 +8,7 @@ CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-A9"' ...@@ -8,7 +8,7 @@ CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-A9"'
# Cortex-A9 should support unaligned-access, but qemu doesn't seem to. # Cortex-A9 should support unaligned-access, but qemu doesn't seem to.
CFLAGS += -mno-unaligned-access CFLAGS += -mno-unaligned-access
LDSCRIPT = imx6.ld LDSCRIPT = mcu/arm/imx6.ld
SRC_BOARD_O = shared/runtime/gchelper_generic.o SRC_BOARD_O = shared/runtime/gchelper_generic.o
......
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'
...@@ -37,6 +37,8 @@ ...@@ -37,6 +37,8 @@
#define MICROPY_EMIT_THUMB (1) #define MICROPY_EMIT_THUMB (1)
#define MICROPY_EMIT_INLINE_THUMB (1) #define MICROPY_EMIT_INLINE_THUMB (1)
#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1))
#elif defined(__riscv)
#define MICROPY_EMIT_RV32 (1)
#endif #endif
#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) #define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1)
...@@ -47,7 +49,11 @@ ...@@ -47,7 +49,11 @@
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_WARNINGS (1) #define MICROPY_WARNINGS (1)
#define MICROPY_PY_IO_IOBASE (0) #define MICROPY_PY_IO_IOBASE (0)
#if defined(__ARM_ARCH)
#define MICROPY_PY_SYS_PLATFORM "qemu-arm" #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_SYS_STDIO_BUFFER (0)
#define MICROPY_PY_SELECT (0) #define MICROPY_PY_SELECT (0)
#define MICROPY_PY_TIME (0) #define MICROPY_PY_TIME (0)
......
...@@ -24,4 +24,5 @@ ...@@ -24,4 +24,5 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include <stddef.h>
#include "shared/runtime/interrupt_char.h" #include "shared/runtime/interrupt_char.h"
...@@ -3,7 +3,8 @@ ...@@ -3,7 +3,8 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2018-2021 Damien P. George * Copyright (c) 2018-2024 Damien P. George
* Copyright (c) 2023 Alessandro Gatti
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
...@@ -175,4 +176,36 @@ void uart_tx_strn(const char *buf, size_t len) { ...@@ -175,4 +176,36 @@ void uart_tx_strn(const char *buf, size_t len) {
} }
} }
#elif defined(QEMU_SOC_VIRT)
// Line status register bits.
#define UART_LSR_THRE (0x20)
#define UART_LSR_DR (0x01)
typedef struct _UART_t {
volatile uint8_t DR;
volatile uint8_t r0[4];
volatile uint8_t LSR;
} UART_t;
#define UART0 ((UART_t *)(0x10000000))
void uart_init(void) {
}
int uart_rx_chr(void) {
if (UART0->LSR & UART_LSR_DR) {
return UART0->DR;
}
return UART_RX_NO_CHAR;
}
void uart_tx_strn(const char *buffer, size_t length) {
for (size_t index = 0; index < length; index++) {
while (!(UART0->LSR & UART_LSR_THRE)) {
}
UART0->DR = buffer[index];
}
}
#endif #endif
include ../../py/mkenv.mk
-include mpconfigport.mk
# qstr definitions (must come before including py.mk)
QSTR_DEFS = qstrdefsport.h
# MicroPython feature configurations
MICROPY_ROM_TEXT_COMPRESSION ?= 1
# include py core make definitions
include $(TOP)/py/py.mk
include $(TOP)/extmod/extmod.mk
BOARD ?= virt
CROSS_COMPILE ?= riscv64-unknown-elf-
GCC_VERSION = $(word 1, $(subst ., , $(shell $(CC) -dumpversion)))
# 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.
SPECS_FRAGMENT =
else
SPECS_FRAGMENT = --specs=$(PICOLIBC_SPECS)
CFLAGS += $(SPECS_FRAGMENT)
endif
ifeq ($(BOARD),virt)
ABI = ilp32
# GCC 10 and lower do not recognise the Zicsr extension in the
# architecture name. "Make" unfortunately does not provide any simple way
# to perform numeric comparisons, so to keep things simple we assume that
# GCC is at least version 10 for the time being.
ifeq ($(GCC_VERSION),10)
ARCH ?= rv32imac
else
# Recent GCC versions explicitly require to declare extensions.
ARCH ?= rv32imac_zicsr
ARCH_LD ?= rv32imac_zicsr
endif
AFLAGS = -mabi=$(ABI) -march=$(ARCH)
CFLAGS += $(AFLAGS)
CFLAGS += -DQEMU_SOC_VIRT
ARCH_LD ?= $(ARCH)
LDSCRIPT = virt.ld
LDFLAGS += -mabi=$(ABI) -march=$(ARCH_LD) -T $(LDSCRIPT) -Wl,-EL
SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_rv32i.o
SRC_BOARD_O += entrypoint.o
endif
INC += -I.
INC += -I$(TOP)
INC += -I$(BUILD)
CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Werror -std=gnu99 $(COPT) \
-ffunction-sections -fdata-sections
CFLAGS += $(CFLAGS_EXTRA)
LD = $(CC)
# Debugging/Optimization
ifeq ($(DEBUG), 1)
CFLAGS += -g
COPT = -O0
else
COPT += -Os -DNDEBUG
endif
LDFLAGS += $(SPECS_FRAGMENT) -Wl,--gc-sections -Wl,-Map=$(@:.elf=.map)
SRC_COMMON_C = \
interrupts.c \
startup.c \
uart.c \
shared/libc/string0.c \
shared/runtime/sys_stdio_mphal.c \
SRC_RUN_C = \
main.c \
SRC_TEST_C = \
test_main.c \
lib/tinytest/tinytest.c \
LIB_SRC_C += $(SRC_LIB_LIBM_C)
LIB_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C)
OBJ_COMMON =
OBJ_COMMON += $(PY_O)
OBJ_COMMON += $(addprefix $(BUILD)/, $(SRC_COMMON_C:.c=.o))
OBJ_COMMON += $(addprefix $(BUILD)/, $(SRC_BOARD_O))
OBJ_COMMON += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o))
OBJ_RUN =
OBJ_RUN += $(addprefix $(BUILD)/, $(SRC_RUN_C:.c=.o))
ALL_OBJ_RUN = $(OBJ_COMMON) $(OBJ_RUN)
OBJ_TEST =
OBJ_TEST += $(addprefix $(BUILD)/, $(SRC_TEST_C:.c=.o))
ALL_OBJ_TEST = $(OBJ_COMMON) $(OBJ_TEST)
# All object files, needed to get dependencies correct
OBJ = $(OBJ_COMMON) $(OBJ_RUN) $(OBJ_TEST)
# List of sources for qstr extraction
SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C)
all: run
# `make debug` will block QEMU until a debugger is connected to port 1234.
debug: $(BUILD)/firmware.elf
qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -serial mon:stdio -S -s -kernel $<
run: $(BUILD)/firmware.elf
qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $<
$(BUILD)/firmware.elf: $(LDSCRIPT) $(ALL_OBJ_RUN)
$(Q)$(LD) $(LDFLAGS) -o $@ $(ALL_OBJ_RUN) $(LIBS)
$(Q)$(SIZE) $@
include $(TOP)/py/mkrules.mk
LIB_SRC_C = shared/upytesthelper/upytesthelper.c
include Makefile
CFLAGS += -DTEST
.PHONY: $(BUILD)/genhdr/tests.h
TESTS_PROFILE = $(dir $(abspath $(firstword $(MAKEFILE_LIST))))/tests_profile.txt
$(BUILD)/test_main.o: $(BUILD)/genhdr/tests.h
$(BUILD)/genhdr/tests.h:
(cd $(TOP)/tests; ./run-tests.py --target=qemu-riscv --write-exp)
$(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py --profile $(TESTS_PROFILE) $(addprefix --exclude ,$(TESTS_EXCLUDE))) > $@
$(BUILD)/lib/tinytest/tinytest.o: CFLAGS += -DNO_FORKING
$(BUILD)/firmware-test.elf: $(LDSCRIPT) $(ALL_OBJ_TEST)
$(Q)$(LD) $(LDFLAGS) -o $@ $(ALL_OBJ_TEST) $(LIBS)
$(Q)$(SIZE) $@
# Note: Using timeout(1) to handle cases where qemu hangs (e.g. this can happen with alignment errors).
test: $(BUILD)/firmware-test.elf
timeout --foreground -k 5s 60s qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< > $(BUILD)/console.out
$(Q)tail -n2 $(BUILD)/console.out
$(Q)tail -n1 $(BUILD)/console.out | grep -q "status: 0"
# `make debugtest` will block QEMU until a debugger is connected to port 1234.
debugtest: $(BUILD)/firmware-test.elf
qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -serial mon:stdio -S -s -kernel $<
This is an experimental, community-supported port for RISC-V RV32IMC emulation
as provided by QEMU (http://qemu.org).
The purposes of this port are to enable:
1. Continuous integration
- run tests against architecture-specific parts of code base
2. Experimentation
- simulation & prototyping of anything that has architecture-specific
code
- exploring instruction set in terms of optimising some part of
MicroPython or a module
3. Streamlined debugging
- no need for JTAG or even an MCU chip itself
- no need to use OpenOCD or anything else that might slow down the
process in terms of plugging things together, pressing buttons, etc.
This port 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/).
To build and run the image with builtin testsuite:
make -f Makefile.test test
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014-2023 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include "py/compile.h"
#include "py/runtime.h"
#include "py/stackctrl.h"
#include "py/gc.h"
#include "py/mperrno.h"
void do_str(const char *src, mp_parse_input_kind_t input_kind) {
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) {
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
qstr source_name = lex->source_name;
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true);
mp_call_function_0(module_fun);
nlr_pop();
} else {
// uncaught exception
mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
}
}
int main(int argc, char **argv) {
mp_stack_ctrl_init();
mp_stack_set_limit(10240);
uint32_t heap[16 * 1024 / 4];
gc_init(heap, (char *)heap + 16 * 1024);
mp_init();
do_str("print('hello world!')", MP_PARSE_SINGLE_INPUT);
mp_deinit();
return 0;
}
void gc_collect(void) {
}
mp_lexer_t *mp_lexer_new_from_file(qstr filename) {
mp_raise_OSError(MP_ENOENT);
}
void nlr_jump_fail(void *val) {
printf("uncaught NLR\n");
exit(1);
}
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014-2024 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdint.h>
// options to control how MicroPython is built
#define MICROPY_CONFIG_ROM_LEVEL (MICROPY_CONFIG_ROM_LEVEL_EXTRA_FEATURES)
#define MICROPY_EMIT_RV32 (1)
#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1)
#define MICROPY_MEM_STATS (1)
#define MICROPY_ENABLE_GC (1)
#define MICROPY_KBD_EXCEPTION (0)
#define MICROPY_HELPER_REPL (0)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_WARNINGS (1)
#define MICROPY_PY_BUILTINS_INPUT (0)
#define MICROPY_PY_BUILTINS_HELP (0)
#define MICROPY_PY_IO_IOBASE (0)
#define MICROPY_PY_SYS_PLATFORM "qemu-riscv32"
#define MICROPY_PY_SYS_STDFILES (0)
#define MICROPY_PY_SYS_STDIO_BUFFER (0)
#define MICROPY_PY_SELECT (0)
#define MICROPY_PY_TIME (0)
#define MICROPY_PY_ASYNCIO (0)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/qemu-arm/modmachine.c"
#define MICROPY_PY_MACHINE_PIN_BASE (1)
#define MICROPY_VFS (1)
// type definitions for the specific machine
#define MP_SSIZE_MAX (0x7fffffff)
#define UINT_FMT "%lu"
#define INT_FMT "%ld"
typedef int32_t mp_int_t; // must be pointer size
typedef uint32_t mp_uint_t; // must be pointer size
typedef long mp_off_t;
// We need an implementation of the log2 function which is not a macro.
#define MP_NEED_LOG2 (1)
// We need to provide a declaration/definition of alloca()
#include <alloca.h>
#ifdef TEST
#include "shared/upytesthelper/upytesthelper.h"
#undef MP_PLAT_PRINT_STRN
#define MP_PLAT_PRINT_STRN(str, len) upytest_output(str, len)
#endif
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2016-2018 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stddef.h>
#include "uart.h"
#define mp_hal_stdin_rx_chr() (0)
#define mp_hal_stdout_tx_strn_cooked(s, l) uart_tx_strn((s), (l))
// qstrs specific to this port
// *FORMAT-OFF*
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2014 Ilya Dmitrichenko
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdlib.h>
#include <stdio.h>
#include "py/compile.h"
#include "py/runtime.h"
#include "py/stackctrl.h"
#include "py/gc.h"
#include "py/mperrno.h"
#include "shared/runtime/gchelper.h"
#include "lib/tinytest/tinytest.h"
#include "lib/tinytest/tinytest_macros.h"
#define HEAP_SIZE (200 * 1024)
#include "genhdr/tests.h"
int main() {
mp_stack_ctrl_init();
mp_stack_set_limit(10240);
static uint32_t heap[HEAP_SIZE / sizeof(uint32_t)];
upytest_set_heap(heap, (char *)heap + HEAP_SIZE);
int r = tinytest_main(0, NULL, groups);
printf("status: %d\n", r);
return r;
}
void gc_collect(void) {
gc_collect_start();
gc_helper_collect_regs_and_stack();
gc_collect_end();
}
mp_lexer_t *mp_lexer_new_from_file(qstr filename) {
mp_raise_OSError(MP_ENOENT);
}
void nlr_jump_fail(void *val) {
printf("uncaught NLR\n");
exit(1);
}
# Port-specific test directories.
test_dirs.update(())
# Port-specific tests exclusion list.
exclude_tests.update(())
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2023 Alessandro Gatti
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#include <stdio.h>
#include <stddef.h>
#include "uart.h"
#if defined(QEMU_SOC_VIRT)
volatile unsigned char *uart_buffer = (volatile unsigned char *)0x10000000;
void uart_init(void) {
}
void uart_tx_strn(const char *buffer, size_t length) {
for (size_t index = 0; index < length; index++) {
*uart_buffer = buffer[index];
}
}
#endif // QEMU_SOC_VIRT
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 2018 Damien P. George
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
#ifndef MICROPY_INCLUDED_QEMU_RISCV_UART_H
#define MICROPY_INCLUDED_QEMU_RISCV_UART_H
void uart_init(void);
void uart_tx_strn(const char *buf, size_t len);
#endif // MICROPY_INCLUDED_QEMU_RISCV_UART_H
...@@ -246,14 +246,21 @@ function ci_powerpc_build { ...@@ -246,14 +246,21 @@ function ci_powerpc_build {
######################################################################################## ########################################################################################
# ports/qemu-arm # ports/qemu-arm
function ci_qemu_arm_setup { function ci_qemu_setup_arm {
ci_gcc_arm_setup ci_gcc_arm_setup
sudo apt-get update sudo apt-get update
sudo apt-get install qemu-system sudo apt-get install qemu-system
qemu-system-arm --version qemu-system-arm --version
} }
function ci_qemu_arm_build { function ci_qemu_setup_rv32 {
ci_gcc_riscv_setup
sudo apt-get update
sudo apt-get install qemu-system
qemu-system-riscv32 --version
}
function ci_qemu_build_arm {
make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C mpy-cross
make ${MAKEOPTS} -C ports/qemu-arm submodules make ${MAKEOPTS} -C ports/qemu-arm submodules
make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1
...@@ -262,23 +269,10 @@ function ci_qemu_arm_build { ...@@ -262,23 +269,10 @@ function ci_qemu_arm_build {
make ${MAKEOPTS} -C ports/qemu-arm BOARD=SABRELITE test make ${MAKEOPTS} -C ports/qemu-arm BOARD=SABRELITE test
} }
######################################################################################## function ci_qemu_build_rv32 {
# ports/qemu-riscv
function ci_qemu_riscv_setup {
ci_gcc_riscv_setup
sudo apt-get update
sudo apt-get install qemu-system
qemu-system-riscv32 --version
}
function ci_qemu_riscv_build {
make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C mpy-cross
make ${MAKEOPTS} -C ports/qemu-riscv submodules make ${MAKEOPTS} -C ports/qemu-arm BOARD=VIRT_RV32 submodules
make ${MAKEOPTS} -C ports/qemu-riscv make ${MAKEOPTS} -C ports/qemu-arm BOARD=VIRT_RV32 test
make ${MAKEOPTS} -C ports/qemu-riscv clean
make ${MAKEOPTS} -C ports/qemu-riscv -f Makefile.test submodules
make ${MAKEOPTS} -C ports/qemu-riscv -f Makefile.test test
} }
######################################################################################## ########################################################################################
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment