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).
This commit is contained in:
parent
775c7b86f0
commit
e7332b0584
@ -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
|
||||
|
@ -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"
|
||||
|
@ -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);
|
||||
|
@ -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) },
|
||||
|
@ -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))
|
||||
|
38
ports/qemu-arm/mps2.ld
Normal file
38
ports/qemu-arm/mps2.ld
Normal file
@ -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
|
||||
}
|
39
ports/qemu-arm/nrf51.ld
Normal file
39
ports/qemu-arm/nrf51.ld
Normal file
@ -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
|
||||
}
|
84
ports/qemu-arm/startup.c
Normal file
84
ports/qemu-arm/startup.c
Normal file
@ -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;
|
39
ports/qemu-arm/stm32.ld
Normal file
39
ports/qemu-arm/stm32.ld
Normal file
@ -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
|
||||
}
|
@ -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;
|
||||
|
||||
|
78
ports/qemu-arm/uart.c
Normal file
78
ports/qemu-arm/uart.c
Normal file
@ -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
|
2
ports/qemu-arm/uart.h
Normal file
2
ports/qemu-arm/uart.h
Normal file
@ -0,0 +1,2 @@
|
||||
void uart_init(void);
|
||||
void uart_tx_strn(const char *buf, size_t len);
|
Loading…
x
Reference in New Issue
Block a user