Skip to content

Commit df00d02

Browse files
committed
qemu-arm: Rework to provide a REPL and run tests via a pty serial port.
Signed-off-by: Damien George <damien@micropython.org>
1 parent 2b62ee8 commit df00d02

File tree

15 files changed

+184
-155
lines changed

15 files changed

+184
-155
lines changed

.github/workflows/ports_qemu-arm.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,4 @@ jobs:
2929
run: source tools/ci.sh && ci_qemu_arm_build
3030
- name: Print failures
3131
if: failure()
32-
run: grep --before-context=100 --text "FAIL" ports/qemu-arm/build/console.out
32+
run: tests/run-tests.py --print-failures

ports/qemu-arm/Makefile

+30-6
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@ QSTR_DEFS = qstrdefsport.h
1111

1212
# MicroPython feature configurations
1313
MICROPY_ROM_TEXT_COMPRESSION ?= 1
14+
FROZEN_MANIFEST ?= "freeze('test-frzmpy')"
1415

1516
# include py core make definitions
1617
include $(TOP)/py/py.mk
1718
include $(TOP)/extmod/extmod.mk
1819

20+
CFLAGS += -DMICROPY_HW_BOARD_NAME='"$(BOARD)"'
21+
1922
ifeq ($(BOARD),netduino2)
2023
CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
2124
CFLAGS += -DQEMU_SOC_STM32
25+
CFLAGS += -DMICROPY_HW_MCU_NAME='"STM32"'
2226
LDSCRIPT = stm32.ld
2327
SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o
2428
MPY_CROSS_FLAGS += -march=armv7m
@@ -27,6 +31,7 @@ endif
2731
ifeq ($(BOARD),microbit)
2832
CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft
2933
CFLAGS += -DQEMU_SOC_NRF51
34+
CFLAGS += -DMICROPY_HW_MCU_NAME='"nRF51"'
3035
LDSCRIPT = nrf51.ld
3136
QEMU_EXTRA = -global nrf51-soc.flash-size=1048576 -global nrf51-soc.sram-size=262144
3237
SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb1.o
@@ -36,6 +41,7 @@ endif
3641
ifeq ($(BOARD),mps2-an385)
3742
CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
3843
CFLAGS += -DQEMU_SOC_MPS2
44+
CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-M3"'
3945
LDSCRIPT = mps2.ld
4046
SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o
4147
MPY_CROSS_FLAGS += -march=armv7m
@@ -44,11 +50,16 @@ endif
4450
ifeq ($(BOARD),sabrelite)
4551
CFLAGS += -mcpu=cortex-a9
4652
CFLAGS += -DQEMU_SOC_IMX6
53+
CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-A9"'
4754
LDSCRIPT = imx6.ld
4855
QEMU_EXTRA = -m 128M
4956
SRC_BOARD_O = shared/runtime/gchelper_generic.o
5057
# It's really armv7a but closest supported value is armv6.
5158
MPY_CROSS_FLAGS += -march=armv6
59+
# Cortex-A9 should support unaligned-access, but qemu doesn't seem to.
60+
CFLAGS += -mno-unaligned-access
61+
# These don't work on Cortex-A9.
62+
TESTS_EXCLUDE = --exclude '(asmdiv|asmspecialregs).py'
5263
endif
5364

5465
CROSS_COMPILE ?= arm-none-eabi-
@@ -81,16 +92,18 @@ LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
8192
SRC_COMMON_C = \
8293
startup.c \
8394
uart.c \
95+
mphalport.c \
8496
shared/libc/string0.c \
97+
shared/readline/readline.c \
98+
shared/runtime/interrupt_char.c \
99+
shared/runtime/pyexec.c \
100+
shared/runtime/semihosting_arm.c \
101+
shared/runtime/stdout_helpers.c \
85102
shared/runtime/sys_stdio_mphal.c \
86103

87104
SRC_RUN_C = \
88105
main.c \
89106

90-
SRC_TEST_C = \
91-
test_main.c \
92-
lib/tinytest/tinytest.c \
93-
94107
LIB_SRC_C += $(SRC_LIB_LIBM_C)
95108
LIB_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C)
96109

@@ -116,10 +129,21 @@ OBJ = $(OBJ_COMMON) $(OBJ_RUN) $(OBJ_TEST)
116129
# List of sources for qstr extraction
117130
SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C)
118131

119-
all: run
132+
all: $(BUILD)/firmware.elf
120133

134+
.PHONY: repl
135+
repl: $(BUILD)/firmware.elf
136+
$(ECHO) "Use machine.reset() to exit"
137+
qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -serial mon:stdio -kernel $<
138+
139+
.PHONY: run
121140
run: $(BUILD)/firmware.elf
122-
qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $<
141+
qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -serial pty -kernel $<
142+
143+
.PHONY: test
144+
test: $(BUILD)/firmware.elf
145+
$(eval DIRNAME=ports/$(notdir $(CURDIR)))
146+
cd $(TOP)/tests && ./run-tests.py --target qemu-arm --device execpty:"qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -serial pty -kernel ../$(DIRNAME)/$<" $(TESTS_EXCLUDE)
123147

124148
## `$(LD)` doesn't seem to like `--specs` for some reason, but we can just use `$(CC)` here.
125149
$(BUILD)/firmware.elf: $(LDSCRIPT) $(ALL_OBJ_RUN)

ports/qemu-arm/Makefile.test

-33
This file was deleted.

ports/qemu-arm/README.md

-12
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,3 @@ The purposes of this port are to enable:
1414
- no need for JTAG or even an MCU chip itself
1515
- no need to use OpenOCD or anything else that might slow down the
1616
process in terms of plugging things together, pressing buttons, etc.
17-
18-
This port will only work with the [GNU ARM Embedded Toolchain](
19-
https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads
20-
and not with CodeSourcery toolchain. You will need to modify
21-
`LDFLAGS` if you want to use CodeSourcery's version of `arm-none-eabi`.
22-
The difference is that CodeSourcery needs `-T generic-m-hosted.ld` while
23-
ARM's version requires `--specs=nano.specs --specs=rdimon.specs` to be
24-
passed to the linker.
25-
26-
To build and run image with builtin testsuite:
27-
28-
make -f Makefile.test test

ports/qemu-arm/main.c

+31-22
Original file line numberDiff line numberDiff line change
@@ -25,48 +25,57 @@
2525
*/
2626

2727
#include <stdlib.h>
28-
#include <stdio.h>
2928

3029
#include "py/compile.h"
3130
#include "py/runtime.h"
3231
#include "py/stackctrl.h"
3332
#include "py/gc.h"
3433
#include "py/mperrno.h"
34+
#include "shared/runtime/gchelper.h"
35+
#include "shared/runtime/pyexec.h"
3536

36-
void do_str(const char *src, mp_parse_input_kind_t input_kind) {
37-
nlr_buf_t nlr;
38-
if (nlr_push(&nlr) == 0) {
39-
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
40-
qstr source_name = lex->source_name;
41-
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
42-
mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true);
43-
mp_call_function_0(module_fun);
44-
nlr_pop();
45-
} else {
46-
// uncaught exception
47-
mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
48-
}
49-
}
37+
#define HEAP_SIZE (100 * 1024)
38+
39+
static uint32_t gc_heap[HEAP_SIZE / sizeof(uint32_t)];
5040

5141
int main(int argc, char **argv) {
5242
mp_stack_ctrl_init();
5343
mp_stack_set_limit(10240);
54-
uint32_t heap[16 * 1024 / 4];
55-
gc_init(heap, (char *)heap + 16 * 1024);
56-
mp_init();
57-
do_str("print('hello world!')", MP_PARSE_SINGLE_INPUT);
58-
mp_deinit();
59-
return 0;
44+
gc_init(gc_heap, (char *)gc_heap + HEAP_SIZE);
45+
46+
for (;;) {
47+
mp_init();
48+
49+
for (;;) {
50+
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
51+
if (pyexec_raw_repl() != 0) {
52+
break;
53+
}
54+
} else {
55+
if (pyexec_friendly_repl() != 0) {
56+
break;
57+
}
58+
}
59+
}
60+
61+
mp_printf(&mp_plat_print, "MPY: soft reboot\n");
62+
63+
gc_sweep_all();
64+
mp_deinit();
65+
}
6066
}
6167

6268
void gc_collect(void) {
69+
gc_collect_start();
70+
gc_helper_collect_regs_and_stack();
71+
gc_collect_end();
6372
}
6473

6574
mp_lexer_t *mp_lexer_new_from_file(qstr filename) {
6675
mp_raise_OSError(MP_ENOENT);
6776
}
6877

6978
void nlr_jump_fail(void *val) {
70-
printf("uncaught NLR\n");
79+
mp_printf(&mp_plat_print, "uncaught NLR\n");
7180
exit(1);
7281
}

ports/qemu-arm/modmachine.c

+16
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,22 @@
2727
// This file is never compiled standalone, it's included directly from
2828
// extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE.
2929

30+
#include <stdlib.h>
31+
3032
static void mp_machine_idle(void) {
3133
// Do nothing.
3234
}
35+
36+
#if MICROPY_PY_MACHINE_RESET
37+
38+
static void mp_machine_reset(void) {
39+
// Exit qemu (via semihosting call).
40+
exit(0);
41+
}
42+
43+
static mp_int_t mp_machine_reset_cause(void) {
44+
// Not implemented.
45+
return 0;
46+
}
47+
48+
#endif

ports/qemu-arm/mpconfigport.h

+3-10
Original file line numberDiff line numberDiff line change
@@ -42,22 +42,19 @@
4242
#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1)
4343
#define MICROPY_MEM_STATS (1)
4444
#define MICROPY_ENABLE_GC (1)
45-
#define MICROPY_KBD_EXCEPTION (0)
46-
#define MICROPY_HELPER_REPL (0)
45+
#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
4746
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
4847
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
4948
#define MICROPY_WARNINGS (1)
50-
#define MICROPY_PY_BUILTINS_INPUT (0)
51-
#define MICROPY_PY_BUILTINS_HELP (0)
5249
#define MICROPY_PY_IO_IOBASE (0)
5350
#define MICROPY_PY_SYS_PLATFORM "qemu-arm"
54-
#define MICROPY_PY_SYS_STDFILES (0)
5551
#define MICROPY_PY_SYS_STDIO_BUFFER (0)
5652
#define MICROPY_PY_SELECT (0)
5753
#define MICROPY_PY_TIME (0)
5854
#define MICROPY_PY_ASYNCIO (0)
5955
#define MICROPY_PY_MACHINE (1)
6056
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/qemu-arm/modmachine.c"
57+
#define MICROPY_PY_MACHINE_RESET (1)
6158
#define MICROPY_PY_MACHINE_PIN_BASE (1)
6259
#define MICROPY_VFS (1)
6360

@@ -78,8 +75,4 @@ typedef long mp_off_t;
7875
// We need an implementation of the log2 function which is not a macro.
7976
#define MP_NEED_LOG2 (1)
8077

81-
#ifdef TEST
82-
#include "shared/upytesthelper/upytesthelper.h"
83-
#undef MP_PLAT_PRINT_STRN
84-
#define MP_PLAT_PRINT_STRN(str, len) upytest_output(str, len)
85-
#endif
78+
#define MP_STATE_PORT MP_STATE_VM

ports/qemu-arm/mphalport.c

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* This file is part of the MicroPython project, http://micropython.org/
3+
*
4+
* The MIT License (MIT)
5+
*
6+
* Copyright (c) 2024 Damien P. George
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+
#include "py/mphal.h"
28+
#include "shared/runtime/semihosting_arm.h"
29+
#include "uart.h"
30+
31+
// UART is better behaved with redirection under qemu-system-arm, so prefer that for stdio.
32+
#define USE_UART (1)
33+
#define USE_SEMIHOSTING (0)
34+
35+
uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
36+
// Not implemented.
37+
return 0;
38+
}
39+
40+
int mp_hal_stdin_rx_chr(void) {
41+
for (;;) {
42+
#if USE_UART
43+
int c = uart_rx_chr();
44+
if (c >= 0) {
45+
return c;
46+
}
47+
#endif
48+
#if USE_SEMIHOSTING
49+
char str[1];
50+
int ret = mp_semihosting_rx_chars(str, 1);
51+
if (ret == 0) {
52+
return str[0];
53+
}
54+
#endif
55+
}
56+
}
57+
58+
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
59+
#if USE_UART
60+
uart_tx_strn(str, len);
61+
#endif
62+
#if USE_SEMIHOSTING
63+
mp_semihosting_tx_strn(str, len);
64+
#endif
65+
return len;
66+
}

ports/qemu-arm/mphalport.h

+1-4
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,4 @@
2424
* THE SOFTWARE.
2525
*/
2626

27-
#include "uart.h"
28-
29-
#define mp_hal_stdin_rx_chr() (0)
30-
#define mp_hal_stdout_tx_strn_cooked(s, l) uart_tx_strn((s), (l))
27+
#include "shared/runtime/interrupt_char.h"

0 commit comments

Comments
 (0)