diff --git a/ports/qemu-arm/Makefile b/ports/qemu-arm/Makefile index 95f349beba..b3d9cdf566 100644 --- a/ports/qemu-arm/Makefile +++ b/ports/qemu-arm/Makefile @@ -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 diff --git a/ports/qemu-arm/Makefile.test b/ports/qemu-arm/Makefile.test index a9aace6d07..347c2fefd3 100644 --- a/ports/qemu-arm/Makefile.test +++ b/ports/qemu-arm/Makefile.test @@ -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" diff --git a/ports/qemu-arm/main.c b/ports/qemu-arm/main.c index d23ef576f9..4cdd148287 100644 --- a/ports/qemu-arm/main.c +++ b/ports/qemu-arm/main.c @@ -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); diff --git a/ports/qemu-arm/mpconfigport.h b/ports/qemu-arm/mpconfigport.h index 5d86191988..3d4abd52ff 100644 --- a/ports/qemu-arm/mpconfigport.h +++ b/ports/qemu-arm/mpconfigport.h @@ -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 -#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) }, diff --git a/ports/qemu-arm/mphalport.h b/ports/qemu-arm/mphalport.h index d996402ae4..29f080805f 100644 --- a/ports/qemu-arm/mphalport.h +++ b/ports/qemu-arm/mphalport.h @@ -1,2 +1,5 @@ +#include +#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)) diff --git a/ports/qemu-arm/mps2.ld b/ports/qemu-arm/mps2.ld new file mode 100644 index 0000000000..5c1aa21ca2 --- /dev/null +++ b/ports/qemu-arm/mps2.ld @@ -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 +} diff --git a/ports/qemu-arm/nrf51.ld b/ports/qemu-arm/nrf51.ld new file mode 100644 index 0000000000..70269773cb --- /dev/null +++ b/ports/qemu-arm/nrf51.ld @@ -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 +} diff --git a/ports/qemu-arm/startup.c b/ports/qemu-arm/startup.c new file mode 100644 index 0000000000..5198d089f9 --- /dev/null +++ b/ports/qemu-arm/startup.c @@ -0,0 +1,84 @@ +#include +#include + +#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 + +int setvbuf(FILE *stream, char *buf, int mode, size_t size) { + return 0; +} + +struct _reent *_impure_ptr; diff --git a/ports/qemu-arm/stm32.ld b/ports/qemu-arm/stm32.ld new file mode 100644 index 0000000000..4e541526b7 --- /dev/null +++ b/ports/qemu-arm/stm32.ld @@ -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 +} diff --git a/ports/qemu-arm/test_main.c b/ports/qemu-arm/test_main.c index adbdf04e18..2cb9e73859 100644 --- a/ports/qemu-arm/test_main.c +++ b/ports/qemu-arm/test_main.c @@ -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; diff --git a/ports/qemu-arm/uart.c b/ports/qemu-arm/uart.c new file mode 100644 index 0000000000..a0ce737c06 --- /dev/null +++ b/ports/qemu-arm/uart.c @@ -0,0 +1,78 @@ +#include +#include + +#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 diff --git a/ports/qemu-arm/uart.h b/ports/qemu-arm/uart.h new file mode 100644 index 0000000000..a89e3f26e2 --- /dev/null +++ b/ports/qemu-arm/uart.h @@ -0,0 +1,2 @@ +void uart_init(void); +void uart_tx_strn(const char *buf, size_t len);