diff --git a/ports/bare-arm/Makefile b/ports/bare-arm/Makefile index 72d5f1a277..455a9dc5ac 100644 --- a/ports/bare-arm/Makefile +++ b/ports/bare-arm/Makefile @@ -1,52 +1,59 @@ +# Include the core environment definitions; this will set $(TOP). include ../../py/mkenv.mk -# qstr definitions (must come before including py.mk) -QSTR_DEFS = qstrdefsport.h - -# MicroPython feature configurations -MICROPY_ROM_TEXT_COMPRESSION ?= 1 - -# include py core make definitions +# Include py core make definitions. include $(TOP)/py/py.mk +# Set makefile-level MicroPython feature configurations. +MICROPY_ROM_TEXT_COMPRESSION ?= 1 + +# Define toolchain and other tools. CROSS_COMPILE ?= arm-none-eabi- +DFU ?= $(TOP)/tools/dfu.py +PYDFU ?= $(TOP)/tools/pydfu.py -INC += -I. -INC += -I$(TOP) -INC += -I$(BUILD) +# Set CFLAGS. +CFLAGS += -I. -I$(TOP) -I$(BUILD) +CFLAGS += -Wall -Werror -std=c99 -nostdlib +CFLAGS += -mthumb -mtune=cortex-m4 -mcpu=cortex-m4 -msoft-float +CSUPEROPT = -Os # save some code space for performance-critical code -CFLAGS_CORTEX_M4 = -mthumb -mtune=cortex-m4 -mabi=aapcs-linux -mcpu=cortex-m4 -mfpu=fpv4-sp-d16 -mfloat-abi=hard -fsingle-precision-constant -Wdouble-promotion -CFLAGS = $(INC) -Wall -Werror -std=c99 -nostdlib $(CFLAGS_CORTEX_M4) $(COPT) -CSUPEROPT = -Os # save some code space - -#Debugging/Optimization +# Select debugging or optimisation build. ifeq ($(DEBUG), 1) -CFLAGS += -O0 -ggdb +CFLAGS += -Og else CFLAGS += -Os -DNDEBUG +CFLAGS += -fdata-sections -ffunction-sections endif -LDFLAGS = -nostdlib -T stm32f405.ld -Map=$@.map --cref -LIBS = +# Set linker flags. +LDFLAGS += -nostdlib -T stm32f405.ld --gc-sections -SRC_C = \ - main.c \ -# printf.c \ - string0.c \ - malloc0.c \ - gccollect.c \ +# Define the required source files. +SRC_C += lib.c main.c system.c -SRC_S = \ -# startup_stm32f40xx.s \ - gchelper.s \ +# Define the required object files. +OBJ += $(PY_CORE_O) +OBJ += $(addprefix $(BUILD)/, $(SRC_C:.c=.o)) -OBJ = $(PY_CORE_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o) $(SRC_S:.s=.o)) - -all: $(BUILD)/firmware.elf +# Define the top-level target, the main firmware. +all: $(BUILD)/firmware.dfu $(BUILD)/firmware.elf: $(OBJ) $(ECHO) "LINK $@" - $(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS) + $(Q)$(LD) $(LDFLAGS) -o $@ $^ $(Q)$(SIZE) $@ +$(BUILD)/firmware.bin: $(BUILD)/firmware.elf + $(ECHO) "Create $@" + $(Q)$(OBJCOPY) -O binary -j .isr_vector -j .text -j .data $^ $@ + +$(BUILD)/firmware.dfu: $(BUILD)/firmware.bin + $(ECHO) "Create $@" + $(Q)$(PYTHON) $(DFU) -b 0x08000000:$^ $@ + +deploy: $(BUILD)/firmware.dfu + $(Q)$(PYTHON) $(PYDFU) -u $^ + +# Include remaining core make rules. include $(TOP)/py/mkrules.mk diff --git a/ports/bare-arm/README.md b/ports/bare-arm/README.md new file mode 100644 index 0000000000..496ee9c755 --- /dev/null +++ b/ports/bare-arm/README.md @@ -0,0 +1,21 @@ +The bare-arm port +================= + +This port is intended to be the bare-minimum amount of code and configuration +required to get MicroPython compiling and running on a bare-metal ARM-based +target. No external dependencies or libraries are needed for this build and +it shows exactly what hardware and system functionality MicroPython needs to +run. + +To build, simply run `make` in this directory. The output will be +`build/firmware.elf` (and also corresponding `.bin` and `.dfu` files). This +firmware can run on an STM32F405-based board (eg a PYBv1.x) and `make deploy` +will program it to such an MCU when put in USB DFU mode. The output is a UART +at 115200 baud, with TX on PA0. + +There are some simple demonstration code strings (see `main.c`) which are +compiled and executed when the firmware starts. They produce output on the +system's stdout. + +The size of the firmware (the machine code that is programmed to the +microcontroller's flash/ROM) is currently around 61200 bytes. diff --git a/ports/bare-arm/lib.c b/ports/bare-arm/lib.c new file mode 100644 index 0000000000..ee7c1d765e --- /dev/null +++ b/ports/bare-arm/lib.c @@ -0,0 +1,128 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 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 + +// These memory functions are needed when the garbage collector is disabled. +// A full implementation should be provided, or the garbage collector enabled. +// The functions here are very simple. + +extern char _heap_start; + +void *malloc(size_t n) { + static char *cur_heap = NULL; + if (cur_heap == NULL) { + cur_heap = &_heap_start; + } + void *ptr = cur_heap; + cur_heap += (n + 7) & ~7; + return ptr; +} + +void *realloc(void *ptr, size_t size) { + void *ptr2 = malloc(size); + if (ptr && size) { + memcpy(ptr2, ptr, size); // size may be greater than ptr's region, do copy anyway + } + return ptr2; +} + +void free(void *p) { +} + +// These standard string functions are needed by the runtime, and can be +// provided either by the system or lib/libc/string0.c. The implementations +// here are very simple. + +int memcmp(const void *s1, const void *s2, size_t n) { + const unsigned char *ss1 = s1, *ss2 = s2; + while (n--) { + int c = *ss1++ - *ss2++; + if (c) { + return c; + } + } + return 0; +} + +void *memcpy(void *dest, const void *src, size_t n) { + return memmove(dest, src, n); +} + +void *memmove(void *dest, const void *src, size_t n) { + unsigned char *d = dest; + const unsigned char *s = src; + if (s < d && d < s + n) { + // Need to copy backwards. + d += n - 1; + s += n - 1; + while (n--) { + *d-- = *s--; + } + } else { + // Can copy forwards. + while (n--) { + *d++ = *s++; + } + } + return dest; +} + +void *memset(void *s, int c, size_t n) { + unsigned char *ss = s; + while (n--) { + *ss++ = c; + } + return s; +} + +char *strchr(const char *s, int c) { + while (*s) { + if (*s == c) { + return (char *)s; + } + ++s; + } + return NULL; +} + +int strcmp(const char *s1, const char *s2) { + while (*s1 && *s2) { + int c = *s1++ - *s2++; + if (c) { + return c; + } + } + return *s1 - *s2; +} + +size_t strlen(const char *s) { + const char *ss = s; + while (*ss) { + ++ss; + } + return ss - s; +} diff --git a/ports/bare-arm/main.c b/ports/bare-arm/main.c index df66c04eff..82c27bd097 100644 --- a/ports/bare-arm/main.c +++ b/ports/bare-arm/main.c @@ -1,15 +1,48 @@ -#include -#include -#include +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2021 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 #include "py/compile.h" #include "py/runtime.h" -#include "py/repl.h" -#include "py/mperrno.h" -void do_str(const char *src, mp_parse_input_kind_t input_kind) { +static const char *demo_single_input = + "print('hello world!', list(x + 1 for x in range(10)), end='eol\\n')"; + +static const char *demo_file_input = + "import micropython\n" + "\n" + "print(dir(micropython))\n" + "\n" + "for i in range(10):\n" + " print('iter {:08}'.format(i))"; + +static void do_str(const char *src, mp_parse_input_kind_t input_kind) { nlr_buf_t nlr; if (nlr_push(&nlr) == 0) { + // Compile, parse and execute the given string. 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); @@ -17,121 +50,29 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) { mp_call_function_0(module_fun); nlr_pop(); } else { - // uncaught exception + // Uncaught exception: print it out. mp_obj_print_exception(&mp_plat_print, (mp_obj_t)nlr.ret_val); } } -int main(int argc, char **argv) { +// Main entry point: initialise the runtime and execute demo strings. +void bare_main(void) { mp_init(); - do_str("print('hello world!', list(x+1 for x in range(10)), end='eol\\n')", MP_PARSE_SINGLE_INPUT); - do_str("for i in range(10):\n print(i)", MP_PARSE_FILE_INPUT); + do_str(demo_single_input, MP_PARSE_SINGLE_INPUT); + do_str(demo_file_input, MP_PARSE_FILE_INPUT); mp_deinit(); - return 0; } -mp_lexer_t *mp_lexer_new_from_file(const char *filename) { - mp_raise_OSError(MP_ENOENT); -} - -mp_import_stat_t mp_import_stat(const char *path) { - return MP_IMPORT_STAT_NO_EXIST; -} - -mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); - +// Called if an exception is raised outside all C exception-catching handlers. void nlr_jump_fail(void *val) { - while (1) { - ; - } -} - -void NORETURN __fatal_error(const char *msg) { - while (1) { - ; + for (;;) { } } #ifndef NDEBUG +// Used when debugging is enabled. void MP_WEAK __assert_func(const char *file, int line, const char *func, const char *expr) { - printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); - __fatal_error("Assertion failed"); + for (;;) { + } } #endif - -/* -int _lseek() {return 0;} -int _read() {return 0;} -int _write() {return 0;} -int _close() {return 0;} -void _exit(int x) {for(;;){}} -int _sbrk() {return 0;} -int _kill() {return 0;} -int _getpid() {return 0;} -int _fstat() {return 0;} -int _isatty() {return 0;} -*/ - -void *malloc(size_t n) { - return NULL; -} -void *calloc(size_t nmemb, size_t size) { - return NULL; -} -void *realloc(void *ptr, size_t size) { - return NULL; -} -void free(void *p) { -} -int printf(const char *m, ...) { - return 0; -} -void *memcpy(void *dest, const void *src, size_t n) { - return NULL; -} -int memcmp(const void *s1, const void *s2, size_t n) { - return 0; -} -void *memmove(void *dest, const void *src, size_t n) { - return NULL; -} -void *memset(void *s, int c, size_t n) { - return NULL; -} -int strcmp(const char *s1, const char *s2) { - return 0; -} -int strncmp(const char *s1, const char *s2, size_t n) { - return 0; -} -size_t strlen(const char *s) { - return 0; -} -char *strcat(char *dest, const char *src) { - return NULL; -} -char *strchr(const char *dest, int c) { - return NULL; -} -#include -int vprintf(const char *format, va_list ap) { - return 0; -} -int vsnprintf(char *str, size_t size, const char *format, va_list ap) { - return 0; -} - -#undef putchar -int putchar(int c) { - return 0; -} -int puts(const char *s) { - return 0; -} - -void _start(void) { - main(0, NULL); -} diff --git a/ports/bare-arm/mpconfigport.h b/ports/bare-arm/mpconfigport.h index 7fd236bfba..41b6ee71a3 100644 --- a/ports/bare-arm/mpconfigport.h +++ b/ports/bare-arm/mpconfigport.h @@ -1,71 +1,71 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2014-2021 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 -// options to control how MicroPython is built +// Options to control how MicroPython is built -#define MICROPY_QSTR_BYTES_IN_HASH (1) -#define MICROPY_ALLOC_PATH_MAX (512) -#define MICROPY_EMIT_X64 (0) -#define MICROPY_EMIT_THUMB (0) -#define MICROPY_EMIT_INLINE_THUMB (0) -#define MICROPY_COMP_MODULE_CONST (0) -#define MICROPY_COMP_CONST (0) -#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (0) -#define MICROPY_COMP_TRIPLE_TUPLE_ASSIGN (0) -#define MICROPY_MEM_STATS (0) -#define MICROPY_DEBUG_PRINTERS (0) -#define MICROPY_ENABLE_GC (0) -#define MICROPY_HELPER_REPL (0) -#define MICROPY_HELPER_LEXER_UNIX (0) -#define MICROPY_ENABLE_SOURCE_LINE (0) -#define MICROPY_ENABLE_DOC_STRING (0) -#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) -#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) -#define MICROPY_PY_ASYNC_AWAIT (0) -#define MICROPY_PY_ASSIGN_EXPR (0) -#define MICROPY_PY_BUILTINS_BYTEARRAY (0) -#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (0) -#define MICROPY_PY_BUILTINS_MEMORYVIEW (0) -#define MICROPY_PY_BUILTINS_ENUMERATE (0) -#define MICROPY_PY_BUILTINS_FROZENSET (0) -#define MICROPY_PY_BUILTINS_REVERSED (0) -#define MICROPY_PY_BUILTINS_SET (0) -#define MICROPY_PY_BUILTINS_SLICE (0) -#define MICROPY_PY_BUILTINS_PROPERTY (0) -#define MICROPY_PY_BUILTINS_STR_COUNT (0) -#define MICROPY_PY_BUILTINS_STR_OP_MODULO (0) -#define MICROPY_PY___FILE__ (0) -#define MICROPY_PY_GC (0) -#define MICROPY_PY_ARRAY (0) -#define MICROPY_PY_ATTRTUPLE (0) -#define MICROPY_PY_COLLECTIONS (0) -#define MICROPY_PY_MATH (0) -#define MICROPY_PY_CMATH (0) -#define MICROPY_PY_IO (0) -#define MICROPY_PY_STRUCT (0) -#define MICROPY_PY_SYS (0) -#define MICROPY_CPYTHON_COMPAT (0) -#define MICROPY_MODULE_GETATTR (0) -#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_NONE) -#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_NONE) -#define MICROPY_USE_INTERNAL_PRINTF (0) +// Memory allocation policy +#define MICROPY_QSTR_BYTES_IN_HASH (1) -// type definitions for the specific machine +// Compiler configuration +#define MICROPY_COMP_CONST (0) +#define MICROPY_COMP_DOUBLE_TUPLE_ASSIGN (0) -#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) +// Python internal features +#define MICROPY_ENABLE_EXTERNAL_IMPORT (0) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_TERSE) +#define MICROPY_CPYTHON_COMPAT (0) +#define MICROPY_MODULE_GETATTR (0) +#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) -#define UINT_FMT "%lu" -#define INT_FMT "%ld" +// Fine control over Python builtins, classes, modules, etc +#define MICROPY_PY_ASYNC_AWAIT (0) +#define MICROPY_PY_ASSIGN_EXPR (0) +#define MICROPY_PY_BUILTINS_STR_COUNT (0) +#define MICROPY_PY_BUILTINS_STR_OP_MODULO (0) +#define MICROPY_PY_BUILTINS_BYTEARRAY (0) +#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (0) +#define MICROPY_PY_BUILTINS_SET (0) +#define MICROPY_PY_BUILTINS_SLICE (0) +#define MICROPY_PY_BUILTINS_PROPERTY (0) +#define MICROPY_PY_BUILTINS_ENUMERATE (0) +#define MICROPY_PY_BUILTINS_REVERSED (0) +#define MICROPY_PY___FILE__ (0) +#define MICROPY_PY_ARRAY (0) +#define MICROPY_PY_COLLECTIONS (0) +#define MICROPY_PY_IO (0) +#define MICROPY_PY_STRUCT (0) +#define MICROPY_PY_SYS (0) + +// Type definitions for the specific machine 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; -// dummy print -#define MP_PLAT_PRINT_STRN(str, len) (void)0 - -// 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) }, - -// We need to provide a declaration/definition of alloca() +// Need to provide a declaration/definition of alloca() #include diff --git a/ports/bare-arm/qstrdefsport.h b/ports/bare-arm/qstrdefsport.h deleted file mode 100644 index 00d3e2ae3c..0000000000 --- a/ports/bare-arm/qstrdefsport.h +++ /dev/null @@ -1,2 +0,0 @@ -// qstrs specific to this port -// *FORMAT-OFF* diff --git a/ports/bare-arm/stm32f405.ld b/ports/bare-arm/stm32f405.ld index dd688a0246..33a5502d60 100644 --- a/ports/bare-arm/stm32f405.ld +++ b/ports/bare-arm/stm32f405.ld @@ -1,117 +1,58 @@ -/* - GNU linker script for STM32F405 -*/ +/* GNU linker script for STM32F405 */ -/* Specify the memory areas */ MEMORY { FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 0x100000 /* entire flash, 1 MiB */ - FLASH_ISR (rx) : ORIGIN = 0x08000000, LENGTH = 0x004000 /* sector 0, 16 KiB */ - FLASH_TEXT (rx) : ORIGIN = 0x08020000, LENGTH = 0x080000 /* sectors 5,6,7,8, 4*128KiB = 512 KiB (could increase it more) */ - CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 0x010000 /* 64 KiB */ RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 0x020000 /* 128 KiB */ } -/* top end of the stack */ _estack = ORIGIN(RAM) + LENGTH(RAM); -/* RAM extents for the garbage collector */ -_ram_end = ORIGIN(RAM) + LENGTH(RAM); -_heap_end = 0x2001c000; /* tunable */ - -/* define output sections */ SECTIONS { - /* The startup code goes first into FLASH */ .isr_vector : { . = ALIGN(4); - KEEP(*(.isr_vector)) /* Startup code */ - + KEEP(*(.isr_vector)) . = ALIGN(4); - } >FLASH_ISR + } >FLASH - /* The program code and other data goes into FLASH */ .text : { . = ALIGN(4); - *(.text) /* .text sections (code) */ - *(.text*) /* .text* sections (code) */ - *(.rodata) /* .rodata sections (constants, strings, etc.) */ - *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ - /* *(.glue_7) */ /* glue arm to thumb code */ - /* *(.glue_7t) */ /* glue thumb to arm code */ - + *(.text) + *(.text*) + *(.rodata) + *(.rodata*) . = ALIGN(4); - _etext = .; /* define a global symbol at end of code */ - _sidata = _etext; /* This is used by the startup in order to initialize the .data secion */ - } >FLASH_TEXT - - /* - .ARM.extab : - { - *(.ARM.extab* .gnu.linkonce.armextab.*) + _etext = .; + _sidata = _etext; } >FLASH - .ARM : - { - __exidx_start = .; - *(.ARM.exidx*) - __exidx_end = .; - } >FLASH - */ - - /* This is the initialized data section - The program executes knowing that the data is in the RAM - but the loader puts the initial values in the FLASH (inidata). - It is one task of the startup to copy the initial values from FLASH to RAM. */ .data : AT ( _sidata ) { . = ALIGN(4); - _sdata = .; /* create a global symbol at data start; used by startup code in order to initialise the .data section in RAM */ - _ram_start = .; /* create a global symbol at ram start for garbage collector */ - *(.data) /* .data sections */ - *(.data*) /* .data* sections */ - + _sdata = .; + *(.data) + *(.data*) . = ALIGN(4); - _edata = .; /* define a global symbol at data end; used by startup code in order to initialise the .data section in RAM */ + _edata = .; } >RAM - /* Uninitialized data section */ .bss : { . = ALIGN(4); - _sbss = .; /* define a global symbol at bss start; used by startup code */ + _sbss = .; *(.bss) *(.bss*) *(COMMON) - . = ALIGN(4); - _ebss = .; /* define a global symbol at bss end; used by startup code */ + _ebss = .; } >RAM - /* this is to define the start of the heap, and make sure we have a minimum size */ .heap : { - . = ALIGN(4); - _heap_start = .; /* define a global symbol at heap start */ + . = ALIGN(8); + _heap_start = .; } >RAM - - /* this just checks there is enough RAM for the stack */ - .stack : - { - . = ALIGN(4); - } >RAM - - /* Remove information from the standard libraries */ - /* - /DISCARD/ : - { - libc.a ( * ) - libm.a ( * ) - libgcc.a ( * ) - } - */ - - .ARM.attributes 0 : { *(.ARM.attributes) } } diff --git a/ports/bare-arm/system.c b/ports/bare-arm/system.c new file mode 100644 index 0000000000..f66dd2d80a --- /dev/null +++ b/ports/bare-arm/system.c @@ -0,0 +1,142 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2021 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 +#include + +#define RCC ((periph_rcc_t *)0x40023800) +#define GPIOA ((periph_gpio_t *)0x40020000) +#define UART4 ((periph_uart_t *)0x40004C00) + +typedef struct { + volatile uint32_t CR; + volatile uint32_t PLLCFGR; + volatile uint32_t CFGR; + volatile uint32_t CIR; + uint32_t _1[8]; + volatile uint32_t AHB1ENR; + volatile uint32_t AHB2ENR; + volatile uint32_t AHB3ENR; + uint32_t _2; + volatile uint32_t APB1ENR; + volatile uint32_t APB2ENR; +} periph_rcc_t; + +typedef struct { + volatile uint32_t MODER; + volatile uint32_t OTYPER; + volatile uint32_t OSPEEDR; + volatile uint32_t PUPDR; + volatile uint32_t IDR; + volatile uint32_t ODR; + volatile uint16_t BSRRL; + volatile uint16_t BSRRH; + volatile uint32_t LCKR; + volatile uint32_t AFR[2]; +} periph_gpio_t; + +typedef struct { + volatile uint32_t SR; + volatile uint32_t DR; + volatile uint32_t BRR; + volatile uint32_t CR1; +} periph_uart_t; + +extern uint32_t _estack, _sidata, _sdata, _edata, _sbss, _ebss; + +void Reset_Handler(void) __attribute__((naked)); +void bare_main(void); + +static void stm32_init(void); +static void gpio_init_alt(periph_gpio_t *gpio, int pin, int alt); + +// Very simple ARM vector table. +const uint32_t isr_vector[] __attribute__((section(".isr_vector"))) = { + (uint32_t)&_estack, + (uint32_t)&Reset_Handler, +}; + +// The CPU runs this function after a reset. +void Reset_Handler(void) { + // Set stack pointer. + __asm volatile ("ldr sp, =_estack"); + + // Copy .data section from flash to RAM. + memcpy(&_sdata, &_sidata, (char *)&_edata - (char *)&_sdata); + + // Zero out .bss section. + memset(&_sbss, 0, (char *)&_ebss - (char *)&_sbss); + + // SCB->CCR: enable 8-byte stack alignment for IRQ handlers, in accord with EABI. + *((volatile uint32_t *)0xe000ed14) |= 1 << 9; + + // Initialise the cpu and peripherals. + stm32_init(); + + // Now that there is a basic system up and running, call the main application code. + bare_main(); + + // This function must not return. + for (;;) { + } +} + +// Set up the STM32 MCU. +static void stm32_init(void) { + // Note: default clock is internal 16MHz. + RCC->AHB1ENR |= 1 << 0; // GPIOAEN + RCC->APB1ENR |= 1 << 19; // UART4EN + gpio_init_alt(GPIOA, 0, 8); + UART4->BRR = (8 << 4) | 11; // 16MHz/(16*8.6875) = 115107 baud + UART4->CR1 = 0x00002008; // USART enable, tx enable, rx enable +} + +// Configure a GPIO pin in alternate-function mode. +static void gpio_init_alt(periph_gpio_t *gpio, int pin, int alt) { + gpio->MODER = (gpio->MODER & ~(3 << (2 * pin))) | (2 << (2 * pin)); + // OTYPER is left as default push-pull + // OSPEEDR is left as default low speed + // PUPDR is left as default no-pull + gpio->AFR[pin >> 3] = (gpio->AFR[pin >> 3] & ~(15 << (4 * (pin & 7)))) | (alt << (4 * (pin & 7))); +} + +// Write a character out to the UART. +static inline void uart_write_char(int c) { + // Wait for TXE, then write the character. + while ((UART4->SR & (1 << 7)) == 0) { + } + UART4->DR = c; +} + +// Send string of given length to stdout, converting \n to \r\n. +void mp_hal_stdout_tx_strn_cooked(const char *str, size_t len) { + while (len--) { + if (*str == '\n') { + uart_write_char('\r'); + } + uart_write_char(*str++); + } +}