Skip to content

Commit ecd4b75

Browse files
committed
tests/run-tests, ports/qemu-riscv: Add 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. Signed-off-by: Alessandro Gatti <a.gatti@frob.it>
1 parent 35b2edf commit ecd4b75

18 files changed

+1205
-0
lines changed

ports/qemu-riscv/Makefile

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
include ../../py/mkenv.mk
2+
-include mpconfigport.mk
3+
4+
# qstr definitions (must come before including py.mk)
5+
QSTR_DEFS = qstrdefsport.h
6+
7+
# MicroPython feature configurations
8+
MICROPY_ROM_TEXT_COMPRESSION ?= 1
9+
10+
# include py core make definitions
11+
include $(TOP)/py/py.mk
12+
include $(TOP)/extmod/extmod.mk
13+
14+
BOARD ?= virt
15+
16+
CROSS_COMPILE ?= riscv64-unknown-elf-
17+
18+
# The GCC version string starts like this:
19+
# <target>-gcc (<optional git hash>) <major>.<minor>.<patch>
20+
GCC_VERSION = $(subst ., , $(word 3, $(shell $(CC) --version)))
21+
GCC_MAJOR = $(word 1, $(GCC_VERSION))
22+
GCC_MINOR = $(word 2, $(GCC_VERSION))
23+
GCC_PATCH = $(word 3, $(GCC_VERSION))
24+
25+
ifeq ($(BOARD),virt)
26+
ABI=ilp32
27+
# GCC 10 and lower do not recognise the Zicsr extension in the
28+
# architecture name. "Make" unfortunately does not provide any simple way
29+
# to perform numeric comparisons, so to keep things simple we assume that
30+
# GCC is at least version 10 for the time being.
31+
ifeq ($(GCC_MAJOR),10)
32+
ARCH=rv32imac
33+
else
34+
# Recent GCC versions explicitly require to declare extensions.
35+
ARCH=rv32imac_zicsr
36+
endif
37+
AFLAGS = -mabi=$(ABI) -march=$(ARCH)
38+
CFLAGS += -mabi=$(ABI) -march=$(ARCH) -mcmodel=medany
39+
CFLAGS += -DQEMU_SOC_VIRT
40+
ifeq ($(PICOLIBC),1)
41+
CFLAGS += -DUSE_PICOLIBC -D__TMP_FLT_EVAL_METHOD
42+
endif
43+
LDSCRIPT = virt.ld
44+
LDFLAGS += -T $(LDSCRIPT) -EL
45+
SRC_BOARD_O = shared/runtime/gchelper_generic.o setjmp.o
46+
SRC_BOARD_O += entrypoint.o
47+
endif
48+
49+
INC += -I.
50+
INC += -I$(TOP)
51+
INC += -I$(BUILD)
52+
53+
CFLAGS += $(INC) -Wall -Wpointer-arith -Wdouble-promotion -Wfloat-conversion -Werror -std=gnu99 $(COPT) \
54+
-ffunction-sections -fdata-sections
55+
CFLAGS += $(CFLAGS_EXTRA)
56+
57+
# Debugging/Optimization
58+
ifeq ($(DEBUG), 1)
59+
CFLAGS += -g
60+
COPT = -O0
61+
else
62+
COPT += -Os -DNDEBUG
63+
endif
64+
65+
LDFLAGS += --gc-sections -Map=$(@:.elf=.map)
66+
LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
67+
68+
SRC_COMMON_C = \
69+
interrupts.c \
70+
startup.c \
71+
uart.c \
72+
modmachine.c \
73+
shared/libc/string0.c \
74+
shared/runtime/sys_stdio_mphal.c \
75+
76+
SRC_RUN_C = \
77+
../qemu-arm/main.c \
78+
79+
SRC_TEST_C = \
80+
test_main.c \
81+
lib/tinytest/tinytest.c \
82+
83+
LIB_SRC_C += $(addprefix lib/,\
84+
libm/math.c \
85+
libm/fmodf.c \
86+
libm/nearbyintf.c \
87+
libm/ef_sqrt.c \
88+
libm/kf_rem_pio2.c \
89+
libm/kf_sin.c \
90+
libm/kf_cos.c \
91+
libm/kf_tan.c \
92+
libm/ef_rem_pio2.c \
93+
libm/sf_sin.c \
94+
libm/sf_cos.c \
95+
libm/sf_tan.c \
96+
libm/sf_frexp.c \
97+
libm/sf_modf.c \
98+
libm/sf_ldexp.c \
99+
libm/asinfacosf.c \
100+
libm/atanf.c \
101+
libm/atan2f.c \
102+
libm/roundf.c \
103+
)
104+
105+
OBJ_COMMON =
106+
OBJ_COMMON += $(PY_O)
107+
OBJ_COMMON += $(addprefix $(BUILD)/, $(SRC_COMMON_C:.c=.o))
108+
OBJ_COMMON += $(addprefix $(BUILD)/, $(SRC_BOARD_O))
109+
OBJ_COMMON += $(addprefix $(BUILD)/, $(LIB_SRC_C:.c=.o))
110+
111+
OBJ_RUN =
112+
OBJ_RUN += $(addprefix $(BUILD)/, $(SRC_RUN_C:.c=.o))
113+
114+
ALL_OBJ_RUN = $(OBJ_COMMON) $(OBJ_RUN)
115+
116+
OBJ_TEST =
117+
OBJ_TEST += $(addprefix $(BUILD)/, $(SRC_TEST_C:.c=.o))
118+
119+
ALL_OBJ_TEST = $(OBJ_COMMON) $(OBJ_TEST)
120+
121+
# All object files, needed to get dependencies correct
122+
OBJ = $(OBJ_COMMON) $(OBJ_RUN) $(OBJ_TEST)
123+
124+
# List of sources for qstr extraction
125+
SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C)
126+
127+
all: run
128+
129+
# `make debug` will block QEMU until a debugger is connected to port 1234.
130+
debug: $(BUILD)/firmware.elf
131+
qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -serial mon:stdio -S -s -kernel $<
132+
133+
run: $(BUILD)/firmware.elf
134+
qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $<
135+
136+
$(BUILD)/firmware.elf: $(LDSCRIPT) $(ALL_OBJ_RUN)
137+
$(Q)$(LD) $(LDFLAGS) -o $@ $(ALL_OBJ_RUN) $(LIBS)
138+
$(Q)$(SIZE) $@
139+
140+
include $(TOP)/py/mkrules.mk

ports/qemu-riscv/Makefile.test

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
LIB_SRC_C = shared/upytesthelper/upytesthelper.c
2+
3+
include Makefile
4+
5+
CFLAGS += -DTEST
6+
7+
.PHONY: $(BUILD)/genhdr/tests.h
8+
9+
TESTS_PROFILE = $(dir $(abspath $(firstword $(MAKEFILE_LIST))))/tests_profile.txt
10+
11+
$(BUILD)/test_main.o: $(BUILD)/genhdr/tests.h
12+
$(BUILD)/genhdr/tests.h:
13+
(cd $(TOP)/tests; ./run-tests.py --target=qemu-riscv --write-exp)
14+
$(Q)echo "Generating $@";(cd $(TOP)/tests; ../tools/tinytest-codegen.py --profile $(TESTS_PROFILE) $(addprefix --exclude ,$(TESTS_EXCLUDE))) > $@
15+
16+
$(BUILD)/lib/tinytest/tinytest.o: CFLAGS += -DNO_FORKING
17+
18+
$(BUILD)/firmware-test.elf: $(LDSCRIPT) $(ALL_OBJ_TEST)
19+
$(Q)$(LD) $(LDFLAGS) -o $@ $(ALL_OBJ_TEST) $(LIBS)
20+
$(Q)$(SIZE) $@
21+
22+
# Note: Using timeout(1) to handle cases where qemu hangs (e.g. this can happen with alignment errors).
23+
test: $(BUILD)/firmware-test.elf
24+
timeout --foreground -k 5s 60s qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< > $(BUILD)/console.out
25+
$(Q)tail -n2 $(BUILD)/console.out
26+
$(Q)tail -n1 $(BUILD)/console.out | grep -q "status: 0"
27+
28+
# `make debugtest` will block QEMU until a debugger is connected to port 1234.
29+
30+
debugtest: $(BUILD)/firmware-test.elf
31+
qemu-system-riscv32 -machine $(BOARD) -bios none $(QEMU_EXTRA) -nographic -monitor null -semihosting -serial mon:stdio -S -s -kernel $<

ports/qemu-riscv/README.md

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
This is an experimental, community-supported port for RISC-V RV32IMC emulation
2+
as provided by QEMU (http://qemu.org).
3+
4+
The purposes of this port are to enable:
5+
6+
1. Continuous integration
7+
- run tests against architecture-specific parts of code base
8+
2. Experimentation
9+
- simulation & prototyping of anything that has architecture-specific
10+
code
11+
- exploring instruction set in terms of optimising some part of
12+
MicroPython or a module
13+
3. Streamlined debugging
14+
- no need for JTAG or even an MCU chip itself
15+
- no need to use OpenOCD or anything else that might slow down the
16+
process in terms of plugging things together, pressing buttons, etc.
17+
18+
This port requires a newlib based bare metal/ELF RISC-V toolchain, either
19+
with multilib support or 32 bits specific (M, C, and Zicsr extensions must
20+
be supported, along with ilp32 ABI).
21+
22+
If your toolchain does not support the above requirements, either download
23+
a pre-built toolchain (and that's a hit or miss situation in its own right)
24+
or follow the instructions on [RISC-V's reference toolchain Github repo](https://github.com/riscv-collab/riscv-gnu-toolchain)
25+
to get a newlib-based multilib toolchain (the target will be
26+
`riscv64-unknown-elf` but said toolchain will be able to generate 32 bits
27+
code as well).
28+
29+
That said, when in doubt, build your own toolchain - it's the fastest way to
30+
get things going for sure. To build toolchain that will work with this port
31+
on a recent Linux installation after the necessary
32+
[prerequisites](https://github.com/riscv-collab/riscv-gnu-toolchain#prerequisites)
33+
are installed:
34+
35+
```bash
36+
# Check out the bootstrap source code for the toolchain.
37+
git clone -b 2023.10.18 https://github.com/riscv-collab/riscv-gnu-toolchain
38+
cd riscv-gnu-toolchain
39+
# Configure the toolchain.
40+
# NOTE 1: The toolchain that will be built will only generate code for one
41+
# single ABI/Architecture combination, which is the one that this
42+
# port supports. If you want a generic RISC-V toolchain, omit the
43+
# `--with-multilib-generator` parameter.
44+
# NOTE 2: Pick a prefix that is writable by the user that is building the
45+
# toolchain, or installation will fail.
46+
./configure \
47+
--with-multilib-generator="rv32imc_zicsr-ilp32--" \
48+
--prefix=$TARGETDIR \
49+
--enable-multilib
50+
# Build the toolchain using all available CPU cores.
51+
make -j `nproc`
52+
# The toolchain is going to be available in $TARGETDIR, just remember to
53+
# add $TARGETDIR/bin to the $PATH environment variable, or it won't be made
54+
# available to the system.
55+
```
56+
57+
To build and run image with builtin testsuite:
58+
59+
make -f Makefile.test test
60+
61+
However, if your toolchain comes with picolibc rather than newlib (like the
62+
one available on Ubuntu 22.04, where the libc headers are not shipped with
63+
the toolchain), you must pass `PICOLIBC=1` to make and make sure the picolibc
64+
headers are visible to the compiler. The headers location can also be
65+
provided via a compiler argument in the CFLAGS environment variable. So, for
66+
example, if you want to run the test suite on Ubuntu 22.04 where picolibc must
67+
be explicitly installed and the headers are not visible to the compiler, your
68+
command line should look like this:
69+
70+
```bash
71+
CFLAGS="-I/usr/lib/picolibc/riscv64-unknown-elf/include" \
72+
make -f Makefile.test test PICOLIBC=1
73+
```

ports/qemu-riscv/entrypoint.s

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2023 Alessandro Gatti
7+
*
8+
* Permission is hereby granted, free of charge, to any person obtaining a copy
9+
* of this software and associated documentation files (the "Software"), to deal
10+
* in the Software without restriction, including without limitation the rights
11+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12+
* copies of the Software, and to permit persons to whom the Software is
13+
* furnished to do so, subject to the following conditions:
14+
*
15+
* The above copyright notice and this permission notice shall be included in
16+
* all copies or substantial portions of the Software.
17+
*
18+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
24+
* THE SOFTWARE.
25+
*/
26+
27+
.section .start
28+
.option norvc /* Do not emit compressed instructions. */
29+
.type start, @function
30+
.global start
31+
32+
start:
33+
.cfi_startproc
34+
35+
.option push
36+
.option norelax
37+
la gp, _global_pointer /* Load global pointer register. */
38+
.option pop
39+
40+
csrw satp, zero /* Disable supervisor mode. */
41+
42+
/* Fill stack with a canary value. */
43+
44+
li t0, 0x12345678 /* Load upper canary value. */
45+
la t1, _sstack /* Load stack area start address. */
46+
la t2, _estack /* Load stack area end address. */
47+
1:
48+
sw t0, (t1) /* Write canary. */
49+
addi t1, t1, 4 /* Next word. */
50+
bltu t1, t2, 1b /* Loop until stack is filled. */
51+
52+
la sp, _estack /* Load stack pointer. */
53+
54+
/* Clear BSS area. */
55+
56+
la t1, _sbss /* Load BSS area start address. */
57+
la t2, _ebss /* Load BSS area end address. */
58+
1:
59+
sw zero, (t1) /* Clear word. */
60+
addi t1, t1, 4 /* Next word. */
61+
bltu t1, t2, 1b /* Loop until BSS is cleared. */
62+
63+
/* Set program counter. */
64+
65+
la t0, _entry_point /* Load program counter address. */
66+
csrw mepc, t0 /* Store it into machine exception PC. */
67+
tail _entry_point /* Jump to entry point. */
68+
69+
.cfi_endproc
70+
.end

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy