diff options
| -rw-r--r-- | .github/workflows/ports_qemu.yml | 12 | ||||
| -rw-r--r-- | ports/qemu/Dockerfile.riscv | 39 | ||||
| -rw-r--r-- | ports/qemu/Makefile | 37 | ||||
| -rw-r--r-- | ports/qemu/README.md | 54 | ||||
| -rw-r--r-- | ports/qemu/boards/VIRT_RV64/mpconfigboard.mk | 11 | ||||
| -rw-r--r-- | ports/qemu/mcu/rv64/entrypoint.s | 70 | ||||
| -rw-r--r-- | ports/qemu/mcu/rv64/interrupts.c | 294 | ||||
| -rw-r--r-- | ports/qemu/mcu/rv64/startup.c | 86 | ||||
| -rw-r--r-- | ports/qemu/mcu/rv64/virt.ld | 98 | ||||
| -rw-r--r-- | ports/qemu/mpconfigport.h | 12 | ||||
| -rwxr-xr-x | tools/ci.sh | 13 |
11 files changed, 717 insertions, 9 deletions
diff --git a/.github/workflows/ports_qemu.yml b/.github/workflows/ports_qemu.yml index 1284f11bb..e743f29c9 100644 --- a/.github/workflows/ports_qemu.yml +++ b/.github/workflows/ports_qemu.yml @@ -50,3 +50,15 @@ jobs: - name: Print failures if: failure() run: tests/run-tests.py --print-failures + + build_and_test_rv64: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install packages + run: source tools/ci.sh && ci_qemu_setup_rv64 + - name: Build and run test suite + run: source tools/ci.sh && ci_qemu_build_rv64 + - name: Print failures + if: failure() + run: tests/run-tests.py --print-failures diff --git a/ports/qemu/Dockerfile.riscv b/ports/qemu/Dockerfile.riscv new file mode 100644 index 000000000..9521872fe --- /dev/null +++ b/ports/qemu/Dockerfile.riscv @@ -0,0 +1,39 @@ +# This file is part of the MicroPython project, https://micropython.org/ +# +# The MIT License (MIT) +# +# Copyright (c) 2025 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. + +FROM ubuntu:24.04 + +RUN apt-get update && \ + apt-get install -y \ + gcc-riscv64-unknown-elf \ + picolibc-riscv64-unknown-elf \ + python3 \ + python3-pip \ + python3-platformdirs \ + python3-pyelftools \ + python3-serial \ + qemu-system && \ + pip3 install ar --break-system-packages + +WORKDIR /micropython diff --git a/ports/qemu/Makefile b/ports/qemu/Makefile index d213c8fc0..685bb3536 100644 --- a/ports/qemu/Makefile +++ b/ports/qemu/Makefile @@ -35,6 +35,10 @@ ifeq ($(QEMU_ARCH),riscv32) MICROPY_HEAP_SIZE ?= 143360 FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_asm_rv32.py', 'frozen_const.py', 'frozen_viper.py', 'native_frozen_align.py'))" endif +ifeq ($(QEMU_ARCH),riscv64) +MICROPY_HEAP_SIZE ?= 204800 +FROZEN_MANIFEST ?= "require('unittest'); freeze('test-frzmpy', ('frozen_const.py',))" +endif MICROPY_FLOAT_IMPL ?= float @@ -111,6 +115,39 @@ SRC_BOARD_O += mcu/rv32/entrypoint.o endif ################################################################################ +# RISC-V 64-bit specific settings + +ifeq ($(QEMU_ARCH),riscv64) + +CROSS_COMPILE ?= riscv64-unknown-elf- + +GCC_VERSION = $(word 1, $(subst ., , $(shell $(CC) -dumpversion))) + +RV64_ABI = lp64 + +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) +RV64_ARCH ?= rv64imac +else +# Recent GCC versions explicitly require to declare extensions. +RV64_ARCH ?= rv64imac_zicsr +endif + +AFLAGS += -mabi=$(RV64_ABI) -march=$(RV64_ARCH) +CFLAGS += $(AFLAGS) -mcmodel=medany +LDFLAGS += -mabi=$(RV64_ABI) -march=$(RV64_ARCH) -Wl,-EL -mcmodel=medany + +SRC_C += \ + mcu/rv64/interrupts.c \ + mcu/rv64/startup.c \ + +SRC_BOARD_O += mcu/rv64/entrypoint.o + +endif + +################################################################################ # Project specific settings and compiler/linker flags QEMU_SYSTEM = qemu-system-$(QEMU_ARCH) diff --git a/ports/qemu/README.md b/ports/qemu/README.md index d8ef3ac55..bf5788961 100644 --- a/ports/qemu/README.md +++ b/ports/qemu/README.md @@ -1,8 +1,8 @@ MicroPython port to qemu ======================== -This is experimental, community-supported port for Cortex-M and RISC-V RV32IMC -emulation as provided by QEMU (http://qemu.org). +This is experimental, community-supported port for Cortex-M and RISC-V +RV32IMC/RV64IMC emulation as provided by QEMU (http://qemu.org). The purposes of this port are to enable: @@ -26,9 +26,9 @@ Dependencies For ARM-based boards the build requires a bare-metal ARM toolchain, such as `arm-none-eabi-gcc`. -### RISC-V +### RISC-V 32 -For RISC-V-based boards the build requires a bare metal RISC-V toolchain with GCC 10 +For RV32-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. @@ -37,6 +37,47 @@ 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/). +### RISC-V 64 + +For RV64-based boards the build requires a bare metal RISC-V toolchain with GCC 10 +or later, either with multilib support or 64 bits specific (M, C, and Zicsr +extensions must be supported, along with lp64 ABI). In other words, if your +compiler can build a binary that's compliant to any ratified RVA profiles, +you *might* be good to go: if you run your cross-compiler `gcc` binary with `-v` as +its only argument and you don't see mentions of `-mcmodel=medany`, your +toolchain is probably not going to work for this situation (more later). Both newlib +and picolibc are supported, with the latter having precedence if found. + +RISC-V 64 toolchains are a bit more temperamental. Some toolchains have their `libc`, +`libm`, and `libgcc` libraries built in a way that they cannot be used with code +placed at an offset beyond 0x7FFFF7FF. QEMU's `VIRT_RV64` board code address space +starts at 0x80000000, making those toolchains not viable if code from those libraries +is brought in by the build process. A lot of readily available RISC-V toolchains are +unfortunately broken from this point of view. + +However, to work around this, a Docker container file is provided to be able to run +tests using a known working compiler (the same one running MicroPython's RV32 and RV64 +CI jobs). + +To build the provided toolchain image you can use the following commands: + +```bash +cd $MICROPYTHON_SOURCE_ROOT/ports/qemu +docker build -t micropython/mpy-qemu-riscv -f Dockerfile.riscv . +``` + +and then tests can be run with: + +```bash +cd $MICROPYTHON_SOURCE_ROOT +docker container run -v .:/micropython --rm -it micropython/mpy-qemu-riscv make -C ports/qemu -- BOARD=VIRT_RV64 test +``` + +(If you use Podman, you can replace `docker` with `podman` in the commands above). + +Also, if you replace `VIRT_RV64` with `VIRT_RV32` you should be able to run +32-bits tests as well! + Build instructions ------------------ @@ -63,6 +104,7 @@ Available boards are: | `NETDUINO2` | `arm` | `netduino2` | | `SABRELITE` | `arm` | `sabrelite` | | `VIRT_RV32` | `riscv32` | `virt` | +| `VIRT_RV64` | `riscv64` | `virt` | Running ------- @@ -100,8 +142,8 @@ tests against the serial device, for example: $ ./run-tests.py -t /dev/pts/1 Selected native modules that come as examples with the MicroPython source tree -can also be tested with this command (this is currently supported only for the -`VIRT_RV32` board): +can also be tested with this command (this is currently not supported for the +`VIRT_RV64` board): $ make test_natmod diff --git a/ports/qemu/boards/VIRT_RV64/mpconfigboard.mk b/ports/qemu/boards/VIRT_RV64/mpconfigboard.mk new file mode 100644 index 000000000..dae4ca3e0 --- /dev/null +++ b/ports/qemu/boards/VIRT_RV64/mpconfigboard.mk @@ -0,0 +1,11 @@ +QEMU_ARCH = riscv64 +QEMU_MACHINE = virt + +CFLAGS += -DQEMU_SOC_VIRT +CFLAGS += -DMICROPY_HW_MCU_NAME='"$(RV64_ARCH)"' + +LDSCRIPT = mcu/rv64/virt.ld + +SRC_BOARD_O += shared/runtime/gchelper_native.o shared/runtime/gchelper_rv64i.o + +MPY_CROSS_FLAGS += -march=rv64imc diff --git a/ports/qemu/mcu/rv64/entrypoint.s b/ports/qemu/mcu/rv64/entrypoint.s new file mode 100644 index 000000000..7bf7c24c6 --- /dev/null +++ b/ports/qemu/mcu/rv64/entrypoint.s @@ -0,0 +1,70 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 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, 0xBBBBBBBBBBBBBBBB /* Load canary value. */ + la t1, _sstack /* Load stack area start address. */ + la t2, _estack /* Load stack area end address. */ +1: + sd t0, (t1) /* Write canary. */ + addi t1, t1, 8 /* 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: + sd zero, (t1) /* Clear word. */ + addi t1, t1, 8 /* 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/mcu/rv64/interrupts.c b/ports/qemu/mcu/rv64/interrupts.c new file mode 100644 index 000000000..e616e82d2 --- /dev/null +++ b/ports/qemu/mcu/rv64/interrupts.c @@ -0,0 +1,294 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 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" + ); +} + +static volatile uintptr_t registers_copy[35] = { 0 }; + +static 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 +}; + +#define MSB64 (1ULL << 63) + +static const char *lookup_cause(uintptr_t mcause) { + if (mcause & MSB64) { + switch (mcause & (MSB64 - 1)) { + 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 & (MSB64 - 1)) >= 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=8") + +__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 + "sd x1, 0(x31) \n" // Save X1 + "sd x2, 8(x31) \n" // Save X2 + "sd x3, 16(x31) \n" // Save X3 + "sd x4, 24(x31) \n" // Save X4 + "sd x5, 32(x31) \n" // Save X5 + "sd x6, 40(x31) \n" // Save X6 + "sd x7, 48(x31) \n" // Save X7 + "sd x8, 56(x31) \n" // Save X8 + "sd x9, 64(x31) \n" // Save X9 + "sd x10, 72(x31) \n" // Save X10 + "sd x11, 80(x31) \n" // Save X11 + "sd x12, 88(x31) \n" // Save X12 + "sd x13, 96(x31) \n" // Save X13 + "sd x14, 104(x31) \n" // Save X14 + "sd x15, 112(x31) \n" // Save X15 + "sd x16, 120(x31) \n" // Save X16 + "sd x17, 128(x31) \n" // Save X17 + "sd x18, 136(x31) \n" // Save X18 + "sd x19, 144(x31) \n" // Save X19 + "sd x20, 152(x31) \n" // Save X20 + "sd x21, 160(x31) \n" // Save X21 + "sd x22, 168(x31) \n" // Save X22 + "sd x23, 176(x31) \n" // Save X23 + "sd x24, 184(x31) \n" // Save X24 + "sd x25, 192(x31) \n" // Save X25 + "sd x26, 200(x31) \n" // Save X26 + "sd x27, 208(x31) \n" // Save X27 + "sd x28, 216(x31) \n" // Save X28 + "sd x29, 224(x31) \n" // Save X29 + "sd x30, 232(x31) \n" // Save X30 + "csrr x30, mscratch \n" // Restore X31 + "sd x30, 240(x31) \n" // Save X31 + "csrr x30, mepc \n" // Load MEPC + "sd x30, 248(x31) \n" // Save MEPC + "csrr x30, mcause \n" // Load MCAUSE + "sd x30, 256(x31) \n" // Save MCAUSE + "csrr x30, mtval \n" // Load MTVAL + "sd x30, 264(x31) \n" // Save MTVAL + "csrr x30, mstatus \n" // Load MSTATUS + "sd x30, 272(x31) \n" // Save MSTATUS + "ld x30, 232(x31) \n" // Restore X30 + "ld x31, 240(x31) \n" // Restore X31 + : + : + : "memory" + ); + + printf("\nMACHINE EXCEPTION CAUGHT:\n\n"); + + printf(" RA=%016lX SP=%016lX GP=%016lX TP=%016lX T0=%016lX T1=%016lX\n", + registers_copy[0], registers_copy[1], registers_copy[2], + registers_copy[3], registers_copy[4], registers_copy[5]); + printf(" T2=%016lX S0=%016lX S1=%016lX A0=%016lX A1=%016lX A2=%016lX\n", + registers_copy[6], registers_copy[7], registers_copy[8], + registers_copy[9], registers_copy[10], registers_copy[11]); + printf(" A3=%016lX A4=%016lX A5=%016lX A6=%016lX A7=%016lX S2=%016lX\n", + registers_copy[12], registers_copy[13], registers_copy[14], + registers_copy[15], registers_copy[16], registers_copy[17]); + printf(" S3=%016lX S4=%016lX S5=%016lX S6=%016lX S7=%016lX S8=%016lX\n", + registers_copy[18], registers_copy[19], registers_copy[20], + registers_copy[21], registers_copy[22], registers_copy[23]); + printf(" S9=%016lX S10=%016lX S11=%016lX T3=%016lX T4=%016lX T5=%016lX\n", + registers_copy[24], registers_copy[25], registers_copy[26], + registers_copy[27], registers_copy[28], registers_copy[29]); + printf(" T6=%016lX\n\n", registers_copy[30]); + + printf(" MEPC=%016lX MTVAL=%016lX MSTATUS=%016lx MCAUSE=%016lx (%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/mcu/rv64/startup.c b/ports/qemu/mcu/rv64/startup.c new file mode 100644 index 000000000..8c7c5b731 --- /dev/null +++ b/ports/qemu/mcu/rv64/startup.c @@ -0,0 +1,86 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 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) { + uintptr_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 + "sd t0, 0(a1) \n" // ADP_Stopped_ApplicationExit + "sd %1, 8(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/mcu/rv64/virt.ld b/ports/qemu/mcu/rv64/virt.ld new file mode 100644 index 000000000..7727839e0 --- /dev/null +++ b/ports/qemu/mcu/rv64/virt.ld @@ -0,0 +1,98 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 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 64 bits little endian. */ +OUTPUT_FORMAT("elf64-littleriscv", "elf64-littleriscv", "elf64-littleriscv"); + +/* + * Memory layout: + * + * 0x0000_0000_8000_0000: .text + * .........: .rodata + * 0x0000_0000_8040_0000: .data + * .........: _global_pointer + * .........: .bss + * 0x0000_0000_8060_0000: .stack + * 0x0000_0000_8060_0000: _sstack + * 0x0000_0000_8061_FFFF: _estack + */ +MEMORY +{ + ROM (xr) : ORIGIN = 0x0000000080000000, LENGTH = 4M + RAM (xrw) : ORIGIN = ORIGIN(ROM) + LENGTH(ROM), LENGTH = 2M + STACK (rw) : ORIGIN = ORIGIN(RAM) + LENGTH(RAM), LENGTH = 128K +} + +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/ports/qemu/mpconfigport.h b/ports/qemu/mpconfigport.h index 6c6dd6853..1649d8a01 100644 --- a/ports/qemu/mpconfigport.h +++ b/ports/qemu/mpconfigport.h @@ -39,7 +39,7 @@ #define MICROPY_EMIT_INLINE_THUMB (1) #endif #define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) -#elif defined(__riscv) +#elif defined(__riscv) && (__riscv_xlen == 32) #define MICROPY_EMIT_RV32 (1) #define MICROPY_EMIT_INLINE_RV32 (1) #endif @@ -67,14 +67,20 @@ // type definitions for the specific machine +#if defined(__riscv) && (__riscv_xlen == 64) +#define MP_SSIZE_MAX (0x7fffffffffffffff) +typedef int64_t mp_int_t; // must be pointer size +typedef uint64_t mp_uint_t; // must be pointer size +#else #define MP_SSIZE_MAX (0x7fffffff) +typedef int32_t mp_int_t; // must be pointer size +typedef uint32_t mp_uint_t; // must be pointer size +#endif #define UINT_FMT "%lu" #define INT_FMT "%ld" #define HEX_FMT "%lx" -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 to provide a declaration/definition of alloca() diff --git a/tools/ci.sh b/tools/ci.sh index 6b974f983..26def3cc4 100755 --- a/tools/ci.sh +++ b/tools/ci.sh @@ -337,6 +337,13 @@ function ci_qemu_setup_rv32 { qemu-system-riscv32 --version } +function ci_qemu_setup_rv64 { + ci_gcc_riscv_setup + sudo apt-get update + sudo apt-get install qemu-system + qemu-system-riscv64 --version +} + function ci_qemu_build_arm_prepare { make ${MAKEOPTS} -C mpy-cross make ${MAKEOPTS} -C ports/qemu submodules @@ -388,6 +395,12 @@ function ci_qemu_build_rv32 { make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV32 test_natmod } +function ci_qemu_build_rv64 { + make ${MAKEOPTS} -C mpy-cross + make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV64 submodules + make ${MAKEOPTS} -C ports/qemu BOARD=VIRT_RV64 test +} + ######################################################################################## # ports/renesas-ra |
