Commit d9a0fdda authored by Damien George's avatar Damien George

qemu-arm: Rework to provide a REPL and run tests via a pty serial port.

Currently, the qemu-arm (and qemu-riscv) port has two build modes:
- a simple test that executes a Python string; and
- a full test that uses tinytest to embed all tests within the firmware,
  then executes that and captures the output.

This is very different to all the other ports.  A difficulty with using
tinytest is that with the large number of tests the firmware overflows its
virtual flash size.  It's also hard to run tests via .mpy files and with
the native emitter.  Being different to the other ports also means an extra
burden on maintenance.

This commit reworks the qemu-arm port so that it has a single build target
that creates a standard firmware which has a REPL.  When run under
qemu-system-arm, the REPL acts like any other bare-metal port, complete
with soft reset (use machine.reset() to turn it off and exit
qemu-system-arm).

This approach gives many benefits:
- allows playing with a REPL without hardware;
- allows running the test suite as it would on a bare-metal board, by
  making qemu-system-arm redirect the UART serial of the virtual device to
  a /dev/pts/xx file, and then running run-tests.py against that serial
  device;
- skipping tests is now done via the logic in `run-tests.py` and no longer
  needs multiple places to define which tests to skip
  (`tools/tinytest-codegen.py`, `ports/qemu-arm/tests_profile.txt` and also
  `tests/run-tests.py`);
- allows testing/using mpremote with the qemu-arm port.

Eventually the qemu-riscv port would have a similar change.

Prior to this commit the test results were:

    743 tests ok.  (121 skipped)

With this commit the test results are:

    753 tests performed (22673 individual testcases)
    753 tests passed
    138 tests skipped

More tests are skipped because more are included in the run. But overall
more tests pass.
Signed-off-by: default avatarDamien George <damien@micropython.org>
parent 8a3842eb
...@@ -29,4 +29,4 @@ jobs: ...@@ -29,4 +29,4 @@ jobs:
run: source tools/ci.sh && ci_qemu_arm_build run: source tools/ci.sh && ci_qemu_arm_build
- name: Print failures - name: Print failures
if: failure() if: failure()
run: grep --before-context=100 --text "FAIL" ports/qemu-arm/build/console.out run: tests/run-tests.py --print-failures
...@@ -11,14 +11,19 @@ QSTR_DEFS = qstrdefsport.h ...@@ -11,14 +11,19 @@ QSTR_DEFS = qstrdefsport.h
# MicroPython feature configurations # MicroPython feature configurations
MICROPY_ROM_TEXT_COMPRESSION ?= 1 MICROPY_ROM_TEXT_COMPRESSION ?= 1
FROZEN_MANIFEST ?= "freeze('test-frzmpy')"
# include py core make definitions # include py core make definitions
include $(TOP)/py/py.mk include $(TOP)/py/py.mk
include $(TOP)/extmod/extmod.mk include $(TOP)/extmod/extmod.mk
CFLAGS += -DMICROPY_HW_BOARD_NAME='"$(BOARD)"'
QEMU_ARGS += -machine $(BOARD) -nographic -monitor null -semihosting
ifeq ($(BOARD),netduino2) ifeq ($(BOARD),netduino2)
CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_STM32 CFLAGS += -DQEMU_SOC_STM32
CFLAGS += -DMICROPY_HW_MCU_NAME='"STM32"'
LDSCRIPT = stm32.ld LDSCRIPT = stm32.ld
SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o
MPY_CROSS_FLAGS += -march=armv7m MPY_CROSS_FLAGS += -march=armv7m
...@@ -27,8 +32,9 @@ endif ...@@ -27,8 +32,9 @@ endif
ifeq ($(BOARD),microbit) ifeq ($(BOARD),microbit)
CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_NRF51 CFLAGS += -DQEMU_SOC_NRF51
CFLAGS += -DMICROPY_HW_MCU_NAME='"nRF51"'
LDSCRIPT = nrf51.ld LDSCRIPT = nrf51.ld
QEMU_EXTRA = -global nrf51-soc.flash-size=1048576 -global nrf51-soc.sram-size=262144 QEMU_ARGS += -global nrf51-soc.flash-size=1048576 -global nrf51-soc.sram-size=262144
SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb1.o SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb1.o
MPY_CROSS_FLAGS += -march=armv7m MPY_CROSS_FLAGS += -march=armv7m
endif endif
...@@ -36,6 +42,7 @@ endif ...@@ -36,6 +42,7 @@ endif
ifeq ($(BOARD),mps2-an385) ifeq ($(BOARD),mps2-an385)
CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_MPS2 CFLAGS += -DQEMU_SOC_MPS2
CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-M3"'
LDSCRIPT = mps2.ld LDSCRIPT = mps2.ld
SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o SRC_BOARD_O = shared/runtime/gchelper_native.o shared/runtime/gchelper_thumb2.o
MPY_CROSS_FLAGS += -march=armv7m MPY_CROSS_FLAGS += -march=armv7m
...@@ -44,11 +51,16 @@ endif ...@@ -44,11 +51,16 @@ endif
ifeq ($(BOARD),sabrelite) ifeq ($(BOARD),sabrelite)
CFLAGS += -mcpu=cortex-a9 CFLAGS += -mcpu=cortex-a9
CFLAGS += -DQEMU_SOC_IMX6 CFLAGS += -DQEMU_SOC_IMX6
CFLAGS += -DMICROPY_HW_MCU_NAME='"Cortex-A9"'
LDSCRIPT = imx6.ld LDSCRIPT = imx6.ld
QEMU_EXTRA = -m 128M QEMU_ARGS += -m 128M
SRC_BOARD_O = shared/runtime/gchelper_generic.o SRC_BOARD_O = shared/runtime/gchelper_generic.o
# It's really armv7a but closest supported value is armv6. # It's really armv7a but closest supported value is armv6.
MPY_CROSS_FLAGS += -march=armv6 MPY_CROSS_FLAGS += -march=armv6
# Cortex-A9 should support unaligned-access, but qemu doesn't seem to.
CFLAGS += -mno-unaligned-access
# These don't work on Cortex-A9.
TESTS_EXCLUDE = --exclude '(asmdiv|asmspecialregs).py'
endif endif
CROSS_COMPILE ?= arm-none-eabi- CROSS_COMPILE ?= arm-none-eabi-
...@@ -81,16 +93,18 @@ LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) ...@@ -81,16 +93,18 @@ LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
SRC_COMMON_C = \ SRC_COMMON_C = \
startup.c \ startup.c \
uart.c \ uart.c \
mphalport.c \
shared/libc/string0.c \ shared/libc/string0.c \
shared/readline/readline.c \
shared/runtime/interrupt_char.c \
shared/runtime/pyexec.c \
shared/runtime/semihosting_arm.c \
shared/runtime/stdout_helpers.c \
shared/runtime/sys_stdio_mphal.c \ shared/runtime/sys_stdio_mphal.c \
SRC_RUN_C = \ SRC_RUN_C = \
main.c \ main.c \
SRC_TEST_C = \
test_main.c \
lib/tinytest/tinytest.c \
LIB_SRC_C += $(SRC_LIB_LIBM_C) LIB_SRC_C += $(SRC_LIB_LIBM_C)
LIB_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C) LIB_SRC_C += $(SRC_LIB_LIBM_SQRT_SW_C)
...@@ -116,10 +130,21 @@ OBJ = $(OBJ_COMMON) $(OBJ_RUN) $(OBJ_TEST) ...@@ -116,10 +130,21 @@ OBJ = $(OBJ_COMMON) $(OBJ_RUN) $(OBJ_TEST)
# List of sources for qstr extraction # List of sources for qstr extraction
SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C) SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C)
all: run all: $(BUILD)/firmware.elf
.PHONY: repl
repl: $(BUILD)/firmware.elf
$(ECHO) "Use machine.reset() to exit"
qemu-system-arm $(QEMU_ARGS) -serial mon:stdio -kernel $<
.PHONY: run
run: $(BUILD)/firmware.elf run: $(BUILD)/firmware.elf
qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $< qemu-system-arm $(QEMU_ARGS) -serial pty -kernel $<
.PHONY: test
test: $(BUILD)/firmware.elf
$(eval DIRNAME=ports/$(notdir $(CURDIR)))
cd $(TOP)/tests && ./run-tests.py --target qemu-arm --device execpty:"qemu-system-arm $(QEMU_ARGS) -serial pty -kernel ../$(DIRNAME)/$<" $(TESTS_EXCLUDE)
## `$(LD)` doesn't seem to like `--specs` for some reason, but we can just use `$(CC)` here. ## `$(LD)` doesn't seem to like `--specs` for some reason, but we can just use `$(CC)` here.
$(BUILD)/firmware.elf: $(LDSCRIPT) $(ALL_OBJ_RUN) $(BUILD)/firmware.elf: $(LDSCRIPT) $(ALL_OBJ_RUN)
......
LIB_SRC_C = shared/upytesthelper/upytesthelper.c
FROZEN_MANIFEST ?= "freeze('test-frzmpy')"
include Makefile
ifeq ($(BOARD),sabrelite)
# These don't work on Cortex-A9.
TESTS_EXCLUDE = inlineasm/asmdiv.py inlineasm/asmspecialregs.py
endif
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-arm --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 30s qemu-system-arm -machine $(BOARD) $(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"
MicroPython port to qemu-arm
============================
This is experimental, community-supported port for Cortex-M emulation as This is experimental, community-supported port for Cortex-M emulation as
provided by QEMU (http://qemu.org). provided by QEMU (http://qemu.org).
...@@ -15,14 +18,52 @@ The purposes of this port are to enable: ...@@ -15,14 +18,52 @@ The purposes of this port are to enable:
- no need to use OpenOCD or anything else that might slow down the - no need to use OpenOCD or anything else that might slow down the
process in terms of plugging things together, pressing buttons, etc. process in terms of plugging things together, pressing buttons, etc.
This port will only work with the [GNU ARM Embedded Toolchain]( Build instructions
https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads ------------------
and not with CodeSourcery toolchain. You will need to modify
`LDFLAGS` if you want to use CodeSourcery's version of `arm-none-eabi`. First make sure the MicroPython cross-compiler is built (run from this directory):
The difference is that CodeSourcery needs `-T generic-m-hosted.ld` while
ARM's version requires `--specs=nano.specs --specs=rdimon.specs` to be $ make -C ../../mpy-cross
passed to the linker.
Then build using:
$ make
The default qemu-supported board is `mps2-an385`, a Cortex-M3 board. To select a
different board pass the `BOARD` argument to `make`, for example:
$ make BOARD=sabrelite
Running
-------
When the firmware is run it will provide a REPL on the emulated hardware UART.
To access the REPL directly use:
$ make repl
This will start `qemu-system-arm` with the UART redirected to stdio. It's also
possible to redirect the UART to a pty device using:
$ make run
This will start the emulation and the name of the pty device will be printed to
stdout. This serial device then be accessed via a serial terminal program,
for example `mpremote`:
$ mpremote connect /dev/pts/1
You can disconnect and reconnect to the serial device multiple times. Once you
are finished, stop the `make run` command by pressing Ctrl-C where that command
was started (or execute `machine.reset()` at the REPL).
The test suite can be run against the firmware by using the UART redirection.
You can either do this automatically using the single command:
$ make test
To build and run image with builtin testsuite: Or manually by first starting the emulation with `make run` and then running the
tests against the serial device, for example:
make -f Makefile.test test $ cd ../../tests
$ ./run-tests.py --target qemu-arm --device /dev/pts/1
...@@ -25,41 +25,50 @@ ...@@ -25,41 +25,50 @@
*/ */
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h>
#include "py/compile.h" #include "py/compile.h"
#include "py/runtime.h" #include "py/runtime.h"
#include "py/stackctrl.h" #include "py/stackctrl.h"
#include "py/gc.h" #include "py/gc.h"
#include "py/mperrno.h" #include "py/mperrno.h"
#include "shared/runtime/gchelper.h"
#include "shared/runtime/pyexec.h"
void do_str(const char *src, mp_parse_input_kind_t input_kind) { #define HEAP_SIZE (100 * 1024)
nlr_buf_t nlr;
if (nlr_push(&nlr) == 0) { static uint32_t gc_heap[HEAP_SIZE / sizeof(uint32_t)];
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
qstr source_name = lex->source_name;
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true);
mp_call_function_0(module_fun);
nlr_pop();
} else {
// uncaught exception
mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
}
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
mp_stack_ctrl_init(); mp_stack_ctrl_init();
mp_stack_set_limit(10240); mp_stack_set_limit(10240);
uint32_t heap[16 * 1024 / 4]; gc_init(gc_heap, (char *)gc_heap + HEAP_SIZE);
gc_init(heap, (char *)heap + 16 * 1024);
mp_init(); for (;;) {
do_str("print('hello world!')", MP_PARSE_SINGLE_INPUT); mp_init();
mp_deinit();
return 0; for (;;) {
if (pyexec_mode_kind == PYEXEC_MODE_RAW_REPL) {
if (pyexec_raw_repl() != 0) {
break;
}
} else {
if (pyexec_friendly_repl() != 0) {
break;
}
}
}
mp_printf(&mp_plat_print, "MPY: soft reboot\n");
gc_sweep_all();
mp_deinit();
}
} }
void gc_collect(void) { 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_lexer_t *mp_lexer_new_from_file(qstr filename) {
...@@ -67,6 +76,6 @@ mp_lexer_t *mp_lexer_new_from_file(qstr filename) { ...@@ -67,6 +76,6 @@ mp_lexer_t *mp_lexer_new_from_file(qstr filename) {
} }
void nlr_jump_fail(void *val) { void nlr_jump_fail(void *val) {
printf("uncaught NLR\n"); mp_printf(&mp_plat_print, "uncaught NLR\n");
exit(1); exit(1);
} }
...@@ -27,6 +27,22 @@ ...@@ -27,6 +27,22 @@
// This file is never compiled standalone, it's included directly from // This file is never compiled standalone, it's included directly from
// extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE. // extmod/modmachine.c via MICROPY_PY_MACHINE_INCLUDEFILE.
#include <stdlib.h>
static void mp_machine_idle(void) { static void mp_machine_idle(void) {
// Do nothing. // Do nothing.
} }
#if MICROPY_PY_MACHINE_RESET
static void mp_machine_reset(void) {
// Exit qemu (via semihosting call).
exit(0);
}
static mp_int_t mp_machine_reset_cause(void) {
// Not implemented.
return 0;
}
#endif
...@@ -42,22 +42,19 @@ ...@@ -42,22 +42,19 @@
#define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1) #define MICROPY_MALLOC_USES_ALLOCATED_SIZE (1)
#define MICROPY_MEM_STATS (1) #define MICROPY_MEM_STATS (1)
#define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_GC (1)
#define MICROPY_KBD_EXCEPTION (0) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1)
#define MICROPY_HELPER_REPL (0)
#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ)
#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT)
#define MICROPY_WARNINGS (1) #define MICROPY_WARNINGS (1)
#define MICROPY_PY_BUILTINS_INPUT (0)
#define MICROPY_PY_BUILTINS_HELP (0)
#define MICROPY_PY_IO_IOBASE (0) #define MICROPY_PY_IO_IOBASE (0)
#define MICROPY_PY_SYS_PLATFORM "qemu-arm" #define MICROPY_PY_SYS_PLATFORM "qemu-arm"
#define MICROPY_PY_SYS_STDFILES (0)
#define MICROPY_PY_SYS_STDIO_BUFFER (0) #define MICROPY_PY_SYS_STDIO_BUFFER (0)
#define MICROPY_PY_SELECT (0) #define MICROPY_PY_SELECT (0)
#define MICROPY_PY_TIME (0) #define MICROPY_PY_TIME (0)
#define MICROPY_PY_ASYNCIO (0) #define MICROPY_PY_ASYNCIO (0)
#define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MACHINE_INCLUDEFILE "ports/qemu-arm/modmachine.c" #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/qemu-arm/modmachine.c"
#define MICROPY_PY_MACHINE_RESET (1)
#define MICROPY_PY_MACHINE_PIN_BASE (1) #define MICROPY_PY_MACHINE_PIN_BASE (1)
#define MICROPY_VFS (1) #define MICROPY_VFS (1)
...@@ -78,8 +75,4 @@ typedef long mp_off_t; ...@@ -78,8 +75,4 @@ typedef long mp_off_t;
// We need an implementation of the log2 function which is not a macro. // We need an implementation of the log2 function which is not a macro.
#define MP_NEED_LOG2 (1) #define MP_NEED_LOG2 (1)
#ifdef TEST #define MP_STATE_PORT MP_STATE_VM
#include "shared/upytesthelper/upytesthelper.h"
#undef MP_PLAT_PRINT_STRN
#define MP_PLAT_PRINT_STRN(str, len) upytest_output(str, len)
#endif
/*
* This file is part of the MicroPython project, http://micropython.org/
*
* The MIT License (MIT)
*
* Copyright (c) 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 "py/mphal.h"
#include "shared/runtime/semihosting_arm.h"
#include "uart.h"
// UART is better behaved with redirection under qemu-system-arm, so prefer that for stdio.
#define USE_UART (1)
#define USE_SEMIHOSTING (0)
uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) {
// Not implemented.
return 0;
}
int mp_hal_stdin_rx_chr(void) {
for (;;) {
#if USE_UART
int c = uart_rx_chr();
if (c >= 0) {
return c;
}
#endif
#if USE_SEMIHOSTING
char str[1];
int ret = mp_semihosting_rx_chars(str, 1);
if (ret == 0) {
return str[0];
}
#endif
}
}
mp_uint_t mp_hal_stdout_tx_strn(const char *str, size_t len) {
#if USE_UART
uart_tx_strn(str, len);
#endif
#if USE_SEMIHOSTING
mp_semihosting_tx_strn(str, len);
#endif
return len;
}
...@@ -24,7 +24,4 @@ ...@@ -24,7 +24,4 @@
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "uart.h" #include "shared/runtime/interrupt_char.h"
#define mp_hal_stdin_rx_chr() (0)
#define mp_hal_stdout_tx_strn_cooked(s, l) uart_tx_strn((s), (l))
...@@ -28,6 +28,7 @@ ...@@ -28,6 +28,7 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include "shared/runtime/semihosting_arm.h"
#include "uart.h" #include "uart.h"
extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss; extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss;
...@@ -97,6 +98,8 @@ const uint32_t isr_vector[] __attribute__((section(".isr_vector"))) = { ...@@ -97,6 +98,8 @@ const uint32_t isr_vector[] __attribute__((section(".isr_vector"))) = {
#endif #endif
void _start(void) { void _start(void) {
mp_semihosting_init();
// Enable the UART // Enable the UART
uart_init(); uart_init();
...@@ -108,21 +111,9 @@ void _start(void) { ...@@ -108,21 +111,9 @@ void _start(void) {
exit(0); exit(0);
} }
__attribute__((naked)) void exit(int status) { void exit(int status) {
// Force qemu to exit using ARM Semihosting // Force qemu to exit using ARM Semihosting
__asm volatile ( mp_semihosting_exit(status);
"mov r1, r0\n"
"cmp r1, #0\n"
"bne .notclean\n"
"ldr r1, =0x20026\n" // ADP_Stopped_ApplicationExit, a clean exit
".notclean:\n"
"movs r0, #0x18\n" // SYS_EXIT
#if defined(__ARM_ARCH_ISA_ARM)
"svc 0x00123456\n"
#elif defined(__ARM_ARCH_ISA_THUMB)
"bkpt 0xab\n"
#endif
);
for (;;) { for (;;) {
} }
} }
...@@ -134,13 +125,3 @@ void __assert_func(const char *file, int line, const char *func, const char *exp ...@@ -134,13 +125,3 @@ void __assert_func(const char *file, int line, const char *func, const char *exp
exit(1); exit(1);
} }
#endif #endif
// The following are needed for tinytest
#include <stdio.h>
int setvbuf(FILE *stream, char *buf, int mode, size_t size) {
return 0;
}
struct _reent *_impure_ptr;
# Port-specific test directories.
test_dirs.update(("inlineasm", "ports/qemu-arm"))
# Port-specific tests exclusion list.
exclude_tests.update(
(
# inline asm FP tests (require Cortex-M4)
"inlineasm/asmfpaddsub.py",
"inlineasm/asmfpcmp.py",
"inlineasm/asmfpldrstr.py",
"inlineasm/asmfpmuldiv.py",
"inlineasm/asmfpsqrt.py",
)
)
...@@ -81,7 +81,7 @@ SRC_COMMON_C = \ ...@@ -81,7 +81,7 @@ SRC_COMMON_C = \
shared/runtime/sys_stdio_mphal.c \ shared/runtime/sys_stdio_mphal.c \
SRC_RUN_C = \ SRC_RUN_C = \
ports/qemu-arm/main.c \ main.c \
SRC_TEST_C = \ SRC_TEST_C = \
test_main.c \ test_main.c \
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* The MIT License (MIT) * The MIT License (MIT)
* *
* Copyright (c) 2014 Ilya Dmitrichenko * Copyright (c) 2014-2023 Damien P. George
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
...@@ -32,28 +32,34 @@ ...@@ -32,28 +32,34 @@
#include "py/stackctrl.h" #include "py/stackctrl.h"
#include "py/gc.h" #include "py/gc.h"
#include "py/mperrno.h" #include "py/mperrno.h"
#include "shared/runtime/gchelper.h"
#include "lib/tinytest/tinytest.h"
#include "lib/tinytest/tinytest_macros.h"
#define HEAP_SIZE (100 * 1024) void do_str(const char *src, mp_parse_input_kind_t input_kind) {
nlr_buf_t nlr;
#include "genhdr/tests.h" if (nlr_push(&nlr) == 0) {
mp_lexer_t *lex = mp_lexer_new_from_str_len(MP_QSTR__lt_stdin_gt_, src, strlen(src), 0);
qstr source_name = lex->source_name;
mp_parse_tree_t parse_tree = mp_parse(lex, input_kind);
mp_obj_t module_fun = mp_compile(&parse_tree, source_name, true);
mp_call_function_0(module_fun);
nlr_pop();
} else {
// uncaught exception
mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val);
}
}
int main() { int main(int argc, char **argv) {
mp_stack_ctrl_init(); mp_stack_ctrl_init();
mp_stack_set_limit(10240); mp_stack_set_limit(10240);
static uint32_t heap[HEAP_SIZE / sizeof(uint32_t)]; uint32_t heap[16 * 1024 / 4];
upytest_set_heap(heap, (char *)heap + HEAP_SIZE); gc_init(heap, (char *)heap + 16 * 1024);
int r = tinytest_main(0, NULL, groups); mp_init();
printf("status: %d\n", r); do_str("print('hello world!')", MP_PARSE_SINGLE_INPUT);
return r; mp_deinit();
return 0;
} }
void gc_collect(void) { 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_lexer_t *mp_lexer_new_from_file(qstr filename) {
......
...@@ -676,7 +676,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1): ...@@ -676,7 +676,11 @@ def run_tests(pyb, tests, args, result_dir, num_threads=1):
"extmod/time_time_ns.py" "extmod/time_time_ns.py"
) # RA fsp rtc function doesn't support nano sec info ) # RA fsp rtc function doesn't support nano sec info
elif args.target == "qemu-arm": elif args.target == "qemu-arm":
skip_tests.add("misc/print_exception.py") # requires sys stdfiles skip_tests.add("inlineasm/asmfpaddsub.py") # requires Cortex-M4
skip_tests.add("inlineasm/asmfpcmp.py")
skip_tests.add("inlineasm/asmfpldrstr.py")
skip_tests.add("inlineasm/asmfpmuldiv.py")
skip_tests.add("inlineasm/asmfpsqrt.py")
elif args.target == "qemu-riscv": elif args.target == "qemu-riscv":
skip_tests.add("misc/print_exception.py") # requires sys stdfiles skip_tests.add("misc/print_exception.py") # requires sys stdfiles
elif args.target == "webassembly": elif args.target == "webassembly":
...@@ -1043,7 +1047,6 @@ the last matching regex is used: ...@@ -1043,7 +1047,6 @@ the last matching regex is used:
LOCAL_TARGETS = ( LOCAL_TARGETS = (
"unix", "unix",
"qemu-arm",
"qemu-riscv", "qemu-riscv",
"webassembly", "webassembly",
) )
...@@ -1054,6 +1057,7 @@ the last matching regex is used: ...@@ -1054,6 +1057,7 @@ the last matching regex is used:
"esp32", "esp32",
"minimal", "minimal",
"nrf", "nrf",
"qemu-arm",
"renesas-ra", "renesas-ra",
"rp2", "rp2",
) )
...@@ -1141,10 +1145,6 @@ the last matching regex is used: ...@@ -1141,10 +1145,6 @@ the last matching regex is used:
"ports/unix", "ports/unix",
) )
elif args.target == "qemu-arm": elif args.target == "qemu-arm":
if not args.write_exp:
raise ValueError("--target=qemu-arm 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 += ( test_dirs += (
"float", "float",
"inlineasm", "inlineasm",
......
...@@ -258,10 +258,8 @@ function ci_qemu_arm_build { ...@@ -258,10 +258,8 @@ function ci_qemu_arm_build {
make ${MAKEOPTS} -C ports/qemu-arm submodules make ${MAKEOPTS} -C ports/qemu-arm submodules
make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1 make ${MAKEOPTS} -C ports/qemu-arm CFLAGS_EXTRA=-DMP_ENDIANNESS_BIG=1
make ${MAKEOPTS} -C ports/qemu-arm clean make ${MAKEOPTS} -C ports/qemu-arm clean
make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test submodules make ${MAKEOPTS} -C ports/qemu-arm test
make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test test make ${MAKEOPTS} -C ports/qemu-arm BOARD=sabrelite test
make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test clean
make ${MAKEOPTS} -C ports/qemu-arm -f Makefile.test BOARD=sabrelite test
} }
######################################################################################## ########################################################################################
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment