From fc6222fd60b2a0a612d5f8ec79e5ea6c0739cce1 Mon Sep 17 00:00:00 2001 From: Rahix <rahix@rahix.de> Date: Sun, 9 Jun 2019 15:49:54 +0200 Subject: [PATCH] build(micropython): Integrate upy into meson build system Signed-off-by: Rahix <rahix@rahix.de> --- hw-tests/meson.build | 1 + hw-tests/upy-minimal/.gdbinit | 2 + hw-tests/upy-minimal/main.c | 72 ++++++++++++++ hw-tests/upy-minimal/meson.build | 58 ++++++++++++ hw-tests/upy-minimal/mpconfigport.h | 96 +++++++++++++++++++ hw-tests/upy-minimal/mphalport.h | 3 + hw-tests/upy-minimal/systick.c | 22 +++++ hw-tests/upy-minimal/uart.c | 23 +++++ lib/meson.build | 2 + lib/micropython/gen-modules.py | 39 ++++++++ lib/micropython/gen-qstr.sh | 37 ++++++++ lib/micropython/gen-version.sh | 13 +++ lib/micropython/meson.build | 142 ++++++++++++++++++++++++++++ 13 files changed, 510 insertions(+) create mode 100644 hw-tests/upy-minimal/.gdbinit create mode 100644 hw-tests/upy-minimal/main.c create mode 100644 hw-tests/upy-minimal/meson.build create mode 100644 hw-tests/upy-minimal/mpconfigport.h create mode 100644 hw-tests/upy-minimal/mphalport.h create mode 100644 hw-tests/upy-minimal/systick.c create mode 100644 hw-tests/upy-minimal/uart.c create mode 100644 lib/micropython/gen-modules.py create mode 100755 lib/micropython/gen-qstr.sh create mode 100755 lib/micropython/gen-version.sh create mode 100644 lib/micropython/meson.build diff --git a/hw-tests/meson.build b/hw-tests/meson.build index 1c04f9e68..0523f5006 100644 --- a/hw-tests/meson.build +++ b/hw-tests/meson.build @@ -7,3 +7,4 @@ subdir('hello-freertos/') subdir('hello-world/') subdir('imutest/') subdir('ips/') +subdir('upy-minimal/') diff --git a/hw-tests/upy-minimal/.gdbinit b/hw-tests/upy-minimal/.gdbinit new file mode 100644 index 000000000..51a2201ec --- /dev/null +++ b/hw-tests/upy-minimal/.gdbinit @@ -0,0 +1,2 @@ +file ../../build/hw-tests/upy-minimal/upy-minimal.elf +source ../../.gdbinit diff --git a/hw-tests/upy-minimal/main.c b/hw-tests/upy-minimal/main.c new file mode 100644 index 000000000..22d88bdfe --- /dev/null +++ b/hw-tests/upy-minimal/main.c @@ -0,0 +1,72 @@ +#include <stdint.h> +#include <stdio.h> +#include <string.h> + +#include "py/compile.h" +#include "py/runtime.h" +#include "py/repl.h" +#include "py/gc.h" +#include "py/mperrno.h" +#include "lib/utils/pyexec.h" +#include "leds.h" + +static char *stack_top; + +#if MICROPY_ENABLE_GC +static char heap[4096]; +#endif + +int mp_hal_stdin_rx_chr(void); + +int main(int argc, char **argv) { + int stack_dummy; + stack_top = (char*)&stack_dummy; + leds_init(); + + #if MICROPY_ENABLE_GC + gc_init(heap, heap + sizeof(heap)); + #endif + + mp_init(); + pyexec_friendly_repl(); + mp_deinit(); + return 0; +} + +void gc_collect(void) { + // WARNING: This gc_collect implementation doesn't try to get root + // pointers from CPU registers, and thus may function incorrectly. + void *dummy; + gc_collect_start(); + gc_collect_root(&dummy, ((mp_uint_t)stack_top - (mp_uint_t)&dummy) / sizeof(mp_uint_t)); + gc_collect_end(); + gc_dump_info(); +} + +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); + +void nlr_jump_fail(void *val) { + while (1); +} + +void NORETURN __fatal_error(const char *msg) { + while (1); +} + +#ifndef NDEBUG +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"); +} +#endif diff --git a/hw-tests/upy-minimal/meson.build b/hw-tests/upy-minimal/meson.build new file mode 100644 index 000000000..5c0b52149 --- /dev/null +++ b/hw-tests/upy-minimal/meson.build @@ -0,0 +1,58 @@ +name = 'upy-minimal' + +################################# +# MicroPython Generated Headers # +################################# + +version_h = custom_target( + 'mpversion.h', + output: 'mpversion.h', + command: [ + micropython_gen_version, + python3, + micropython_dir, + '@OUTPUT@', + ], +) + +modules_h = custom_target( + 'moduledefs.h', + output: 'moduledefs.h', + input: micropython_sources, + command: [micropython_gen_modules, '@OUTPUT@', '@INPUT@'], +) + +qstr_h = custom_target( + 'qstrdefs.generated.h', + output: 'qstrdefs.generated.h', + input: [modules_h, version_h, micropython_sources], + command: [micropython_gen_qstr, meson.current_source_dir(), '@OUTPUT@', '@INPUT@'], +) + +################### +# MicroPython Lib # +################### + +upy = static_library( + 'micropython', + micropython_sources, + micropython_additional_sources, + modules_h, + qstr_h, + version_h, + include_directories: micropython_includes, +) + +executable( + name + '.elf', + 'main.c', + 'uart.c', + 'systick.c', + qstr_h, + include_directories: micropython_includes, + dependencies: [libcard10, max32665_startup], + link_with: upy, + link_args: [ + '-Wl,-Map=' + meson.current_build_dir() + '/' + name + '.map', + ], +) diff --git a/hw-tests/upy-minimal/mpconfigport.h b/hw-tests/upy-minimal/mpconfigport.h new file mode 100644 index 000000000..39a2bdcbb --- /dev/null +++ b/hw-tests/upy-minimal/mpconfigport.h @@ -0,0 +1,96 @@ +#include <stdint.h> + +// options to control how MicroPython is built + +// You can disable the built-in MicroPython compiler by setting the following +// config option to 0. If you do this then you won't get a REPL prompt, but you +// will still be able to execute pre-compiled scripts, compiled with mpy-cross. +#define MICROPY_ENABLE_COMPILER (1) + +#define MICROPY_QSTR_BYTES_IN_HASH (1) +// #define MICROPY_QSTR_EXTRA_POOL mp_qstr_frozen_const_pool +#define MICROPY_ALLOC_PATH_MAX (256) +#define MICROPY_ALLOC_PARSE_CHUNK_INIT (16) +#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 (1) +#define MICROPY_ENABLE_GC (1) +#define MICROPY_GC_ALLOC_THRESHOLD (0) +#define MICROPY_HELPER_REPL (1) +#define MICROPY_HELPER_LEXER_UNIX (0) +#define MICROPY_ENABLE_SOURCE_LINE (1) +#define MICROPY_ENABLE_DOC_STRING (1) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_DETAILED) +#define MICROPY_BUILTIN_METHOD_CHECK_SELF_ARG (0) +#define MICROPY_PY_ASYNC_AWAIT (0) +#define MICROPY_PY_BUILTINS_BYTEARRAY (1) +#define MICROPY_PY_BUILTINS_DICT_FROMKEYS (1) +#define MICROPY_PY_BUILTINS_MEMORYVIEW (1) +#define MICROPY_PY_BUILTINS_ENUMERATE (1) +#define MICROPY_PY_BUILTINS_FILTER (1) +#define MICROPY_PY_BUILTINS_FROZENSET (1) +#define MICROPY_PY_BUILTINS_REVERSED (1) +#define MICROPY_PY_BUILTINS_SET (1) +#define MICROPY_PY_BUILTINS_SLICE (1) +#define MICROPY_PY_BUILTINS_PROPERTY (1) +#define MICROPY_PY_BUILTINS_MIN_MAX (1) +#define MICROPY_PY_BUILTINS_STR_COUNT (1) +#define MICROPY_PY_BUILTINS_STR_OP_MODULO (0) +#define MICROPY_PY_BUILTINS_HELP (1) +#define MICROPY_PY_BUILTINS_HELP_MODULES (1) +#define MICROPY_PY___FILE__ (0) +#define MICROPY_PY_GC (0) +#define MICROPY_PY_ARRAY (1) +#define MICROPY_PY_ATTRTUPLE (1) +#define MICROPY_PY_COLLECTIONS (1) +#define MICROPY_PY_MATH (1) +#define MICROPY_PY_CMATH (1) +#define MICROPY_PY_IO (1) +#define MICROPY_PY_STRUCT (1) +#define MICROPY_PY_SYS (1) +#define MICROPY_MODULE_FROZEN_MPY (0) +#define MICROPY_CPYTHON_COMPAT (0) +#define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_LONGLONG) +#define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) +#define MICROPY_PY_UTIME_MP_HAL (1) +#define MODULE_BUZZER_ENABLED (1) +#define MODULE_UTIME_ENABLED (1) + +// type definitions for the specific machine + +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void*)((mp_uint_t)(p) | 1)) + +// This port is intended to be 32-bit, but unfortunately, int32_t for +// different targets may be defined in different ways - either as int +// or as long. This requires different printf formatting specifiers +// to print such value. So, we avoid int32_t and use int directly. +#define UINT_FMT "%u" +#define INT_FMT "%d" +typedef int mp_int_t; // must be pointer size +typedef unsigned mp_uint_t; // must be pointer size + +typedef long mp_off_t; + +#define MP_PLAT_PRINT_STRN(str, len) mp_hal_stdout_tx_strn_cooked(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) }, + +// We need to provide a declaration/definition of alloca() +#include <alloca.h> + +#define MICROPY_HW_BOARD_NAME "card10" +#define MICROPY_HW_MCU_NAME "max32665" + +#define MP_STATE_PORT MP_STATE_VM + +#define MICROPY_PORT_ROOT_POINTERS \ + const char *readline_hist[8]; + diff --git a/hw-tests/upy-minimal/mphalport.h b/hw-tests/upy-minimal/mphalport.h new file mode 100644 index 000000000..0afb2c957 --- /dev/null +++ b/hw-tests/upy-minimal/mphalport.h @@ -0,0 +1,3 @@ +#pragma once + +void mp_hal_set_interrupt_char(char c); diff --git a/hw-tests/upy-minimal/systick.c b/hw-tests/upy-minimal/systick.c new file mode 100644 index 000000000..635e7d3ed --- /dev/null +++ b/hw-tests/upy-minimal/systick.c @@ -0,0 +1,22 @@ +#include "py/mpconfig.h" +#include "mxc_delay.h" + +void mp_hal_delay_ms(mp_uint_t ms) { + mxc_delay (ms * 1000); // TODO check return value +} + +void mp_hal_delay_us(mp_uint_t us) { + mxc_delay (us); // TODO check return value +} + +mp_uint_t mp_hal_ticks_ms(void) { + return 0; +} + +mp_uint_t mp_hal_ticks_us(void) { + return 0; +} + +mp_uint_t mp_hal_ticks_cpu(void) { + return 0; +} diff --git a/hw-tests/upy-minimal/uart.c b/hw-tests/upy-minimal/uart.c new file mode 100644 index 000000000..2ef443177 --- /dev/null +++ b/hw-tests/upy-minimal/uart.c @@ -0,0 +1,23 @@ +#include <unistd.h> +#include "py/mpconfig.h" +#include "uart.h" + +/* + * Core UART functions to implement for a port + */ + +extern mxc_uart_regs_t * ConsoleUart; + +// Receive single character +int mp_hal_stdin_rx_chr(void) { + return UART_ReadByte(ConsoleUart); +} + +// Send string of given length +void mp_hal_stdout_tx_strn(const char *str, mp_uint_t len) { + UART_Write(ConsoleUart, (uint8_t*)str, len); +} + +void mp_hal_set_interrupt_char(char c) { + return; +} diff --git a/lib/meson.build b/lib/meson.build index 80fd654a8..ed96479d3 100644 --- a/lib/meson.build +++ b/lib/meson.build @@ -6,5 +6,7 @@ subdir('./vendor/Bosch/BMA400/') subdir('./vendor/Maxim/MAX77650/') subdir('./gfx/') +subdir('./micropython/') + subdir('./card10/') subdir('./ff13/') diff --git a/lib/micropython/gen-modules.py b/lib/micropython/gen-modules.py new file mode 100644 index 000000000..64868e33e --- /dev/null +++ b/lib/micropython/gen-modules.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# Usage: gen-modules.py <source path> <output.h> <input.c ...> + +import sys +import os + + +def main(): + # Get 'official' makemoduledefs.py + path = sys.argv[1] + "/micropython/py" + sys.path.insert(0, path) + import makemoduledefs + + modules = set() + for source in sys.argv[3:]: + modules |= makemoduledefs.find_module_registrations(source) + + stdout = sys.stdout + with open(sys.argv[2], "w") as f: + sys.stdout = f + makemoduledefs.generate_module_table_header(sorted(modules)) + sys.stdout = stdout + + try: + os.mkdir(os.path.dirname(sys.argv[2]) + "/genhdr") + except FileExistsError: + pass + + linkname = os.path.dirname(sys.argv[2]) + "/genhdr/" + os.path.basename(sys.argv[2]) + if os.path.exists(linkname): + os.unlink(linkname) + os.symlink( + "../" + os.path.basename(sys.argv[2]), + linkname, + ) + + +if __name__ == "__main__": + main() diff --git a/lib/micropython/gen-qstr.sh b/lib/micropython/gen-qstr.sh new file mode 100755 index 000000000..1ae13ff95 --- /dev/null +++ b/lib/micropython/gen-qstr.sh @@ -0,0 +1,37 @@ +#!/bin/bash +set -e + +PYTHON="$1" +SOURCE_DIR="$2" +PROJECT_SRC="$3" +OUTPUT="$4" +# IGNORE="$5" + +shift 5 + +OUTPUT_DIR="$(dirname "$OUTPUT")" + +mkdir -p "$OUTPUT_DIR/genhdr" +ln -sfr "$OUTPUT" "$OUTPUT_DIR/genhdr/$(basename "$OUTPUT")" + +# call gcc -E to generate qstr.i.last +gcc -E -DNO_QSTR -I"$SOURCE_DIR/micropython" -I"$PROJECT_SRC" -I"$OUTPUT_DIR" "$@" >"$OUTPUT_DIR/qstr.i.last" + +# Generate qstr.split +"$PYTHON" "$SOURCE_DIR/micropython/py/makeqstrdefs.py" split \ + "$OUTPUT_DIR/qstr.i.last" "$OUTPUT_DIR/qstr" "$OUTPUT_DIR/qstrdefs.collected.h" >/dev/null + +# Generate qstr.collected.h +"$PYTHON" "$SOURCE_DIR/micropython/py/makeqstrdefs.py" cat \ + "$OUTPUT_DIR/qstr.i.last" "$OUTPUT_DIR/qstr" "$OUTPUT_DIR/qstrdefs.collected.h" >/dev/null + +# Preprocess Header ... I did not come up with this, this is code copied from +# the official make file. Seriously. +cat "$SOURCE_DIR/micropython/py/qstrdefs.h" "$OUTPUT_DIR/qstrdefs.collected.h" \ + | sed 's/^Q(.*)/"&"/' \ + | gcc -E -I"$SOURCE_DIR/micropython" -I"$PROJECT_SRC" -I"$OUTPUT_DIR" - \ + | sed 's/^\"\(Q(.*)\)\"/\1/' \ + >"$OUTPUT_DIR/qstrdefs.preprocessed.h" + +# Call makeqstrdata +"$PYTHON" "$SOURCE_DIR/micropython/py/makeqstrdata.py" "$OUTPUT_DIR/qstrdefs.preprocessed.h" >"$OUTPUT" diff --git a/lib/micropython/gen-version.sh b/lib/micropython/gen-version.sh new file mode 100755 index 000000000..fd56bd99b --- /dev/null +++ b/lib/micropython/gen-version.sh @@ -0,0 +1,13 @@ +#!/bin/sh +set -e + +# makeversionhdr has to be executed in the micropython subdir +# so it picks up the correct version +OUT="$(realpath "$3")" +OUTDIR="$(dirname "$OUT")" + +mkdir -p "$OUTDIR/genhdr" +ln -sfr "$OUT" "$OUTDIR/genhdr/$(basename "$OUT")" + +cd "$2/micropython" +"$1" "$2/micropython/py/makeversionhdr.py" "$OUT" diff --git a/lib/micropython/meson.build b/lib/micropython/meson.build new file mode 100644 index 000000000..9cff4dfa7 --- /dev/null +++ b/lib/micropython/meson.build @@ -0,0 +1,142 @@ +# Scripts +micropython_gen_modules = [ + python3, + files('gen-modules.py'), + meson.current_source_dir(), +] + +micropython_dir = meson.current_source_dir() +micropython_gen_version = files('./gen-version.sh') + +micropython_gen_qstr = [ + files('gen-qstr.sh'), + python3, + meson.current_source_dir(), +] + +# Sources +micropython_includes = include_directories( + './micropython/', + './micropython/lib/utils', +) + +micropython_sources = files( + 'micropython/py/argcheck.c', + 'micropython/py/asmarm.c', 'micropython/py/asmbase.c', + 'micropython/py/asmthumb.c', + 'micropython/py/asmx64.c', + 'micropython/py/asmx86.c', + 'micropython/py/asmxtensa.c', + 'micropython/py/bc.c', + 'micropython/py/binary.c', + 'micropython/py/builtinevex.c', + 'micropython/py/builtinhelp.c', + 'micropython/py/builtinimport.c', + 'micropython/py/compile.c', + 'micropython/py/emitbc.c', + 'micropython/py/emitcommon.c', + 'micropython/py/emitglue.c', + 'micropython/py/emitinlinethumb.c', + 'micropython/py/emitinlinextensa.c', + 'micropython/py/emitnarm.c', + 'micropython/py/emitnative.c', + 'micropython/py/emitnthumb.c', + 'micropython/py/emitnx64.c', + 'micropython/py/emitnx86.c', + 'micropython/py/emitnxtensa.c', + 'micropython/py/formatfloat.c', + 'micropython/py/frozenmod.c', + 'micropython/py/gc.c', + 'micropython/py/lexer.c', + 'micropython/py/malloc.c', + 'micropython/py/map.c', + 'micropython/py/modarray.c', + 'micropython/py/modbuiltins.c', + 'micropython/py/modcmath.c', + 'micropython/py/modcollections.c', + 'micropython/py/modgc.c', + 'micropython/py/modio.c', + 'micropython/py/modmath.c', + 'micropython/py/modmicropython.c', + 'micropython/py/modstruct.c', + 'micropython/py/modsys.c', + 'micropython/py/modthread.c', + 'micropython/py/moduerrno.c', + 'micropython/py/mpprint.c', + 'micropython/py/mpstate.c', + 'micropython/py/mpz.c', + 'micropython/py/nativeglue.c', + 'micropython/py/nlr.c', + 'micropython/py/nlrsetjmp.c', + 'micropython/py/nlrthumb.c', + 'micropython/py/nlrx64.c', + 'micropython/py/nlrx86.c', + 'micropython/py/nlrxtensa.c', + 'micropython/py/obj.c', + 'micropython/py/objarray.c', + 'micropython/py/objattrtuple.c', + 'micropython/py/objbool.c', + 'micropython/py/objboundmeth.c', + 'micropython/py/objcell.c', + 'micropython/py/objclosure.c', + 'micropython/py/objcomplex.c', + 'micropython/py/objdeque.c', + 'micropython/py/objdict.c', + 'micropython/py/objenumerate.c', + 'micropython/py/objexcept.c', + 'micropython/py/objfilter.c', + 'micropython/py/objfloat.c', + 'micropython/py/objfun.c', + 'micropython/py/objgenerator.c', + 'micropython/py/objgetitemiter.c', + 'micropython/py/objint.c', + 'micropython/py/objint_longlong.c', + 'micropython/py/objint_mpz.c', + 'micropython/py/objlist.c', + 'micropython/py/objmap.c', + 'micropython/py/objmodule.c', + 'micropython/py/objnamedtuple.c', + 'micropython/py/objnone.c', + 'micropython/py/objobject.c', + 'micropython/py/objpolyiter.c', + 'micropython/py/objproperty.c', + 'micropython/py/objrange.c', + 'micropython/py/objreversed.c', + 'micropython/py/objset.c', + 'micropython/py/objsingleton.c', + 'micropython/py/objslice.c', + 'micropython/py/objstr.c', + 'micropython/py/objstringio.c', + 'micropython/py/objstrunicode.c', + 'micropython/py/objtuple.c', + 'micropython/py/objtype.c', + 'micropython/py/objzip.c', + 'micropython/py/opmethods.c', + 'micropython/py/parse.c', + 'micropython/py/parsenum.c', + 'micropython/py/parsenumbase.c', + 'micropython/py/persistentcode.c', + 'micropython/py/pystack.c', + 'micropython/py/qstr.c', + 'micropython/py/reader.c', + 'micropython/py/repl.c', + 'micropython/py/runtime.c', + 'micropython/py/runtime_utils.c', + 'micropython/py/scheduler.c', + 'micropython/py/scope.c', + 'micropython/py/sequence.c', + 'micropython/py/showbc.c', + 'micropython/py/smallint.c', + 'micropython/py/stackctrl.c', + 'micropython/py/stream.c', + 'micropython/py/unicode.c', + 'micropython/py/vm.c', + 'micropython/py/vstr.c', + 'micropython/py/warning.c', +) + +micropython_additional_sources = files( + 'micropython/lib/utils/stdout_helpers.c', + 'micropython/lib/utils/pyexec.c', + 'micropython/lib/mp-readline/readline.c', +) -- GitLab