summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlessandro Gatti <a.gatti@frob.it>2023-10-31 21:55:07 +0100
committerDamien George <damien@micropython.org>2024-06-17 12:06:09 +1000
commit2d69aab7b350810322a4cba2667626949ee3d974 (patch)
tree8d86580ef23e790704aa804f2d61e8aedd4fac1b
parentd7d77d91becd0716ac1672c92652d9dd72ec613f (diff)
qemu-riscv: Add new QEMU RV32 port.
This adds a QEMU-based bare metal RISC-V 32 bits port. For the time being only QEMU's "virt" 32 bits board is supported, using the ilp32 ABI and the RV32IMC architecture. The top-level README and the run-tests.py files are updated for this new port. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
-rw-r--r--README.md3
-rw-r--r--ports/qemu-riscv/Makefile128
-rw-r--r--ports/qemu-riscv/Makefile.test31
-rw-r--r--ports/qemu-riscv/README.md29
-rw-r--r--ports/qemu-riscv/entrypoint.s70
-rw-r--r--ports/qemu-riscv/interrupts.c292
-rw-r--r--ports/qemu-riscv/mpconfigport.h76
-rw-r--r--ports/qemu-riscv/mphalport.h32
-rw-r--r--ports/qemu-riscv/qstrdefsport.h2
-rw-r--r--ports/qemu-riscv/startup.c86
-rw-r--r--ports/qemu-riscv/test_main.c66
-rw-r--r--ports/qemu-riscv/tests_profile.txt53
-rw-r--r--ports/qemu-riscv/uart.c45
-rw-r--r--ports/qemu-riscv/uart.h33
-rw-r--r--ports/qemu-riscv/virt.ld98
-rwxr-xr-xtests/run-tests.py9
16 files changed, 1052 insertions, 1 deletions
diff --git a/README.md b/README.md
index f5bc6d78f..a20e0aaf1 100644
--- a/README.md
+++ b/README.md
@@ -114,7 +114,8 @@ In addition, the following ports are provided in this repository:
- [nrf](ports/nrf) -- Nordic Semiconductor nRF51 and nRF52.
- [pic16bit](ports/pic16bit) -- Microchip PIC 16-bit.
- [powerpc](ports/powerpc) -- IBM PowerPC (including Microwatt)
- - [qemu-arm](ports/qemu-arm) -- QEMU-based emulated target, for testing)
+ - [qemu-arm](ports/qemu-arm) -- QEMU-based Arm emulated target (for testing)
+ - [qemu-riscv](ports/qemu-riscv) -- QEMU-based RISC-V emulated target (for testing)
- [renesas-ra](ports/renesas-ra) -- Renesas RA family.
- [rp2](ports/rp2) -- Raspberry Pi RP2040 (including Pico and Pico W).
- [samd](ports/samd) -- Microchip (formerly Atmel) SAMD21 and SAMD51.
diff --git a/ports/qemu-riscv/Makefile b/ports/qemu-riscv/Makefile
new file mode 100644
index 000000000..6f15ce52e
--- /dev/null
+++ b/ports/qemu-riscv/Makefile
@@ -0,0 +1,128 @@
+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 = \
+ ports/qemu-arm/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
diff --git a/ports/qemu-riscv/Makefile.test b/ports/qemu-riscv/Makefile.test
new file mode 100644
index 000000000..df64a8d7c
--- /dev/null
+++ b/ports/qemu-riscv/Makefile.test
@@ -0,0 +1,31 @@
+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 $<
diff --git a/ports/qemu-riscv/README.md b/ports/qemu-riscv/README.md
new file mode 100644
index 000000000..50b1a6537
--- /dev/null
+++ b/ports/qemu-riscv/README.md
@@ -0,0 +1,29 @@
+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
diff --git a/ports/qemu-riscv/entrypoint.s b/ports/qemu-riscv/entrypoint.s
new file mode 100644
index 000000000..9144c0c48
--- /dev/null
+++ b/ports/qemu-riscv/entrypoint.s
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+
+ .section .start
+ .option norvc /* Do not emit compressed instructions. */
+ .type start, @function
+ .global start
+
+start:
+ .cfi_startproc
+
+ .option push
+ .option norelax
+ la gp, _global_pointer /* Load global pointer register. */
+ .option pop
+
+ csrw satp, zero /* Disable supervisor mode. */
+
+ /* Fill stack with a canary value. */
+
+ li t0, 0xBBBBBBBB /* Load canary value. */
+ la t1, _sstack /* Load stack area start address. */
+ la t2, _estack /* Load stack area end address. */
+1:
+ sw t0, (t1) /* Write canary. */
+ addi t1, t1, 4 /* Next word. */
+ bltu t1, t2, 1b /* Loop until stack is filled. */
+
+ la sp, _estack /* Load stack pointer. */
+
+ /* Clear BSS area. */
+
+ la t1, _sbss /* Load BSS area start address. */
+ la t2, _ebss /* Load BSS area end address. */
+1:
+ sw zero, (t1) /* Clear word. */
+ addi t1, t1, 4 /* Next word. */
+ bltu t1, t2, 1b /* Loop until BSS is cleared. */
+
+ /* Set program counter. */
+
+ la t0, _entry_point /* Load program counter address. */
+ csrw mepc, t0 /* Store it into machine exception PC. */
+ tail _entry_point /* Jump to entry point. */
+
+ .cfi_endproc
+ .end
diff --git a/ports/qemu-riscv/interrupts.c b/ports/qemu-riscv/interrupts.c
new file mode 100644
index 000000000..ccb4644f9
--- /dev/null
+++ b/ports/qemu-riscv/interrupts.c
@@ -0,0 +1,292 @@
+/*
+ * 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 <stdlib.h>
+#include <stdint.h>
+
+// Vector table
+
+void mtvec_table(void) __attribute__((naked, section(".text.mtvec"), aligned(256)));
+
+// Default interrupts
+
+#define ASSIGN_EMPTY_MACHINE_INTERRUPT(interrupt_name) \
+ void interrupt_name(void) __attribute__((alias("mtvec_nop")))
+
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_ssi);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_msi);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_sti);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_mti);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_sei);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_mei);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq0);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq1);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq2);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq3);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq4);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq5);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq6);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq7);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq8);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq9);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq10);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq11);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq12);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq13);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq14);
+ASSIGN_EMPTY_MACHINE_INTERRUPT(mtvec_plat_irq15);
+
+void mtvec_table(void) {
+ __asm volatile (
+ ".org mtvec_table + 0 \n"
+ "jal zero, mtvec_exception \n" // Exception Handler
+ ".org mtvec_table + 4 \n"
+ "jal zero, mtvec_ssi \n" // Supervisor Software Interrupt
+ ".org mtvec_table + 12 \n"
+ "jal zero, mtvec_msi \n" // Machine Software Interrupt
+ ".org mtvec_table + 20 \n"
+ "jal zero, mtvec_sti \n" // Supervisor Timer Interrupt
+ ".org mtvec_table + 28 \n"
+ "jal zero, mtvec_mti \n" // Machine Timer Interrupt
+ ".org mtvec_table + 36 \n"
+ "jal zero, mtvec_sei \n" // Supervisor External Interrupt
+ ".org mtvec_table + 44 \n"
+ "jal zero, mtvec_mei \n" // Machine External Interrupt
+ // Not sure how many platform interrupts QEMU handles...
+ ".org mtvec_table + 48 \n"
+ "jal mtvec_plat_irq0 \n" // Platform Interrupt #0
+ "jal mtvec_plat_irq1 \n" // Platform Interrupt #1
+ "jal mtvec_plat_irq2 \n" // Platform Interrupt #2
+ "jal mtvec_plat_irq3 \n" // Platform Interrupt #3
+ "jal mtvec_plat_irq4 \n" // Platform Interrupt #4
+ "jal mtvec_plat_irq5 \n" // Platform Interrupt #5
+ "jal mtvec_plat_irq6 \n" // Platform Interrupt #6
+ "jal mtvec_plat_irq7 \n" // Platform Interrupt #7
+ "jal mtvec_plat_irq8 \n" // Platform Interrupt #8
+ "jal mtvec_plat_irq9 \n" // Platform Interrupt #9
+ "jal mtvec_plat_irq10 \n" // Platform Interrupt #10
+ "jal mtvec_plat_irq11 \n" // Platform Interrupt #11
+ "jal mtvec_plat_irq12 \n" // Platform Interrupt #12
+ "jal mtvec_plat_irq13 \n" // Platform Interrupt #13
+ "jal mtvec_plat_irq14 \n" // Platform Interrupt #14
+ "jal mtvec_plat_irq15 \n" // Platform Interrupt #15
+ :
+ :
+ : "memory"
+ );
+}
+
+volatile uint32_t registers_copy[35] = { 0 };
+
+const char *exception_causes[] = {
+ "Reserved", // 0
+ "Supervisor software interrupt", // 1
+ "Machine software interrupt", // 2
+ "Supervisor timer interrupt", // 3
+ "Machine timer interrupt", // 4
+ "Supervisor external interrupt", // 5
+ "Machine external interrupt", // 6
+ "Designated for platform use", // 7
+ "Instruction address misaligned", // 8
+ "Instruction address fault", // 9
+ "Illegal instruction", // 10
+ "Breakpoint", // 11
+ "Load address misaligned", // 12
+ "Load address fault", // 13
+ "Store/AMO address misaligned", // 14
+ "Store/AMO access fault", // 15
+ "Environment call from U-mode", // 16
+ "Environment call from S-mode", // 17
+ "Environment call from M-mode", // 18
+ "Instruction page fault", // 19
+ "Load page fault", // 20
+ "Store/AMO page fault", // 21
+ "Designated for custom use" // 22
+};
+
+const char *lookup_cause(uint32_t mcause) {
+ if (mcause & 0x80000000) {
+ switch (mcause & 0x7FFFFFFF) {
+ case 1:
+ return exception_causes[1];
+ case 3:
+ return exception_causes[2];
+ case 5:
+ return exception_causes[3];
+ case 7:
+ return exception_causes[4];
+ case 9:
+ return exception_causes[5];
+ case 11:
+ return exception_causes[6];
+ default:
+ return (mcause >= 16) ?
+ exception_causes[7] :
+ exception_causes[0];
+ }
+ }
+
+ switch (mcause) {
+ case 0:
+ return exception_causes[8];
+ case 1:
+ return exception_causes[9];
+ case 2:
+ return exception_causes[10];
+ case 3:
+ return exception_causes[11];
+ case 4:
+ return exception_causes[12];
+ case 5:
+ return exception_causes[13];
+ case 6:
+ return exception_causes[14];
+ case 7:
+ return exception_causes[15];
+ case 8:
+ return exception_causes[16];
+ case 9:
+ return exception_causes[17];
+ case 11:
+ return exception_causes[18];
+ case 12:
+ return exception_causes[19];
+ case 13:
+ return exception_causes[20];
+ case 15:
+ return exception_causes[21];
+ default: {
+ if ((mcause >= 24 && mcause <= 31) ||
+ (mcause >= 48 && mcause <= 63)) {
+ return exception_causes[22];
+ }
+
+ return exception_causes[0];
+ }
+ }
+}
+
+#pragma GCC push_options
+#pragma GCC optimize ("align-functions=4")
+
+__attribute__((interrupt("machine"), weak)) void mtvec_nop(void) {
+}
+
+__attribute__((interrupt("machine"), weak)) void mtvec_exception(void) {
+ __asm volatile (
+ "csrrw x31, mscratch, x31 \n" // Save X31
+ "la x31, registers_copy \n" // Load target address
+ "sw x1, 0(x31) \n" // Save X1
+ "sw x2, 4(x31) \n" // Save X2
+ "sw x3, 8(x31) \n" // Save X3
+ "sw x4, 12(x31) \n" // Save X4
+ "sw x5, 16(x31) \n" // Save X5
+ "sw x6, 20(x31) \n" // Save X6
+ "sw x7, 24(x31) \n" // Save X7
+ "sw x8, 28(x31) \n" // Save X8
+ "sw x9, 32(x31) \n" // Save X9
+ "sw x10, 36(x31) \n" // Save X10
+ "sw x11, 40(x31) \n" // Save X11
+ "sw x12, 44(x31) \n" // Save X12
+ "sw x13, 48(x31) \n" // Save X13
+ "sw x14, 52(x31) \n" // Save X14
+ "sw x15, 56(x31) \n" // Save X15
+ "sw x16, 60(x31) \n" // Save X16
+ "sw x17, 64(x31) \n" // Save X17
+ "sw x18, 68(x31) \n" // Save X18
+ "sw x19, 72(x31) \n" // Save X19
+ "sw x20, 76(x31) \n" // Save X20
+ "sw x21, 80(x31) \n" // Save X21
+ "sw x22, 84(x31) \n" // Save X22
+ "sw x23, 88(x31) \n" // Save X23
+ "sw x24, 92(x31) \n" // Save X24
+ "sw x25, 98(x31) \n" // Save X25
+ "sw x26, 100(x31) \n" // Save X26
+ "sw x27, 104(x31) \n" // Save X27
+ "sw x28, 108(x31) \n" // Save X28
+ "sw x29, 112(x31) \n" // Save X29
+ "sw x30, 116(x31) \n" // Save X30
+ "csrr x30, mscratch \n" // Restore X31
+ "sw x30, 120(x31) \n" // Save X31
+ "csrr x30, mepc \n" // Load MEPC
+ "sw x30, 124(x31) \n" // Save MEPC
+ "csrr x30, mcause \n" // Load MCAUSE
+ "sw x30, 128(x31) \n" // Save MCAUSE
+ "csrr x30, mtval \n" // Load MTVAL
+ "sw x30, 132(x31) \n" // Save MTVAL
+ "csrr x30, mstatus \n" // Load MSTATUS
+ "sw x30, 138(x31) \n" // Save MSTATUS
+ "lw x30, 116(x31) \n" // Restore X30
+ "lw x31, 120(x31) \n" // Restore X31
+ :
+ :
+ : "memory"
+ );
+
+ printf("\nMACHINE EXCEPTION CAUGHT:\n\n");
+
+ printf(" RA=%08lX SP=%08lX GP=%08lX TP=%08lX T0=%08lX T1=%08lX\n",
+ registers_copy[0], registers_copy[1], registers_copy[2],
+ registers_copy[3], registers_copy[4], registers_copy[5]);
+ printf(" T2=%08lX S0=%08lX S1=%08lX A0=%08lX A1=%08lX A2=%08lX\n",
+ registers_copy[6], registers_copy[7], registers_copy[8],
+ registers_copy[9], registers_copy[10], registers_copy[11]);
+ printf(" A3=%08lX A4=%08lX A5=%08lX A6=%08lX A7=%08lX S2=%08lX\n",
+ registers_copy[12], registers_copy[13], registers_copy[14],
+ registers_copy[15], registers_copy[16], registers_copy[17]);
+ printf(" S3=%08lX S4=%08lX S5=%08lX S6=%08lX S7=%08lX S8=%08lX\n",
+ registers_copy[18], registers_copy[19], registers_copy[20],
+ registers_copy[21], registers_copy[22], registers_copy[23]);
+ printf(" S9=%08lX S10=%08lX S11=%08lX T3=%08lX T4=%08lX T5=%08lX\n",
+ registers_copy[24], registers_copy[25], registers_copy[26],
+ registers_copy[27], registers_copy[28], registers_copy[29]);
+ printf(" T6=%08lX\n\n", registers_copy[30]);
+
+ printf(" MEPC=%08lX MTVAL=%08lX MSTATUS=%08lx MCAUSE=%08lx (%s)\n",
+ registers_copy[31], registers_copy[33], registers_copy[34],
+ registers_copy[32], lookup_cause(registers_copy[32]));
+
+ exit(-1);
+}
+
+#pragma GCC pop_options
+
+void set_interrupt_table(void) {
+ __asm volatile (
+ "csrrci s0, mstatus, 8 \n" // S0 = MSTATUS & ~MIE
+ "csrw mstatus, s0 \n" // Global machine interrupts are disabled
+ "csrw mie, zero \n" // Disable machine interrupts
+ "csrw mip, zero \n" // Clear pending machine interrupts
+ "addi s0, %0, 1 \n" // Vectored machine interrupts enabled
+ "csrw mtvec, s0 \n" // Set new machine vector table
+ "csrrsi s0, mstatus, 8 \n" // S0 = MSTATUS | MIE
+ "csrw mstatus, s0 \n" // Global machine interrupts are enabled
+ :
+ : "r" (mtvec_table)
+ : "memory"
+ );
+}
diff --git a/ports/qemu-riscv/mpconfigport.h b/ports/qemu-riscv/mpconfigport.h
new file mode 100644
index 000000000..8f50828e0
--- /dev/null
+++ b/ports/qemu-riscv/mpconfigport.h
@@ -0,0 +1,76 @@
+/*
+ * 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_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
diff --git a/ports/qemu-riscv/mphalport.h b/ports/qemu-riscv/mphalport.h
new file mode 100644
index 000000000..0cbdfc039
--- /dev/null
+++ b/ports/qemu-riscv/mphalport.h
@@ -0,0 +1,32 @@
+/*
+ * 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))
diff --git a/ports/qemu-riscv/qstrdefsport.h b/ports/qemu-riscv/qstrdefsport.h
new file mode 100644
index 000000000..00d3e2ae3
--- /dev/null
+++ b/ports/qemu-riscv/qstrdefsport.h
@@ -0,0 +1,2 @@
+// qstrs specific to this port
+// *FORMAT-OFF*
diff --git a/ports/qemu-riscv/startup.c b/ports/qemu-riscv/startup.c
new file mode 100644
index 000000000..298bd605e
--- /dev/null
+++ b/ports/qemu-riscv/startup.c
@@ -0,0 +1,86 @@
+/*
+ * 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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "uart.h"
+
+extern void set_interrupt_table(void);
+extern int main(int argc, char **argv);
+
+void _entry_point(void) {
+ // Set interrupt table
+ set_interrupt_table();
+ // Enable UART
+ uart_init();
+ // Now that we have a basic system up and running we can call main
+ main(0, 0);
+ // Finished
+ exit(0);
+}
+
+void exit(int status) {
+ uint32_t semihosting_arguments[2] = { 0 };
+
+ // Exit via QEMU's RISC-V semihosting support.
+ __asm volatile (
+ ".option push \n" // Transient options
+ ".option norvc \n" // Do not emit compressed instructions
+ ".align 4 \n" // 16 bytes alignment
+ "mv a1, %0 \n" // Load buffer
+ "li t0, 0x20026 \n" // ADP_Stopped_ApplicationExit
+ "sw t0, 0(a1) \n" // ADP_Stopped_ApplicationExit
+ "sw %1, 4(a1) \n" // Exit code
+ "addi a0, zero, 0x20 \n" // TARGET_SYS_EXIT_EXTENDED
+ "slli zero, zero, 0x1F \n" // Entry NOP
+ "ebreak \n" // Give control to the debugger
+ "srai zero, zero, 7 \n" // Semihosting call
+ ".option pop \n" // Restore previous options set
+ :
+ : "r" (semihosting_arguments), "r" (status)
+ : "memory"
+ );
+
+ // Should never reach here.
+ for (;;) {
+ }
+}
+
+#ifndef NDEBUG
+void __assert_func(const char *file, int line, const char *func, const char *expr) {
+ (void)func;
+ printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line);
+ exit(1);
+}
+#endif
+
+// Picolibc requires `stdout` to be explicitly defined.
+
+#ifdef _PICOLIBC__
+FILE *const stdout;
+#endif
diff --git a/ports/qemu-riscv/test_main.c b/ports/qemu-riscv/test_main.c
new file mode 100644
index 000000000..ca796766a
--- /dev/null
+++ b/ports/qemu-riscv/test_main.c
@@ -0,0 +1,66 @@
+/*
+ * 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);
+}
diff --git a/ports/qemu-riscv/tests_profile.txt b/ports/qemu-riscv/tests_profile.txt
new file mode 100644
index 000000000..2f3bb7300
--- /dev/null
+++ b/ports/qemu-riscv/tests_profile.txt
@@ -0,0 +1,53 @@
+# Port-specific tests exclusion list.
+
+exclude_tests = exclude_tests.union(
+ (
+ # Native code generation is not yet supported.
+ "micropython/native_closure.py",
+ "micropython/native_const.py",
+ "micropython/native_const_intbig.py",
+ "micropython/native_for.py",
+ "micropython/native_fun_attrs.py",
+ "micropython/native_gen.py",
+ "micropython/native_misc.py",
+ "micropython/native_try.py",
+ "micropython/native_try_deep.py",
+ "micropython/native_while.py",
+ "micropython/native_with.py",
+
+ # Viper code generator is not yet supported.
+ "micropython/viper_addr.py",
+ "micropython/viper_args.py",
+ "micropython/viper_binop_arith.py",
+ "micropython/viper_binop_arith_uint.py",
+ "micropython/viper_binop_bitwise_uint.py",
+ "micropython/viper_binop_comp.py",
+ "micropython/viper_binop_comp_imm.py",
+ "micropython/viper_binop_comp_uint.py",
+ "micropython/viper_binop_divmod.py",
+ "micropython/viper_binop_multi_comp.py",
+ "micropython/viper_cond.py",
+ "micropython/viper_const.py",
+ "micropython/viper_const_intbig.py",
+ "micropython/viper_error.py",
+ "micropython/viper_globals.py",
+ "micropython/viper_import.py",
+ "micropython/viper_misc.py",
+ "micropython/viper_misc2.py",
+ "micropython/viper_misc3.py",
+ "micropython/viper_misc_intbig.py",
+ "micropython/viper_ptr16_load.py",
+ "micropython/viper_ptr16_store.py",
+ "micropython/viper_ptr32_load.py",
+ "micropython/viper_ptr32_store.py",
+ "micropython/viper_ptr8_load.py",
+ "micropython/viper_ptr8_store.py",
+ "micropython/viper_storeattr.py",
+ "micropython/viper_subscr.py",
+ "micropython/viper_subscr_multi.py",
+ "micropython/viper_try.py",
+ "micropython/viper_types.py",
+ "micropython/viper_unop.py",
+ "micropython/viper_with.py",
+ )
+)
diff --git a/ports/qemu-riscv/uart.c b/ports/qemu-riscv/uart.c
new file mode 100644
index 000000000..a266d6e6f
--- /dev/null
+++ b/ports/qemu-riscv/uart.c
@@ -0,0 +1,45 @@
+/*
+ * 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
diff --git a/ports/qemu-riscv/uart.h b/ports/qemu-riscv/uart.h
new file mode 100644
index 000000000..fdaae424e
--- /dev/null
+++ b/ports/qemu-riscv/uart.h
@@ -0,0 +1,33 @@
+/*
+ * 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
diff --git a/ports/qemu-riscv/virt.ld b/ports/qemu-riscv/virt.ld
new file mode 100644
index 000000000..64675ad4e
--- /dev/null
+++ b/ports/qemu-riscv/virt.ld
@@ -0,0 +1,98 @@
+/*
+ * 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.
+ */
+
+/* Output format is 32 bits little endian. */
+OUTPUT_FORMAT("elf32-littleriscv", "elf32-littleriscv", "elf32-littleriscv");
+
+/*
+ * Memory layout:
+ *
+ * 0x8000_0000: .text
+ * .........: .rodata
+ * 0x8040_0000: .data
+ * .........: _global_pointer
+ * .........: .bss
+ * 0x8060_0000: .stack
+ * 0x8060_0000: _sstack
+ * 0x8060_FFFF: _estack
+ */
+MEMORY
+{
+ ROM (xr) : ORIGIN = 0x80000000, LENGTH = 4M
+ RAM (xrw) : ORIGIN = ORIGIN(ROM) + LENGTH(ROM), LENGTH = 2M
+ STACK (rw) : ORIGIN = ORIGIN(RAM) + LENGTH(RAM), LENGTH = 64K
+}
+
+SECTIONS
+{
+ /* Code + Read-Only data segment */
+
+ .text : ALIGN (4K)
+ {
+ *(.start)
+ *(.text)
+ . = ALIGN (4K);
+ *(.rodata)
+ _sirodata = .;
+ } > ROM
+
+ .rodata : AT (_sirodata) ALIGN (4K)
+ {
+ *(.rodata)
+ } > ROM
+
+ /* Data + BSS segment */
+
+ .data : ALIGN (4K)
+ {
+ *(.data)
+ _sibss = .;
+ } > RAM
+
+ .bss : AT (_sibss) ALIGN (4K)
+ {
+ /* Mark global pointer address. */
+ _global_pointer = .;
+
+ /* Mark BSS start. */
+ . = . + 4;
+ _sbss = .;
+ *(.bss)
+ /* Mark BSS end. */
+ _ebss = .;
+ } > RAM
+
+ /* Isolated stack segment. */
+
+ .stack : ALIGN(4K)
+ {
+ /* Mark stack start. */
+ _sstack = .;
+ . = LENGTH(STACK);
+ /* Mark stack end. */
+ _estack = .;
+ } > STACK
+}
diff --git a/tests/run-tests.py b/tests/run-tests.py
index 1e1bd160a..fa6c2a033 100755
--- a/tests/run-tests.py
+++ b/tests/run-tests.py
@@ -678,6 +678,8 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
) # RA fsp rtc function doesn't support nano sec info
elif args.target == "qemu-arm":
skip_tests.add("misc/print_exception.py") # requires sys stdfiles
+ elif args.target == "qemu-riscv":
+ skip_tests.add("misc/print_exception.py") # requires sys stdfiles
elif args.target == "webassembly":
skip_tests.add("basics/string_format_modulo.py") # can't print nulls to stdout
skip_tests.add("basics/string_strip.py") # can't print nulls to stdout
@@ -1048,6 +1050,7 @@ the last matching regex is used:
LOCAL_TARGETS = (
"unix",
"qemu-arm",
+ "qemu-riscv",
"webassembly",
)
EXTERNAL_TARGETS = (
@@ -1151,6 +1154,12 @@ the last matching regex is used:
"inlineasm",
"ports/qemu-arm",
)
+ elif args.target == "qemu-riscv":
+ if not args.write_exp:
+ raise ValueError("--target=qemu-riscv must be used with --write-exp")
+ # Generate expected output files for qemu run.
+ # This list should match the test_dirs tuple in tinytest-codegen.py.
+ test_dirs += ("float",)
elif args.target == "webassembly":
test_dirs += ("float", "ports/webassembly")
else: