qemu-arm: Rework to run bare-metal on boards with Cortex-M CPUs.

Adds support for 3 Cortex-M boards, selectable via "BOARD" in the Makefile:
- microbit, Cortex-M0 via nRF51822
- netduino2, Cortex-M3 via STM32F205
- mps2-an385, Cortex-M3 via FPGA

netduino2 is the default board because it's supported by older qemu
versions (down to at least 2.5.0).
pull/4470/head
Damien George 2018-09-24 12:09:28 +10:00
rodzic 775c7b86f0
commit e7332b0584
12 zmienionych plików z 320 dodań i 18 usunięć

Wyświetl plik

@ -7,6 +7,27 @@ QSTR_DEFS = qstrdefsport.h
# include py core make definitions
include $(TOP)/py/py.mk
BOARD ?= netduino2
ifeq ($(BOARD),netduino2)
CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_STM32
LDSCRIPT = stm32.ld
endif
ifeq ($(BOARD),microbit)
CFLAGS += -mthumb -mcpu=cortex-m0 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_NRF51
LDSCRIPT = nrf51.ld
QEMU_EXTRA = -global nrf51-soc.flash-size=1048576 -global nrf51-soc.sram-size=262144
endif
ifeq ($(BOARD),mps2-an385)
CFLAGS += -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
CFLAGS += -DQEMU_SOC_MPS2
LDSCRIPT = mps2.ld
endif
CROSS_COMPILE = arm-none-eabi-
TINYTEST = $(TOP)/lib/tinytest
@ -16,8 +37,7 @@ INC += -I$(TOP)
INC += -I$(BUILD)
INC += -I$(TINYTEST)
CFLAGS_CORTEX_M3 = -mthumb -mcpu=cortex-m3 -mfloat-abi=soft
CFLAGS = $(INC) -Wall -Wpointer-arith -Werror -std=gnu99 $(CFLAGS_CORTEX_M3) $(COPT) \
CFLAGS += $(INC) -Wall -Wpointer-arith -Werror -std=gnu99 $(COPT) \
-ffunction-sections -fdata-sections
#Debugging/Optimization
@ -34,9 +54,12 @@ endif
## else instead and according to the following files, this is what we need to pass to `$(CC).
## - gcc-arm-none-eabi-4_8-2014q1/share/gcc-arm-none-eabi/samples/src/makefile.conf
## - gcc-arm-none-eabi-4_8-2014q1/share/gcc-arm-none-eabi/samples/src/qemu/Makefile
LDFLAGS= --specs=nano.specs --specs=rdimon.specs -Wl,--gc-sections -Wl,-Map=$(@:.elf=.map)
LDFLAGS= -T $(LDSCRIPT) --gc-sections -Map=$(@:.elf=.map)
LIBS = $(shell $(CC) $(CFLAGS) -print-libgcc-file-name)
SRC_COMMON_C = \
startup.c \
uart.c \
moduos.c \
modmachine.c \
@ -47,6 +70,7 @@ SRC_TEST_C = \
test_main.c \
LIB_SRC_C += $(addprefix lib/,\
libc/string0.c \
libm/math.c \
libm/fmodf.c \
libm/nearbyintf.c \
@ -89,11 +113,11 @@ SRC_QSTR += $(SRC_COMMON_C) $(SRC_RUN_C) $(LIB_SRC_C)
all: run
run: $(BUILD)/firmware.elf
qemu-system-arm -machine integratorcp -cpu cortex-m3 -nographic -monitor null -serial null -semihosting -kernel $(BUILD)/firmware.elf
qemu-system-arm -machine $(BOARD) $(QEMU_EXTRA) -nographic -monitor null -semihosting -kernel $<
## `$(LD)` doesn't seem to like `--specs` for some reason, but we can just use `$(CC)` here.
$(BUILD)/firmware.elf: $(OBJ_COMMON) $(OBJ_RUN)
$(Q)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
$(Q)$(SIZE) $@
include $(TOP)/py/mkrules.mk

Wyświetl plik

@ -15,10 +15,10 @@ $(BUILD)/tinytest.o:
$(Q)$(CC) $(CFLAGS) -DNO_FORKING -o $@ -c $(TINYTEST)/tinytest.c
$(BUILD)/firmware-test.elf: $(OBJ_COMMON) $(OBJ_TEST)
$(Q)$(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^ $(LIBS)
$(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS)
$(Q)$(SIZE) $@
test: $(BUILD)/firmware-test.elf
qemu-system-arm -machine integratorcp -cpu cortex-m3 -nographic -monitor null -serial null -semihosting -kernel $(BUILD)/firmware-test.elf > $(BUILD)/console.out
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"

Wyświetl plik

@ -30,7 +30,7 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) {
int main(int argc, char **argv) {
mp_stack_ctrl_init();
mp_stack_set_limit(10240);
void *heap = malloc(16 * 1024);
uint32_t heap[16*1024 / 4];
gc_init(heap, (char*)heap + 16 * 1024);
mp_init();
do_str("print('hello world!')", MP_PARSE_SINGLE_INPUT);

Wyświetl plik

@ -38,7 +38,7 @@
#define MICROPY_PY_UHASHLIB (1)
#define MICROPY_PY_MACHINE (1)
#define MICROPY_PY_MICROPYTHON_MEM_INFO (1)
#define MICROPY_USE_INTERNAL_PRINTF (0)
#define MICROPY_USE_INTERNAL_PRINTF (1)
#define MICROPY_VFS (1)
// type definitions for the specific machine
@ -54,9 +54,6 @@ 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;
#include <unistd.h>
#define MP_PLAT_PRINT_STRN(str, len) write(1, str, len)
// extra built in names to add to the global namespace
#define MICROPY_PORT_BUILTINS \
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mp_builtin_open_obj) },

Wyświetl plik

@ -1,2 +1,5 @@
#include <stddef.h>
#include "uart.h"
#define mp_hal_stdin_rx_chr() (0)
#define mp_hal_stdout_tx_strn_cooked(s, l) write(1, (s), (l))
#define mp_hal_stdout_tx_strn_cooked(s, l) uart_tx_strn((s), (l))

Wyświetl plik

@ -0,0 +1,38 @@
MEMORY
{
RAM : ORIGIN = 0x00000000, LENGTH = 4M
}
_estack = ORIGIN(RAM) + LENGTH(RAM);
SECTIONS
{
.text : {
. = ALIGN(4);
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
. = ALIGN(4);
_etext = .;
_sidata = _etext;
} > RAM
.data : AT ( _sidata )
{
. = ALIGN(4);
_sdata = .;
*(.data*)
. = ALIGN(4);
_edata = .;
} >RAM
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >RAM
}

Wyświetl plik

@ -0,0 +1,39 @@
MEMORY
{
ROM : ORIGIN = 0x00000000, LENGTH = 1M
RAM : ORIGIN = 0x20000000, LENGTH = 256K
}
_estack = ORIGIN(RAM) + LENGTH(RAM);
SECTIONS
{
.text : {
. = ALIGN(4);
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
. = ALIGN(4);
_etext = .;
_sidata = _etext;
} > ROM
.data : AT ( _sidata )
{
. = ALIGN(4);
_sdata = .;
*(.data*)
. = ALIGN(4);
_edata = .;
} >RAM
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >RAM
}

Wyświetl plik

@ -0,0 +1,84 @@
#include <stdint.h>
#include <stdlib.h>
#include "uart.h"
extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss;
__attribute__((naked)) void Reset_Handler(void) {
// set stack pointer
__asm volatile ("ldr r0, =_estack");
__asm volatile ("mov sp, r0");
// copy .data section from flash to RAM
for (uint32_t *src = &_sidata, *dest = &_sdata; dest < &_edata;) {
*dest++ = *src++;
}
// zero out .bss section
for (uint32_t *dest = &_sbss; dest < &_ebss;) {
*dest++ = 0;
}
// jump to board initialisation
void _start(void);
_start();
}
void Default_Handler(void) {
for (;;) {
}
}
const uint32_t isr_vector[] __attribute__((section(".isr_vector"))) = {
(uint32_t)&_estack,
(uint32_t)&Reset_Handler,
(uint32_t)&Default_Handler, // NMI_Handler
(uint32_t)&Default_Handler, // HardFault_Handler
(uint32_t)&Default_Handler, // MemManage_Handler
(uint32_t)&Default_Handler, // BusFault_Handler
(uint32_t)&Default_Handler, // UsageFault_Handler
0,
0,
0,
0,
(uint32_t)&Default_Handler, // SVC_Handler
(uint32_t)&Default_Handler, // DebugMon_Handler
0,
(uint32_t)&Default_Handler, // PendSV_Handler
(uint32_t)&Default_Handler, // SysTick_Handler
};
void _start(void) {
// Enable the UART
uart_init();
// Now that we have a basic system up and running we can call main
extern int main();
main(0, 0);
// Finished
exit(0);
}
__attribute__((naked)) void exit(int status) {
// Force qemu to exit using ARM Semihosting
__asm volatile (
"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
"bkpt 0xab\n"
);
for (;;) {
}
}
// 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;

Wyświetl plik

@ -0,0 +1,39 @@
MEMORY
{
ROM : ORIGIN = 0x00000000, LENGTH = 1M
RAM : ORIGIN = 0x20000000, LENGTH = 128K
}
_estack = ORIGIN(RAM) + LENGTH(RAM);
SECTIONS
{
.text : {
. = ALIGN(4);
KEEP(*(.isr_vector))
*(.text*)
*(.rodata*)
. = ALIGN(4);
_etext = .;
_sidata = _etext;
} > ROM
.data : AT ( _sidata )
{
. = ALIGN(4);
_sdata = .;
*(.data*)
. = ALIGN(4);
_edata = .;
} >RAM
.bss :
{
. = ALIGN(4);
_sbss = .;
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .;
} >RAM
}

Wyświetl plik

@ -15,15 +15,14 @@
#include "tinytest.h"
#include "tinytest_macros.h"
#define HEAP_SIZE (128 * 1024)
STATIC void *heap;
#define HEAP_SIZE (100 * 1024)
#include "genhdr/tests.h"
int main() {
mp_stack_ctrl_init();
mp_stack_set_limit(10240);
heap = malloc(HEAP_SIZE);
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);
@ -34,8 +33,7 @@ void gc_collect(void) {
gc_collect_start();
// get the registers and the sp
jmp_buf env;
setjmp(env);
// TODO get registers
volatile mp_uint_t dummy;
void *sp = (void*)&dummy;

Wyświetl plik

@ -0,0 +1,78 @@
#include <stdint.h>
#include <stddef.h>
#include "uart.h"
#if defined(QEMU_SOC_STM32)
typedef struct _UART_t {
volatile uint32_t SR;
volatile uint32_t DR;
} UART_t;
#define UART0 ((UART_t*)(0x40011000))
void uart_init(void) {
}
void uart_tx_strn(const char *buf, size_t len) {
for (size_t i = 0; i < len; ++i) {
UART0->DR = buf[i];
}
}
#elif defined(QEMU_SOC_NRF51)
typedef struct _UART_t {
volatile uint32_t r0[2];
volatile uint32_t STARTTX; // 0x008
volatile uint32_t r1[(0x500 - 0x008) / 4 - 1];
volatile uint32_t ENABLE; // 0x500
volatile uint32_t r2[(0x51c - 0x500) / 4 - 1];
volatile uint32_t TXD; // 0x51c
} UART_t;
#define UART0 ((UART_t*)(0x40002000))
void uart_init(void) {
UART0->ENABLE = 4;
UART0->STARTTX = 1;
}
void uart_tx_strn(const char *buf, size_t len) {
for (size_t i = 0; i < len; ++i) {
UART0->TXD = buf[i];
}
}
#elif defined(QEMU_SOC_MPS2)
#define UART_STATE_TXFULL (1 << 0)
#define UART_CTRL_TX_EN (1 << 0)
#define UART_CTRL_RX_EN (1 << 1)
typedef struct _UART_t {
volatile uint32_t DATA;
volatile uint32_t STATE;
volatile uint32_t CTRL;
volatile uint32_t INTSTATUS;
volatile uint32_t BAUDDIV;
} UART_t;
#define UART0 ((UART_t*)(0x40004000))
void uart_init(void) {
UART0->BAUDDIV = 16;
UART0->CTRL = UART_CTRL_TX_EN;
}
void uart_tx_strn(const char *buf, size_t len) {
for (size_t i = 0; i < len; ++i) {
while (UART0->STATE & UART_STATE_TXFULL) {
}
UART0->DATA = buf[i];
}
}
#endif

Wyświetl plik

@ -0,0 +1,2 @@
void uart_init(void);
void uart_tx_strn(const char *buf, size_t len);