diff --git a/examples/natmod/esp-counter/Makefile b/examples/natmod/esp-counter/Makefile new file mode 100644 index 0000000000000..19cf46b919316 --- /dev/null +++ b/examples/natmod/esp-counter/Makefile @@ -0,0 +1,35 @@ +# Location of top-level MicroPython directory +MPY_DIR = ../../.. + +# Espressif ESP-IDF path +IDF_PATH := /home/src/esp32/esp-idf-micropython +# Board to get correct ESP-IDF config +BOARD := GENERIC +# xtensa toolchain bin dir +PATH := $(IDF_PATH)/xtensa-esp32-elf/bin:$(PATH) + +# Name of module +MOD = espidf + +# Source files (.c or .py) +SRC = modespidf.c + +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +ARCH = xtensawin + +# Include to get the rules for compiling and linking the module +include dyn_esp32.mk +include $(MPY_DIR)/py/dynruntime.mk +CFLAGS += -std=gnu99 # override -std=c99 in dynruntime.mk +CFLAGS += -DMPY_DYN_MODULE=1 + +modespidf.c: $(MPY_DIR)/ports/esp32/plat_relo.py build/sdkconfig.h + $(ECHO) "GEN $@" + $(Q)mkdir -p build + $(Q)$< --module >$@ + +build/sdkconfig.h: $(BOARD_BUILD)/sdkconfig.h build + $(Q)cp $< $@ + +build: + $(Q)mkdir -p $@ diff --git a/examples/natmod/esp-counter/counter.py b/examples/natmod/esp-counter/counter.py new file mode 100644 index 0000000000000..02278feeb656f --- /dev/null +++ b/examples/natmod/esp-counter/counter.py @@ -0,0 +1,101 @@ +import struct, espidf + +# Directions (PCNT_COUNT_* in components/driver/include/driver/pcnt.h) +UP=1 +DOWN=2 + +# Edges +RISING=1 +FALLING=2 + +# Private constants +_COUNT_DIS = 0 +_COUNT_INC = 1 +_COUNT_DEC = 2 +_MODE_KEEP = 0 +_MODE_REVERSE = 1 +_MODE_DISABLE = 2 + +def _err_raise(err): + if err == 0: return + raise ValueError(espidf.esp_err_to_name(err)) + +class Counter: + def __init__(self, unit, pin, direction=UP, edge=RISING, limit=0, reset=True): + # check unit number + if unit < 0 or unit >= 8: + raise ValueError("unit must be in range 0..7") + self.unit = unit + # init obj + self.pin = pin + # init unit + self.init(direction, edge, limit, reset) + + def init(self, direction=UP, edge=RISING, limit=0, reset=True): + # init config elements + channel = 0 + pulse_gpio_num = self.pin + ctrl_gpio_num = -1 # not yet supported + counter_h_lim = 0 + counter_l_lim = 0 + pos_mode = _COUNT_DIS + neg_mode = _COUNT_DIS + lctrl_mode = _MODE_KEEP + hctrl_mode = _MODE_KEEP + # + self.dir_up = direction == UP + self.edges = edge + if self.dir_up: mode = _COUNT_INC + else: mode = _COUNT_DEC + # tweak config according to parameters + if edge & RISING: pos_mode = mode + if edge & FALLING: neg_mode = mode + self.limit = limit + if self.limit == 0: limit = 0x7ffff + if self.dir_up: + counter_h_lim = self.limit + else: + counter_l_lim = -self.limit + # convert pcnt_config to C struct + print('IIIIIIhhII', pulse_gpio_num, ctrl_gpio_num, + lctrl_mode, hctrl_mode, pos_mode, neg_mode, counter_h_lim, counter_l_lim, + self.unit, channel) + pcnt_config_struct = bytearray(struct.calcsize('IIIIIIhhII')) + struct.pack_into('IIIIIIhhII', pcnt_config_struct, 0, + pulse_gpio_num, ctrl_gpio_num, lctrl_mode, hctrl_mode, pos_mode, neg_mode, + counter_h_lim, counter_l_lim, self.unit, channel) + _err_raise(espidf.pcnt_unit_config(pcnt_config_struct)) + _err_raise(espidf.pcnt_counter_resume(self.unit)) # apparently it starts paused... + if reset: + _err_raise(espidf.pcnt_counter_clear(self.unit)) + + def __del__(self): + _err_raise(espidf.pcnt_counter_pause(self.unit)) + _err_raise(espidf.pcnt_intr_disable(self.unit)) + _err_raise(espidf.pcnt_set_pin(self.unit, self.channel, -1, -1)) + self.unit = -1 + self.pin = -1 + + def resume(self): + _err_raise(espidf.pcnt_unit_resume(self.unit)) + + def pause(self): + _err_raise(espidf.pcnt_unit_pause(self.unit)) + + def value(self, new_value=None): + if new_value == None: + # GET value + v_raw = bytearray(2) + _err_raise(espidf.pcnt_get_counter_value(self.unit, v_raw)) + v = struct.unpack('h', v_raw) + if not self.dir_up: + v[0] += self.limit # adjust to limit..0 interval + return v[0] + else: + # SET value + if self.dir_up and new_value != 0: + raise ValueError("only v=0 supported") + if not self.dir_up and new_value != self.limit: + raise ValueError("only v=limit supported") + _err_raise(espidf.pcnt_counter_clear(self.unit)) + return None diff --git a/examples/natmod/esp-counter/dyn_esp32.mk b/examples/natmod/esp-counter/dyn_esp32.mk new file mode 100644 index 0000000000000..14c4dfca2e43d --- /dev/null +++ b/examples/natmod/esp-counter/dyn_esp32.mk @@ -0,0 +1,239 @@ +# Select the board to build for: if not given on the command line, +# then default to GENERIC. +BOARD ?= GENERIC + +PORT_DIR := $(MPY_DIR)/ports/esp32 + +# If the build directory is not given, make it reflect the board name. +BUILD ?= build + +BOARD_DIR ?= $(PORT_DIR)/boards/$(BOARD) +BOARD_BUILD ?= $(PORT_DIR)/build-$(BOARD) +#ifeq ($(wildcard $(BOARD_DIR)/.),) +#$(error Invalid BOARD specified: $(BOARD_DIR)) +#endif +# +#include ../../py/mkenv.mk +# +## Optional (not currently used for ESP32) +#-include mpconfigport.mk +# +#ifneq ($(SDKCONFIG),) +#$(error Use the BOARD variable instead of SDKCONFIG) +#endif + +# Expected to set SDKCONFIG +include $(BOARD_DIR)/mpconfigboard.mk +SDKCONFIG := $(addprefix $(PORT_DIR)/,$(SDKCONFIG)) + +## qstr definitions (must come before including py.mk) +#QSTR_DEFS = qstrdefsport.h +#QSTR_GLOBAL_DEPENDENCIES = $(BOARD_DIR)/mpconfigboard.h +#QSTR_GLOBAL_REQUIREMENTS = $(SDKCONFIG_H) + +#MICROPY_PY_USSL = 0 +#MICROPY_SSL_AXTLS = 0 +#MICROPY_PY_BTREE = 1 +#MICROPY_VFS_FAT = 1 +#MICROPY_VFS_LFS2 = 1 + +#FROZEN_MANIFEST ?= boards/manifest.py + +## include py core make definitions +#include $(TOP)/py/py.mk + +#GIT_SUBMODULES = lib/berkeley-db-1.xx + +#PORT ?= /dev/ttyUSB0 +#BAUD ?= 460800 +#FLASH_MODE ?= dio +#FLASH_FREQ ?= 40m +#FLASH_SIZE ?= 4MB +#CROSS_COMPILE ?= xtensa-esp32-elf- +#OBJDUMP = $(CROSS_COMPILE)objdump + +#SDKCONFIG_COMBINED = $(BUILD)/sdkconfig.combined +#SDKCONFIG_H = $(BOARD_BUILD)/sdkconfig.h + +# the git hash of the currently supported ESP IDF version +ESPIDF_SUPHASH_V3 := 6ccb4cf5b7d1fdddb8c2492f9cbc926abaf230df +ESPIDF_SUPHASH_V4 := 463a9d8b7f9af8205222b80707f9bdbba7c530e1 + +define print_supported_git_hash +$(info Supported git hash (v3.3): $(ESPIDF_SUPHASH_V3)) +$(info Supported git hash (v4.0) (experimental): $(ESPIDF_SUPHASH_V4)) +endef + +# paths to ESP IDF and its components +ifeq ($(ESPIDF),) +ifneq ($(IDF_PATH),) +ESPIDF = $(IDF_PATH) +else +$(info The ESPIDF variable has not been set, please set it to the root of the esp-idf repository.) +$(info See README.md for installation instructions.) +$(call print_supported_git_hash) +$(error ESPIDF not set) +endif +endif + +ESPCOMP = $(ESPIDF)/components +#ESPTOOL ?= $(ESPCOMP)/esptool_py/esptool/esptool.py +ESPCOMP_KCONFIGS = $(shell find $(ESPCOMP) -name Kconfig) +ESPCOMP_KCONFIGS_PROJBUILD = $(shell find $(ESPCOMP) -name Kconfig.projbuild) + +# verify the ESP IDF version +ESPIDF_CURHASH := $(shell git -C $(ESPIDF) show -s --pretty=format:'%H') + +ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V3)) +$(info Building with ESP IDF v3) +else ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) +$(info Building with ESP IDF v4) + +PYPARSING_VERSION = $(shell python3 -c 'import pyparsing; print(pyparsing.__version__)') +ifneq ($(PYPARSING_VERSION),2.3.1) +$(info ** ERROR **) +$(info EDP IDF requires pyparsing version less than 2.4) +$(info You will need to set up a Python virtual environment with pyparsing 2.3.1) +$(info Please see README.md for more information) +$(error Incorrect pyparsing version) +endif +else +$(info ** WARNING **) +$(info The git hash of ESP IDF does not match the supported version) +$(info The build may complete and the firmware may work but it is not guaranteed) +$(info ESP IDF path: $(ESPIDF)) +$(info Current git hash: $(ESPIDF_CURHASH)) +$(call print_supported_git_hash) +endif + +# pretty format of ESP IDF version, used internally by the IDF +IDF_VER := $(shell git -C $(ESPIDF) describe) + +#ifeq ($(shell which $(CC) 2> /dev/null),) +#$(info ** ERROR **) +#$(info Cannot find C compiler $(CC)) +#$(info Add the xtensa toolchain to your PATH. See README.md) +#$(error C compiler missing) +#endif + +## Support BLE by default when building with IDF 4.x. +## Can be explicitly disabled on the command line or board config. +#ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) +#MICROPY_PY_BLUETOOTH ?= 1 +#ifeq ($(MICROPY_PY_BLUETOOTH),1) +#SDKCONFIG += boards/sdkconfig.ble +#MICROPY_BLUETOOTH_NIMBLE = 1 +#endif +#endif +# +# include sdkconfig to get needed configuration values +include $(SDKCONFIG) +#$(shell cp $(BOARD_BUILD)/sdkconfig* $(BUILD)) + +################################################################################ +# Compiler and linker flags + +INC_ESPCOMP += -I$(PORT_DIR) +#INC += -I$(TOP) +#INC += -I$(TOP)/lib/mp-readline +#INC += -I$(TOP)/lib/netutils +#INC += -I$(TOP)/lib/timeutils +INC_ESPCOMP += -I$(BUILD) + +INC_ESPCOMP += -I$(ESPCOMP)/bootloader_support/include +INC_ESPCOMP += -I$(ESPCOMP)/bootloader_support/include_bootloader +INC_ESPCOMP += -I$(ESPCOMP)/console +INC_ESPCOMP += -I$(ESPCOMP)/driver/include +INC_ESPCOMP += -I$(ESPCOMP)/driver/include/driver +INC_ESPCOMP += -I$(ESPCOMP)/efuse/include +INC_ESPCOMP += -I$(ESPCOMP)/efuse/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/espcoredump/include +INC_ESPCOMP += -I$(ESPCOMP)/soc/include +INC_ESPCOMP += -I$(ESPCOMP)/soc/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/heap/include +INC_ESPCOMP += -I$(ESPCOMP)/log/include +INC_ESPCOMP += -I$(ESPCOMP)/newlib/platform_include +INC_ESPCOMP += -I$(ESPCOMP)/newlib/include +INC_ESPCOMP += -I$(ESPCOMP)/nvs_flash/include +INC_ESPCOMP += -I$(ESPCOMP)/freertos/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_ringbuf/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_event/include +INC_ESPCOMP += -I$(ESPCOMP)/tcpip_adapter/include +INC_ESPCOMP += -I$(ESPCOMP)/lwip/lwip/src/include +INC_ESPCOMP += -I$(ESPCOMP)/lwip/port/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/apps +INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/mbedtls/include +INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/port/include +INC_ESPCOMP += -I$(ESPCOMP)/mdns/include +INC_ESPCOMP += -I$(ESPCOMP)/mdns/private_include +INC_ESPCOMP += -I$(ESPCOMP)/spi_flash/include +INC_ESPCOMP += -I$(ESPCOMP)/ulp/include +INC_ESPCOMP += -I$(ESPCOMP)/vfs/include +INC_ESPCOMP += -I$(ESPCOMP)/xtensa-debug-module/include +INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/include +INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/port/include +INC_ESPCOMP += -I$(ESPCOMP)/app_trace/include +INC_ESPCOMP += -I$(ESPCOMP)/app_update/include +INC_ESPCOMP += -I$(ESPCOMP)/pthread/include +INC_ESPCOMP += -I$(ESPCOMP)/smartconfig_ack/include +INC_ESPCOMP += -I$(ESPCOMP)/sdmmc/include + +ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) +INC_ESPCOMP += -I$(ESPCOMP)/esp_common/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_eth/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_event/private_include +INC_ESPCOMP += -I$(ESPCOMP)/esp_rom/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_wifi/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_wifi/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/apps/sntp +INC_ESPCOMP += -I$(ESPCOMP)/spi_flash/private_include +INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/include/esp_supplicant +INC_ESPCOMP += -I$(ESPCOMP)/xtensa/include +INC_ESPCOMP += -I$(ESPCOMP)/xtensa/esp32/include +ifeq ($(CONFIG_BT_NIMBLE_ENABLED),y) +INC_ESPCOMP += -I$(ESPCOMP)/bt/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/common/osi/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/common/btc/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/common/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/porting/nimble/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/port/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/ans/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/bas/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/gap/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/gatt/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/ias/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/lls/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/tps/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/util/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/store/ram/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/store/config/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/porting/npl/freertos/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/ext/tinycrypt/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/esp-hci/include +endif +else +INC_ESPCOMP += -I$(ESPCOMP)/ethernet/include +INC_ESPCOMP += -I$(ESPCOMP)/expat/expat/expat/lib +INC_ESPCOMP += -I$(ESPCOMP)/expat/port/include +INC_ESPCOMP += -I$(ESPCOMP)/json/include +INC_ESPCOMP += -I$(ESPCOMP)/json/port/include +INC_ESPCOMP += -I$(ESPCOMP)/micro-ecc/micro-ecc +INC_ESPCOMP += -I$(ESPCOMP)/nghttp/port/include +INC_ESPCOMP += -I$(ESPCOMP)/nghttp/nghttp2/lib/includes +endif + +ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) +ifeq ($(MICROPY_PY_BLUETOOTH),1) +CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH=1 +CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1 + +ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) +CFLAGS_MOD += -DMICROPY_BLUETOOTH_NIMBLE=1 +endif +endif +endif + +CFLAGS = $(INC_ESPCOMP) $(CFLAGS_MOD) diff --git a/examples/natmod/esp-idf/Makefile b/examples/natmod/esp-idf/Makefile new file mode 100644 index 0000000000000..0aa05d76cca5e --- /dev/null +++ b/examples/natmod/esp-idf/Makefile @@ -0,0 +1,23 @@ +# Location of top-level MicroPython directory +MPY_DIR = ../../.. + +# Espressif ESP-IDF path +IDF_PATH := /home/src/esp32/esp-idf-micropython +# Board to get correct ESP-IDF config +BOARD := GENERIC +# xtensa toolchain bin dir +PATH := $(IDF_PATH)/xtensa-esp32-elf/bin:$(PATH) + +# Name of module +MOD = counter + +# Source files (.c or .py) +SRC = counter.c + +# Architecture to build for (x86, x64, armv7m, xtensa, xtensawin) +ARCH = xtensawin + +# Include to get the rules for compiling and linking the module +include dyn_esp32.mk +include $(MPY_DIR)/py/dynruntime.mk +CFLAGS += -std=gnu99 # override -std=c99 in dynruntime.mk diff --git a/examples/natmod/esp-idf/counter.c b/examples/natmod/esp-idf/counter.c new file mode 100644 index 0000000000000..fd8c5247a926c --- /dev/null +++ b/examples/natmod/esp-idf/counter.c @@ -0,0 +1,58 @@ +/* This example demonstrates using an ESP32 esp-idf function +*/ + +// Include the header file to get access to the MicroPython API +#include "py/dynruntime.h" + +// ESP-IDF imports +#include +#include + +// This is the function which will be called from Python as counter_init(x) +STATIC mp_obj_t counter_init(mp_obj_t x_obj) { + // Extract the integer from the MicroPython input object + mp_int_t x = mp_obj_get_int(x_obj); + // Call pcnt_unit_config + pcnt_config_t cfg = { + .unit = 0, + .channel = 0, + .pulse_gpio_num = x, + .ctrl_gpio_num = -1, // not yet supported + .counter_h_lim = 0, + .counter_l_lim = 0, + .pos_mode = PCNT_COUNT_DIS, // positive edge is no-op + .neg_mode = PCNT_COUNT_DIS, // negative edge is no-op + .lctrl_mode = PCNT_MODE_KEEP, // ctrl pin is no-op + .hctrl_mode = PCNT_MODE_KEEP, // ctrl pin is no-op + }; + esp_err_t err = pcnt_unit_config(&cfg); + // Convert the result to a MicroPython integer object and return it + return mp_obj_new_int(err); +} +// Define a Python reference to the function above +STATIC MP_DEFINE_CONST_FUN_OBJ_1(counter_init_obj, counter_init); + +// Another function that calls into the firmware, this time to mp_micropython_mem_info() +// just as a simple demo which prints something to make it easy to see whether it's working or not. +STATIC mp_obj_t mem_info() { + extern int mp_micropython_mem_info(int, int); + mp_printf(&mp_plat_print, "mp_fun_table is at 0x%x\n", &mp_fun_table); + mp_printf(&mp_plat_print, "calling mp_micropython_mem_info\n"); + int i = mp_micropython_mem_info(0, 0); + mp_printf(&mp_plat_print, "mp_micropython_mem_info: %d/0x%x\n", i, i); + return mp_const_none; +} +STATIC MP_DEFINE_CONST_FUN_OBJ_0(mem_info_obj, mem_info); + +// This is the entry point and is called when the module is imported +mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) { + // This must be first, it sets up the globals dict and other things + MP_DYNRUNTIME_INIT_ENTRY + + // Make the function available in the module's namespace + mp_store_global(MP_QSTR_counter_init, MP_OBJ_FROM_PTR(&counter_init_obj)); + mp_store_global(MP_QSTR_mem_info, MP_OBJ_FROM_PTR(&mem_info_obj)); + + // This must be last, it restores the globals dict + MP_DYNRUNTIME_INIT_EXIT +} diff --git a/examples/natmod/esp-idf/dyn_esp32.mk b/examples/natmod/esp-idf/dyn_esp32.mk new file mode 100644 index 0000000000000..5995e80090513 --- /dev/null +++ b/examples/natmod/esp-idf/dyn_esp32.mk @@ -0,0 +1,239 @@ +# Select the board to build for: if not given on the command line, +# then default to GENERIC. +BOARD ?= GENERIC + +PORT_DIR := $(MPY_DIR)/ports/esp32 + +# If the build directory is not given, make it reflect the board name. +BUILD ?= build + +BOARD_DIR ?= $(PORT_DIR)/boards/$(BOARD) +BOARD_BUILD ?= $(PORT_DIR)/build-$(BOARD) +#ifeq ($(wildcard $(BOARD_DIR)/.),) +#$(error Invalid BOARD specified: $(BOARD_DIR)) +#endif +# +#include ../../py/mkenv.mk +# +## Optional (not currently used for ESP32) +#-include mpconfigport.mk +# +#ifneq ($(SDKCONFIG),) +#$(error Use the BOARD variable instead of SDKCONFIG) +#endif + +# Expected to set SDKCONFIG +include $(BOARD_DIR)/mpconfigboard.mk +SDKCONFIG := $(addprefix $(PORT_DIR)/,$(SDKCONFIG)) + +## qstr definitions (must come before including py.mk) +#QSTR_DEFS = qstrdefsport.h +#QSTR_GLOBAL_DEPENDENCIES = $(BOARD_DIR)/mpconfigboard.h +#QSTR_GLOBAL_REQUIREMENTS = $(SDKCONFIG_H) + +#MICROPY_PY_USSL = 0 +#MICROPY_SSL_AXTLS = 0 +#MICROPY_PY_BTREE = 1 +#MICROPY_VFS_FAT = 1 +#MICROPY_VFS_LFS2 = 1 + +#FROZEN_MANIFEST ?= boards/manifest.py + +## include py core make definitions +#include $(TOP)/py/py.mk + +#GIT_SUBMODULES = lib/berkeley-db-1.xx + +#PORT ?= /dev/ttyUSB0 +#BAUD ?= 460800 +#FLASH_MODE ?= dio +#FLASH_FREQ ?= 40m +#FLASH_SIZE ?= 4MB +#CROSS_COMPILE ?= xtensa-esp32-elf- +#OBJDUMP = $(CROSS_COMPILE)objdump + +#SDKCONFIG_COMBINED = $(BUILD)/sdkconfig.combined +#SDKCONFIG_H = $(BOARD_BUILD)/sdkconfig.h + +# the git hash of the currently supported ESP IDF version +ESPIDF_SUPHASH_V3 := 6ccb4cf5b7d1fdddb8c2492f9cbc926abaf230df +ESPIDF_SUPHASH_V4 := 463a9d8b7f9af8205222b80707f9bdbba7c530e1 + +define print_supported_git_hash +$(info Supported git hash (v3.3): $(ESPIDF_SUPHASH_V3)) +$(info Supported git hash (v4.0) (experimental): $(ESPIDF_SUPHASH_V4)) +endef + +# paths to ESP IDF and its components +ifeq ($(ESPIDF),) +ifneq ($(IDF_PATH),) +ESPIDF = $(IDF_PATH) +else +$(info The ESPIDF variable has not been set, please set it to the root of the esp-idf repository.) +$(info See README.md for installation instructions.) +$(call print_supported_git_hash) +$(error ESPIDF not set) +endif +endif + +ESPCOMP = $(ESPIDF)/components +#ESPTOOL ?= $(ESPCOMP)/esptool_py/esptool/esptool.py +ESPCOMP_KCONFIGS = $(shell find $(ESPCOMP) -name Kconfig) +ESPCOMP_KCONFIGS_PROJBUILD = $(shell find $(ESPCOMP) -name Kconfig.projbuild) + +# verify the ESP IDF version +ESPIDF_CURHASH := $(shell git -C $(ESPIDF) show -s --pretty=format:'%H') + +ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V3)) +$(info Building with ESP IDF v3) +else ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) +$(info Building with ESP IDF v4) + +PYPARSING_VERSION = $(shell python3 -c 'import pyparsing; print(pyparsing.__version__)') +ifneq ($(PYPARSING_VERSION),2.3.1) +$(info ** ERROR **) +$(info EDP IDF requires pyparsing version less than 2.4) +$(info You will need to set up a Python virtual environment with pyparsing 2.3.1) +$(info Please see README.md for more information) +$(error Incorrect pyparsing version) +endif +else +$(info ** WARNING **) +$(info The git hash of ESP IDF does not match the supported version) +$(info The build may complete and the firmware may work but it is not guaranteed) +$(info ESP IDF path: $(ESPIDF)) +$(info Current git hash: $(ESPIDF_CURHASH)) +$(call print_supported_git_hash) +endif + +# pretty format of ESP IDF version, used internally by the IDF +IDF_VER := $(shell git -C $(ESPIDF) describe) + +#ifeq ($(shell which $(CC) 2> /dev/null),) +#$(info ** ERROR **) +#$(info Cannot find C compiler $(CC)) +#$(info Add the xtensa toolchain to your PATH. See README.md) +#$(error C compiler missing) +#endif + +## Support BLE by default when building with IDF 4.x. +## Can be explicitly disabled on the command line or board config. +#ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) +#MICROPY_PY_BLUETOOTH ?= 1 +#ifeq ($(MICROPY_PY_BLUETOOTH),1) +#SDKCONFIG += boards/sdkconfig.ble +#MICROPY_BLUETOOTH_NIMBLE = 1 +#endif +#endif +# +# include sdkconfig to get needed configuration values +include $(SDKCONFIG) +$(shell cp $(BOARD_BUILD)/sdkconfig* $(BUILD)) + +################################################################################ +# Compiler and linker flags + +INC_ESPCOMP += -I$(PORT_DIR) +#INC += -I$(TOP) +#INC += -I$(TOP)/lib/mp-readline +#INC += -I$(TOP)/lib/netutils +#INC += -I$(TOP)/lib/timeutils +INC_ESPCOMP += -I$(BUILD) + +INC_ESPCOMP += -I$(ESPCOMP)/bootloader_support/include +INC_ESPCOMP += -I$(ESPCOMP)/bootloader_support/include_bootloader +INC_ESPCOMP += -I$(ESPCOMP)/console +INC_ESPCOMP += -I$(ESPCOMP)/driver/include +INC_ESPCOMP += -I$(ESPCOMP)/driver/include/driver +INC_ESPCOMP += -I$(ESPCOMP)/efuse/include +INC_ESPCOMP += -I$(ESPCOMP)/efuse/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/espcoredump/include +INC_ESPCOMP += -I$(ESPCOMP)/soc/include +INC_ESPCOMP += -I$(ESPCOMP)/soc/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/heap/include +INC_ESPCOMP += -I$(ESPCOMP)/log/include +INC_ESPCOMP += -I$(ESPCOMP)/newlib/platform_include +INC_ESPCOMP += -I$(ESPCOMP)/newlib/include +INC_ESPCOMP += -I$(ESPCOMP)/nvs_flash/include +INC_ESPCOMP += -I$(ESPCOMP)/freertos/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_ringbuf/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_event/include +INC_ESPCOMP += -I$(ESPCOMP)/tcpip_adapter/include +INC_ESPCOMP += -I$(ESPCOMP)/lwip/lwip/src/include +INC_ESPCOMP += -I$(ESPCOMP)/lwip/port/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/apps +INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/mbedtls/include +INC_ESPCOMP += -I$(ESPCOMP)/mbedtls/port/include +INC_ESPCOMP += -I$(ESPCOMP)/mdns/include +INC_ESPCOMP += -I$(ESPCOMP)/mdns/private_include +INC_ESPCOMP += -I$(ESPCOMP)/spi_flash/include +INC_ESPCOMP += -I$(ESPCOMP)/ulp/include +INC_ESPCOMP += -I$(ESPCOMP)/vfs/include +INC_ESPCOMP += -I$(ESPCOMP)/xtensa-debug-module/include +INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/include +INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/port/include +INC_ESPCOMP += -I$(ESPCOMP)/app_trace/include +INC_ESPCOMP += -I$(ESPCOMP)/app_update/include +INC_ESPCOMP += -I$(ESPCOMP)/pthread/include +INC_ESPCOMP += -I$(ESPCOMP)/smartconfig_ack/include +INC_ESPCOMP += -I$(ESPCOMP)/sdmmc/include + +ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) +INC_ESPCOMP += -I$(ESPCOMP)/esp_common/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_eth/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_event/private_include +INC_ESPCOMP += -I$(ESPCOMP)/esp_rom/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_wifi/include +INC_ESPCOMP += -I$(ESPCOMP)/esp_wifi/esp32/include +INC_ESPCOMP += -I$(ESPCOMP)/lwip/include/apps/sntp +INC_ESPCOMP += -I$(ESPCOMP)/spi_flash/private_include +INC_ESPCOMP += -I$(ESPCOMP)/wpa_supplicant/include/esp_supplicant +INC_ESPCOMP += -I$(ESPCOMP)/xtensa/include +INC_ESPCOMP += -I$(ESPCOMP)/xtensa/esp32/include +ifeq ($(CONFIG_BT_NIMBLE_ENABLED),y) +INC_ESPCOMP += -I$(ESPCOMP)/bt/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/common/osi/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/common/btc/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/common/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/porting/nimble/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/port/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/ans/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/bas/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/gap/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/gatt/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/ias/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/lls/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/services/tps/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/util/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/store/ram/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/nimble/host/store/config/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/porting/npl/freertos/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/nimble/ext/tinycrypt/include +INC_ESPCOMP += -I$(ESPCOMP)/bt/host/nimble/esp-hci/include +endif +else +INC_ESPCOMP += -I$(ESPCOMP)/ethernet/include +INC_ESPCOMP += -I$(ESPCOMP)/expat/expat/expat/lib +INC_ESPCOMP += -I$(ESPCOMP)/expat/port/include +INC_ESPCOMP += -I$(ESPCOMP)/json/include +INC_ESPCOMP += -I$(ESPCOMP)/json/port/include +INC_ESPCOMP += -I$(ESPCOMP)/micro-ecc/micro-ecc +INC_ESPCOMP += -I$(ESPCOMP)/nghttp/port/include +INC_ESPCOMP += -I$(ESPCOMP)/nghttp/nghttp2/lib/includes +endif + +ifeq ($(ESPIDF_CURHASH),$(ESPIDF_SUPHASH_V4)) +ifeq ($(MICROPY_PY_BLUETOOTH),1) +CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH=1 +CFLAGS_MOD += -DMICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE=1 + +ifeq ($(MICROPY_BLUETOOTH_NIMBLE),1) +CFLAGS_MOD += -DMICROPY_BLUETOOTH_NIMBLE=1 +endif +endif +endif + +CFLAGS = $(INC_ESPCOMP) $(CFLAGS_MOD) diff --git a/ports/esp32/Makefile b/ports/esp32/Makefile index fe4787b7ae338..ef826ea64e438 100644 --- a/ports/esp32/Makefile +++ b/ports/esp32/Makefile @@ -761,20 +761,30 @@ APP_LD_ARGS += -L$(ESPCOMP)/esp32/lib -lcore -lmesh -lnet80211 -lphy -lrtc -lpp endif APP_LD_ARGS += $(OBJ) APP_LD_ARGS += $(LIB) +APP_LD_ARGS += $(BUILD)/plat_relo.o APP_LD_ARGS += --end-group $(BUILD)/esp32_out.ld: $(SDKCONFIG_H) $(Q)$(CC) -I$(BUILD) -C -P -x c -E $(ESPCOMP)/esp32/ld/esp32.ld -o $@ -$(BUILD)/application.bin: $(BUILD)/application.elf +$(BUILD)/application.bin: $(BUILD)/application.elf $(BUILD)/application.sym $(ECHO) "Create $@" $(Q)$(ESPTOOL) --chip esp32 elf2image --flash_mode $(FLASH_MODE) --flash_freq $(FLASH_FREQ) --flash_size $(FLASH_SIZE) $< -$(BUILD)/application.elf: $(OBJ) $(LIB) $(BUILD)/esp32_out.ld $(BUILD)/esp32.project.ld +# Memory map produced by nm, for troubleshooting purposes, not required for operation +$(BUILD)/application.sym: $(BUILD)/application.elf + $(ECHO) "Create $@" + $(Q)xtensa-esp32-elf-nm -l -n $< >$@ + +$(BUILD)/application.elf: $(OBJ) $(LIB) $(BUILD)/esp32_out.ld $(BUILD)/esp32.project.ld $(BUILD)/plat_relo.o $(ECHO) "LINK $@" $(Q)$(LD) $(LDFLAGS) -o $@ $(APP_LD_ARGS) $(Q)$(SIZE) $@ +$(BUILD)/plat_relo.s: plat_relo.py + $(ECHO) "GEN platform relocations" + $(Q)./plat_relo.py --tab >$@ + define compile_cxx $(ECHO) "CXX $<" $(Q)$(CXX) $(CXXFLAGS) -c -MD -o $@ $< diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index bca68538b13db..236866f9e4a33 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -162,6 +162,7 @@ #define MICROPY_PY_USOCKET_EVENTS (MICROPY_PY_WEBREPL) #define MICROPY_PY_BLUETOOTH_RANDOM_ADDR (1) #define MICROPY_PY_BLUETOOTH_DEFAULT_NAME ("ESP32") +#define MICROPY_PY_PLAT_RELO (1) // fatfs configuration #define MICROPY_FATFS_ENABLE_LFN (1) @@ -191,6 +192,7 @@ extern const struct _mp_obj_module_t mp_module_machine; extern const struct _mp_obj_module_t mp_module_network; extern const struct _mp_obj_module_t mp_module_onewire; + #define MICROPY_PORT_BUILTIN_MODULES \ { MP_OBJ_NEW_QSTR(MP_QSTR_esp), (mp_obj_t)&esp_module }, \ { MP_OBJ_NEW_QSTR(MP_QSTR_esp32), (mp_obj_t)&esp32_module }, \ diff --git a/ports/esp32/plat_relo.py b/ports/esp32/plat_relo.py new file mode 100755 index 0000000000000..398a500ecf89f --- /dev/null +++ b/ports/esp32/plat_relo.py @@ -0,0 +1,116 @@ +#! /usr/bin/python3 + +# Linking table for platform-specific functions. +# +# The entry points below list a number of functions in the MP firmware that are to be exposed to +# native modules. This is typically used to expose ESP-IDF functions so they can be called directly +# from C code in dynamically loaded native modules. The native module can then #include the esp-idf +# header files as usual and call the functions normally. + +# TODO: this needs to be loaded from a file, not hard-coded here +entry_points = [ + ("esp_chip_info", "P", ""), + ("esp_get_idf_version", "", "s"), + ("esp_err_to_name", "I", "s"), + ("pcnt_unit_config", "P", "I"), + ("pcnt_get_counter_value", "IP", "I"), + ("pcnt_counter_pause", "I", "I"), + ("pcnt_counter_resume", "I", "I"), + ("pcnt_counter_clear", "I", "I"), + ("pcnt_intr_disable", "I", "I"), + ] + +imports = [ + "esp_err.h", + "esp_idf_version.h", + "esp_system.h", + "driver/pcnt.h", + ] + +type_map = { "I": "uint", "i": "int", "H": "uint16", "h": "int16", "B": "uint8", "b": "int8", + "P": "void*", "s": "char*", "": "void" } + +return_map = { "I": "mp_obj_new_int({})", "s": "mp_obj_new_str({0}, strlen({0}))", "": "{}" } + +import sys + +if len(sys.argv) > 1 and sys.argv[1] == '--tab': + # Generate platform relocation table compiled into the MP firmware + print('\t.file\t"plat_relo.py"') + print('\t.section\t.rodata.plat_relo_tab,"a",@progbits') + print('\t.align\t4') + print('\t.global\tplat_relo_tab') + print('\t.type\tplat_relo_tab, @object') + print('plat_relo_tab:') + for name, _, _ in entry_points: + print('\t.word\t' + name) + print('\t.size\tplat_relo_tab, .-plat_relo_tab') + +elif len(sys.argv) > 1 and (sys.argv[1] == '--module' or sys.argv[1] == '--builtin'): + # Generate espidf module to expose functions to python + builtin = sys.argv[1] == '--builtin' + print('#include ') + print('#include ') + if builtin: + print('#include "py/runtime.h"') + else: + print('#include "py/dynruntime.h"') + + print('// ESP-IDF imports') + for im in imports: + print("#include <{}>".format(im)) + for name, args, res in entry_points: + print() + print("STATIC mp_obj_t espidf_{}(".format(name), end='') + print(", ".join(["mp_obj_t arg{}".format(ix) for ix in range(len(args))]), end='') + print(") {") + for ix, fmt in enumerate(args): + print("\t// convert arg{}".format(ix)) + if fmt == "P": + print("\tmp_buffer_info_t val{}_buf;".format(ix)) + print("\tmp_get_buffer_raise(arg{0}, &val{0}_buf, MP_BUFFER_RW);".format(ix)) + print("\tvoid *val{0} = (void *)(val{0}_buf.buf);".format(ix)) + + elif fmt in frozenset(["I", "i", "H", "h", "B", "b"]): + print("\t{1} val{0} = ({1})mp_obj_get_int(arg{0});".format(ix, type_map[fmt])) + print("\t// call") + if res == "": + print("\tmp_obj_t ret = mp_const_none;") + print("\t{}(".format(name), end='') + else: + print("\tconst {} ret = {}(".format(type_map[res], name), end='') + print(", ".join([ "val{}".format(ix) for ix in range(len(args)) ]), end='') + print(");") + print("\treturn " + return_map[res].format('ret') + ";\n}") + if len(args) < 4: + print("STATIC MP_DEFINE_CONST_FUN_OBJ_{}(espidf_{}_obj, espidf_{});".format( + len(args), name, name)) + else: + print("STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(espidf_{}_obj, {}, {}, espidf_{});".format( + len(args), len(args), name, name)) + print() + if sys.argv[1] == '--builtin': + # generate module built into the firmware + print("STATIC const mp_rom_map_elem_t mp_module_espidf_globals_table[] = {") + print("\t{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_espidf) },") + for name, _, _ in entry_points: + print("\t{{ MP_ROM_QSTR(MP_QSTR_{}), MP_ROM_PTR(&espidf_{}) }},".format(name, name)) + print('''\ +}; +STATIC MP_DEFINE_CONST_DICT(mp_module_espidf_globals, mp_module_espidf_globals_table); + +const mp_obj_module_t mp_module_espidf = { + .base = { &mp_type_module }, + .globals = (mp_obj_dict_t*)&mp_module_espidf_globals, +}; + +#endif +''', end='') + else: + # generate dynamically loadable module + print("mp_obj_t mpy_init(mp_obj_fun_bc_t *self, size_t n_args, size_t n_kw, mp_obj_t *args) {") + print("\tMP_DYNRUNTIME_INIT_ENTRY") + for name, _, _ in entry_points: + print("\tmp_store_global(MP_QSTR_{0}, MP_OBJ_FROM_PTR(&espidf_{0}_obj));".format(name)) + print("\tMP_DYNRUNTIME_INIT_EXIT") + print("}") diff --git a/py/dynruntime.mk b/py/dynruntime.mk index 8b65745afdf78..9b459cf6a7a64 100644 --- a/py/dynruntime.mk +++ b/py/dynruntime.mk @@ -128,6 +128,11 @@ $(BUILD)/%.o: %.c $(CONFIG_H) Makefile $(ECHO) "CC $<" $(Q)$(CROSS)gcc $(CFLAGS) -o $@ -c $< +# Build .o from .s source files +%.o: %.s $(CONFIG_H) Makefile + $(ECHO) "AS $<" + $(Q)$(CROSS)gcc $(CFLAGS) -o $@ -c $< + # Build .mpy from .py source files $(BUILD)/%.mpy: %.py $(ECHO) "MPY $<" diff --git a/py/persistentcode.c b/py/persistentcode.c index 7039f9f57a3c6..4369df831fbf9 100644 --- a/py/persistentcode.c +++ b/py/persistentcode.c @@ -189,12 +189,16 @@ STATIC void arch_link_qstr(uint8_t *pc, bool is_obj, qstr qst) { #endif } +extern size_t plat_relo_tab[]; + void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) { // Relocate native code reloc_info_t *ri = ri_in; uint8_t op; uintptr_t *addr_to_adjust = NULL; while ((op = read_byte(ri->reader)) != 0xff) { + mp_printf(&mp_plat_print, "mp_native_relocate %d\n", op); + // if lsb==1 then an offset follows the op byte, else addr_to_adjust auto-increments if (op & 1) { // Point to new location to make adjustments size_t addr = read_uint(ri->reader, NULL); @@ -225,14 +229,20 @@ void mp_native_relocate(void *ri_in, uint8_t *text, uintptr_t reloc_text) { } else if (op == 6) { // Destination is mp_fun_table itself dest = (uintptr_t)&mp_fun_table; + } else if (op == 126) { + size_t index = read_uint(ri->reader, NULL); + dest = ((uintptr_t*)&plat_relo_tab)[index]; + mp_printf(&mp_plat_print, "mp_native_relocate: op=%d, index=%d, dest=0x%08x\n", op, index, dest); } else { // Destination is an entry in mp_fun_table dest = ((uintptr_t*)&mp_fun_table)[op - 7]; + mp_printf(&mp_plat_print, "mp_native_relocate: op=%d, dest=0x%08x\n", op, dest); } while (n--) { *addr_to_adjust++ += dest; } } + mp_printf(&mp_plat_print, "mp_native_relocate DONE\n"); } #endif diff --git a/tools/mpy_ld.py b/tools/mpy_ld.py index 31c3912991c48..bd11b164c0ff3 100755 --- a/tools/mpy_ld.py +++ b/tools/mpy_ld.py @@ -34,6 +34,26 @@ sys.path.append(os.path.dirname(__file__) + '/../py') import makeqstrdata as qstrutil +# Platform-specific relocation table, which contains the addresses of a number of platform specific +# functions compiled into the firmware. These entries are used by the dynamic linker to fix-up +# relocation entries in dynamically loaded mpy modules. +# TODO: this needs to be loaded from a file, not hard-coded here. +plat_relo_entries = { name : idx + for idx, name in enumerate([ + "esp_chip_info", + "esp_get_idf_version", + "esp_err_to_name", + "pcnt_unit_config", + "pcnt_get_counter_value", + "pcnt_counter_pause", + "pcnt_counter_resume", + "pcnt_counter_clear", + "pcnt_intr_disable", + "mp_micropython_mem_info", + "strlen", + ]) +} + # MicroPython constants MPY_VERSION = 5 MP_NATIVE_ARCH_X86 = 1 @@ -256,6 +276,10 @@ def isrodata(self): def isbss(self): return self.sec_name.startswith('.bss') + def __repr__(self): # for debugging purposes + keys = ['name', 'sym', 'offset', 'link_addr', 'sec_name'] + return "GOTEntry(" + repr({ k:getattr(self, k) for k in keys}) + ")" + class LiteralEntry: def __init__(self, value, offset): self.value = value @@ -381,8 +405,11 @@ def populate_got(env): dest = '.data.rel.ro' elif got_entry.sec_name.startswith('.bss'): dest = '.bss' + elif got_entry.sec_name == '.external.plat_relo_tab': + dest = 1000+got_entry.sym.plat_relo_offset + print("GOT entry: {}->{} ({})".format(got_entry.name, dest, repr(got_entry))) else: - assert 0, (got_entry.name, got_entry.sec_name) + assert 0, repr(got_entry) env.mpy_relocs.append(('.text', env.got_section.addr + got_entry.offset, dest)) # Print out the final GOT @@ -680,6 +707,7 @@ def link_objects(env, native_qstr_vals_len, native_qstr_objs_len): 'mp_stream_write_obj', ]) } + plat_relo_sec = Section('.external.plat_relo_tab', b'', 0) for sym in env.unresolved_syms: assert sym['st_value'] == 0 if sym.name == '_GLOBAL_OFFSET_TABLE_': @@ -692,12 +720,15 @@ def link_objects(env, native_qstr_vals_len, native_qstr_objs_len): sym.section = env.qstr_obj_section elif sym.name in env.known_syms: sym.resolved = env.known_syms[sym.name] + elif sym.name in fun_table: + sym.section = mp_fun_table_sec + sym.mp_fun_table_offset = fun_table[sym.name] + elif sym.name in plat_relo_entries: + sym.section = plat_relo_sec + sym.plat_relo_offset = plat_relo_entries[sym.name] + print("Unresolved: {} -> plat_relo_sec+{}".format(sym.name, sym.plat_relo_offset)) else: - if sym.name in fun_table: - sym.section = mp_fun_table_sec - sym.mp_fun_table_offset = fun_table[sym.name] - else: - raise LinkError('{}: undefined symbol: {}'.format(sym.filename, sym.name)) + raise LinkError('{}: undefined symbol: {}'.format(sym.filename, sym.name)) # Align sections, assign their addresses, and create full_text env.full_text = bytearray(env.arch.asm_jump(8)) # dummy, to be filled in later @@ -766,12 +797,29 @@ def write_qstr(self, s): self.write_bytes(s) def write_reloc(self, base, offset, dest, n): + """ + Write a relocation entry into the mpy that the dynamic linker will have to resolve + when the mpy is loaded. A relocation entry consists of a kind/op byte, followed by an + optional offset word, followed by an optional count word. + - base+offset is the location where the fixup is to be done, base is '.text' or 'rodata'. + - dest is the target whose address needs to be placed into the fixup: 0=string table, + 1=rodata_const_table, 2=bss_const_table, 3..5: unused, 6=mp_fun_table, + 7..126:entry in mp_fun_table, 1000..66535:entry in plat_relo_tab. + - n is number of consecutive words to fix up. + """ need_offset = not (base == self.prev_base and offset == self.prev_offset + 1) self.prev_offset = offset + n - 1 + index = None if dest <= 2: dest = (dest << 1) | (n > 1) + elif dest >= 1000: + # offset into plat_relo_tab + assert dest < 1000+65536 + index = dest-1000 + dest = 126 + print("write_reloc plat_relo_tab+{}".format(index)) else: - assert 6 <= dest <= 127 + assert 6 <= dest < 126 assert n == 1 dest = dest << 1 | need_offset assert 0 <= dest <= 0xfe, dest @@ -782,6 +830,8 @@ def write_reloc(self, base, offset, dest, n): elif base == '.rodata': base = 1 self.write_uint(offset << 1 | base) + if index is not None: + self.write_uint(index) if n > 1: self.write_uint(n) @@ -865,8 +915,14 @@ def build_mpy(env, entry_offset, fmpy, native_qstr_vals, native_qstr_objs): kind = bss_const_table_idx elif kind == 'mp_fun_table': kind = 6 + elif isinstance(kind, int): + if kind < 1000: + # element of mp_fun_table + kind = 7 + kind + else: # element of plat_relo_tab + pass else: - kind = 7 + kind + assert 0, kind assert addr % env.arch.word_size == 0, addr offset = addr // env.arch.word_size if kind == prev_kind and base == prev_base and offset == prev_offset + 1: