diff options
Diffstat (limited to 'ports')
344 files changed, 7562 insertions, 2130 deletions
diff --git a/ports/alif/Makefile b/ports/alif/Makefile index 6870667436..d258b27b1d 100644 --- a/ports/alif/Makefile +++ b/ports/alif/Makefile @@ -21,6 +21,7 @@ Reset\n\ Exit ALIF_TOC_CONFIG = alif_cfg.json +ALIF_TOC_BIN = $(BUILD)/firmware.toc.bin ALIF_TOC_APPS = $(BUILD)/$(ALIF_TOC_CONFIG) ALIF_TOC_CFLAGS += -DTOC_CFG_FILE=$(ALIF_TOOLKIT_CFG_FILE) @@ -69,7 +70,7 @@ include $(TOP)/extmod/extmod.mk # Main targets .PHONY: all -all: $(BUILD)/firmware.toc.bin +all: $(BUILD)/firmware.zip # Force make commands to run the targets every time # regardless of whether firmware.toc.bin already exists @@ -81,24 +82,28 @@ $(BUILD): $(MKDIR) -p $@ $(BUILD)/M55_HP/firmware.bin: - make -f alif.mk MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0 + make -f alif.mk BUILD=$(BUILD)/M55_HP MCU_CORE=M55_HP MICROPY_PY_OPENAMP_MODE=0 $(BUILD)/M55_HE/firmware.bin: - make -f alif.mk MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1 + make -f alif.mk BUILD=$(BUILD)/M55_HE MCU_CORE=M55_HE MICROPY_PY_OPENAMP_MODE=1 $(BUILD)/$(ALIF_TOC_CONFIG): mcu/$(ALIF_TOC_CONFIG).in | $(BUILD) $(ECHO) "Preprocess toc config $@" $(Q)$(CPP) -P -E $(ALIF_TOC_CFLAGS) - < mcu/$(ALIF_TOC_CONFIG).in > $@ -$(BUILD)/firmware.toc.bin: $(ALIF_TOC_APPS) +$(ALIF_TOC_BIN): $(ALIF_TOC_APPS) $(Q)python $(ALIF_TOOLS)/app-gen-toc.py \ --filename $(abspath $(BUILD)/$(ALIF_TOC_CONFIG)) \ --output-dir $(BUILD) \ --firmware-dir $(BUILD) \ --output $@ +$(BUILD)/firmware.zip: $(ALIF_TOC_BIN) $(ALIF_TOC_APPS) + $(ECHO) "Create $@" + $(Q)$(ZIP) -q - $(BUILD)/application_package.ds $^ > $@ + .PHONY: deploy -deploy: $(BUILD)/firmware.toc.bin +deploy: $(ALIF_TOC_BIN) $(ECHO) "Writing $< to the board" $(Q)python $(ALIF_TOOLS)/app-write-mram.py \ --cfg-part $(ALIF_TOOLKIT_CFG_PART) \ diff --git a/ports/alif/README.md b/ports/alif/README.md index 824a63da18..dcf327060f 100644 --- a/ports/alif/README.md +++ b/ports/alif/README.md @@ -19,3 +19,61 @@ The following more advanced features will follow later: - Ethernet support. - SDRAM support. - Other machine modules. + +Build instructions +------------------ + +Before building the firmware for a given board the MicroPython cross-compiler +must be built; it will be used to pre-compile some of the built-in scripts to +bytecode. The cross-compiler is built and run on the host machine, using: +```bash +$ make -C mpy-cross +``` + +This command should be executed from the root directory of this repository. +All other commands below should be executed from the ports/alif/ directory. + +An ARM compiler is required for the build, along with the associated binary +utilities. The recommended toolchain version to use with this port is +Arm GNU toolchain version 13.3.Rel1. The compiler can be changed using the +`CROSS_COMPILE` variable when invoking `make`. + +Next, the board to build must be selected. The default board is `ALIF_ENSEMBLE` +but any of the names of the subdirectories in the `boards/` directory is valid. +The board name must be passed as the argument to `BOARD=` when invoking `make`. + +All boards require certain submodules to be obtained before they can be built. +The correct set of submodules can be initialised using (with `ALIF_ENSEMBLE` +as an example of the selected board): +```bash +make BOARD=ALIF_ENSEMBLE submodules +``` + +Then to build the board's firmware run: +```bash +make BOARD=ALIF_ENSEMBLE +``` + +The above command should produce binary images in the `build-ALIF_ENSEMBLE/` +subdirectory (or the equivalent directory for the board specified). + +### Update the SE Firmware + +The SE firmware must be updated **before** flashing the main firmware to match +the version used by MicroPython. This step only needs to be performed once. +Connect the board to your PC via the **SE UART USB** port (on the Ensemble kit, +this is labeled **PRG USB**), then run: + +```bash +make update-system-package +``` + +**Note:** The board must be power-cycled after this step. + +### Deploy MicroPython + +To flash the firmware, run: + +```bash +make BOARD=ALIF_ENSEMBLE deploy +``` diff --git a/ports/alif/alif.mk b/ports/alif/alif.mk index eee27b3b7d..265418aa07 100644 --- a/ports/alif/alif.mk +++ b/ports/alif/alif.mk @@ -3,7 +3,6 @@ BOARD ?= ALIF_ENSEMBLE BOARD_DIR ?= boards/$(BOARD) -BUILD ?= build-$(BOARD)/$(MCU_CORE) ifeq ($(wildcard $(BOARD_DIR)/.),) $(error Invalid BOARD specified: $(BOARD_DIR)) @@ -103,10 +102,6 @@ CFLAGS += -Wl,-T$(BUILD)/ensemble.ld \ -Wl,--print-memory-usage \ -Wl,--no-warn-rwx-segment -ifeq ($(MCU_CORE),M55_HP) -CFLAGS += -Wl,--wrap=dcd_event_handler -endif - ################################################################################ # Source files and libraries diff --git a/ports/alif/boards/ALIF_ENSEMBLE/board.json b/ports/alif/boards/ALIF_ENSEMBLE/board.json new file mode 100644 index 0000000000..0c63494f7a --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/board.json @@ -0,0 +1,17 @@ +{ + "deploy": [ + "./deploy.md" + ], + "docs": "", + "features": [ + "Ethernet" + ], + "images": [ + "ensemble-devkit-gen-2.jpg" + ], + "mcu": "AE722F80F55D5XX", + "product": "Ensemble E7 DevKit", + "thumbnail": "", + "url": "https://alifsemi.com/support/kits/ensemble-devkit/", + "vendor": "Alif Semiconductor" +} diff --git a/ports/alif/boards/ALIF_ENSEMBLE/deploy.md b/ports/alif/boards/ALIF_ENSEMBLE/deploy.md new file mode 100644 index 0000000000..acbc85617e --- /dev/null +++ b/ports/alif/boards/ALIF_ENSEMBLE/deploy.md @@ -0,0 +1,37 @@ +### Alif Security Toolkit + +This board can be programmed via the SE UART interface using the Alif Security +Toolkit. MicroPython uses a custom version of the toolkit, which can be downloaded +from [here](https://github.com/micropython/alif-security-toolkit/) or cloned: + +```bash +git clone https://github.com/micropython/alif-security-toolkit/ +``` + +--- + +### Update the SE Firmware (Optional) + +If needed, update the SE firmware to match the version used by MicroPython. Ensure +you have the correct port, part number, and chip revision. + +```bash +python alif-security-toolkit/toolkit/updateSystemPackage.py --port /dev/ttyACM0 --cfg-part AE722F80F55D5LS --cfg-rev B4 +``` + +**Note:** The board must be power-cycled after this step. + +--- + +### Deploy MicroPython + +Download (or build) the firmware package, unzip it, then deploy it: + +```bash + +python alif-security-toolkit/toolkit/app-write-mram.py --port /dev/ttyACM0 --cfg-part AE722F80F55D5LS --cfg-rev B4 --pad --images file:build-ALIF_ENSEMBLE/application_package.ds +``` + +The MicroPython REPL is available on the second USB serial port, eg `/dev/ttyACM1`. + +--- diff --git a/ports/alif/cyw43_configport.h b/ports/alif/cyw43_configport.h index 4b5cce6713..bfd4364ab9 100644 --- a/ports/alif/cyw43_configport.h +++ b/ports/alif/cyw43_configport.h @@ -27,13 +27,8 @@ #define MICROPY_INCLUDED_ALIF_CYW43_CONFIGPORT_H // The board-level config will be included here, so it can set some CYW43 values. -#include "py/mpconfig.h" -#include "py/mperrno.h" -#include "py/mphal.h" -#include "py/runtime.h" -#include "extmod/modnetwork.h" #include "extmod/mpbthci.h" -#include "pendsv.h" +#include "extmod/cyw43_config_common.h" #ifndef static_assert #define static_assert(expr, msg) typedef int static_assert_##__LINE__[(expr) ? 1 : -1] @@ -53,51 +48,15 @@ #define CYW43_WARN(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) #endif -#define CYW43_IOCTL_TIMEOUT_US (1000000) -#define CYW43_SLEEP_MAX (50) -#define CYW43_NETUTILS (1) #define CYW43_CLEAR_SDIO_INT (1) -#define CYW43_EPERM MP_EPERM // Operation not permitted -#define CYW43_EIO MP_EIO // I/O error -#define CYW43_EINVAL MP_EINVAL // Invalid argument -#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out - -#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER -#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT -#define CYW43_THREAD_LOCK_CHECK - -#define CYW43_HOST_NAME mod_network_hostname_data - #define CYW43_SDPCM_SEND_COMMON_WAIT __WFE() #define CYW43_DO_IOCTL_WAIT // __WFE() -#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait() - -#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) - -#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT -#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT -#define CYW43_HAL_PIN_PULL_NONE 0 - -#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 -#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR - -#define cyw43_hal_ticks_us mp_hal_ticks_us -#define cyw43_hal_ticks_ms mp_hal_ticks_ms - -#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t -#define cyw43_hal_pin_read mp_hal_pin_read -#define cyw43_hal_pin_low mp_hal_pin_low -#define cyw43_hal_pin_high mp_hal_pin_high #define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate #define cyw43_hal_uart_write mp_bluetooth_hci_uart_write #define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar -#define cyw43_hal_get_mac mp_hal_get_mac -#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii -#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac - #define CYW43_PIN_WL_REG_ON pin_WL_REG_ON #define CYW43_PIN_WL_IRQ pin_WL_IRQ @@ -110,15 +69,7 @@ void cyw43_post_poll_hook(void); -static inline void cyw43_delay_us(uint32_t us) { - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - } -} - -static inline void cyw43_delay_ms(uint32_t ms) { - mp_hal_delay_ms(ms); -} +#undef cyw43_hal_pin_config // mp_hal_pin_config on alif port is not API compatible static inline void cyw43_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uint32_t pull, uint32_t alt) { if (mode == MP_HAL_PIN_MODE_INPUT) { @@ -127,7 +78,6 @@ static inline void cyw43_hal_pin_config(mp_hal_pin_obj_t pin, uint32_t mode, uin mp_hal_pin_output(pin); } } - static inline void cyw43_hal_pin_config_irq_falling(mp_hal_pin_obj_t pin, bool enable) { mp_hal_pin_config_irq_falling(pin, enable); } diff --git a/ports/alif/irq.h b/ports/alif/irq.h index 08b8ef8086..02df524a49 100644 --- a/ports/alif/irq.h +++ b/ports/alif/irq.h @@ -48,6 +48,7 @@ #define IRQ_PRI_USB NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 7, 0) #define IRQ_PRI_HWSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 8, 0) #define IRQ_PRI_GPU NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 10, 0) +#define IRQ_PRI_GPIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 50, 0) #define IRQ_PRI_RTC NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 100, 0) #define IRQ_PRI_CYW43 NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 126, 0) #define IRQ_PRI_PENDSV NVIC_EncodePriority(NVIC_PRIORITYGROUP_7, 127, 0) diff --git a/ports/alif/machine_pin.c b/ports/alif/machine_pin.c index 02c5e482ad..b8773f103f 100644 --- a/ports/alif/machine_pin.c +++ b/ports/alif/machine_pin.c @@ -30,8 +30,76 @@ #include "extmod/virtpin.h" #include "shared/runtime/mpirq.h" +#ifndef MACHINE_PIN_NUM_VECTORS +#define MACHINE_PIN_NUM_PORT_IO (8) +#define MACHINE_PIN_NUM_VECTORS (16 * MACHINE_PIN_NUM_PORT_IO) +#endif + +typedef struct _machine_pin_irq_obj_t { + mp_irq_obj_t base; + uint32_t flags; + uint32_t trigger; + IRQn_Type irq_num; + bool reserved; // for use by other drivers +} machine_pin_irq_obj_t; + +#define MACHINE_PIN_IRQ_INDEX(port, pin) \ + ((port) * MACHINE_PIN_NUM_PORT_IO + (pin)) + +#define MACHINE_PIN_IRQ_OBJECT(port, pin) \ + (MP_STATE_PORT(machine_pin_irq_obj[MACHINE_PIN_IRQ_INDEX((port), (pin))])) + +// Defines a single GPIO IRQ handler +#define DEFINE_GPIO_IRQ_HANDLER(pname, port, pin) \ + void pname##_IRQ##pin##Handler(void) { \ + machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(port, pin); \ + machine_pin_obj_t *self = MP_OBJ_TO_PTR(irq->base.parent); \ + gpio_interrupt_eoi(self->gpio, pin); \ + irq->flags = irq->trigger; \ + mp_irq_handler(&irq->base); \ + } + +// Defines all 8 pin IRQ handlers for a port +#define DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(gpio, port) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 0) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 1) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 2) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 3) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 4) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 5) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 6) \ + DEFINE_GPIO_IRQ_HANDLER(gpio, port, 7) + +// Generate handlers for GPIO ports 0 to 14 + LPGPIO +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO0, 0) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO1, 1) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO2, 2) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO3, 3) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO4, 4) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO5, 5) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO6, 6) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO7, 7) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO8, 8) + +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 0) +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 1) +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 2) +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 3) +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 4) +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 5) +// DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 6) // Reserved for WiFi +DEFINE_GPIO_IRQ_HANDLER(GPIO9, 9, 7) + +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO10, 10) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO11, 11) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO12, 12) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO13, 13) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(GPIO14, 14) +DEFINE_GPIO_IRQ_HANDLERS_FOR_PORT(LPGPIO, 15) + extern const mp_obj_dict_t machine_pin_cpu_pins_locals_dict; extern const mp_obj_dict_t machine_pin_board_pins_locals_dict; +static const mp_irq_methods_t machine_pin_irq_methods; static const machine_pin_obj_t *machine_pin_find_named(const mp_obj_dict_t *named_pins, mp_obj_t name) { const mp_map_t *named_map = &named_pins->map; @@ -171,6 +239,7 @@ mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); machine_pin_obj_init_helper(self, n_args - 1, args + 1, &kw_args); } + return MP_OBJ_FROM_PTR(self); } @@ -225,6 +294,129 @@ static mp_obj_t machine_pin_toggle(mp_obj_t self_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(machine_pin_toggle_obj, machine_pin_toggle); +static mp_uint_t machine_pin_irq_trigger(mp_obj_t self_in, mp_uint_t trigger) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin); + + irq->flags = 0; + irq->trigger = trigger; + + // Disable IRQs. + gpio_disable_interrupt(self->gpio, self->pin); + gpio_mask_interrupt(self->gpio, self->pin); + + NVIC_ClearPendingIRQ(irq->irq_num); + NVIC_DisableIRQ(irq->irq_num); + + // Return if the trigger is disabled. + if (trigger == 0) { + return 0; + } + + // Clear and enable GPIO IRQ. + gpio_enable_interrupt(self->gpio, self->pin); + gpio_unmask_interrupt(self->gpio, self->pin); + + // Clear GPIO config. + self->gpio->GPIO_INT_BOTHEDGE &= ~(1 << self->pin); + self->gpio->GPIO_INT_POLARITY &= ~(1 << self->pin); + self->gpio->GPIO_INTTYPE_LEVEL &= ~(1 << self->pin); + + // Configure GPIO IRQ trigger + if (trigger == MP_HAL_PIN_TRIGGER_FALL) { + gpio_interrupt_set_edge_trigger(self->gpio, self->pin); + gpio_interrupt_set_polarity_low(self->gpio, self->pin); + } else if (trigger == MP_HAL_PIN_TRIGGER_RISE) { + gpio_interrupt_set_edge_trigger(self->gpio, self->pin); + gpio_interrupt_set_polarity_high(self->gpio, self->pin); + } else if (trigger == (MP_HAL_PIN_TRIGGER_FALL | MP_HAL_PIN_TRIGGER_RISE)) { + gpio_interrupt_set_both_edge_trigger(self->gpio, self->pin); + } else { + mp_raise_ValueError(MP_ERROR_TEXT("Invalid IRQ trigger")); + } + + // Clear GPIO IRQ (must be done after configuring trigger) + gpio_interrupt_eoi(self->gpio, self->pin); + + // Clear and enable NVIC GPIO IRQ. + NVIC_ClearPendingIRQ(irq->irq_num); + NVIC_SetPriority(irq->irq_num, IRQ_PRI_GPIO); + NVIC_EnableIRQ(irq->irq_num); + + return 0; +} + +static mp_uint_t machine_pin_irq_info(mp_obj_t self_in, mp_uint_t info_type) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(self_in); + machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin); + + if (info_type == MP_IRQ_INFO_FLAGS) { + return irq->flags; + } else if (info_type == MP_IRQ_INFO_TRIGGERS) { + return irq->trigger; + } + return 0; +} + +static const mp_irq_methods_t machine_pin_irq_methods = { + .trigger = machine_pin_irq_trigger, + .info = machine_pin_irq_info, +}; + +// pin.irq(handler=None, trigger=IRQ_FALLING|IRQ_RISING, hard=False) +static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + enum { ARG_handler, ARG_trigger, ARG_hard }; + static const mp_arg_t allowed_args[] = { + { MP_QSTR_handler, MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_trigger, MP_ARG_INT, {.u_int = MP_HAL_PIN_TRIGGER_FALL | MP_HAL_PIN_TRIGGER_RISE} }, + { MP_QSTR_hard, MP_ARG_BOOL, {.u_bool = false} }, + }; + + machine_pin_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; + mp_arg_parse_all(n_args - 1, pos_args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); + + machine_pin_irq_obj_t *irq = MACHINE_PIN_IRQ_OBJECT(self->port, self->pin); + + // Allocate a new IRQ object if it doesn't exist. + if (irq == NULL) { + irq = m_new_obj(machine_pin_irq_obj_t); + uint32_t idx = MACHINE_PIN_IRQ_INDEX(self->port, self->pin); + + irq->base.base.type = &mp_irq_type; + irq->base.methods = (mp_irq_methods_t *)&machine_pin_irq_methods; + irq->base.parent = MP_OBJ_FROM_PTR(self); + irq->base.handler = mp_const_none; + irq->base.ishard = false; + irq->reserved = false; + irq->irq_num = (self->port < 15) ? (GPIO0_IRQ0_IRQn + idx) : (LPGPIO_IRQ0_IRQn + self->pin); + MP_STATE_PORT(machine_pin_irq_obj[idx]) = irq; + } + + if (n_args > 1 || kw_args->used != 0) { + if (irq->reserved) { + mp_raise_msg(&mp_type_ValueError, MP_ERROR_TEXT("Pin IRQ is reserved")); + } + irq->base.handler = args[ARG_handler].u_obj; + irq->base.ishard = args[ARG_hard].u_bool; + machine_pin_irq_trigger(self, args[ARG_trigger].u_int); + } + + return MP_OBJ_FROM_PTR(irq); +} +static MP_DEFINE_CONST_FUN_OBJ_KW(machine_pin_irq_obj, 1, machine_pin_irq); + +void machine_pin_irq_deinit(void) { + for (size_t i = 0; i < MP_ARRAY_SIZE(MP_STATE_PORT(machine_pin_irq_obj)); i++) { + machine_pin_irq_obj_t *irq = MP_STATE_PORT(machine_pin_irq_obj[i]); + if (irq != NULL) { + machine_pin_obj_t *self = MP_OBJ_TO_PTR(irq->base.parent); + machine_pin_irq_trigger(self, 0); + MP_STATE_PORT(machine_pin_irq_obj[i]) = NULL; + } + } +} + static MP_DEFINE_CONST_OBJ_TYPE( pin_cpu_pins_obj_type, MP_QSTR_cpu, @@ -248,6 +440,7 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_off), MP_ROM_PTR(&machine_pin_low_obj) }, { MP_ROM_QSTR(MP_QSTR_on), MP_ROM_PTR(&machine_pin_high_obj) }, { MP_ROM_QSTR(MP_QSTR_toggle), MP_ROM_PTR(&machine_pin_toggle_obj) }, + { MP_ROM_QSTR(MP_QSTR_irq), MP_ROM_PTR(&machine_pin_irq_obj) }, // class attributes { MP_ROM_QSTR(MP_QSTR_board), MP_ROM_PTR(&pin_board_pins_obj_type) }, @@ -259,6 +452,8 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(MP_HAL_PIN_MODE_OPEN_DRAIN) }, { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(MP_HAL_PIN_PULL_UP) }, { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(MP_HAL_PIN_PULL_DOWN) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_FALLING), MP_ROM_INT(MP_HAL_PIN_TRIGGER_FALL) }, + { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(MP_HAL_PIN_TRIGGER_RISE) }, }; static MP_DEFINE_CONST_DICT(machine_pin_locals_dict, machine_pin_locals_dict_table); @@ -296,3 +491,5 @@ MP_DEFINE_CONST_OBJ_TYPE( mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t obj) { return machine_pin_find(obj); } + +MP_REGISTER_ROOT_POINTER(void *machine_pin_irq_obj[MACHINE_PIN_NUM_VECTORS]); diff --git a/ports/alif/machine_spi.c b/ports/alif/machine_spi.c index abb60bc4e8..b2c14745cf 100644 --- a/ports/alif/machine_spi.c +++ b/ports/alif/machine_spi.c @@ -39,6 +39,7 @@ typedef struct _machine_spi_obj_t { uint8_t id; SPI_Type *inst; bool is_lp; + uint32_t bits; } machine_spi_obj_t; static machine_spi_obj_t machine_spi_obj[] = { @@ -246,6 +247,9 @@ mp_obj_t machine_spi_make_new(const mp_obj_type_t *type, size_t n_args, size_t n // Get static peripheral object. machine_spi_obj_t *self = &machine_spi_obj[spi_id]; + // Set args + self->bits = args[ARG_bits].u_int; + // here we would check the sck/mosi/miso pins and configure them, but it's not implemented if (args[ARG_sck].u_obj != MP_OBJ_NULL || args[ARG_mosi].u_obj != MP_OBJ_NULL || @@ -294,22 +298,50 @@ static void machine_spi_deinit(mp_obj_base_t *self_in) { } } +static void machine_spi_poll_flag(SPI_Type *spi, uint32_t flag, uint32_t timeout) { + mp_uint_t tick_start = mp_hal_ticks_ms(); + while (!(spi->SPI_SR & flag)) { + if (mp_hal_ticks_ms() - tick_start >= timeout) { + mp_raise_OSError(MP_ETIMEDOUT); + } + mp_event_handle_nowait(); + } +} + static void machine_spi_transfer(mp_obj_base_t *self_in, size_t len, const uint8_t *src, uint8_t *dest) { machine_spi_obj_t *self = (machine_spi_obj_t *)self_in; - spi_transfer_t spi_xfer = { - .tx_buff = src, - .tx_total_cnt = len, - .rx_buff = dest, - .rx_total_cnt = len, - .tx_default_val = 0xFF, - .tx_default_enable = true, - .mode = SPI_TMOD_TX_AND_RX, - }; - // TODO redo transfer_blocking to timeout and poll events. - if (!self->is_lp) { - spi_transfer_blocking(self->inst, &spi_xfer); - } else { - lpspi_transfer_blocking(self->inst, &spi_xfer); + volatile uint32_t *dr = self->inst->SPI_DR; + + spi_set_tmod(self->inst, SPI_TMOD_TX_AND_RX); + + for (size_t i = 0; i < len; i++) { + // Wait for space in the TX FIFO + machine_spi_poll_flag(self->inst, SPI_SR_TFNF, 100); + + // Send data + if (src == NULL) { + *dr = 0xFFFFFFFFU; + } else if (self->bits > 16) { + *dr = ((uint32_t *)src)[i]; + } else if (self->bits > 8) { + *dr = ((uint16_t *)src)[i]; + } else { + *dr = ((uint8_t *)src)[i]; + } + + // Wait for data in the RX FIFO + machine_spi_poll_flag(self->inst, SPI_SR_RFNE, 100); + + // Recv data + if (dest == NULL) { + (void)*dr; + } else if (self->bits > 16) { + ((uint32_t *)dest)[i] = *dr; + } else if (self->bits > 8) { + ((uint16_t *)dest)[i] = *dr; + } else { + ((uint8_t *)dest)[i] = *dr; + } } } diff --git a/ports/alif/main.c b/ports/alif/main.c index fd35604cbc..ab5e85d5b9 100644 --- a/ports/alif/main.c +++ b/ports/alif/main.c @@ -55,8 +55,9 @@ extern uint8_t __StackTop, __StackLimit; extern uint8_t __GcHeapStart, __GcHeapEnd; +extern void machine_pin_irq_deinit(void); -NORETURN void panic(const char *msg) { +MP_NORETURN void panic(const char *msg) { mp_hal_stdout_tx_strn("\nFATAL ERROR:\n", 14); mp_hal_stdout_tx_strn(msg, strlen(msg)); for (;;) { @@ -164,6 +165,7 @@ int main(void) { mp_bluetooth_deinit(); #endif soft_timer_deinit(); + machine_pin_irq_deinit(); gc_sweep_all(); mp_deinit(); } diff --git a/ports/alif/modmachine.c b/ports/alif/modmachine.c index 9868abbeea..71b5be5193 100644 --- a/ports/alif/modmachine.c +++ b/ports/alif/modmachine.c @@ -47,11 +47,11 @@ static mp_obj_t mp_machine_unique_id(void) { return mp_obj_new_bytes(id, sizeof(id)); } -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { se_services_reset_soc(); } -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { __disable_irq(); MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); @@ -112,7 +112,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { #endif } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { #if MICROPY_HW_ENABLE_USBDEV mp_machine_enable_usb(false); #endif diff --git a/ports/alif/mpconfigport.h b/ports/alif/mpconfigport.h index 6c9e6af635..ddfc551bf8 100644 --- a/ports/alif/mpconfigport.h +++ b/ports/alif/mpconfigport.h @@ -158,19 +158,11 @@ #define MICROPY_FATFS_MAX_SS (MICROPY_HW_FLASH_BLOCK_SIZE_BYTES) #endif -#if MICROPY_PY_NETWORK_CYW43 -extern const struct _mp_obj_type_t mp_network_cyw43_type; -#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, -#else -#define MICROPY_HW_NIC_CYW43 -#endif - #ifndef MICROPY_BOARD_NETWORK_INTERFACES #define MICROPY_BOARD_NETWORK_INTERFACES #endif #define MICROPY_PORT_NETWORK_INTERFACES \ - MICROPY_HW_NIC_CYW43 \ MICROPY_BOARD_NETWORK_INTERFACES \ // Bluetooth code only runs in the scheduler, no locking/mutex required. diff --git a/ports/alif/mphalport.h b/ports/alif/mphalport.h index 4f81439b54..f03dc7c1c1 100644 --- a/ports/alif/mphalport.h +++ b/ports/alif/mphalport.h @@ -90,6 +90,9 @@ extern ringbuf_t stdin_ringbuf; #define MP_HAL_PIN_SPEED_LOW (0) #define MP_HAL_PIN_SPEED_HIGH (PADCTRL_SLEW_RATE_FAST) +#define MP_HAL_PIN_TRIGGER_FALL (1) +#define MP_HAL_PIN_TRIGGER_RISE (2) + #define mp_hal_pin_obj_t const machine_pin_obj_t * #define MP_HAL_PIN_ALT(function, unit) (MP_HAL_PIN_ALT_MAKE((MP_HAL_PIN_ALT_##function), (unit))) diff --git a/ports/cc3200/misc/mperror.c b/ports/cc3200/misc/mperror.c index 6d6c0ff0ba..69f0a25371 100644 --- a/ports/cc3200/misc/mperror.c +++ b/ports/cc3200/misc/mperror.c @@ -159,7 +159,7 @@ void mperror_heartbeat_signal (void) { } } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { #ifdef DEBUG if (msg != NULL) { // wait for 20ms diff --git a/ports/cc3200/misc/mperror.h b/ports/cc3200/misc/mperror.h index 1c3eb62697..30effbbf91 100644 --- a/ports/cc3200/misc/mperror.h +++ b/ports/cc3200/misc/mperror.h @@ -27,7 +27,7 @@ #ifndef MICROPY_INCLUDED_CC3200_MISC_MPERROR_H #define MICROPY_INCLUDED_CC3200_MISC_MPERROR_H -extern void NORETURN __fatal_error(const char *msg); +extern void MP_NORETURN __fatal_error(const char *msg); void mperror_init0 (void); void mperror_bootloader_check_reset_cause (void); diff --git a/ports/cc3200/mods/modmachine.c b/ports/cc3200/mods/modmachine.c index f7184df442..8986635ac6 100644 --- a/ports/cc3200/mods/modmachine.c +++ b/ports/cc3200/mods/modmachine.c @@ -93,7 +93,7 @@ extern OsiTaskHandle xSimpleLinkSpawnTaskHndl; /******************************************************************************/ // MicroPython bindings; -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { // disable wlan wlan_stop(SL_STOP_TIMEOUT_LONG); // reset the cpu and it's peripherals @@ -161,7 +161,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { pyb_sleep_sleep(); } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { pyb_sleep_deepsleep(); for (;;) { } diff --git a/ports/cc3200/mods/modtime.c b/ports/cc3200/mods/modtime.c index 21388568ab..254678fb2d 100644 --- a/ports/cc3200/mods/modtime.c +++ b/ports/cc3200/mods/modtime.c @@ -50,5 +50,5 @@ static mp_obj_t mp_time_localtime_get(void) { // Returns the number of seconds, as an integer, since the Epoch. static mp_obj_t mp_time_time_get(void) { - return mp_obj_new_int(pyb_rtc_get_seconds()); + return timeutils_obj_from_timestamp(pyb_rtc_get_seconds()); } diff --git a/ports/cc3200/mods/pybsleep.c b/ports/cc3200/mods/pybsleep.c index 9d04dd411d..291860de8d 100644 --- a/ports/cc3200/mods/pybsleep.c +++ b/ports/cc3200/mods/pybsleep.c @@ -136,7 +136,7 @@ static MP_DEFINE_CONST_OBJ_TYPE( ******************************************************************************/ static pyb_sleep_obj_t *pyb_sleep_find (mp_obj_t obj); static void pyb_sleep_flash_powerdown (void); -static NORETURN void pyb_sleep_suspend_enter (void); +static MP_NORETURN void pyb_sleep_suspend_enter (void); void pyb_sleep_suspend_exit (void); static void pyb_sleep_obj_wakeup (void); static void PRCMInterruptHandler (void); @@ -360,7 +360,7 @@ static void pyb_sleep_flash_powerdown (void) { MAP_SPICSDisable(SSPI_BASE); } -static NORETURN void pyb_sleep_suspend_enter (void) { +static MP_NORETURN void pyb_sleep_suspend_enter (void) { // enable full RAM retention MAP_PRCMSRAMRetentionEnable(PRCM_SRAM_COL_1 | PRCM_SRAM_COL_2 | PRCM_SRAM_COL_3 | PRCM_SRAM_COL_4, PRCM_SRAM_LPDS_RET); diff --git a/ports/esp32/README.md b/ports/esp32/README.md index d9115072a4..d8b55e45f3 100644 --- a/ports/esp32/README.md +++ b/ports/esp32/README.md @@ -31,7 +31,7 @@ manage the ESP32 microcontroller, as well as a way to manage the required build environment and toolchains needed to build the firmware. The ESP-IDF changes quickly and MicroPython only supports certain versions. -Currently MicroPython supports v5.2, v5.2.2, v5.3 and v5.4. +Currently MicroPython supports v5.2, v5.2.2, v5.3, v5.4 and v5.4.1. To install the ESP-IDF the full instructions can be found at the [Espressif Getting Started guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html#installation-step-by-step). @@ -49,10 +49,10 @@ The steps to take are summarised below. To check out a copy of the IDF use git clone: ```bash -$ git clone -b v5.2.2 --recursive https://github.com/espressif/esp-idf.git +$ git clone -b v5.4.1 --recursive https://github.com/espressif/esp-idf.git ``` -You can replace `v5.2.2` with any other supported version. +You can replace `v5.4.1` with any other supported version. (You don't need a full recursive clone; see the `ci_esp32_setup` function in `tools/ci.sh` in this repository for more detailed set-up commands.) @@ -61,7 +61,7 @@ MicroPython and update the submodules using: ```bash $ cd esp-idf -$ git checkout v5.2.2 +$ git checkout v5.4.1 $ git submodule update --init --recursive ``` @@ -221,6 +221,30 @@ import machine antenna = machine.Pin(16, machine.Pin.OUT, value=0) ``` +Partition table and filesystem size +----------------------------------- + +ESP32 firmware contains a bootloader, partition table and main application +firmware, which are all stored in (external) SPI flash. The user filesystem +is also stored in the same SPI flash. By default, MicroPython does not have +a fixed entry in the ESP32 partition table for the filesystem. Instead it +will automatically determine the size of the SPI flash upon boot, and then -- +so long as there is no existing partition called "vfs" or "ffat" -- it will +create a partition for the filesystem called "vfs" which takes all of the +remaining flash between the end of the last defined partition up until the +end of flash. + +This means that firmware built for, say, a 4MiB flash will work on any +device that has at least 4MiB flash. The user "vfs" filesystem will then +take up as much space as possible. + +This auto-detection behaviour can be overridden: if the ESP32 partition +table does contain an entry called "vfs" or "ffat" then these are used for +the user filesystem and no automatic "vfs" partition is added. This is +useful in cases where only the MicroPython ESP32 application is flashed to +the device (and not the bootloader or partition table), eg when deploying +.uf2 files. + Defining a custom ESP32 board ----------------------------- diff --git a/ports/esp32/adc.c b/ports/esp32/adc.c index 83af6dce0b..d0e5e81330 100644 --- a/ports/esp32/adc.c +++ b/ports/esp32/adc.c @@ -26,83 +26,73 @@ */ #include "py/mphal.h" +#include "py/mperrno.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_cali_scheme.h" -#define DEFAULT_VREF 1100 +static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten); -void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits) { - if (bits < SOC_ADC_RTC_MIN_BITWIDTH && bits > SOC_ADC_RTC_MAX_BITWIDTH) { - // Invalid value for the current chip, raise exception in the switch below. - bits = -1; +void adc_is_init_guard(machine_adc_block_obj_t *self) { + if (!self->handle) { + mp_raise_OSError(MP_EPERM); } - switch (bits) { - case 9: - self->width = ADC_BITWIDTH_9; - break; - case 10: - self->width = ADC_BITWIDTH_10; - break; - case 11: - self->width = ADC_BITWIDTH_11; - break; - case 12: - self->width = ADC_BITWIDTH_12; - break; - case 13: - self->width = ADC_BITWIDTH_13; - break; - default: - mp_raise_ValueError(MP_ERROR_TEXT("invalid bits")); - } - self->bits = bits; +} - if (self->unit_id == ADC_UNIT_1) { - adc1_config_width(self->width); - } +esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten) { + adc_is_init_guard(self->block); + + adc_oneshot_chan_cfg_t config = { + .atten = atten, + .bitwidth = self->block->bitwidth, + }; + esp_err_t ret = adc_oneshot_config_channel(self->block->handle, self->channel_id, &config); + return ret; } mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id) { - int raw = 0; - if (self->unit_id == ADC_UNIT_1) { - raw = adc1_get_raw(channel_id); - } else { - #if (SOC_ADC_PERIPH_NUM >= 2) - check_esp_err(adc2_get_raw(channel_id, self->width, &raw)); - #endif - } - return raw; + adc_is_init_guard(self); + + int reading = 0; + adc_oneshot_read(self->handle, channel_id, &reading); + return reading; +} + +/* +During testing, it turned out that the function `adc_cali_raw_to_voltage` does not account for the lower resolution, +instead it expects the full resolution value as an argument, hence the scaling applied here +*/ +mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { + int raw = madcblock_read_helper(self, channel_id); + int uv = 0; + + check_esp_err(ensure_adc_calibration(self, atten)); + check_esp_err(adc_cali_raw_to_voltage(self->calib[atten], (raw << (ADC_WIDTH_MAX - self->bitwidth)), &uv)); + return (mp_int_t)uv * 1000; } static esp_err_t ensure_adc_calibration(machine_adc_block_obj_t *self, adc_atten_t atten) { - if (self->handle[atten] != NULL) { + if (self->calib[atten] != NULL) { return ESP_OK; } + esp_err_t ret = ESP_OK; + #if ADC_CALI_SCHEME_CURVE_FITTING_SUPPORTED adc_cali_curve_fitting_config_t cali_config = { .unit_id = self->unit_id, .atten = atten, - .bitwidth = self->width, + .bitwidth = self->bitwidth, }; - return adc_cali_create_scheme_curve_fitting(&cali_config, &self->handle[atten]); + ret = adc_cali_create_scheme_curve_fitting(&cali_config, &self->calib[atten]); #else adc_cali_line_fitting_config_t cali_config = { .unit_id = self->unit_id, .atten = atten, - .bitwidth = self->width, + .bitwidth = self->bitwidth, }; - return adc_cali_create_scheme_line_fitting(&cali_config, &self->handle[atten]); + ret = adc_cali_create_scheme_line_fitting(&cali_config, &self->calib[atten]); #endif -} -mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten) { - int raw = madcblock_read_helper(self, channel_id); - int uv; - - check_esp_err(ensure_adc_calibration(self, atten)); - check_esp_err(adc_cali_raw_to_voltage(self->handle[atten], raw, &uv)); - - return (mp_int_t)uv * 1000; + return ret; } diff --git a/ports/esp32/adc.h b/ports/esp32/adc.h index 5688e0a29a..ebf7fcc21a 100644 --- a/ports/esp32/adc.h +++ b/ports/esp32/adc.h @@ -29,17 +29,45 @@ #define MICROPY_INCLUDED_ESP32_ADC_H #include "py/runtime.h" -#include "esp_adc_cal.h" +#include "esp_adc/adc_oneshot.h" #include "esp_adc/adc_cali_scheme.h" -#define ADC_ATTEN_MAX SOC_ADC_ATTEN_NUM +#define ADC_ATTEN_COUNT SOC_ADC_ATTEN_NUM +#define ADC_ATTEN_MIN ADC_ATTEN_DB_0 +#define ADC_ATTEN_MAX ADC_ATTEN_DB_11 + +/* +https://github.com/espressif/esp-idf/issues/13128 +https://github.com/espressif/esp-idf/blob/release/v5.2/components/soc/esp32s3/include/soc/soc_caps.h +https://docs.espressif.com/projects/esp-chip-errata/en/latest/esp32s2/03-errata-description/index.html +https://docs.espressif.com/projects/esp-idf/en/v4.2/esp32/api-reference/peripherals/adc.html + +Looks like only the original esp32 is capable of bitwidth adjustment, all others are stuck at 12 bits, +except the S2, which is locked at 13 bits, otherwise attenuation doesn't work. +*/ +#if CONFIG_IDF_TARGET_ESP32S2 + +#define ADC_WIDTH_MIN ADC_BITWIDTH_13 +#define ADC_WIDTH_MAX ADC_BITWIDTH_13 + +#elif CONFIG_IDF_TARGET_ESP32 + +#define ADC_WIDTH_MIN ADC_BITWIDTH_9 +#define ADC_WIDTH_MAX ADC_BITWIDTH_12 + +#else + +#define ADC_WIDTH_MIN ADC_BITWIDTH_12 +#define ADC_WIDTH_MAX ADC_BITWIDTH_12 + +#endif typedef struct _machine_adc_block_obj_t { mp_obj_base_t base; adc_unit_t unit_id; - mp_int_t bits; - adc_bits_width_t width; - adc_cali_handle_t handle[ADC_ATTEN_MAX]; + adc_oneshot_unit_handle_t handle; + adc_bitwidth_t bitwidth; + adc_cali_handle_t calib[ADC_ATTEN_COUNT]; } machine_adc_block_obj_t; typedef struct _machine_adc_obj_t { @@ -51,11 +79,18 @@ typedef struct _machine_adc_obj_t { extern machine_adc_block_obj_t madcblock_obj[]; -void madcblock_bits_helper(machine_adc_block_obj_t *self, mp_int_t bits); +esp_err_t apply_self_adc_channel_atten(const machine_adc_obj_t *self, uint8_t atten); + mp_int_t madcblock_read_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id); mp_int_t madcblock_read_uv_helper(machine_adc_block_obj_t *self, adc_channel_t channel_id, adc_atten_t atten); const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id); void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self); +void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten); + +mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self); +void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width); + #endif // MICROPY_INCLUDED_ESP32_ADC_H diff --git a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json index 9d0016017f..936a498b5e 100644 --- a/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json +++ b/ports/esp32/boards/ARDUINO_NANO_ESP32/board.json @@ -17,8 +17,8 @@ "ABX00092_01.iso_1000x750.jpg" ], "mcu": "esp32s3", - "product": "Arduino Nano ESP32", + "product": "Nano ESP32", "thumbnail": "", - "url": "https://store.arduino.cc/products/arduino-nano-esp32", + "url": "https://docs.arduino.cc/hardware/nano-esp32/", "vendor": "Arduino" } diff --git a/ports/esp32/boards/ESP32_GENERIC/board.md b/ports/esp32/boards/ESP32_GENERIC/board.md index 9346d18d84..06972e436f 100644 --- a/ports/esp32/boards/ESP32_GENERIC/board.md +++ b/ports/esp32/boards/ESP32_GENERIC/board.md @@ -1,5 +1,5 @@ The following files are firmware that should work on most ESP32-based boards -with 4MiB of flash, including WROOM WROVER, SOLO, PICO, and MINI modules. +with 4MiB or more of flash, including WROOM WROVER, SOLO, PICO, and MINI modules. This board has multiple variants available: diff --git a/ports/esp32/boards/ESP32_GENERIC_C3/board.md b/ports/esp32/boards/ESP32_GENERIC_C3/board.md index 1604b879c5..6097eb85df 100644 --- a/ports/esp32/boards/ESP32_GENERIC_C3/board.md +++ b/ports/esp32/boards/ESP32_GENERIC_C3/board.md @@ -1,6 +1,6 @@ The following files are firmware images that should work on most -ESP32-C3-based boards with 4MiB of flash, including WROOM and MINI modules, -that use the revision 3 silicon (or newer). +ESP32-C3-based boards with 4MiB or more of flash, including WROOM and MINI +modules, that use the revision 3 silicon (or newer). USB serial/JTAG support is enabled on pin 18 and 19. Note that this is not a full USB stack, the C3 just provides a CDC/ACM class serial diff --git a/ports/esp32/boards/ESP32_GENERIC_S2/board.md b/ports/esp32/boards/ESP32_GENERIC_S2/board.md index 3e46c02467..d065ecbbb9 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S2/board.md +++ b/ports/esp32/boards/ESP32_GENERIC_S2/board.md @@ -1,5 +1,5 @@ The following files are firmware that should work on most ESP32-S2-based -boards with 4MiB of flash, including WROOM, WROVER, and MINI modules. +boards with 4MiB or more of flash, including WROOM, WROVER, and MINI modules. This firmware supports configurations with and without SPIRAM (also known as PSRAM) and will auto-detect a connected SPIRAM chip at startup and allocate diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.json b/ports/esp32/boards/ESP32_GENERIC_S3/board.json index fd0c9edce0..7a546d35fc 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/board.json +++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.json @@ -21,7 +21,6 @@ "url": "https://www.espressif.com/en/products/modules", "vendor": "Espressif", "variants": { - "SPIRAM_OCT": "Support for Octal-SPIRAM", - "FLASH_4M": "4MiB flash" + "SPIRAM_OCT": "Support for Octal-SPIRAM" } } diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/board.md b/ports/esp32/boards/ESP32_GENERIC_S3/board.md index 16930c9193..68ce0917e0 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/board.md +++ b/ports/esp32/boards/ESP32_GENERIC_S3/board.md @@ -1,9 +1,7 @@ The following files are firmware that should work on most ESP32-S3-based -boards with 4/8MiB of flash, including WROOM and MINI modules. +boards with 4MiB or more of flash, including WROOM and MINI modules. This firmware supports configurations with and without SPIRAM (also known as PSRAM) and will auto-detect a connected SPIRAM chip at startup and allocate the MicroPython heap accordingly. However if your board has Octal SPIRAM, then use the "spiram-oct" variant. - -If your board has 4MiB flash (including ESP32-S3FH4R2 based ones with embedded flash), then use the "flash-4m" build. diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake b/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake deleted file mode 100644 index e832cdb60f..0000000000 --- a/ports/esp32/boards/ESP32_GENERIC_S3/mpconfigvariant_FLASH_4M.cmake +++ /dev/null @@ -1,4 +0,0 @@ -set(SDKCONFIG_DEFAULTS - ${SDKCONFIG_DEFAULTS} - boards/ESP32_GENERIC_S3/sdkconfig.flash_4m -) diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board index 9839b0d300..369330682f 100644 --- a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board +++ b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.board @@ -1,8 +1,2 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y - -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" diff --git a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m b/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m deleted file mode 100644 index c967c46ae6..0000000000 --- a/ports/esp32/boards/ESP32_GENERIC_S3/sdkconfig.flash_4m +++ /dev/null @@ -1,4 +0,0 @@ -CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= - -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv" diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json new file mode 100644 index 0000000000..d2ada7cccd --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/board.json @@ -0,0 +1,23 @@ +{ + "deploy": [ + "../deploy_nativeusb.md" + ], + "deploy_options": { + "flash_offset": "0" + }, + "docs": "", + "features": [ + "BLE", + "External Flash", + "RGB LED", + "WiFi" + ], + "images": [ + "GAR-PYBSTICK26-C3-mUSB-00.JPG" + ], + "mcu": "esp32c3", + "product": "PYBSTICK26_ESP32C3", + "thumbnail": "", + "url": "https://shop.mchobby.be/fr/pybstick/2505-pybstick26-esp32-c3-micropython-et-arduino-3232100025059.html", + "vendor": "McHobby" +} diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake new file mode 100644 index 0000000000..81cfff1d77 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.cmake @@ -0,0 +1,7 @@ +set(IDF_TARGET esp32c3) + +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board +) diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h new file mode 100644 index 0000000000..c9796d1f65 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/mpconfigboard.h @@ -0,0 +1,10 @@ +#define MICROPY_HW_BOARD_NAME "PYBSTICK26_ESP32C3" +#define MICROPY_HW_MCU_NAME "ESP32C3" +#define MICROPY_PY_NETWORK_HOSTNAME_DEFAULT "pybstick26_esp32c3" + +#define MICROPY_HW_I2C0_SCL (1) +#define MICROPY_HW_I2C0_SDA (0) + +#define MICROPY_HW_SPI1_MOSI (7) +#define MICROPY_HW_SPI1_MISO (2) +#define MICROPY_HW_SPI1_SCK (6) diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv new file mode 100644 index 0000000000..d786654903 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/pins.csv @@ -0,0 +1,13 @@ +GP0,GPIO0 +GP1,GPIO1 +GP2,GPIO2 +GP3,GPIO3 +GP4,GPIO4 +GP5,GPIO5 +GP6,GPIO6 +GP7,GPIO7 +GP8,GPIO8 +GP9,GPIO9 +GP10,GPIO10 +GP20,GPIO20 +GP21,GPIO21 diff --git a/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board new file mode 100644 index 0000000000..70e5f1f0d1 --- /dev/null +++ b/ports/esp32/boards/GARATRONIC_PYBSTICK26_ESP32C3/sdkconfig.board @@ -0,0 +1,4 @@ +CONFIG_ESP32C3_REV_MIN_3=y + +# Workaround for https://github.com/espressif/esp-idf/issues/14456 +CONFIG_ESP_SYSTEM_HW_STACK_GUARD=n diff --git a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json index d68a9baadd..7521c01a6b 100644 --- a/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json +++ b/ports/esp32/boards/LILYGO_TTGO_LORA32/board.json @@ -19,8 +19,8 @@ "lilygo-ttgo-lora-32-v1-6.jpg" ], "mcu": "esp32", - "product": "LILYGO TTGO LoRa32", + "product": "TTGO LoRa32", "thumbnail": "", - "url": "http://www.lilygo.cn/prod_view.aspx?TypeId=50060&Id=1270&FId=t3:50060:3", + "url": "https://www.lilygo.cc/products/lora3", "vendor": "LILYGO" } diff --git a/ports/esp32/boards/M5STACK_ATOM/board.json b/ports/esp32/boards/M5STACK_ATOM/board.json index 3a1e7ce359..c3908a4779 100644 --- a/ports/esp32/boards/M5STACK_ATOM/board.json +++ b/ports/esp32/boards/M5STACK_ATOM/board.json @@ -18,8 +18,8 @@ "m5stack_atom.jpg" ], "mcu": "esp32", - "product": "M5 Stack Atom", + "product": "Atom", "thumbnail": "", - "url": "https://m5stack.com/", - "vendor": "M5 Stack" + "url": "https://shop.m5stack.com/products/atom-matrix-esp32-development-kit", + "vendor": "M5Stack" } diff --git a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json index fe0e97f9f9..26b1a4d54a 100644 --- a/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json +++ b/ports/esp32/boards/M5STACK_ATOMS3_LITE/board.json @@ -20,5 +20,5 @@ "product": "AtomS3 Lite", "thumbnail": "", "url": "https://shop.m5stack.com/products/atoms3-lite-esp32s3-dev-kit", - "vendor": "M5 Stack" + "vendor": "M5Stack" } diff --git a/ports/esp32/boards/M5STACK_NANOC6/board.json b/ports/esp32/boards/M5STACK_NANOC6/board.json index 087851ae5e..ddf3406c83 100644 --- a/ports/esp32/boards/M5STACK_NANOC6/board.json +++ b/ports/esp32/boards/M5STACK_NANOC6/board.json @@ -18,7 +18,7 @@ "m5stack_nanoc6.jpg" ], "mcu": "esp32c6", - "product": "M5Stack NanoC6", + "product": "NanoC6", "url": "https://shop.m5stack.com/products/m5stack-nanoc6-dev-kit", "vendor": "M5Stack" } diff --git a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json index 3eb9a5e0a7..198f1d4b6d 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json +++ b/ports/esp32/boards/OLIMEX_ESP32_EVB/board.json @@ -18,8 +18,8 @@ "ESP32-EVB_Rev_K1.png" ], "mcu": "esp32", - "product": "Olimex ESP32 EVB", + "product": "ESP32 EVB", "thumbnail": "", - "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB/open-source-hardware", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-EVB", + "vendor": "Olimex" } diff --git a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json index cda26178d7..1b00d213c7 100644 --- a/ports/esp32/boards/OLIMEX_ESP32_POE/board.json +++ b/ports/esp32/boards/OLIMEX_ESP32_POE/board.json @@ -19,8 +19,8 @@ "ESP32-POE-ISO-1.jpg" ], "mcu": "esp32", - "product": "Olimex ESP32 POE", + "product": "ESP32 POE", "thumbnail": "", - "url": "https://www.olimex.com/", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/IoT/ESP32/ESP32-POE", + "vendor": "Olimex" } diff --git a/ports/esp32/boards/SIL_WESP32/board.json b/ports/esp32/boards/SIL_WESP32/board.json index 53a50f3286..7126fa7ea1 100644 --- a/ports/esp32/boards/SIL_WESP32/board.json +++ b/ports/esp32/boards/SIL_WESP32/board.json @@ -18,7 +18,7 @@ "wesp32-top.jpg" ], "mcu": "esp32", - "product": "SIL WESP32", + "product": "wESP32", "thumbnail": "", "url": "https://wesp32.com/", "vendor": "Silicognition" diff --git a/ports/esp32/boards/SIL_WESP32/sdkconfig.board b/ports/esp32/boards/SIL_WESP32/sdkconfig.board index 741a4db439..36155cd5e3 100644 --- a/ports/esp32/boards/SIL_WESP32/sdkconfig.board +++ b/ports/esp32/boards/SIL_WESP32/sdkconfig.board @@ -1,9 +1,6 @@ -# 16 MB flash +# 8 MB+ flash -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y -CONFIG_ESPTOOLPY_FLASHSIZE="16MB" +CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y # Fast flash @@ -14,7 +11,7 @@ CONFIG_ESP32_REV_MIN_1=y # OTA CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB-ota.csv" +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiBplus-ota.csv" # Network name diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json new file mode 100644 index 0000000000..8bce1a0013 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/board.json @@ -0,0 +1,23 @@ +{ + "deploy": [ + "../deploy.md" + ], + "deploy_options": { + "flash_offset": "0x1000" + }, + "docs": "", + "features": [ + "BLE", + "External Flash", + "WiFi", + "USB-C" + ], + "images": [ + "19177-Sparkfun_IoT_Redboard-ESP32.jpg" + ], + "mcu": "esp32", + "product": "ESP32 / WROOM", + "thumbnail": "", + "url": "https://www.sparkfun.com/sparkfun-iot-redboard-esp32-development-board.html", + "vendor": "SparkFun" +} diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py new file mode 100644 index 0000000000..73446ecac9 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/manifest.py @@ -0,0 +1,2 @@ +include("$(PORT_DIR)/boards/manifest.py") +require("sdcard") diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake new file mode 100644 index 0000000000..0ffcc38c87 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.cmake @@ -0,0 +1,7 @@ +set(SDKCONFIG_DEFAULTS + boards/sdkconfig.base + boards/sdkconfig.ble + boards/sdkconfig.240mhz +) + +set(MICROPY_FROZEN_MANIFEST ${MICROPY_BOARD_DIR}/manifest.py) diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h new file mode 100644 index 0000000000..4fe888ac17 --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/mpconfigboard.h @@ -0,0 +1,14 @@ +// Board and hardware specific configuration + +#define MICROPY_HW_BOARD_NAME "SparkFun IoT RedBoard ESP32" +#define MICROPY_HW_MCU_NAME "ESP32" + +// Enable UART REPL for modules that have an external USB-UART and don't use native USB. +#define MICROPY_HW_ENABLE_UART_REPL (1) + +#define MICROPY_HW_I2C0_SCL (22) +#define MICROPY_HW_I2C0_SDA (21) + +#define MICROPY_HW_SPI1_SCK (18) +#define MICROPY_HW_SPI1_MOSI (23) +#define MICROPY_HW_SPI1_MISO (19) diff --git a/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv new file mode 100644 index 0000000000..d37c51af8f --- /dev/null +++ b/ports/esp32/boards/SPARKFUN_IOT_REDBOARD_ESP32/pins.csv @@ -0,0 +1,22 @@ +TX,GPIO1 +RX,GPIO3 +ALERT,GPIO4 +TCK,GPIO13 +TMS,GPIO14 +CS,GPIO5 +PICO,GPIO23 +POCI,GPIO19 +SCK,GPIO18 +SDA,GPIO21 +SCL,GPIO22 +A0,GPIO36 +A3,GPIO39 +A4,GPIO32 +A5,GPIO33 +A6,GPIO34 +A7,GPIO35 +LED,GPIO18 +LED_BLUE,GPIO18 +BLUE_LED,GPIO18 +RGB_LED,GPIO2 +NEOPIXEL,GPIO2 diff --git a/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board index 9ab58f215f..162de0da14 100644 --- a/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board +++ b/ports/esp32/boards/UM_FEATHERS2/sdkconfig.board @@ -3,11 +3,6 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_SPIRAM_MEMTEST= -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv" - # LWIP CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS2" # end of LWIP diff --git a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board index 3ca0c4b243..3092e35598 100644 --- a/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board +++ b/ports/esp32/boards/UM_FEATHERS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3" diff --git a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board index d0355a94dc..25b8d7689d 100644 --- a/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board +++ b/ports/esp32/boards/UM_FEATHERS3NEO/sdkconfig.board @@ -3,11 +3,6 @@ CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y CONFIG_SPIRAM_MEMTEST= -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMFeatherS3Neo" diff --git a/ports/esp32/boards/UM_NANOS3/sdkconfig.board b/ports/esp32/boards/UM_NANOS3/sdkconfig.board index 2a39c64337..e06f7a4245 100644 --- a/ports/esp32/boards/UM_NANOS3/sdkconfig.board +++ b/ports/esp32/boards/UM_NANOS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMNanoS3" diff --git a/ports/esp32/boards/UM_OMGS3/sdkconfig.board b/ports/esp32/boards/UM_OMGS3/sdkconfig.board index 84a8ce449e..8a0bf0b13a 100644 --- a/ports/esp32/boards/UM_OMGS3/sdkconfig.board +++ b/ports/esp32/boards/UM_OMGS3/sdkconfig.board @@ -2,12 +2,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMOMGS3" diff --git a/ports/esp32/boards/UM_PROS3/sdkconfig.board b/ports/esp32/boards/UM_PROS3/sdkconfig.board index 5752e03e60..75ca58d80b 100644 --- a/ports/esp32/boards/UM_PROS3/sdkconfig.board +++ b/ports/esp32/boards/UM_PROS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB= -CONFIG_ESPTOOLPY_FLASHSIZE_16MB=y CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-16MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMProS3" diff --git a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board index ef3f38af47..7d244fdc16 100644 --- a/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board +++ b/ports/esp32/boards/UM_RGBTOUCH_MINI/sdkconfig.board @@ -2,12 +2,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMRGBTouchMini" diff --git a/ports/esp32/boards/UM_TINYC6/sdkconfig.board b/ports/esp32/boards/UM_TINYC6/sdkconfig.board index 7917467b12..213d28d8b4 100644 --- a/ports/esp32/boards/UM_TINYC6/sdkconfig.board +++ b/ports/esp32/boards/UM_TINYC6/sdkconfig.board @@ -2,9 +2,4 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y CONFIG_ESPTOOLPY_AFTER_NORESET=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" diff --git a/ports/esp32/boards/UM_TINYS3/sdkconfig.board b/ports/esp32/boards/UM_TINYS3/sdkconfig.board index d1d19761a0..2474c5fb27 100644 --- a/ports/esp32/boards/UM_TINYS3/sdkconfig.board +++ b/ports/esp32/boards/UM_TINYS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyS3" diff --git a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board index 1380e15ce8..10121d235d 100644 --- a/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board +++ b/ports/esp32/boards/UM_TINYWATCHS3/sdkconfig.board @@ -1,12 +1,7 @@ CONFIG_ESPTOOLPY_FLASHMODE_QIO=y CONFIG_ESPTOOLPY_FLASHFREQ_80M=y -CONFIG_ESPTOOLPY_FLASHSIZE_4MB= -CONFIG_ESPTOOLPY_FLASHSIZE_8MB=y -CONFIG_ESPTOOLPY_FLASHSIZE_16MB= CONFIG_SPIRAM_MEMTEST= -CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-8MiB.csv" CONFIG_LWIP_LOCAL_HOSTNAME="UMTinyWATCHS3" diff --git a/ports/esp32/boards/sdkconfig.base b/ports/esp32/boards/sdkconfig.base index 530db42711..30740af434 100644 --- a/ports/esp32/boards/sdkconfig.base +++ b/ports/esp32/boards/sdkconfig.base @@ -50,7 +50,6 @@ CONFIG_ESP_SYSTEM_MEMPROT_FEATURE=n CONFIG_FREERTOS_THREAD_LOCAL_STORAGE_POINTERS=2 CONFIG_FREERTOS_SUPPORT_STATIC_ALLOCATION=y CONFIG_FREERTOS_ENABLE_STATIC_TASK_CLEAN_UP=n -CONFIG_FREERTOS_TASK_PRE_DELETION_HOOK=y # UDP CONFIG_LWIP_PPP_SUPPORT=y @@ -98,7 +97,7 @@ CONFIG_ULP_COPROC_RESERVE_MEM=2040 # For cmake build CONFIG_ESPTOOLPY_FLASHSIZE_4MB=y CONFIG_PARTITION_TABLE_CUSTOM=y -CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiB.csv" +CONFIG_PARTITION_TABLE_CUSTOM_FILENAME="partitions-4MiBplus.csv" # To reduce iRAM usage CONFIG_ESP32_WIFI_IRAM_OPT=n @@ -118,7 +117,6 @@ CONFIG_ADC_CAL_LUT_ENABLE=y CONFIG_UART_ISR_IN_IRAM=y # IDF 5 deprecated -CONFIG_ADC_SUPPRESS_DEPRECATE_WARN=y CONFIG_RMT_SUPPRESS_DEPRECATE_WARN=y CONFIG_ETH_USE_SPI_ETHERNET=y diff --git a/ports/esp32/esp32_common.cmake b/ports/esp32/esp32_common.cmake index c54f5a5403..09b1203913 100644 --- a/ports/esp32/esp32_common.cmake +++ b/ports/esp32/esp32_common.cmake @@ -80,9 +80,7 @@ list(APPEND MICROPY_SOURCE_DRIVERS ${MICROPY_DIR}/drivers/dht/dht.c ) -list(APPEND GIT_SUBMODULES lib/tinyusb) if(MICROPY_PY_TINYUSB) - set(TINYUSB_SRC "${MICROPY_DIR}/lib/tinyusb/src") string(TOUPPER OPT_MCU_${IDF_TARGET} tusb_mcu) list(APPEND MICROPY_DEF_TINYUSB @@ -90,12 +88,6 @@ if(MICROPY_PY_TINYUSB) ) list(APPEND MICROPY_SOURCE_TINYUSB - ${TINYUSB_SRC}/tusb.c - ${TINYUSB_SRC}/common/tusb_fifo.c - ${TINYUSB_SRC}/device/usbd.c - ${TINYUSB_SRC}/device/usbd_control.c - ${TINYUSB_SRC}/class/cdc/cdc_device.c - ${TINYUSB_SRC}/portable/synopsys/dwc2/dcd_dwc2.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd_cdc.c ${MICROPY_DIR}/shared/tinyusb/mp_usbd_descriptor.c @@ -103,13 +95,8 @@ if(MICROPY_PY_TINYUSB) ) list(APPEND MICROPY_INC_TINYUSB - ${TINYUSB_SRC} ${MICROPY_DIR}/shared/tinyusb/ ) - - list(APPEND MICROPY_LINK_TINYUSB - -Wl,--wrap=dcd_event_handler - ) endif() list(APPEND MICROPY_SOURCE_PORT @@ -251,6 +238,7 @@ endif() # Set compile options for this port. target_compile_definitions(${MICROPY_TARGET} PUBLIC + ${MICROPY_DEF_COMPONENT} ${MICROPY_DEF_CORE} ${MICROPY_DEF_BOARD} ${MICROPY_DEF_TINYUSB} @@ -263,15 +251,12 @@ target_compile_definitions(${MICROPY_TARGET} PUBLIC # Disable some warnings to keep the build output clean. target_compile_options(${MICROPY_TARGET} PUBLIC + ${MICROPY_COMPILE_COMPONENT} -Wno-clobbered -Wno-deprecated-declarations -Wno-missing-field-initializers ) -target_link_options(${MICROPY_TARGET} PUBLIC - ${MICROPY_LINK_TINYUSB} -) - # Additional include directories needed for private NimBLE headers. target_include_directories(${MICROPY_TARGET} PUBLIC ${IDF_PATH}/components/bt/host/nimble/nimble diff --git a/ports/esp32/machine_adc.c b/ports/esp32/machine_adc.c index be1725c370..02acaa22da 100644 --- a/ports/esp32/machine_adc.c +++ b/ports/esp32/machine_adc.c @@ -30,7 +30,7 @@ #include "py/mphal.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" #define ADCBLOCK1 (&madcblock_obj[0]) #define ADCBLOCK2 (&madcblock_obj[1]) @@ -126,20 +126,8 @@ static const machine_adc_obj_t madc_obj[] = { #endif }; -// These values are initialised to 0, which means the corresponding ADC channel is not initialised. -// The madc_atten_get/madc_atten_set functions store (atten+1) here so that the uninitialised state -// can be distinguished from the initialised state. static uint8_t madc_obj_atten[MP_ARRAY_SIZE(madc_obj)]; -static inline adc_atten_t madc_atten_get(const machine_adc_obj_t *self) { - uint8_t value = madc_obj_atten[self - &madc_obj[0]]; - return value == 0 ? ADC_ATTEN_MAX : value - 1; -} - -static inline void madc_atten_set(const machine_adc_obj_t *self, adc_atten_t atten) { - madc_obj_atten[self - &madc_obj[0]] = atten + 1; -} - const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_channel_t channel_id, gpio_num_t gpio_id) { for (int i = 0; i < MP_ARRAY_SIZE(madc_obj); i++) { const machine_adc_obj_t *adc = &madc_obj[i]; @@ -152,22 +140,7 @@ const machine_adc_obj_t *madc_search_helper(machine_adc_block_obj_t *block, adc_ static void mp_machine_adc_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { const machine_adc_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, madc_atten_get(self)); -} - -static void madc_atten_helper(const machine_adc_obj_t *self, mp_int_t atten) { - esp_err_t err = ESP_FAIL; - if (self->block->unit_id == ADC_UNIT_1) { - err = adc1_config_channel_atten(self->channel_id, atten); - } else { - #if SOC_ADC_PERIPH_NUM >= 2 - err = adc2_config_channel_atten(self->channel_id, atten); - #endif - } - if (err != ESP_OK) { - mp_raise_ValueError(MP_ERROR_TEXT("invalid atten")); - } - madc_atten_set(self, atten); + mp_printf(print, "ADC(Pin(%u), atten=%u)", self->gpio_id, mp_machine_adc_atten_get_helper(self)); } void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { @@ -182,18 +155,32 @@ void madc_init_helper(const machine_adc_obj_t *self, size_t n_pos_args, const mp mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_pos_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - mp_int_t atten = args[ARG_atten].u_int; - if (atten != -1) { - madc_atten_helper(self, atten); - } else if (madc_atten_get(self) == ADC_ATTEN_MAX) { - madc_atten_helper(self, ADC_ATTEN_DB_0); + + if (!self->block->handle) { + adc_oneshot_unit_init_cfg_t init_config = { + .unit_id = self->block->unit_id + }; + check_esp_err(adc_oneshot_new_unit(&init_config, &self->block->handle)); } + + mp_int_t atten = args[ARG_atten].u_int; + mp_machine_adc_atten_set_helper(self, atten != -1 ? atten : ADC_ATTEN_MAX); + mp_machine_adc_block_width_set_helper(self->block, ADC_WIDTH_MAX); + apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self)); + } static void mp_machine_adc_init_helper(machine_adc_obj_t *self, size_t n_pos_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { madc_init_helper(self, n_pos_args, pos_args, kw_args); } +static void mp_machine_adc_deinit(machine_adc_obj_t *self) { + if (self->block->handle) { + check_esp_err(adc_oneshot_del_unit(self->block->handle)); + self->block->handle = NULL; + } +} + static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_args, size_t n_kw_args, const mp_obj_t *args) { mp_arg_check_num(n_pos_args, n_kw_args, 1, MP_OBJ_FUN_ARGS_MAX, true); gpio_num_t gpio_id = machine_pin_get_id(args[0]); @@ -202,10 +189,6 @@ static mp_obj_t mp_machine_adc_make_new(const mp_obj_type_t *type, size_t n_pos_ mp_raise_ValueError(MP_ERROR_TEXT("invalid pin")); } - if (self->block->width == -1) { - madcblock_bits_helper(self->block, self->block->bits); - } - mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw_args, args + n_pos_args); madc_init_helper(self, n_pos_args - 1, args + 1, &kw_args); @@ -225,20 +208,46 @@ static mp_int_t mp_machine_adc_read(machine_adc_obj_t *self) { static mp_int_t mp_machine_adc_read_u16(machine_adc_obj_t *self) { mp_uint_t raw = madcblock_read_helper(self->block, self->channel_id); // Scale raw reading to 16 bit value using a Taylor expansion (for 8 <= bits <= 16) - mp_int_t bits = self->block->bits; + mp_int_t bits = mp_machine_adc_width_get_helper(self); mp_uint_t u16 = raw << (16 - bits) | raw >> (2 * bits - 16); return u16; } static mp_int_t mp_machine_adc_read_uv(machine_adc_obj_t *self) { - adc_atten_t atten = madc_atten_get(self); - return madcblock_read_uv_helper(self->block, self->channel_id, atten); + return madcblock_read_uv_helper(self->block, self->channel_id, mp_machine_adc_atten_get_helper(self)); +} + +mp_int_t mp_machine_adc_atten_get_helper(const machine_adc_obj_t *self) { + uint8_t value = madc_obj_atten[self - &madc_obj[0]]; + return value == 0 ? ADC_ATTEN_MAX : value - 1; +} + +void mp_machine_adc_atten_set_helper(const machine_adc_obj_t *self, mp_int_t atten) { + if (atten < ADC_ATTEN_MIN || atten > ADC_ATTEN_MAX) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid attenuation")); + } + + madc_obj_atten[self - &madc_obj[0]] = atten + 1; } static void mp_machine_adc_atten_set(machine_adc_obj_t *self, mp_int_t atten) { - madc_atten_helper(self, atten); + mp_machine_adc_atten_set_helper(self, atten); + apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self)); +} + +mp_int_t mp_machine_adc_width_get_helper(const machine_adc_obj_t *self) { + return self->block->bitwidth; +} + +void mp_machine_adc_block_width_set_helper(machine_adc_block_obj_t *self, mp_int_t width) { + if (width < ADC_WIDTH_MIN || width > ADC_WIDTH_MAX) { + mp_raise_ValueError(MP_ERROR_TEXT("invalid bit-width")); + } + + self->bitwidth = width; } static void mp_machine_adc_width_set(machine_adc_obj_t *self, mp_int_t width) { - madcblock_bits_helper(self->block, width); + mp_machine_adc_block_width_set_helper(self->block, width); + apply_self_adc_channel_atten(self, mp_machine_adc_atten_get_helper(self)); } diff --git a/ports/esp32/machine_adc_block.c b/ports/esp32/machine_adc_block.c index a373603b7e..6b06b432c2 100644 --- a/ports/esp32/machine_adc_block.c +++ b/ports/esp32/machine_adc_block.c @@ -29,25 +29,21 @@ #include "py/mphal.h" #include "adc.h" -#include "driver/adc.h" +#include "esp_adc/adc_oneshot.h" machine_adc_block_obj_t madcblock_obj[] = { - {{&machine_adc_block_type}, ADC_UNIT_1, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}}, + {{&machine_adc_block_type}, ADC_UNIT_1, NULL, ADC_WIDTH_MAX, {0}}, #if SOC_ADC_PERIPH_NUM > 1 - {{&machine_adc_block_type}, ADC_UNIT_2, SOC_ADC_RTC_MAX_BITWIDTH, -1, {0}}, + {{&machine_adc_block_type}, ADC_UNIT_2, NULL, ADC_WIDTH_MAX, {0}}, #endif }; static void mp_machine_adc_block_print(const mp_print_t *print, machine_adc_block_obj_t *self) { - mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bits); + mp_printf(print, "ADCBlock(%u, bits=%u)", self->unit_id, self->bitwidth); } static void mp_machine_adc_block_bits_set(machine_adc_block_obj_t *self, mp_int_t bits) { - if (bits != -1) { - madcblock_bits_helper(self, bits); - } else if (self->width == -1) { - madcblock_bits_helper(self, self->bits); - } + mp_machine_adc_block_width_set_helper(self, bits); } static machine_adc_block_obj_t *mp_machine_adc_block_get(mp_int_t unit) { diff --git a/ports/esp32/machine_i2c.c b/ports/esp32/machine_i2c.c index 12b86e4aa0..259101ee7e 100644 --- a/ports/esp32/machine_i2c.c +++ b/ports/esp32/machine_i2c.c @@ -35,9 +35,14 @@ #if MICROPY_PY_MACHINE_I2C || MICROPY_PY_MACHINE_SOFTI2C #ifndef MICROPY_HW_I2C0_SCL +#if CONFIG_IDF_TARGET_ESP32C3 || CONFIG_IDF_TARGET_ESP32S3 +#define MICROPY_HW_I2C0_SCL (GPIO_NUM_9) +#define MICROPY_HW_I2C0_SDA (GPIO_NUM_8) +#else #define MICROPY_HW_I2C0_SCL (GPIO_NUM_18) #define MICROPY_HW_I2C0_SDA (GPIO_NUM_19) #endif +#endif #ifndef MICROPY_HW_I2C1_SCL #if CONFIG_IDF_TARGET_ESP32 diff --git a/ports/esp32/machine_pin.c b/ports/esp32/machine_pin.c index d0c4ee1a7e..4ab79f0a26 100644 --- a/ports/esp32/machine_pin.c +++ b/ports/esp32/machine_pin.c @@ -325,14 +325,17 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_ mp_raise_ValueError(MP_ERROR_TEXT("bad wake value")); } + #if SOC_TOUCH_SENSOR_SUPPORTED if (machine_rtc_config.wake_on_touch) { // not compatible mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif if (!RTC_IS_VALID_EXT_PIN(index)) { mp_raise_ValueError(MP_ERROR_TEXT("invalid pin for wake")); } + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin == -1) { machine_rtc_config.ext0_pin = index; } else if (machine_rtc_config.ext0_pin != index) { @@ -341,10 +344,13 @@ static mp_obj_t machine_pin_irq(size_t n_args, const mp_obj_t *pos_args, mp_map_ machine_rtc_config.ext0_level = trigger == GPIO_INTR_LOW_LEVEL ? 0 : 1; machine_rtc_config.ext0_wake_types = wake; + #endif } else { + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin == index) { machine_rtc_config.ext0_pin = -1; } + #endif if (handler == mp_const_none) { handler = MP_OBJ_NULL; diff --git a/ports/esp32/machine_pwm.c b/ports/esp32/machine_pwm.c index 7826769556..7cb84640ee 100644 --- a/ports/esp32/machine_pwm.c +++ b/ports/esp32/machine_pwm.c @@ -6,7 +6,8 @@ * Copyright (c) 2016-2021 Damien P. George * Copyright (c) 2018 Alan Dragomirecky * Copyright (c) 2020 Antoine Aubert - * Copyright (c) 2021 Ihor Nehrutsa + * Copyright (c) 2021, 2023-2025 Ihor Nehrutsa + * Copyright (c) 2024 Yoann Darche * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,79 +33,41 @@ #include <math.h> #include "py/mphal.h" -#include "driver/ledc.h" #include "esp_err.h" +#include "driver/ledc.h" +#include "soc/ledc_periph.h" #include "soc/gpio_sig_map.h" - -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 1, 0) #include "esp_clk_tree.h" -#endif - -#define PWM_DBG(...) -// #define PWM_DBG(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, "\n"); - -// Total number of channels -#define PWM_CHANNEL_MAX (LEDC_SPEED_MODE_MAX * LEDC_CHANNEL_MAX) - -typedef struct _chan_t { - // Which channel has which GPIO pin assigned? - // (-1 if not assigned) - gpio_num_t pin; - // Which channel has which timer assigned? - // (-1 if not assigned) - int timer_idx; -} chan_t; +#include "py/mpprint.h" -// List of PWM channels -static chan_t chans[PWM_CHANNEL_MAX]; +#define debug_printf(...) mp_printf(&mp_plat_print, __VA_ARGS__); mp_printf(&mp_plat_print, " | %d at %s\n", __LINE__, __FILE__); -// channel_idx is an index (end-to-end sequential numbering) for all channels -// available on the chip and described in chans[] -#define CHANNEL_IDX(mode, channel) (mode * LEDC_CHANNEL_MAX + channel) -#define CHANNEL_IDX_TO_MODE(channel_idx) (channel_idx / LEDC_CHANNEL_MAX) -#define CHANNEL_IDX_TO_CHANNEL(channel_idx) (channel_idx % LEDC_CHANNEL_MAX) +#define FADE 0 -// Total number of timers -#define PWM_TIMER_MAX (LEDC_SPEED_MODE_MAX * LEDC_TIMER_MAX) +// 10-bit user interface resolution compatible with esp8266 PWM.duty() +#define UI_RES_10_BIT (10) +#define DUTY_10 UI_RES_10_BIT +// Maximum duty value on 10-bit resolution is 1024 but reduced to 1023 in UI +#define MAX_10_DUTY (1U << UI_RES_10_BIT) -// List of timer configs -static ledc_timer_config_t timers[PWM_TIMER_MAX]; +// 16-bit user interface resolution used in PWM.duty_u16() +#define UI_RES_16_BIT (16) +#define DUTY_16 UI_RES_16_BIT +// Maximum duty value on 16-bit resolution is 65536 but reduced to 65535 in UI +#define MAX_16_DUTY (1U << UI_RES_16_BIT) -// timer_idx is an index (end-to-end sequential numbering) for all timers -// available on the chip and configured in timers[] -#define TIMER_IDX(mode, timer) (mode * LEDC_TIMER_MAX + timer) -#define TIMER_IDX_TO_MODE(timer_idx) (timer_idx / LEDC_TIMER_MAX) -#define TIMER_IDX_TO_TIMER(timer_idx) (timer_idx % LEDC_TIMER_MAX) +// ns user interface used in PWM.duty_ns() +#define DUTY_NS (1) -// Params for PWM operation // 5khz is default frequency -#define PWM_FREQ (5000) - -// 10-bit resolution (compatible with esp8266 PWM) -#define PWM_RES_10_BIT (LEDC_TIMER_10_BIT) - -// Maximum duty value on 10-bit resolution -#define MAX_DUTY_U10 ((1 << PWM_RES_10_BIT) - 1) -// https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/ledc.html#supported-range-of-frequency-and-duty-resolutions -// duty() uses 10-bit resolution or less -// duty_u16() and duty_ns() use 16-bit resolution or less - -// Possible highest resolution in device -#if (LEDC_TIMER_BIT_MAX - 1) < LEDC_TIMER_16_BIT -#define HIGHEST_PWM_RES (LEDC_TIMER_BIT_MAX - 1) -#else -#define HIGHEST_PWM_RES (LEDC_TIMER_16_BIT) // 20 bit for ESP32, but 16 bit is used -#endif -// Duty resolution of user interface in `duty_u16()` and `duty_u16` parameter in constructor/initializer -#define UI_RES_16_BIT (16) -// Maximum duty value on highest user interface resolution -#define UI_MAX_DUTY ((1 << UI_RES_16_BIT) - 1) -// How much to shift from the HIGHEST_PWM_RES duty resolution to the user interface duty resolution UI_RES_16_BIT -#define UI_RES_SHIFT (UI_RES_16_BIT - HIGHEST_PWM_RES) // 0 for ESP32, 2 for S2, S3, C3 - -#if SOC_LEDC_SUPPORT_REF_TICK +#define PWM_FREQ (5000) +// default duty 50% +#define PWM_DUTY ((1U << UI_RES_16_BIT) / 2) + +// All chips except esp32 and esp32s2 do not have timer-specific clock sources, which means clock source for all timers must be the same one. +#if CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 // If the PWM frequency is less than EMPIRIC_FREQ, then LEDC_REF_CLK_HZ(1 MHz) source is used, else LEDC_APB_CLK_HZ(80 MHz) source is used -#define EMPIRIC_FREQ (10) // Hz +#define EMPIRIC_FREQ (10) // Hz #endif // Config of timer upon which we run all PWM'ed GPIO pins @@ -113,490 +76,675 @@ static bool pwm_inited = false; // MicroPython PWM object struct typedef struct _machine_pwm_obj_t { mp_obj_base_t base; - gpio_num_t pin; - bool active; - int mode; - int channel; - int timer; - int duty_x; // PWM_RES_10_BIT if duty(), HIGHEST_PWM_RES if duty_u16(), -HIGHEST_PWM_RES if duty_ns() - int duty_u10; // stored values from previous duty setters - int duty_u16; // - / - - int duty_ns; // - / - + int8_t pin; + int8_t mode; + int8_t channel; + int8_t timer; + bool lightsleep; + int32_t freq; + int8_t duty_scale; // DUTY_10 if duty(), DUTY_16 if duty_u16(), DUTY_NS if duty_ns() + int duty_ui; // saved values of UI duty + int channel_duty; // saved values of UI duty, calculated to raw channel->duty + bool output_invert; + bool output_invert_prev; } machine_pwm_obj_t; -static bool is_timer_in_use(int current_channel_idx, int timer_idx); -static void set_duty_u16(machine_pwm_obj_t *self, int duty); -static void set_duty_u10(machine_pwm_obj_t *self, int duty); -static void set_duty_ns(machine_pwm_obj_t *self, int ns); +typedef struct _chans_t { + int8_t pin; // Which channel has which GPIO pin assigned? (-1 if not assigned) + int8_t timer; // Which channel has which timer assigned? (-1 if not assigned) + bool lightsleep; // Is light sleep enable has been set for this pin +} chans_t; + +// List of PWM channels +static chans_t chans[LEDC_SPEED_MODE_MAX][LEDC_CHANNEL_MAX]; + +typedef struct _timers_t { + int32_t freq; + int8_t duty_resolution; + ledc_clk_cfg_t clk_cfg; +} timers_t; + +// List of PWM timers +static timers_t timers[LEDC_SPEED_MODE_MAX][LEDC_TIMER_MAX]; + +// register-unregister channel +static void register_channel(int mode, int channel, int pin, int timer) { + chans[mode][channel].pin = pin; + chans[mode][channel].timer = timer; +} + +static void unregister_channel(int mode, int channel) { + register_channel(mode, channel, -1, -1); + chans[mode][channel].lightsleep = false; +} + +static void unregister_timer(int mode, int timer) { + timers[mode][timer].freq = -1; // unused timer freq is -1 + timers[mode][timer].duty_resolution = 0; + timers[mode][timer].clk_cfg = LEDC_AUTO_CLK; +} static void pwm_init(void) { - // Initial condition: no channels assigned - for (int i = 0; i < PWM_CHANNEL_MAX; ++i) { - chans[i].pin = -1; - chans[i].timer_idx = -1; + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + // Initial condition: no channels assigned + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + unregister_channel(mode, channel); + } + // Initial condition: no timers assigned + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + unregister_timer(mode, timer); + } } +} - // Prepare all timers config - // Initial condition: no timers assigned - for (int i = 0; i < PWM_TIMER_MAX; ++i) { - timers[i].duty_resolution = HIGHEST_PWM_RES; - // unset timer is -1 - timers[i].freq_hz = -1; - timers[i].speed_mode = TIMER_IDX_TO_MODE(i); - timers[i].timer_num = TIMER_IDX_TO_TIMER(i); - timers[i].clk_cfg = LEDC_AUTO_CLK; // will reinstall later according to the EMPIRIC_FREQ +// Returns true if the timer is in use in addition to current channel +static bool is_timer_in_use(int mode, int current_channel, int timer) { + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if ((channel != current_channel) && (chans[mode][channel].timer == timer) && (chans[mode][channel].pin >= 0)) { + return true; + } } + return false; } // Deinit channel and timer if the timer is unused -static void pwm_deinit(int channel_idx) { - // Valid channel? - if ((channel_idx >= 0) && (channel_idx < PWM_CHANNEL_MAX)) { +static void pwm_deinit(int mode, int channel, int level) { + // Is valid channel? + if ((mode >= 0) && (mode < LEDC_SPEED_MODE_MAX) && (channel >= 0) && (channel < LEDC_CHANNEL_MAX)) { // Clean up timer if necessary - int timer_idx = chans[channel_idx].timer_idx; - if (timer_idx != -1) { - if (!is_timer_in_use(channel_idx, timer_idx)) { - check_esp_err(ledc_timer_pause(TIMER_IDX_TO_MODE(timer_idx), TIMER_IDX_TO_TIMER(timer_idx))); - ledc_timer_config_t timer_config = { + int timer = chans[mode][channel].timer; + if (timer >= 0) { + if (!is_timer_in_use(mode, channel, timer)) { + check_esp_err(ledc_timer_pause(mode, timer)); + ledc_timer_config_t ledc_timer = { .deconfigure = true, - .speed_mode = TIMER_IDX_TO_MODE(timer_idx), - .timer_num = TIMER_IDX_TO_TIMER(timer_idx), + .speed_mode = mode, + .timer_num = timer, }; - check_esp_err(ledc_timer_config(&timer_config)); - // Flag it unused - timers[chans[channel_idx].timer_idx].freq_hz = -1; + ledc_timer_config(&ledc_timer); + unregister_timer(mode, timer); } } - int pin = chans[channel_idx].pin; - if (pin != -1) { - int mode = CHANNEL_IDX_TO_MODE(channel_idx); - int channel = CHANNEL_IDX_TO_CHANNEL(channel_idx); - // Mark it unused, and tell the hardware to stop routing - check_esp_err(ledc_stop(mode, channel, 0)); - // Disable ledc signal for the pin - // esp_rom_gpio_connect_out_signal(pin, SIG_GPIO_OUT_IDX, false, false); - if (mode == LEDC_LOW_SPEED_MODE) { - esp_rom_gpio_connect_out_signal(pin, LEDC_LS_SIG_OUT0_IDX + channel, false, true); - } else { - #if LEDC_SPEED_MODE_MAX > 1 - #if CONFIG_IDF_TARGET_ESP32 - esp_rom_gpio_connect_out_signal(pin, LEDC_HS_SIG_OUT0_IDX + channel, false, true); - #else - #error Add supported CONFIG_IDF_TARGET_ESP32_xxx - #endif - #endif + int pin = chans[mode][channel].pin; + if (pin >= 0) { + // Disable LEDC output, and set idle level + check_esp_err(ledc_stop(mode, channel, level)); + if (chans[mode][channel].lightsleep) { + // Enable SLP_SEL to change GPIO status automantically in lightsleep. + check_esp_err(gpio_sleep_sel_en(pin)); + chans[mode][channel].lightsleep = false; } } - chans[channel_idx].pin = -1; - chans[channel_idx].timer_idx = -1; + unregister_channel(mode, channel); } } // This called from Ctrl-D soft reboot void machine_pwm_deinit_all(void) { if (pwm_inited) { - for (int channel_idx = 0; channel_idx < PWM_CHANNEL_MAX; ++channel_idx) { - pwm_deinit(channel_idx); + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + pwm_deinit(mode, channel, 0); + } } + #if FADE + ledc_fade_func_uninstall(); + #endif pwm_inited = false; } } -static void configure_channel(machine_pwm_obj_t *self) { - ledc_channel_config_t cfg = { - .channel = self->channel, - .duty = (1 << (timers[TIMER_IDX(self->mode, self->timer)].duty_resolution)) / 2, - .gpio_num = self->pin, - .intr_type = LEDC_INTR_DISABLE, - .speed_mode = self->mode, - .timer_sel = self->timer, - }; - if (ledc_channel_config(&cfg) != ESP_OK) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("PWM not supported on Pin(%d)"), self->pin); - } -} - -static void set_freq(machine_pwm_obj_t *self, unsigned int freq, ledc_timer_config_t *timer) { - esp_err_t err; - if (freq != timer->freq_hz) { - // Configure the new frequency and resolution - timer->freq_hz = freq; - - #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK - timer->clk_cfg = LEDC_USE_PLL_DIV_CLK; - #elif SOC_LEDC_SUPPORT_APB_CLOCK - timer->clk_cfg = LEDC_USE_APB_CLK; - #elif SOC_LEDC_SUPPORT_XTAL_CLOCK - timer->clk_cfg = LEDC_USE_XTAL_CLK; - #else - #error No supported PWM / LEDC clocks. - #endif - #if SOC_LEDC_SUPPORT_REF_TICK - if (freq < EMPIRIC_FREQ) { - timer->clk_cfg = LEDC_USE_REF_TICK; - } - #endif - uint32_t src_clk_freq = 0; - err = esp_clk_tree_src_get_freq_hz(timer->clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_CACHED, &src_clk_freq); - if (err != ESP_OK) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unable to query source clock frequency %d"), (int)timer->clk_cfg); - } - - timer->duty_resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer->freq_hz); - - // Set frequency - err = ledc_timer_config(timer); - if (err != ESP_OK) { - if (err == ESP_FAIL) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("unreachable frequency %d"), freq); - } else { - check_esp_err(err); - } - } - // Reset the timer if low speed - if (self->mode == LEDC_LOW_SPEED_MODE) { - check_esp_err(ledc_timer_rst(self->mode, self->timer)); - } - } - - // Save the same duty cycle when frequency is changed - if (self->duty_x == HIGHEST_PWM_RES) { - set_duty_u16(self, self->duty_u16); - } else if (self->duty_x == PWM_RES_10_BIT) { - set_duty_u10(self, self->duty_u10); - } else if (self->duty_x == -HIGHEST_PWM_RES) { - set_duty_ns(self, self->duty_ns); +static void pwm_is_active(machine_pwm_obj_t *self) { + if (self->timer < 0) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("PWM is inactive")); } } // Calculate the duty parameters based on an ns value static int ns_to_duty(machine_pwm_obj_t *self, int ns) { - ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int64_t duty = ((int64_t)ns * UI_MAX_DUTY * timer.freq_hz + 500000000LL) / 1000000000LL; + pwm_is_active(self); + int64_t duty = ((int64_t)ns * (int64_t)MAX_16_DUTY * self->freq + 500000000LL) / 1000000000LL; if ((ns > 0) && (duty == 0)) { duty = 1; - } else if (duty > UI_MAX_DUTY) { - duty = UI_MAX_DUTY; + } else if (duty > MAX_16_DUTY) { + duty = MAX_16_DUTY; } return duty; } static int duty_to_ns(machine_pwm_obj_t *self, int duty) { - ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int64_t ns = ((int64_t)duty * 1000000000LL + (int64_t)timer.freq_hz * UI_MAX_DUTY / 2) / ((int64_t)timer.freq_hz * UI_MAX_DUTY); - return ns; + pwm_is_active(self); + return ((int64_t)duty * 1000000000LL + (int64_t)self->freq * (int64_t)(MAX_16_DUTY / 2)) / ((int64_t)self->freq * (int64_t)MAX_16_DUTY); } -#define get_duty_raw(self) ledc_get_duty(self->mode, self->channel) +// Reconfigure PWM pin output as input/output. +static void reconfigure_pin(machine_pwm_obj_t *self) { + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 0) + // This allows to read the pin level. + gpio_set_direction(self->pin, GPIO_MODE_INPUT_OUTPUT); + #endif + esp_rom_gpio_connect_out_signal(self->pin, ledc_periph_signal[self->mode].sig_out0_idx + self->channel, self->output_invert, 0); +} -static void pwm_is_active(machine_pwm_obj_t *self) { - if (self->active == false) { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("PWM inactive")); +static void apply_duty(machine_pwm_obj_t *self) { + pwm_is_active(self); + + int duty = 0; + if (self->duty_scale == DUTY_16) { + duty = self->duty_ui; + } else if (self->duty_scale == DUTY_10) { + duty = self->duty_ui << (UI_RES_16_BIT - UI_RES_10_BIT); + } else if (self->duty_scale == DUTY_NS) { + duty = ns_to_duty(self, self->duty_ui); + } + self->channel_duty = duty >> (UI_RES_16_BIT - timers[self->mode][self->timer].duty_resolution); + if ((chans[self->mode][self->channel].pin == -1) || (self->output_invert_prev != self->output_invert)) { + self->output_invert_prev = self->output_invert; + // New PWM assignment + ledc_channel_config_t cfg = { + .channel = self->channel, + .duty = self->channel_duty, + .gpio_num = self->pin, + .intr_type = LEDC_INTR_DISABLE, + .speed_mode = self->mode, + .timer_sel = self->timer, + .hpoint = 0, + .flags.output_invert = self->output_invert, + }; + check_esp_err(ledc_channel_config(&cfg)); + reconfigure_pin(self); + } else { + #if FADE + check_esp_err(ledc_set_duty_and_update(self->mode, self->channel, self->channel_duty, 0)); + #else + check_esp_err(ledc_set_duty(self->mode, self->channel, self->channel_duty)); + check_esp_err(ledc_update_duty(self->mode, self->channel)); + #endif + } + if (self->lightsleep) { + // Disable SLP_SEL to change GPIO status automantically in lightsleep. + check_esp_err(gpio_sleep_sel_dis(self->pin)); + chans[self->mode][self->channel].lightsleep = true; } + register_channel(self->mode, self->channel, self->pin, self->timer); +} + +static uint32_t find_suitable_duty_resolution(uint32_t src_clk_freq, uint32_t timer_freq) { + unsigned int resolution = ledc_find_suitable_duty_resolution(src_clk_freq, timer_freq); + if (resolution > UI_RES_16_BIT) { + // limit resolution to user interface + resolution = UI_RES_16_BIT; + } + // Note: On ESP32, ESP32S2, ESP32S3, ESP32C3, ESP32C2, ESP32C6, ESP32H2, ESP32P4, due to a hardware bug, + // 100% duty cycle (i.e. 2**duty_res) is not reachable when the binded timer selects the maximum duty + // resolution. For example, the max duty resolution on ESP32C3 is 14-bit width, then set duty to (2**14) + // will mess up the duty calculation in hardware. + // Reduce the resolution from 14 to 13 bits to resolve the hardware bug. + if (resolution >= SOC_LEDC_TIMER_BIT_WIDTH) { + resolution -= 1; + } + return resolution; } static uint32_t get_duty_u16(machine_pwm_obj_t *self) { pwm_is_active(self); - int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution; - int duty = ledc_get_duty(self->mode, self->channel); - if (resolution <= UI_RES_16_BIT) { - duty <<= (UI_RES_16_BIT - resolution); + int duty = ledc_get_duty(self->mode, self->channel) << (UI_RES_16_BIT - timers[self->mode][self->timer].duty_resolution); + if (duty != MAX_16_DUTY) { + return duty; } else { - duty >>= (resolution - UI_RES_16_BIT); + return MAX_16_DUTY - 1; } - return duty; } static uint32_t get_duty_u10(machine_pwm_obj_t *self) { - pwm_is_active(self); - return get_duty_u16(self) >> 6; // Scale down from 16 bit to 10 bit resolution + // Scale down from 16 bit to 10 bit resolution + return get_duty_u16(self) >> (UI_RES_16_BIT - UI_RES_10_BIT); } static uint32_t get_duty_ns(machine_pwm_obj_t *self) { - pwm_is_active(self); return duty_to_ns(self, get_duty_u16(self)); } -static void set_duty_u16(machine_pwm_obj_t *self, int duty) { - pwm_is_active(self); - if ((duty < 0) || (duty > UI_MAX_DUTY)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), UI_MAX_DUTY); +static void check_duty_u16(machine_pwm_obj_t *self, int duty) { + if ((duty < 0) || (duty > MAX_16_DUTY - 1)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_u16 must be from 0 to %d"), MAX_16_DUTY); } - ledc_timer_config_t timer = timers[TIMER_IDX(self->mode, self->timer)]; - int channel_duty; - if (timer.duty_resolution <= UI_RES_16_BIT) { - channel_duty = duty >> (UI_RES_16_BIT - timer.duty_resolution); - } else { - channel_duty = duty << (timer.duty_resolution - UI_RES_16_BIT); + if (duty == MAX_16_DUTY - 1) { + duty = MAX_16_DUTY; } - int max_duty = (1 << timer.duty_resolution) - 1; - if (channel_duty < 0) { - channel_duty = 0; - } else if (channel_duty > max_duty) { - channel_duty = max_duty; - } - check_esp_err(ledc_set_duty(self->mode, self->channel, channel_duty)); - check_esp_err(ledc_update_duty(self->mode, self->channel)); + self->duty_scale = DUTY_16; + self->duty_ui = duty; +} - /* - // Bug: Sometimes duty is not set right now. - // Not a bug. It's a feature. The duty is applied at the beginning of the next signal period. - // Bug: It has been experimentally established that the duty is set during 2 signal periods, but 1 period is expected. - // See https://github.com/espressif/esp-idf/issues/7288 - if (duty != get_duty_u16(self)) { - PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz); - esp_rom_delay_us(2 * 1000000 / timer.freq_hz); - if (duty != get_duty_u16(self)) { - PWM_DBG("set_duty_u16(%u), get_duty_u16():%u, channel_duty:%d, duty_resolution:%d, freq_hz:%d", duty, get_duty_u16(self), channel_duty, timer.duty_resolution, timer.freq_hz); - } - } - */ +static void set_duty_u16(machine_pwm_obj_t *self, int duty) { + check_duty_u16(self, duty); + apply_duty(self); +} - self->duty_x = HIGHEST_PWM_RES; - self->duty_u16 = duty; +static void check_duty_u10(machine_pwm_obj_t *self, int duty) { + if ((duty < 0) || (duty > MAX_10_DUTY - 1)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_10_DUTY - 1); + } + if (duty == MAX_10_DUTY - 1) { + duty = MAX_10_DUTY; + } + self->duty_scale = DUTY_10; + self->duty_ui = duty; } static void set_duty_u10(machine_pwm_obj_t *self, int duty) { - pwm_is_active(self); - if ((duty < 0) || (duty > MAX_DUTY_U10)) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty must be from 0 to %u"), MAX_DUTY_U10); + check_duty_u10(self, duty); + apply_duty(self); +} + +static void check_duty_ns(machine_pwm_obj_t *self, int ns) { + if ((ns < 0) || (ns > duty_to_ns(self, MAX_16_DUTY))) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, MAX_16_DUTY)); } - set_duty_u16(self, duty << (UI_RES_16_BIT - PWM_RES_10_BIT)); - self->duty_x = PWM_RES_10_BIT; - self->duty_u10 = duty; + self->duty_scale = DUTY_NS; + self->duty_ui = ns; } static void set_duty_ns(machine_pwm_obj_t *self, int ns) { - pwm_is_active(self); - if ((ns < 0) || (ns > duty_to_ns(self, UI_MAX_DUTY))) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("duty_ns must be from 0 to %d ns"), duty_to_ns(self, UI_MAX_DUTY)); + check_duty_ns(self, ns); + apply_duty(self); +} + +#if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) +// This check if a clock is already set in the timer list, if yes, return the LEDC_XXX value +static ledc_clk_cfg_t find_clock_in_use() { + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if (timers[mode][timer].clk_cfg != LEDC_AUTO_CLK) { + return timers[mode][timer].clk_cfg; + } + } } - set_duty_u16(self, ns_to_duty(self, ns)); - self->duty_x = -HIGHEST_PWM_RES; - self->duty_ns = ns; + return LEDC_AUTO_CLK; } -/******************************************************************************/ +// Check if a timer is already set with a different clock source +static bool is_timer_with_different_clock(int mode, int current_timer, ledc_clk_cfg_t clk_cfg) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if ((timer != current_timer) && (clk_cfg != LEDC_AUTO_CLK) && (timers[mode][timer].clk_cfg != LEDC_AUTO_CLK) && (timers[mode][timer].clk_cfg != clk_cfg)) { + return true; + } + } + return false; +} +#endif -#define SAME_FREQ_ONLY (true) -#define SAME_FREQ_OR_FREE (false) -#define ANY_MODE (-1) +static void check_freq_ranges(machine_pwm_obj_t *self, int freq, int upper_freq) { + if ((freq <= 0) || (freq > upper_freq)) { + mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("frequency must be from 1Hz to %dMHz"), upper_freq / 1000000); + } +} -// Return timer_idx. Use TIMER_IDX_TO_MODE(timer_idx) and TIMER_IDX_TO_TIMER(timer_idx) to get mode and timer -static int find_timer(unsigned int freq, bool same_freq_only, int mode) { - int free_timer_idx_found = -1; - // Find a free PWM Timer using the same freq - for (int timer_idx = 0; timer_idx < PWM_TIMER_MAX; ++timer_idx) { - if ((mode == ANY_MODE) || (mode == TIMER_IDX_TO_MODE(timer_idx))) { - if (timers[timer_idx].freq_hz == freq) { - // A timer already uses the same freq. Use it now. - return timer_idx; - } - if (!same_freq_only && (free_timer_idx_found == -1) && (timers[timer_idx].freq_hz == -1)) { - free_timer_idx_found = timer_idx; - // Continue to check if a channel with the same freq is in use. +// Set timer frequency +static void set_freq(machine_pwm_obj_t *self, unsigned int freq) { + self->freq = freq; + if ((timers[self->mode][self->timer].freq != freq) || (self->lightsleep)) { + ledc_timer_config_t timer = {}; + timer.speed_mode = self->mode; + timer.timer_num = self->timer; + timer.freq_hz = freq; + timer.deconfigure = false; + + timer.clk_cfg = LEDC_AUTO_CLK; + #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) + ledc_clk_cfg_t clk_cfg = find_clock_in_use(); + if (clk_cfg != LEDC_AUTO_CLK) { + timer.clk_cfg = clk_cfg; + } else + #endif + if (self->lightsleep) { + timer.clk_cfg = LEDC_USE_RC_FAST_CLK; // 8 or 20 MHz + } else { + #if SOC_LEDC_SUPPORT_APB_CLOCK + timer.clk_cfg = LEDC_USE_APB_CLK; // 80 MHz + #elif SOC_LEDC_SUPPORT_PLL_DIV_CLOCK + timer.clk_cfg = LEDC_USE_PLL_DIV_CLK; // 60 or 80 or 96 MHz + #elif SOC_LEDC_SUPPORT_XTAL_CLOCK + timer.clk_cfg = LEDC_USE_XTAL_CLK; // 40 MHz + #else + #error No supported PWM / LEDC clocks. + #endif + + #ifdef EMPIRIC_FREQ // ESP32 and ESP32S2 only + if (freq < EMPIRIC_FREQ) { + timer.clk_cfg = LEDC_USE_REF_TICK; // 1 MHz } + #endif } - } + #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) + // Check for clock source conflict + clk_cfg = find_clock_in_use(); + if ((clk_cfg != LEDC_AUTO_CLK) && (clk_cfg != timer.clk_cfg)) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("one or more active timers use a different clock source, not supported by the current SoC.")); + } + #endif - return free_timer_idx_found; + uint32_t src_clk_freq = 0; + check_esp_err(esp_clk_tree_src_get_freq_hz(timer.clk_cfg, ESP_CLK_TREE_SRC_FREQ_PRECISION_APPROX, &src_clk_freq)); + // machine.freq(20_000_000) reduces APB_CLK_FREQ to 20MHz and the highest PWM frequency to 10MHz + check_freq_ranges(self, freq, src_clk_freq / 2); + + // Configure the new resolution + timer.duty_resolution = find_suitable_duty_resolution(src_clk_freq, self->freq); + // Configure timer - Set frequency + if ((timers[self->mode][self->timer].duty_resolution == timer.duty_resolution) && (timers[self->mode][self->timer].clk_cfg == timer.clk_cfg)) { + check_esp_err(ledc_set_freq(self->mode, self->timer, freq)); + } else { + check_esp_err(ledc_timer_config(&timer)); + } + // Reset the timer if low speed + if (self->mode == LEDC_LOW_SPEED_MODE) { + check_esp_err(ledc_timer_rst(self->mode, self->timer)); + } + timers[self->mode][self->timer].freq = freq; + timers[self->mode][self->timer].duty_resolution = timer.duty_resolution; + timers[self->mode][self->timer].clk_cfg = timer.clk_cfg; + } } -// Return true if the timer is in use in addition to current channel -static bool is_timer_in_use(int current_channel_idx, int timer_idx) { - for (int i = 0; i < PWM_CHANNEL_MAX; ++i) { - if ((i != current_channel_idx) && (chans[i].timer_idx == timer_idx)) { +static bool is_free_channels(int mode, int pin) { + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if ((chans[mode][channel].pin < 0) || (chans[mode][channel].pin == pin)) { return true; } } + return false; +} +static bool is_free_timers(int mode, int32_t freq) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if ((timers[mode][timer].freq < 0) || (timers[mode][timer].freq == freq)) { + return true; + } + } return false; } -// Find a free PWM channel, also spot if our pin is already mentioned. -// Return channel_idx. Use CHANNEL_IDX_TO_MODE(channel_idx) and CHANNEL_IDX_TO_CHANNEL(channel_idx) to get mode and channel -static int find_channel(int pin, int mode) { - int avail_idx = -1; - int channel_idx; - for (channel_idx = 0; channel_idx < PWM_CHANNEL_MAX; ++channel_idx) { - if ((mode == ANY_MODE) || (mode == CHANNEL_IDX_TO_MODE(channel_idx))) { - if (chans[channel_idx].pin == pin) { - break; +// Find self channel or free channel +static void find_channel(machine_pwm_obj_t *self, int *ret_mode, int *ret_channel, int32_t freq) { + // Try to find self channel first + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + #if SOC_LEDC_SUPPORT_HS_MODE + if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) { + continue; + } + #endif + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if (chans[mode][channel].pin == self->pin) { + *ret_mode = mode; + *ret_channel = channel; + return; } - if ((avail_idx == -1) && (chans[channel_idx].pin == -1)) { - avail_idx = channel_idx; + } + } + // Find free channel + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + #if SOC_LEDC_SUPPORT_HS_MODE + if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) { + continue; + } + #endif + for (int channel = 0; channel < LEDC_CHANNEL_MAX; ++channel) { + if ((chans[mode][channel].pin < 0) && is_free_timers(mode, freq)) { + *ret_mode = mode; + *ret_channel = channel; + return; } } } - if (channel_idx >= PWM_CHANNEL_MAX) { - channel_idx = avail_idx; +} + +// Returns timer with the same frequency, freq == -1 means free timer +static void find_timer(machine_pwm_obj_t *self, int freq, int *ret_mode, int *ret_timer) { + for (int mode = 0; mode < LEDC_SPEED_MODE_MAX; ++mode) { + #if SOC_LEDC_SUPPORT_HS_MODE + if (self->lightsleep && (mode == LEDC_HIGH_SPEED_MODE)) { + continue; + } + #endif + if (is_free_channels(mode, self->pin)) { + for (int timer = 0; timer < LEDC_TIMER_MAX; ++timer) { + if (timers[mode][timer].freq == freq) { + *ret_mode = mode; + *ret_timer = timer; + return; + } + } + } } - return channel_idx; } -/******************************************************************************/ +// Try to find a timer with the same frequency in the current mode, otherwise in the next mode. +// If no existing timer and channel was found, then try to find free timer in any mode. +// If the mode or channel is changed, release the channel and register a new channel in the next mode. +static void select_timer(machine_pwm_obj_t *self, int freq) { + // mode, channel, timer may be -1(not defined) or actual values + int mode = -1; + int timer = -1; + // Check if an already running timer with the required frequency is running + find_timer(self, freq, &mode, &timer); + if (timer < 0) { + // Try to reuse self timer + if ((self->mode >= 0) && (self->channel >= 0)) { + if (!is_timer_in_use(self->mode, self->channel, self->timer)) { + mode = self->mode; + timer = self->timer; + } + } + // If no existing timer and channel was found, then try to find free timer in any mode + if (timer < 0) { + find_timer(self, -1, &mode, &timer); + } + } + if (timer < 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("out of %sPWM timers:%d"), self->lightsleep ? "light sleep capable " : "", self->lightsleep ? LEDC_TIMER_MAX : LEDC_SPEED_MODE_MAX *LEDC_TIMER_MAX); + } + // If the timer is found, then register + if (self->timer != timer) { + unregister_channel(self->mode, self->channel); + // Rregister the channel to the timer + self->mode = mode; + self->timer = timer; + register_channel(self->mode, self->channel, -1, self->timer); + } + #if !(CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2) + if (is_timer_with_different_clock(self->mode, self->timer, timers[self->mode][self->timer].clk_cfg)) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("one or more active timers use a different clock source, not supported by the current SoC.")); + } + #endif +} + +static void set_freq_duty(machine_pwm_obj_t *self, unsigned int freq) { + select_timer(self, freq); + set_freq(self, freq); + apply_duty(self); +} + +// ****************************************************************************** // MicroPython bindings for PWM static void mp_machine_pwm_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_pwm_obj_t *self = MP_OBJ_TO_PTR(self_in); mp_printf(print, "PWM(Pin(%u)", self->pin); - if (self->active) { + if (self->timer >= 0) { mp_printf(print, ", freq=%u", ledc_get_freq(self->mode, self->timer)); - - if (self->duty_x == PWM_RES_10_BIT) { + if (self->duty_scale == DUTY_10) { mp_printf(print, ", duty=%d", get_duty_u10(self)); - } else if (self->duty_x == -HIGHEST_PWM_RES) { + } else if (self->duty_scale == DUTY_NS) { mp_printf(print, ", duty_ns=%d", get_duty_ns(self)); } else { mp_printf(print, ", duty_u16=%d", get_duty_u16(self)); } - int resolution = timers[TIMER_IDX(self->mode, self->timer)].duty_resolution; - mp_printf(print, ", resolution=%d", resolution); - - mp_printf(print, ", (duty=%.2f%%, resolution=%.3f%%)", 100.0 * get_duty_raw(self) / (1 << resolution), 100.0 * 1 / (1 << resolution)); // percents - - mp_printf(print, ", mode=%d, channel=%d, timer=%d", self->mode, self->channel, self->timer); + if (self->output_invert) { + mp_printf(print, ", invert=True"); + } + if (self->lightsleep) { + mp_printf(print, ", lightsleep=True"); + } + mp_printf(print, ")"); + + #if MICROPY_ERROR_REPORTING > MICROPY_ERROR_REPORTING_NORMAL + mp_printf(print, " # duty=%.2f%%", 100.0 * get_duty_u16(self) / MAX_16_DUTY); + mp_printf(print, ", raw_duty=%d, resolution=%d", ledc_get_duty(self->mode, self->channel), timers[self->mode][self->timer].duty_resolution); + mp_printf(print, ", mode=%d, timer=%d, channel=%d", self->mode, self->timer, self->channel); + int clk_cfg = timers[self->mode][self->timer].clk_cfg; + mp_printf(print, ", clk_cfg=%d=", clk_cfg); + if (clk_cfg == LEDC_USE_RC_FAST_CLK) { + mp_printf(print, "RC_FAST_CLK"); + } + #if SOC_LEDC_SUPPORT_APB_CLOCK + else if (clk_cfg == LEDC_USE_APB_CLK) { + mp_printf(print, "APB_CLK"); + } + #endif + #if SOC_LEDC_SUPPORT_XTAL_CLOCK + else if (clk_cfg == LEDC_USE_XTAL_CLK) { + mp_printf(print, "XTAL_CLK"); + } + #endif + #if SOC_LEDC_SUPPORT_REF_TICK + else if (clk_cfg == LEDC_USE_REF_TICK) { + mp_printf(print, "REF_TICK"); + } + #endif + #if SOC_LEDC_SUPPORT_PLL_DIV_CLOCK + else if (clk_cfg == LEDC_USE_PLL_DIV_CLK) { + mp_printf(print, "PLL_CLK"); + } + #endif + else if (clk_cfg == LEDC_AUTO_CLK) { + mp_printf(print, "AUTO_CLK"); + } else { + mp_printf(print, "UNKNOWN"); + } + #endif + } else { + mp_printf(print, ")"); } - mp_printf(print, ")"); } // This called from pwm.init() method +// +// Check the current mode. +// If the frequency is changed, try to find a timer with the same frequency +// in the current mode, otherwise in the new mode. +// If the mode is changed, release the channel and select a new channel in the new mode. +// Then set the frequency with the same duty. static void mp_machine_pwm_init_helper(machine_pwm_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns }; - static const mp_arg_t allowed_args[] = { + + enum { ARG_freq, ARG_duty, ARG_duty_u16, ARG_duty_ns, ARG_invert, ARG_lightsleep }; + mp_arg_t allowed_args[] = { { MP_QSTR_freq, MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_duty, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_duty_u16, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, { MP_QSTR_duty_ns, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_invert, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = self->output_invert} }, + { MP_QSTR_lightsleep, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = self->lightsleep} }, }; mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); - int channel_idx = find_channel(self->pin, ANY_MODE); - if (channel_idx == -1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in all modes + self->lightsleep = args[ARG_lightsleep].u_bool; + + int freq = args[ARG_freq].u_int; + if (freq != -1) { + check_freq_ranges(self, freq, 40000000); } int duty = args[ARG_duty].u_int; int duty_u16 = args[ARG_duty_u16].u_int; int duty_ns = args[ARG_duty_ns].u_int; - if (((duty != -1) && (duty_u16 != -1)) || ((duty != -1) && (duty_ns != -1)) || ((duty_u16 != -1) && (duty_ns != -1))) { - mp_raise_ValueError(MP_ERROR_TEXT("only one of parameters 'duty', 'duty_u16' or 'duty_ns' is allowed")); + if (duty_u16 >= 0) { + check_duty_u16(self, duty_u16); + } else if (duty_ns >= 0) { + check_duty_ns(self, duty_ns); + } else if (duty >= 0) { + check_duty_u10(self, duty); + } else if (self->duty_scale == 0) { + self->duty_scale = DUTY_16; + self->duty_ui = PWM_DUTY; + } + + self->output_invert = args[ARG_invert].u_bool; + + // Check the current mode and channel + int mode = -1; + int channel = -1; + find_channel(self, &mode, &channel, freq); + if (channel < 0) { + mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("out of %sPWM channels:%d"), self->lightsleep ? "light sleep capable " : "", self->lightsleep ? LEDC_CHANNEL_MAX : LEDC_SPEED_MODE_MAX *LEDC_CHANNEL_MAX); } + self->mode = mode; + self->channel = channel; - int freq = args[ARG_freq].u_int; // Check if freq wasn't passed as an argument - if (freq == -1) { + if ((freq == -1) && (mode >= 0) && (channel >= 0)) { // Check if already set, otherwise use the default freq. // It is possible in case: // pwm = PWM(pin, freq=1000, duty=256) // pwm = PWM(pin, duty=128) - if (chans[channel_idx].timer_idx != -1) { - freq = timers[chans[channel_idx].timer_idx].freq_hz; + if (chans[mode][channel].timer >= 0) { + freq = timers[mode][chans[mode][channel].timer].freq; } if (freq <= 0) { freq = PWM_FREQ; } } - if ((freq <= 0) || (freq > 40000000)) { - mp_raise_ValueError(MP_ERROR_TEXT("frequency must be from 1Hz to 40MHz")); - } - - int timer_idx; - int current_timer_idx = chans[channel_idx].timer_idx; - bool current_in_use = is_timer_in_use(channel_idx, current_timer_idx); - if (current_in_use) { - timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, CHANNEL_IDX_TO_MODE(channel_idx)); - } else { - timer_idx = chans[channel_idx].timer_idx; - } - - if (timer_idx == -1) { - timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, ANY_MODE); - } - if (timer_idx == -1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in all modes - } - - int mode = TIMER_IDX_TO_MODE(timer_idx); - if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) { - // unregister old channel - chans[channel_idx].pin = -1; - chans[channel_idx].timer_idx = -1; - // find new channel - channel_idx = find_channel(self->pin, mode); - if (CHANNEL_IDX_TO_MODE(channel_idx) != mode) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM channels:%d"), PWM_CHANNEL_MAX); // in current mode - } - } - self->mode = mode; - self->timer = TIMER_IDX_TO_TIMER(timer_idx); - self->channel = CHANNEL_IDX_TO_CHANNEL(channel_idx); - - // New PWM assignment - if ((chans[channel_idx].pin == -1) || (chans[channel_idx].timer_idx != timer_idx)) { - configure_channel(self); - chans[channel_idx].pin = self->pin; - } - chans[channel_idx].timer_idx = timer_idx; - self->active = true; - - // Set timer frequency - set_freq(self, freq, &timers[timer_idx]); + set_freq_duty(self, freq); +} - // Set duty cycle? - if (duty_u16 != -1) { - set_duty_u16(self, duty_u16); - } else if (duty_ns != -1) { - set_duty_ns(self, duty_ns); - } else if (duty != -1) { - set_duty_u10(self, duty); - } else if (self->duty_x == 0) { - set_duty_u10(self, (1 << PWM_RES_10_BIT) / 2); // 50% - } +static void self_reset(machine_pwm_obj_t *self) { + self->mode = -1; + self->channel = -1; + self->timer = -1; + self->freq = -1; + self->duty_scale = 0; + self->duty_ui = 0; + self->channel_duty = -1; + self->output_invert = false; + self->output_invert_prev = false; + self->lightsleep = false; } // This called from PWM() constructor static mp_obj_t mp_machine_pwm_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, 2, true); - gpio_num_t pin_id = machine_pin_get_id(args[0]); - - // create PWM object from the given pin - machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); - self->pin = pin_id; - self->active = false; - self->mode = -1; - self->channel = -1; - self->timer = -1; - self->duty_x = 0; // start the PWM subsystem if it's not already running if (!pwm_inited) { pwm_init(); + #if FADE + ledc_fade_func_install(0); + #endif pwm_inited = true; } - // start the PWM running for this channel + // create PWM object from the given pin + machine_pwm_obj_t *self = mp_obj_malloc(machine_pwm_obj_t, &machine_pwm_type); + self_reset(self); + + self->pin = machine_pin_get_id(args[0]); + + // Process the remaining parameters. mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); mp_machine_pwm_init_helper(self, n_args - 1, args + 1, &kw_args); - return MP_OBJ_FROM_PTR(self); } // This called from pwm.deinit() method static void mp_machine_pwm_deinit(machine_pwm_obj_t *self) { - int channel_idx = CHANNEL_IDX(self->mode, self->channel); - pwm_deinit(channel_idx); - self->active = false; - self->mode = -1; - self->channel = -1; - self->timer = -1; - self->duty_x = 0; + pwm_deinit(self->mode, self->channel, self->output_invert); + self_reset(self); } // Set and get methods of PWM class @@ -608,57 +756,11 @@ static mp_obj_t mp_machine_pwm_freq_get(machine_pwm_obj_t *self) { static void mp_machine_pwm_freq_set(machine_pwm_obj_t *self, mp_int_t freq) { pwm_is_active(self); - if ((freq <= 0) || (freq > 40000000)) { - mp_raise_ValueError(MP_ERROR_TEXT("frequency must be from 1Hz to 40MHz")); - } - if (freq == timers[TIMER_IDX(self->mode, self->timer)].freq_hz) { + check_freq_ranges(self, freq, 40000000); + if (freq == timers[self->mode][self->timer].freq) { return; } - - int current_timer_idx = chans[CHANNEL_IDX(self->mode, self->channel)].timer_idx; - bool current_in_use = is_timer_in_use(CHANNEL_IDX(self->mode, self->channel), current_timer_idx); - - // Check if an already running timer with the same freq is running - int new_timer_idx = find_timer(freq, SAME_FREQ_ONLY, self->mode); - - // If no existing timer was found, and the current one is in use, then find a new one - if ((new_timer_idx == -1) && current_in_use) { - // Have to find a new timer - new_timer_idx = find_timer(freq, SAME_FREQ_OR_FREE, self->mode); - - if (new_timer_idx == -1) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("out of PWM timers:%d"), PWM_TIMER_MAX); // in current mode - } - } - - if ((new_timer_idx != -1) && (new_timer_idx != current_timer_idx)) { - // Bind the channel to the new timer - chans[self->channel].timer_idx = new_timer_idx; - - if (ledc_bind_channel_timer(self->mode, self->channel, TIMER_IDX_TO_TIMER(new_timer_idx)) != ESP_OK) { - mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("failed to bind timer to channel")); - } - - if (!current_in_use) { - // Free the old timer - check_esp_err(ledc_timer_pause(self->mode, self->timer)); - ledc_timer_config_t timer_config = { - .deconfigure = true, - .speed_mode = self->mode, - .timer_num = self->timer, - }; - check_esp_err(ledc_timer_config(&timer_config)); - // Flag it unused - timers[current_timer_idx].freq_hz = -1; - } - - current_timer_idx = new_timer_idx; - } - self->mode = TIMER_IDX_TO_MODE(current_timer_idx); - self->timer = TIMER_IDX_TO_TIMER(current_timer_idx); - - // Set the frequency - set_freq(self, freq, &timers[current_timer_idx]); + set_freq_duty(self, freq); } static mp_obj_t mp_machine_pwm_duty_get(machine_pwm_obj_t *self) { diff --git a/ports/esp32/machine_rtc.c b/ports/esp32/machine_rtc.c index a2f4bb132d..ee6b1ad5cb 100644 --- a/ports/esp32/machine_rtc.c +++ b/ports/esp32/machine_rtc.c @@ -82,8 +82,12 @@ _USER_MEM_ATTR uint8_t rtc_user_mem_data[MICROPY_HW_RTC_USER_MEM_MAX]; static const machine_rtc_obj_t machine_rtc_obj = {{&machine_rtc_type}}; machine_rtc_config_t machine_rtc_config = { + #if SOC_PM_SUPPORT_EXT1_WAKEUP .ext1_pins = 0, + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP .ext0_pin = -1 + #endif }; static mp_obj_t machine_rtc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { diff --git a/ports/esp32/machine_rtc.h b/ports/esp32/machine_rtc.h index ce2a5482a2..72717c5936 100644 --- a/ports/esp32/machine_rtc.h +++ b/ports/esp32/machine_rtc.h @@ -31,13 +31,25 @@ #include "modmachine.h" typedef struct { + #if SOC_PM_SUPPORT_EXT1_WAKEUP uint64_t ext1_pins; // set bit == pin# + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP int8_t ext0_pin; // just the pin#, -1 == None + #endif + #if SOC_TOUCH_SENSOR_SUPPORTED bool wake_on_touch : 1; + #endif + #if SOC_ULP_SUPPORTED bool wake_on_ulp : 1; + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP bool ext0_level : 1; wake_type_t ext0_wake_types; + #endif + #if SOC_PM_SUPPORT_EXT1_WAKEUP bool ext1_level : 1; + #endif } machine_rtc_config_t; extern machine_rtc_config_t machine_rtc_config; diff --git a/ports/esp32/machine_timer.c b/ports/esp32/machine_timer.c index 82b432bbac..a104288f6e 100644 --- a/ports/esp32/machine_timer.c +++ b/ports/esp32/machine_timer.c @@ -50,12 +50,13 @@ const mp_obj_type_t machine_timer_type; static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args); +static mp_obj_t machine_timer_deinit(mp_obj_t self_in); void machine_timer_deinit_all(void) { // Disable, deallocate and remove all timers from list machine_timer_obj_t **t = &MP_STATE_PORT(machine_timer_obj_head); while (*t != NULL) { - machine_timer_disable(*t); + machine_timer_deinit(*t); machine_timer_obj_t *next = (*t)->next; m_del_obj(machine_timer_obj_t, *t); *t = next; @@ -96,6 +97,7 @@ machine_timer_obj_t *machine_timer_create(mp_uint_t timer) { self = mp_obj_malloc(machine_timer_obj_t, &machine_timer_type); self->group = group; self->index = index; + self->handle = NULL; // Add the timer to the linked-list of timers self->next = MP_STATE_PORT(machine_timer_obj_head); @@ -131,9 +133,8 @@ void machine_timer_disable(machine_timer_obj_t *self) { } if (self->handle) { - // Free the interrupt handler. - esp_intr_free(self->handle); - self->handle = NULL; + // Disable the interrupt + ESP_ERROR_CHECK(esp_intr_disable(self->handle)); } // We let the disabled timer stay in the list, as it might be @@ -150,12 +151,16 @@ static void machine_timer_isr(void *self_in) { if (self->repeat) { timer_ll_enable_alarm(self->hal_context.dev, self->index, true); } - mp_sched_schedule(self->callback, self); - mp_hal_wake_main_task_from_isr(); + self->handler(self); } } -void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) { +static void machine_timer_isr_handler(machine_timer_obj_t *self) { + mp_sched_schedule(self->callback, self); + mp_hal_wake_main_task_from_isr(); +} + +void machine_timer_enable(machine_timer_obj_t *self) { // Initialise the timer. timer_hal_init(&self->hal_context, self->group, self->index); timer_ll_enable_counter(self->hal_context.dev, self->index, false); @@ -167,10 +172,17 @@ void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)) { // Allocate and enable the alarm interrupt. timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), false); timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index)); - ESP_ERROR_CHECK( - esp_intr_alloc(timer_group_periph_signals.groups[self->group].timer_irq_id[self->index], - TIMER_FLAGS, timer_isr, self, &self->handle) - ); + if (self->handle) { + ESP_ERROR_CHECK(esp_intr_enable(self->handle)); + } else { + ESP_ERROR_CHECK(esp_intr_alloc( + timer_group_periph_signals.groups[self->group].timer_irq_id[self->index], + TIMER_FLAGS, + machine_timer_isr, + self, + &self->handle + )); + } timer_ll_enable_intr(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index), true); // Enable the alarm to trigger at the given period. @@ -224,16 +236,22 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, mp_uint_t n } self->repeat = args[ARG_mode].u_int; + self->handler = machine_timer_isr_handler; self->callback = args[ARG_callback].u_obj; - self->handle = NULL; - machine_timer_enable(self, machine_timer_isr); + machine_timer_enable(self); return mp_const_none; } static mp_obj_t machine_timer_deinit(mp_obj_t self_in) { - machine_timer_disable(self_in); + machine_timer_obj_t *self = self_in; + + machine_timer_disable(self); + if (self->handle) { + ESP_ERROR_CHECK(esp_intr_free(self->handle)); + self->handle = NULL; + } return mp_const_none; } @@ -246,6 +264,9 @@ static MP_DEFINE_CONST_FUN_OBJ_KW(machine_timer_init_obj, 1, machine_timer_init) static mp_obj_t machine_timer_value(mp_obj_t self_in) { machine_timer_obj_t *self = self_in; + if (self->handle == NULL) { + mp_raise_ValueError(MP_ERROR_TEXT("timer not set")); + } uint64_t result = timer_ll_get_counter_value(self->hal_context.dev, self->index); return MP_OBJ_NEW_SMALL_INT((mp_uint_t)(result / (TIMER_SCALE / 1000))); // value in ms } diff --git a/ports/esp32/machine_timer.h b/ports/esp32/machine_timer.h index 914bedd86b..10fe2f39c9 100644 --- a/ports/esp32/machine_timer.h +++ b/ports/esp32/machine_timer.h @@ -55,12 +55,13 @@ typedef struct _machine_timer_obj_t { mp_obj_t callback; intr_handle_t handle; + void (*handler)(struct _machine_timer_obj_t *timer); struct _machine_timer_obj_t *next; } machine_timer_obj_t; machine_timer_obj_t *machine_timer_create(mp_uint_t timer); -void machine_timer_enable(machine_timer_obj_t *self, void (*timer_isr)); +void machine_timer_enable(machine_timer_obj_t *self); void machine_timer_disable(machine_timer_obj_t *self); #endif // MICROPY_INCLUDED_ESP32_MACHINE_TIMER_H diff --git a/ports/esp32/machine_uart.c b/ports/esp32/machine_uart.c index 73089ef463..982d9a7e27 100644 --- a/ports/esp32/machine_uart.c +++ b/ports/esp32/machine_uart.c @@ -59,6 +59,7 @@ #define UART_IRQ_BREAK (1 << UART_BREAK) #define MP_UART_ALLOWED_FLAGS (UART_IRQ_RX | UART_IRQ_RXIDLE | UART_IRQ_BREAK) #define RXIDLE_TIMER_MIN (5000) // 500 us +#define UART_QUEUE_SIZE (3) enum { RXIDLE_INACTIVE, @@ -109,30 +110,19 @@ static const char *_parity_name[] = {"None", "1", "0"}; { MP_ROM_QSTR(MP_QSTR_IRQ_RXIDLE), MP_ROM_INT(UART_IRQ_RXIDLE) }, \ { MP_ROM_QSTR(MP_QSTR_IRQ_BREAK), MP_ROM_INT(UART_IRQ_BREAK) }, \ -static void uart_timer_callback(void *self_in) { - machine_timer_obj_t *self = self_in; - - uint32_t intr_status = timer_ll_get_intr_status(self->hal_context.dev); - - if (intr_status & TIMER_LL_EVENT_ALARM(self->index)) { - timer_ll_clear_intr_status(self->hal_context.dev, TIMER_LL_EVENT_ALARM(self->index)); - if (self->repeat) { - timer_ll_enable_alarm(self->hal_context.dev, self->index, true); - } - } - +static void uart_timer_callback(machine_timer_obj_t *timer) { // The UART object is referred here by the callback field. - machine_uart_obj_t *uart = (machine_uart_obj_t *)self->callback; - if (uart->rxidle_state == RXIDLE_ALERT) { + machine_uart_obj_t *self = (machine_uart_obj_t *)timer->callback; + if (self->rxidle_state == RXIDLE_ALERT) { // At the first call, just switch the state - uart->rxidle_state = RXIDLE_ARMED; - } else if (uart->rxidle_state == RXIDLE_ARMED) { + self->rxidle_state = RXIDLE_ARMED; + } else if (self->rxidle_state == RXIDLE_ARMED) { // At the second call, run the irq callback and stop the timer - uart->rxidle_state = RXIDLE_STANDBY; - uart->mp_irq_flags = UART_IRQ_RXIDLE; - mp_irq_handler(uart->mp_irq_obj); + self->rxidle_state = RXIDLE_STANDBY; + self->mp_irq_flags = UART_IRQ_RXIDLE; + mp_irq_handler(self->mp_irq_obj); mp_hal_wake_main_task_from_isr(); - machine_timer_disable(uart->rxidle_timer); + machine_timer_disable(self->rxidle_timer); } } @@ -149,9 +139,7 @@ static void uart_event_task(void *self_in) { if (self->mp_irq_trigger & UART_IRQ_RXIDLE) { if (self->rxidle_state != RXIDLE_INACTIVE) { if (self->rxidle_state == RXIDLE_STANDBY) { - self->rxidle_timer->repeat = true; - self->rxidle_timer->handle = NULL; - machine_timer_enable(self->rxidle_timer, uart_timer_callback); + machine_timer_enable(self->rxidle_timer); } } self->rxidle_state = RXIDLE_ALERT; @@ -174,6 +162,13 @@ static void uart_event_task(void *self_in) { } } +static inline void uart_event_task_create(machine_uart_obj_t *self) { + if (xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self, + ESP_TASKD_EVENT_PRIO, (TaskHandle_t *)&self->uart_event_task, MP_TASK_COREID) != pdPASS) { + mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("failed to create UART event task")); + } +} + static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); uint32_t baudrate; @@ -250,7 +245,7 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, // wait for all data to be transmitted before changing settings uart_wait_tx_done(self->uart_num, pdMS_TO_TICKS(1000)); - if (args[ARG_txbuf].u_int >= 0 || args[ARG_rxbuf].u_int >= 0) { + if ((args[ARG_txbuf].u_int >= 0 && args[ARG_txbuf].u_int != self->txbuf) || (args[ARG_rxbuf].u_int >= 0 && args[ARG_rxbuf].u_int != self->rxbuf)) { // must reinitialise driver to change the tx/rx buffer size #if MICROPY_HW_ENABLE_UART_REPL if (self->uart_num == MICROPY_HW_UART_REPL) { @@ -275,9 +270,12 @@ static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, check_esp_err(uart_get_word_length(self->uart_num, &uartcfg.data_bits)); check_esp_err(uart_get_parity(self->uart_num, &uartcfg.parity)); check_esp_err(uart_get_stop_bits(self->uart_num, &uartcfg.stop_bits)); - check_esp_err(uart_driver_delete(self->uart_num)); + mp_machine_uart_deinit(self); check_esp_err(uart_param_config(self->uart_num, &uartcfg)); - check_esp_err(uart_driver_install(self->uart_num, self->rxbuf, self->txbuf, 0, NULL, 0)); + check_esp_err(uart_driver_install(self->uart_num, self->rxbuf, self->txbuf, UART_QUEUE_SIZE, &self->uart_queue, 0)); + if (self->mp_irq_obj != NULL && self->mp_irq_obj->handler != mp_const_none) { + uart_event_task_create(self); + } } // set baudrate @@ -437,7 +435,8 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg self->timeout_char = 0; self->invert = 0; self->flowcontrol = 0; - self->uart_event_task = 0; + self->uart_event_task = NULL; + self->uart_queue = NULL; self->rxidle_state = RXIDLE_INACTIVE; switch (uart_num) { @@ -470,12 +469,13 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg { // Remove any existing configuration check_esp_err(uart_driver_delete(self->uart_num)); + self->uart_queue = NULL; // init the peripheral // Setup check_esp_err(uart_param_config(self->uart_num, &uartcfg)); - check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, 3, &self->uart_queue, 0)); + check_esp_err(uart_driver_install(uart_num, self->rxbuf, self->txbuf, UART_QUEUE_SIZE, &self->uart_queue, 0)); } mp_map_t kw_args; @@ -489,7 +489,12 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg } static void mp_machine_uart_deinit(machine_uart_obj_t *self) { + if (self->uart_event_task != NULL) { + vTaskDelete(self->uart_event_task); + self->uart_event_task = NULL; + } check_esp_err(uart_driver_delete(self->uart_num)); + self->uart_queue = NULL; } static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) { @@ -535,11 +540,11 @@ static void uart_irq_configure_timer(machine_uart_obj_t *self, mp_uint_t trigger } self->rxidle_period = period; self->rxidle_timer->period = period; + self->rxidle_timer->handler = uart_timer_callback; // The Python callback is not used. So use this // data field to hold a reference to the UART object. self->rxidle_timer->callback = self; self->rxidle_timer->repeat = true; - self->rxidle_timer->handle = NULL; self->rxidle_state = RXIDLE_STANDBY; } } @@ -568,6 +573,12 @@ static const mp_irq_methods_t uart_irq_methods = { }; static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args, mp_arg_val_t *args) { + #if MICROPY_HW_ENABLE_UART_REPL + if (self->uart_num == MICROPY_HW_UART_REPL) { + mp_raise_ValueError(MP_ERROR_TEXT("UART does not support IRQs")); + } + #endif + if (self->mp_irq_obj == NULL) { self->mp_irq_trigger = 0; self->mp_irq_obj = mp_irq_new(&uart_irq_methods, MP_OBJ_FROM_PTR(self)); @@ -597,9 +608,8 @@ static mp_irq_obj_t *mp_machine_uart_irq(machine_uart_obj_t *self, bool any_args uart_irq_configure_timer(self, trigger); // Start a task for handling events - if (handler != mp_const_none && self->uart_event_task == NULL) { - xTaskCreatePinnedToCore(uart_event_task, "uart_event_task", 2048, self, - ESP_TASKD_EVENT_PRIO, (TaskHandle_t *)&self->uart_event_task, MP_TASK_COREID); + if (handler != mp_const_none && self->uart_event_task == NULL && self->uart_queue != NULL) { + uart_event_task_create(self); } else if (handler == mp_const_none && self->uart_event_task != NULL) { vTaskDelete(self->uart_event_task); self->uart_event_task = NULL; diff --git a/ports/esp32/main.c b/ports/esp32/main.c index 4f0c27ee07..f048aa85f5 100644 --- a/ports/esp32/main.c +++ b/ports/esp32/main.c @@ -38,6 +38,7 @@ #include "nvs_flash.h" #include "esp_task.h" #include "esp_event.h" +#include "esp_flash.h" #include "esp_log.h" #include "esp_memory_utils.h" #include "esp_psram.h" @@ -214,18 +215,45 @@ void boardctrl_startup(void) { nvs_flash_erase(); nvs_flash_init(); } + + // Query the physical size of the SPI flash and store it in the size + // variable of the global, default SPI flash handle. + esp_flash_get_physical_size(NULL, &esp_flash_default_chip->size); + + // If there is no filesystem partition (no "vfs" or "ffat"), add a "vfs" partition + // that extends from the end of the application partition up to the end of flash. + if (esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "vfs") == NULL + && esp_partition_find_first(ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_ANY, "ffat") == NULL) { + // No "vfs" or "ffat" partition, so try to create one. + + // Find the end of the last partition that exists in the partition table. + size_t offset = 0; + esp_partition_iterator_t iter = esp_partition_find(ESP_PARTITION_TYPE_ANY, ESP_PARTITION_SUBTYPE_ANY, NULL); + while (iter != NULL) { + const esp_partition_t *part = esp_partition_get(iter); + offset = MAX(offset, part->address + part->size); + iter = esp_partition_next(iter); + } + + // If we found the application partition and there is some space between the end of + // that and the end of flash, create a "vfs" partition taking up all of that space. + if (offset > 0 && esp_flash_default_chip->size > offset) { + size_t size = esp_flash_default_chip->size - offset; + esp_partition_register_external(esp_flash_default_chip, offset, size, "vfs", ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_FAT, NULL); + } + } } -void app_main(void) { +void MICROPY_ESP_IDF_ENTRY(void) { // Hook for a board to run code at start up. - // This defaults to initialising NVS. + // This defaults to initialising NVS and detecting the flash size. MICROPY_BOARD_STARTUP(); // Create and transfer control to the MicroPython task. xTaskCreatePinnedToCore(mp_task, "mp_task", MICROPY_TASK_STACK_SIZE / sizeof(StackType_t), NULL, MP_TASK_PRIORITY, &mp_main_task_handle, MP_TASK_COREID); } -void nlr_jump_fail(void *val) { +MP_WEAK void nlr_jump_fail(void *val) { printf("NLR jump failed, val=%p\n", val); esp_restart(); } diff --git a/ports/esp32/main/idf_component.yml b/ports/esp32/main/idf_component.yml index ccbde1f27e..f7773f4f4e 100644 --- a/ports/esp32/main/idf_component.yml +++ b/ports/esp32/main/idf_component.yml @@ -5,5 +5,10 @@ dependencies: rules: - if: "target in [esp32s2, esp32s3]" version: "~1.0.0" + espressif/lan867x: + version: "~1.0.0" + rules: + - if: "target == esp32" + - if: "idf_version >=5.3" idf: version: ">=5.2.0" diff --git a/ports/esp32/modesp32.c b/ports/esp32/modesp32.c index 164b191826..fcd6ed9fa8 100644 --- a/ports/esp32/modesp32.c +++ b/ports/esp32/modesp32.c @@ -30,7 +30,6 @@ #include <time.h> #include <sys/time.h> #include "driver/gpio.h" -#include "driver/adc.h" #include "esp_heap_caps.h" #include "multi_heap.h" @@ -48,22 +47,30 @@ #include "../multi_heap_platform.h" #include "../heap_private.h" +#if SOC_TOUCH_SENSOR_SUPPORTED static mp_obj_t esp32_wake_on_touch(const mp_obj_t wake) { + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin != -1) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif machine_rtc_config.wake_on_touch = mp_obj_is_true(wake); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_wake_on_touch_obj, esp32_wake_on_touch); +#endif +#if SOC_PM_SUPPORT_EXT0_WAKEUP static mp_obj_t esp32_wake_on_ext0(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { + #if SOC_TOUCH_SENSOR_SUPPORTED if (machine_rtc_config.wake_on_touch) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif + enum {ARG_pin, ARG_level}; const mp_arg_t allowed_args[] = { { MP_QSTR_pin, MP_ARG_OBJ, {.u_obj = mp_obj_new_int(machine_rtc_config.ext0_pin)} }, @@ -90,7 +97,9 @@ static mp_obj_t esp32_wake_on_ext0(size_t n_args, const mp_obj_t *pos_args, mp_m return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext0_obj, 0, esp32_wake_on_ext0); +#endif +#if SOC_PM_SUPPORT_EXT1_WAKEUP static mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { enum {ARG_pins, ARG_level}; const mp_arg_t allowed_args[] = { @@ -126,15 +135,20 @@ static mp_obj_t esp32_wake_on_ext1(size_t n_args, const mp_obj_t *pos_args, mp_m return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_KW(esp32_wake_on_ext1_obj, 0, esp32_wake_on_ext1); +#endif +#if SOC_ULP_SUPPORTED static mp_obj_t esp32_wake_on_ulp(const mp_obj_t wake) { + #if SOC_PM_SUPPORT_EXT0_WAKEUP if (machine_rtc_config.ext0_pin != -1) { mp_raise_ValueError(MP_ERROR_TEXT("no resources")); } + #endif machine_rtc_config.wake_on_ulp = mp_obj_is_true(wake); return mp_const_none; } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_wake_on_ulp_obj, esp32_wake_on_ulp); +#endif #if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP static mp_obj_t esp32_gpio_deep_sleep_hold(const mp_obj_t enable) { @@ -212,13 +226,64 @@ static mp_obj_t esp32_idf_heap_info(const mp_obj_t cap_in) { } static MP_DEFINE_CONST_FUN_OBJ_1(esp32_idf_heap_info_obj, esp32_idf_heap_info); +#if CONFIG_FREERTOS_USE_TRACE_FACILITY +static mp_obj_t esp32_idf_task_info(void) { + const size_t task_count_max = uxTaskGetNumberOfTasks(); + TaskStatus_t *task_array = m_new(TaskStatus_t, task_count_max); + uint32_t total_time; + const size_t task_count = uxTaskGetSystemState(task_array, task_count_max, &total_time); + + mp_obj_list_t *task_list = MP_OBJ_TO_PTR(mp_obj_new_list(task_count, NULL)); + for (size_t i = 0; i < task_count; i++) { + mp_obj_t task_data[] = { + mp_obj_new_int_from_uint((mp_uint_t)task_array[i].xHandle), + mp_obj_new_str(task_array[i].pcTaskName, strlen(task_array[i].pcTaskName)), + MP_OBJ_NEW_SMALL_INT(task_array[i].eCurrentState), + MP_OBJ_NEW_SMALL_INT(task_array[i].uxCurrentPriority), + #if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS + mp_obj_new_int_from_uint(task_array[i].ulRunTimeCounter), + #else + mp_const_none, + #endif + mp_obj_new_int_from_uint(task_array[i].usStackHighWaterMark), + #if CONFIG_FREERTOS_VTASKLIST_INCLUDE_COREID + MP_OBJ_NEW_SMALL_INT(task_array[i].xCoreID), + #else + mp_const_none, + #endif + }; + task_list->items[i] = mp_obj_new_tuple(7, task_data); + } + + m_del(TaskStatus_t, task_array, task_count_max); + mp_obj_t task_stats[] = { + #if CONFIG_FREERTOS_GENERATE_RUN_TIME_STATS + MP_OBJ_NEW_SMALL_INT(total_time), + #else + mp_const_none, + #endif + task_list + }; + return mp_obj_new_tuple(2, task_stats); +} +static MP_DEFINE_CONST_FUN_OBJ_0(esp32_idf_task_info_obj, esp32_idf_task_info); +#endif + static const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_esp32) }, + #if SOC_TOUCH_SENSOR_SUPPORTED { MP_ROM_QSTR(MP_QSTR_wake_on_touch), MP_ROM_PTR(&esp32_wake_on_touch_obj) }, + #endif + #if SOC_PM_SUPPORT_EXT0_WAKEUP { MP_ROM_QSTR(MP_QSTR_wake_on_ext0), MP_ROM_PTR(&esp32_wake_on_ext0_obj) }, + #endif + #if SOC_PM_SUPPORT_EXT1_WAKEUP { MP_ROM_QSTR(MP_QSTR_wake_on_ext1), MP_ROM_PTR(&esp32_wake_on_ext1_obj) }, + #endif + #if SOC_ULP_SUPPORTED { MP_ROM_QSTR(MP_QSTR_wake_on_ulp), MP_ROM_PTR(&esp32_wake_on_ulp_obj) }, + #endif #if !SOC_GPIO_SUPPORT_HOLD_SINGLE_IO_IN_DSLP { MP_ROM_QSTR(MP_QSTR_gpio_deep_sleep_hold), MP_ROM_PTR(&esp32_gpio_deep_sleep_hold_obj) }, #endif @@ -228,6 +293,9 @@ static const mp_rom_map_elem_t esp32_module_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_mcu_temperature), MP_ROM_PTR(&esp32_mcu_temperature_obj) }, #endif { MP_ROM_QSTR(MP_QSTR_idf_heap_info), MP_ROM_PTR(&esp32_idf_heap_info_obj) }, + #if CONFIG_FREERTOS_USE_TRACE_FACILITY + { MP_ROM_QSTR(MP_QSTR_idf_task_info), MP_ROM_PTR(&esp32_idf_task_info_obj) }, + #endif { MP_ROM_QSTR(MP_QSTR_NVS), MP_ROM_PTR(&esp32_nvs_type) }, { MP_ROM_QSTR(MP_QSTR_Partition), MP_ROM_PTR(&esp32_partition_type) }, diff --git a/ports/esp32/modmachine.c b/ports/esp32/modmachine.c index 0c1b94d02d..0d7ea44c66 100644 --- a/ports/esp32/modmachine.c +++ b/ports/esp32/modmachine.c @@ -178,7 +178,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { machine_sleep_helper(MACHINE_WAKE_SLEEP, n_args, args); }; -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { machine_sleep_helper(MACHINE_WAKE_DEEPSLEEP, n_args, args); mp_machine_reset(); }; @@ -221,7 +221,7 @@ static mp_int_t mp_machine_reset_cause(void) { #include "esp32s3/rom/usb/chip_usb_dw_wrapper.h" #endif -NORETURN static void machine_bootloader_rtc(void) { +MP_NORETURN static void machine_bootloader_rtc(void) { #if CONFIG_IDF_TARGET_ESP32S3 && MICROPY_HW_USB_CDC usb_usj_mode(); usb_dc_prepare_persist(); @@ -233,7 +233,7 @@ NORETURN static void machine_bootloader_rtc(void) { #endif #ifdef MICROPY_BOARD_ENTER_BOOTLOADER -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); for (;;) { } @@ -254,7 +254,7 @@ static mp_obj_t machine_wake_reason(size_t n_args, const mp_obj_t *pos_args, mp_ } static MP_DEFINE_CONST_FUN_OBJ_KW(machine_wake_reason_obj, 0, machine_wake_reason); -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { esp_restart(); } diff --git a/ports/esp32/modnetwork.h b/ports/esp32/modnetwork.h index 387f961976..ba69d5cc0f 100644 --- a/ports/esp32/modnetwork.h +++ b/ports/esp32/modnetwork.h @@ -28,7 +28,32 @@ #include "esp_netif.h" -enum { PHY_LAN8710, PHY_LAN8720, PHY_IP101, PHY_RTL8201, PHY_DP83848, PHY_KSZ8041, PHY_KSZ8081, PHY_KSZ8851SNL = 100, PHY_DM9051, PHY_W5500 }; +// lan867x component requires newer IDF version +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0) && CONFIG_IDF_TARGET_ESP32 +#define PHY_LAN867X_ENABLED (1) +#else +#define PHY_LAN867X_ENABLED (0) +#endif + +// PHY_GENERIC support requires newer IDF version +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) && CONFIG_IDF_TARGET_ESP32 +#define PHY_GENERIC_ENABLED (1) +#else +#define PHY_GENERIC_ENABLED (0) +#endif + +enum { + // PHYs supported by the internal Ethernet MAC: + PHY_LAN8710, PHY_LAN8720, PHY_IP101, PHY_RTL8201, PHY_DP83848, PHY_KSZ8041, PHY_KSZ8081, + #if PHY_LAN867X_ENABLED + PHY_LAN8670, + #endif + #if PHY_GENERIC_ENABLED + PHY_GENERIC, + #endif + // PHYs which are actually SPI Ethernet MAC+PHY chips: + PHY_KSZ8851SNL = 100, PHY_DM9051, PHY_W5500 +}; #define IS_SPI_PHY(NUM) (NUM >= 100) enum { ETH_INITIALIZED, ETH_STARTED, ETH_STOPPED, ETH_CONNECTED, ETH_DISCONNECTED, ETH_GOT_IP }; @@ -52,7 +77,7 @@ extern const mp_obj_type_t esp_network_wlan_type; MP_DECLARE_CONST_FUN_OBJ_0(esp_network_initialize_obj); MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_get_wlan_obj); MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_get_lan_obj); -MP_DECLARE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj); +extern const struct _mp_obj_type_t esp_network_ppp_lwip_type; MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_ifconfig_obj); MP_DECLARE_CONST_FUN_OBJ_KW(esp_network_ipconfig_obj); MP_DECLARE_CONST_FUN_OBJ_KW(esp_nic_ipconfig_obj); @@ -61,7 +86,7 @@ MP_DECLARE_CONST_FUN_OBJ_VAR_BETWEEN(esp_network_phy_mode_obj); mp_obj_t esp_ifname(esp_netif_t *netif); -NORETURN void esp_exceptions_helper(esp_err_t e); +MP_NORETURN void esp_exceptions_helper(esp_err_t e); static inline void esp_exceptions(esp_err_t e) { if (e != ESP_OK) { diff --git a/ports/esp32/modnetwork_globals.h b/ports/esp32/modnetwork_globals.h index 1aad785ee3..12252ddbc5 100644 --- a/ports/esp32/modnetwork_globals.h +++ b/ports/esp32/modnetwork_globals.h @@ -8,7 +8,7 @@ { MP_ROM_QSTR(MP_QSTR_LAN), MP_ROM_PTR(&esp_network_get_lan_obj) }, #endif #if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT) -{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_make_new_obj) }, +{ MP_ROM_QSTR(MP_QSTR_PPP), MP_ROM_PTR(&esp_network_ppp_lwip_type) }, #endif { MP_ROM_QSTR(MP_QSTR_phy_mode), MP_ROM_PTR(&esp_network_phy_mode_obj) }, { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&esp_network_ipconfig_obj) }, @@ -47,6 +47,12 @@ { MP_ROM_QSTR(MP_QSTR_PHY_DP83848), MP_ROM_INT(PHY_DP83848) }, { MP_ROM_QSTR(MP_QSTR_PHY_KSZ8041), MP_ROM_INT(PHY_KSZ8041) }, { MP_ROM_QSTR(MP_QSTR_PHY_KSZ8081), MP_ROM_INT(PHY_KSZ8081) }, +#if PHY_LAN867X_ENABLED +{ MP_ROM_QSTR(MP_QSTR_PHY_LAN8670), MP_ROM_INT(PHY_LAN8670) }, +#endif +#if PHY_GENERIC_ENABLED +{ MP_ROM_QSTR(MP_QSTR_PHY_GENERIC), MP_ROM_INT(PHY_GENERIC) }, +#endif #if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL { MP_ROM_QSTR(MP_QSTR_PHY_KSZ8851SNL), MP_ROM_INT(PHY_KSZ8851SNL) }, diff --git a/ports/esp32/modsocket.c b/ports/esp32/modsocket.c index 7ea5e855d3..2050d1d04d 100644 --- a/ports/esp32/modsocket.c +++ b/ports/esp32/modsocket.c @@ -611,7 +611,7 @@ static MP_DEFINE_CONST_FUN_OBJ_2(socket_setblocking_obj, socket_setblocking); // XXX this can end up waiting a very long time if the content is dribbled in one character // at a time, as the timeout resets each time a recvfrom succeeds ... this is probably not // good behaviour. -static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, +static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, mp_int_t flags, struct sockaddr *from, socklen_t *from_len, int *errcode) { socket_obj_t *sock = MP_OBJ_TO_PTR(self_in); @@ -645,7 +645,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, if (release_gil) { MP_THREAD_GIL_EXIT(); } - int r = lwip_recvfrom(sock->fd, buf, size, 0, from, from_len); + int r = lwip_recvfrom(sock->fd, buf, size, flags, from, from_len); if (release_gil) { MP_THREAD_GIL_ENTER(); } @@ -655,7 +655,7 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, if (r >= 0) { return r; } - if (errno != EWOULDBLOCK) { + if (errno != EWOULDBLOCK || (flags & MSG_DONTWAIT)) { *errcode = errno; return MP_STREAM_ERROR; } @@ -666,14 +666,17 @@ static mp_uint_t _socket_read_data(mp_obj_t self_in, void *buf, size_t size, return MP_STREAM_ERROR; } -mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, +mp_obj_t _socket_recvfrom(size_t n_args, const mp_obj_t *args, struct sockaddr *from, socklen_t *from_len) { - size_t len = mp_obj_get_int(len_in); + mp_obj_t self_in = args[0]; + size_t len = mp_obj_get_int(args[1]); + int flags = n_args > 2 ? mp_obj_get_int(args[2]) : 0; + vstr_t vstr; vstr_init_len(&vstr, len); int errcode; - mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, from, from_len, &errcode); + mp_uint_t ret = _socket_read_data(self_in, vstr.buf, len, flags, from, from_len, &errcode); if (ret == MP_STREAM_ERROR) { mp_raise_OSError(errcode); } @@ -682,17 +685,17 @@ mp_obj_t _socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in, return mp_obj_new_bytes_from_vstr(&vstr); } -static mp_obj_t socket_recv(mp_obj_t self_in, mp_obj_t len_in) { - return _socket_recvfrom(self_in, len_in, NULL, NULL); +static mp_obj_t socket_recv(size_t n_args, const mp_obj_t *args) { + return _socket_recvfrom(n_args, args, NULL, NULL); } -static MP_DEFINE_CONST_FUN_OBJ_2(socket_recv_obj, socket_recv); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recv_obj, 2, 3, socket_recv); -static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { +static mp_obj_t socket_recvfrom(size_t n_args, const mp_obj_t *args) { struct sockaddr from; socklen_t fromlen = sizeof(from); mp_obj_t tuple[2]; - tuple[0] = _socket_recvfrom(self_in, len_in, &from, &fromlen); + tuple[0] = _socket_recvfrom(n_args, args, &from, &fromlen); uint8_t *ip = (uint8_t *)&((struct sockaddr_in *)&from)->sin_addr; mp_uint_t port = lwip_ntohs(((struct sockaddr_in *)&from)->sin_port); @@ -700,7 +703,7 @@ static mp_obj_t socket_recvfrom(mp_obj_t self_in, mp_obj_t len_in) { return mp_obj_new_tuple(2, tuple); } -static MP_DEFINE_CONST_FUN_OBJ_2(socket_recvfrom_obj, socket_recvfrom); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_recvfrom_obj, 2, 3, socket_recvfrom); int _socket_send(socket_obj_t *sock, const char *data, size_t datalen) { int sentlen = 0; @@ -789,7 +792,7 @@ static mp_obj_t socket_makefile(size_t n_args, const mp_obj_t *args) { static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(socket_makefile_obj, 1, 3, socket_makefile); static mp_uint_t socket_stream_read(mp_obj_t self_in, void *buf, mp_uint_t size, int *errcode) { - return _socket_read_data(self_in, buf, size, NULL, NULL, errcode); + return _socket_read_data(self_in, buf, size, 0, NULL, NULL, errcode); } static mp_uint_t socket_stream_write(mp_obj_t self_in, const void *buf, mp_uint_t size, int *errcode) { @@ -1010,6 +1013,8 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = { { MP_ROM_QSTR(MP_QSTR_SO_BINDTODEVICE), MP_ROM_INT(SO_BINDTODEVICE) }, { MP_ROM_QSTR(MP_QSTR_IP_ADD_MEMBERSHIP), MP_ROM_INT(IP_ADD_MEMBERSHIP) }, { MP_ROM_QSTR(MP_QSTR_TCP_NODELAY), MP_ROM_INT(TCP_NODELAY) }, + { MP_ROM_QSTR(MP_QSTR_MSG_PEEK), MP_ROM_INT(MSG_PEEK) }, + { MP_ROM_QSTR(MP_QSTR_MSG_DONTWAIT), MP_ROM_INT(MSG_DONTWAIT) }, }; static MP_DEFINE_CONST_DICT(mp_module_socket_globals, mp_module_socket_globals_table); diff --git a/ports/esp32/modtime.c b/ports/esp32/modtime.c index 4695dd23e7..991f2cf578 100644 --- a/ports/esp32/modtime.c +++ b/ports/esp32/modtime.c @@ -54,5 +54,5 @@ static mp_obj_t mp_time_localtime_get(void) { static mp_obj_t mp_time_time_get(void) { struct timeval tv; gettimeofday(&tv, NULL); - return mp_obj_new_int(tv.tv_sec); + return timeutils_obj_from_timestamp(tv.tv_sec); } diff --git a/ports/esp32/mpconfigport.h b/ports/esp32/mpconfigport.h index b5b7d63a56..a6f103cdef 100644 --- a/ports/esp32/mpconfigport.h +++ b/ports/esp32/mpconfigport.h @@ -62,7 +62,8 @@ #define MICROPY_STACK_CHECK_MARGIN (1024) #define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) -#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) +#define MICROPY_ERROR_REPORTING (MICROPY_ERROR_REPORTING_NORMAL) // Debugging Note: Increase the error reporting level to view + // __FUNCTION__, __LINE__, __FILE__ in check_esp_err() exceptions #define MICROPY_WARNINGS (1) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_STREAMS_POSIX_API (1) @@ -123,6 +124,7 @@ #define MICROPY_PY_MACHINE_ADC_INCLUDEFILE "ports/esp32/machine_adc.c" #define MICROPY_PY_MACHINE_ADC_ATTEN_WIDTH (1) #define MICROPY_PY_MACHINE_ADC_INIT (1) +#define MICROPY_PY_MACHINE_ADC_DEINIT (1) #define MICROPY_PY_MACHINE_ADC_READ (1) #define MICROPY_PY_MACHINE_ADC_READ_UV (1) #define MICROPY_PY_MACHINE_ADC_BLOCK (1) @@ -391,3 +393,8 @@ void boardctrl_startup(void); #ifndef MICROPY_PY_STRING_TX_GIL_THRESHOLD #define MICROPY_PY_STRING_TX_GIL_THRESHOLD (20) #endif + +// Code can override this to provide a custom ESP-IDF entry point. +#ifndef MICROPY_ESP_IDF_ENTRY +#define MICROPY_ESP_IDF_ENTRY app_main +#endif diff --git a/ports/esp32/mpnimbleport.c b/ports/esp32/mpnimbleport.c index ce4b77727a..77185883fd 100644 --- a/ports/esp32/mpnimbleport.c +++ b/ports/esp32/mpnimbleport.c @@ -58,7 +58,7 @@ void mp_bluetooth_nimble_port_start(void) { nimble_port_freertos_init(ble_host_task); } -void mp_bluetooth_nimble_port_shutdown(void) { +int mp_bluetooth_nimble_port_shutdown(void) { DEBUG_printf("mp_bluetooth_nimble_port_shutdown\n"); #if MICROPY_PY_BLUETOOTH_USE_SYNC_EVENTS_WITH_INTERLOCK @@ -79,6 +79,8 @@ void mp_bluetooth_nimble_port_shutdown(void) { // Mark stack as shutdown. mp_bluetooth_nimble_ble_state = MP_BLUETOOTH_NIMBLE_BLE_STATE_OFF; + + return 0; } #endif diff --git a/ports/esp32/mpthreadport.c b/ports/esp32/mpthreadport.c index 962b5780d0..67917e33e5 100644 --- a/ports/esp32/mpthreadport.c +++ b/ports/esp32/mpthreadport.c @@ -41,10 +41,16 @@ #define MP_THREAD_DEFAULT_STACK_SIZE (MP_THREAD_MIN_STACK_SIZE + MICROPY_STACK_CHECK_MARGIN) #define MP_THREAD_PRIORITY (ESP_TASK_PRIO_MIN + 1) +typedef enum { + MP_THREAD_RUN_STATE_NEW, + MP_THREAD_RUN_STATE_RUNNING, + MP_THREAD_RUN_STATE_FINISHED, +} mp_thread_run_state_t; + // this structure forms a linked list, one node per active thread typedef struct _mp_thread_t { TaskHandle_t id; // system id of thread - int ready; // whether the thread is ready and running + mp_thread_run_state_t run_state; // current run state of the thread void *arg; // thread Python args, a GC root pointer void *stack; // pointer to the stack size_t stack_len; // number of words in the stack @@ -60,18 +66,16 @@ void mp_thread_init(void *stack, uint32_t stack_len) { mp_thread_set_state(&mp_state_ctx.thread); // create the first entry in the linked list of all threads thread_entry0.id = xTaskGetCurrentTaskHandle(); - thread_entry0.ready = 1; + thread_entry0.run_state = MP_THREAD_RUN_STATE_RUNNING; thread_entry0.arg = NULL; thread_entry0.stack = stack; thread_entry0.stack_len = stack_len; thread_entry0.next = NULL; + thread = &thread_entry0; mp_thread_mutex_init(&thread_mutex); // memory barrier to ensure above data is committed __sync_synchronize(); - - // vTaskPreDeletionHook needs the thread ready after thread_mutex is ready - thread = &thread_entry0; } void mp_thread_gc_others(void) { @@ -82,7 +86,7 @@ void mp_thread_gc_others(void) { if (th->id == xTaskGetCurrentTaskHandle()) { continue; } - if (!th->ready) { + if (th->run_state != MP_THREAD_RUN_STATE_RUNNING) { continue; } gc_collect_root(th->stack, th->stack_len); @@ -106,7 +110,7 @@ void mp_thread_start(void) { mp_thread_mutex_lock(&thread_mutex, 1); for (mp_thread_t *th = thread; th != NULL; th = th->next) { if (th->id == xTaskGetCurrentTaskHandle()) { - th->ready = 1; + th->run_state = MP_THREAD_RUN_STATE_RUNNING; break; } } @@ -116,12 +120,22 @@ void mp_thread_start(void) { static void *(*ext_thread_entry)(void *) = NULL; static void freertos_entry(void *arg) { + // Run the Python code. if (ext_thread_entry) { ext_thread_entry(arg); } - vTaskDelete(NULL); - for (;;) {; + + // Remove the thread from the linked-list of active threads. + mp_thread_mutex_lock(&thread_mutex, 1); + for (mp_thread_t **th = &thread; *th != NULL; th = &(*th)->next) { + if ((*th)->id == xTaskGetCurrentTaskHandle()) { + *th = (*th)->next; + } } + mp_thread_mutex_unlock(&thread_mutex); + + // Delete this FreeRTOS task (this call to vTaskDelete will not return). + vTaskDelete(NULL); } mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_size, int priority, char *name) { @@ -147,7 +161,7 @@ mp_uint_t mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_s } // add thread to linked list of all threads - th->ready = 0; + th->run_state = MP_THREAD_RUN_STATE_NEW; th->arg = arg; th->stack = pxTaskGetStackStart(th->id); th->stack_len = *stack_size / sizeof(uintptr_t); @@ -167,33 +181,7 @@ void mp_thread_finish(void) { mp_thread_mutex_lock(&thread_mutex, 1); for (mp_thread_t *th = thread; th != NULL; th = th->next) { if (th->id == xTaskGetCurrentTaskHandle()) { - th->ready = 0; - break; - } - } - mp_thread_mutex_unlock(&thread_mutex); -} - -// This is called either from vTaskDelete() or from the FreeRTOS idle task, so -// may not be within Python context. Therefore MP_STATE_THREAD may not be valid -// and it does not have the GIL. -void vTaskPreDeletionHook(void *tcb) { - if (thread == NULL) { - // threading not yet initialised - return; - } - mp_thread_t *prev = NULL; - mp_thread_mutex_lock(&thread_mutex, 1); - for (mp_thread_t *th = thread; th != NULL; prev = th, th = th->next) { - // unlink the node from the list - if ((void *)th->id == tcb) { - if (prev != NULL) { - prev->next = th->next; - } else { - // move the start pointer - thread = th->next; - } - // The "th" memory will eventually be reclaimed by the GC. + th->run_state = MP_THREAD_RUN_STATE_FINISHED; break; } } @@ -221,32 +209,22 @@ void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) { } void mp_thread_deinit(void) { - for (;;) { - // Find a task to delete - TaskHandle_t id = NULL; - mp_thread_mutex_lock(&thread_mutex, 1); - for (mp_thread_t *th = thread; th != NULL; th = th->next) { - // Don't delete the current task - if (th->id != xTaskGetCurrentTaskHandle()) { - id = th->id; - break; - } - } - mp_thread_mutex_unlock(&thread_mutex); + // The current task should be thread_entry0 and should be the last in the linked list. + assert(thread_entry0.id == xTaskGetCurrentTaskHandle()); + assert(thread_entry0.next == NULL); - if (id == NULL) { - // No tasks left to delete - break; - } else { - // Call FreeRTOS to delete the task (it will call vTaskPreDeletionHook) - vTaskDelete(id); + // Delete all tasks except the main one. + mp_thread_mutex_lock(&thread_mutex, 1); + for (mp_thread_t *th = thread; th != NULL; th = th->next) { + if (th != &thread_entry0) { + vTaskDelete(th->id); } } -} - -#else + thread = &thread_entry0; + mp_thread_mutex_unlock(&thread_mutex); -void vTaskPreDeletionHook(void *tcb) { + // Give the idle task a chance to run, to clean up any deleted tasks. + vTaskDelay(1); } #endif // MICROPY_PY_THREAD diff --git a/ports/esp32/network_common.c b/ports/esp32/network_common.c index fce8a0304c..bd34f1b41f 100644 --- a/ports/esp32/network_common.c +++ b/ports/esp32/network_common.c @@ -45,7 +45,7 @@ #include "lwip/sockets.h" #include "lwip/dns.h" -NORETURN void esp_exceptions_helper(esp_err_t e) { +MP_NORETURN void esp_exceptions_helper(esp_err_t e) { switch (e) { case ESP_ERR_WIFI_NOT_INIT: mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Not Initialized")); @@ -77,6 +77,8 @@ NORETURN void esp_exceptions_helper(esp_err_t e) { mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Would Block")); case ESP_ERR_WIFI_NOT_CONNECT: mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Wifi Not Connected")); + case ESP_ERR_NO_MEM: + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("WiFi Out of Memory")); default: mp_raise_msg_varg(&mp_type_RuntimeError, MP_ERROR_TEXT("Wifi Unknown Error 0x%04x"), e); } diff --git a/ports/esp32/network_lan.c b/ports/esp32/network_lan.c index e1a8c95785..309ee0b14a 100644 --- a/ports/esp32/network_lan.c +++ b/ports/esp32/network_lan.c @@ -45,6 +45,10 @@ #include "modnetwork.h" #include "extmod/modnetwork.h" +#if PHY_LAN867X_ENABLED +#include "esp_eth_phy_lan867x.h" +#endif + typedef struct _lan_if_obj_t { base_if_obj_t base; bool initialized; @@ -156,6 +160,12 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar args[ARG_phy_type].u_int != PHY_RTL8201 && args[ARG_phy_type].u_int != PHY_KSZ8041 && args[ARG_phy_type].u_int != PHY_KSZ8081 && + #if PHY_LAN867X_ENABLED + args[ARG_phy_type].u_int != PHY_LAN8670 && + #endif + #if PHY_GENERIC_ENABLED + args[ARG_phy_type].u_int != PHY_GENERIC && + #endif #if CONFIG_ETH_USE_SPI_ETHERNET #if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL args[ARG_phy_type].u_int != PHY_KSZ8851SNL && @@ -231,7 +241,17 @@ static mp_obj_t get_lan(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_ar case PHY_KSZ8081: self->phy = esp_eth_phy_new_ksz80xx(&phy_config); break; + #if PHY_LAN867X_ENABLED + case PHY_LAN8670: + self->phy = esp_eth_phy_new_lan867x(&phy_config); + break; + #endif + #if PHY_GENERIC_ENABLED + case PHY_GENERIC: + self->phy = esp_eth_phy_new_generic(&phy_config); + break; #endif + #endif // CONFIG_IDF_TARGET_ESP32 #if CONFIG_ETH_USE_SPI_ETHERNET #if CONFIG_ETH_SPI_ETHERNET_KSZ8851SNL case PHY_KSZ8851SNL: { diff --git a/ports/esp32/network_ppp.c b/ports/esp32/network_ppp.c index 4dd5a3718c..8b700c98ef 100644 --- a/ports/esp32/network_ppp.c +++ b/ports/esp32/network_ppp.c @@ -4,6 +4,7 @@ * The MIT License (MIT) * * Copyright (c) 2018 "Eric Poulsen" <eric@zyxod.com> + * Copyright (c) 2024 Damien P. George * * Based on the ESP IDF example code which is Public Domain / CC0 * @@ -26,172 +27,265 @@ * THE SOFTWARE. */ +// This file is intended to closely match extmod/network_ppp_lwip.c. Changes can +// and should probably be applied to both files. Compare them directly by using: +// git diff --no-index extmod/network_ppp_lwip.c ports/esp32/network_ppp.c + #include "py/runtime.h" #include "py/mphal.h" -#include "py/objtype.h" #include "py/stream.h" -#include "shared/netutils/netutils.h" -#include "modmachine.h" -#include "ppp_set_auth.h" -#include "netif/ppp/ppp.h" -#include "netif/ppp/pppos.h" -#include "lwip/err.h" -#include "lwip/sockets.h" -#include "lwip/sys.h" -#include "lwip/netdb.h" +#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT) + #include "lwip/dns.h" +#include "netif/ppp/ppp.h" #include "netif/ppp/pppapi.h" +#include "netif/ppp/pppos.h" -#if defined(CONFIG_ESP_NETIF_TCPIP_LWIP) && defined(CONFIG_LWIP_PPP_SUPPORT) +// Includes for port-specific changes compared to network_ppp_lwip.c +#include "shared/netutils/netutils.h" +#include "ppp_set_auth.h" -#define PPP_CLOSE_TIMEOUT_MS (4000) +// Enable this to see the serial data going between the PPP layer. +#define PPP_TRACE_IN_OUT (0) -typedef struct _ppp_if_obj_t { +typedef enum { + STATE_INACTIVE, + STATE_ACTIVE, + STATE_ERROR, + STATE_CONNECTING, + STATE_CONNECTED, +} network_ppp_state_t; + +typedef struct _network_ppp_obj_t { mp_obj_base_t base; - bool active; - bool connected; - volatile bool clean_close; - ppp_pcb *pcb; + network_ppp_state_t state; + int error_code; mp_obj_t stream; - SemaphoreHandle_t inactiveWaitSem; - volatile TaskHandle_t client_task_handle; - struct netif pppif; -} ppp_if_obj_t; + ppp_pcb *pcb; + struct netif netif; +} network_ppp_obj_t; -const mp_obj_type_t ppp_if_type; +const mp_obj_type_t esp_network_ppp_lwip_type; + +static mp_obj_t network_ppp___del__(mp_obj_t self_in); + +static void network_ppp_stream_uart_irq_disable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } -static void ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { - ppp_if_obj_t *self = ctx; - struct netif *pppif = ppp_netif(self->pcb); + // Disable UART IRQ. + mp_obj_t dest[3]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_const_none; + mp_call_method_n_kw(1, 0, dest); +} +static void network_ppp_status_cb(ppp_pcb *pcb, int err_code, void *ctx) { + network_ppp_obj_t *self = ctx; switch (err_code) { case PPPERR_NONE: - #if CONFIG_LWIP_IPV6 - self->connected = (pppif->ip_addr.u_addr.ip4.addr != 0); - #else - self->connected = (pppif->ip_addr.addr != 0); - #endif // CONFIG_LWIP_IPV6 + self->state = STATE_CONNECTED; break; case PPPERR_USER: - self->clean_close = true; - break; - case PPPERR_CONNECT: - self->connected = false; + if (self->state >= STATE_ERROR) { + // Indicate that we are no longer connected and thus + // only need to free the PPP PCB, not close it. + self->state = STATE_ACTIVE; + } + // Clean up the PPP PCB. + network_ppp___del__(MP_OBJ_FROM_PTR(self)); break; default: + self->state = STATE_ERROR; + self->error_code = err_code; break; } } -static mp_obj_t ppp_make_new(mp_obj_t stream) { +static mp_obj_t network_ppp_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { + mp_arg_check_num(n_args, n_kw, 1, 1, false); + + mp_obj_t stream = all_args[0]; + if (stream != mp_const_none) { mp_get_stream_raise(stream, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); } - ppp_if_obj_t *self = mp_obj_malloc_with_finaliser(ppp_if_obj_t, &ppp_if_type); + network_ppp_obj_t *self = mp_obj_malloc_with_finaliser(network_ppp_obj_t, type); + self->state = STATE_INACTIVE; self->stream = stream; - self->active = false; - self->connected = false; - self->clean_close = false; - self->client_task_handle = NULL; + self->pcb = NULL; return MP_OBJ_FROM_PTR(self); } -MP_DEFINE_CONST_FUN_OBJ_1(esp_network_ppp_make_new_obj, ppp_make_new); -#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) -static u32_t ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx) -#else -static u32_t ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) -#endif -{ - ppp_if_obj_t *self = ctx; - - mp_obj_t stream = self->stream; - if (stream == mp_const_none) { - return 0; +static mp_obj_t network_ppp___del__(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->state >= STATE_ACTIVE) { + if (self->state >= STATE_ERROR) { + // Still connected over the stream. + // Force the connection to close, with nocarrier=1. + self->state = STATE_INACTIVE; + pppapi_close(self->pcb, 1); + } + network_ppp_stream_uart_irq_disable(self); + // Free PPP PCB and reset state. + self->state = STATE_INACTIVE; + pppapi_free(self->pcb); + self->pcb = NULL; } - - int err; - return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); + return mp_const_none; } +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp___del___obj, network_ppp___del__); -static void pppos_client_task(void *self_in) { - ppp_if_obj_t *self = (ppp_if_obj_t *)self_in; - uint8_t buf[256]; +static mp_obj_t network_ppp_poll(size_t n_args, const mp_obj_t *args) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); - int len = 0; - while (ulTaskNotifyTake(pdTRUE, len <= 0) == 0) { - mp_obj_t stream = self->stream; - if (stream == mp_const_none) { - len = 0; - } else { - int err; - len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); - if (len > 0) { - pppos_input_tcpip(self->pcb, (u8_t *)buf, len); - } - } + if (self->state <= STATE_ERROR) { + return MP_OBJ_NEW_SMALL_INT(-MP_EPERM); } - self->client_task_handle = NULL; - vTaskDelete(NULL); - for (;;) { + mp_int_t total_len = 0; + mp_obj_t stream = self->stream; + while (stream != mp_const_none) { + uint8_t buf[256]; + int err; + mp_uint_t len = mp_stream_rw(stream, buf, sizeof(buf), &err, 0); + if (len == 0) { + break; + } + #if PPP_TRACE_IN_OUT + mp_printf(&mp_plat_print, "ppp_in(n=%u,data=", len); + for (size_t i = 0; i < len; ++i) { + mp_printf(&mp_plat_print, "%02x:", buf[i]); + } + mp_printf(&mp_plat_print, ")\n"); + #endif + pppos_input(self->pcb, (u8_t *)buf, len); + total_len += len; } + + return MP_OBJ_NEW_SMALL_INT(total_len); } +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_poll_obj, 1, 2, network_ppp_poll); -static mp_obj_t ppp_active(size_t n_args, const mp_obj_t *args) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); +static void network_ppp_stream_uart_irq_enable(network_ppp_obj_t *self) { + if (self->stream == mp_const_none) { + return; + } - if (n_args > 1) { - if (mp_obj_is_true(args[1])) { - if (self->active) { - return mp_const_true; - } + // Enable UART IRQ to call PPP.poll() when incoming data is ready. + mp_obj_t dest[4]; + mp_load_method(self->stream, MP_QSTR_irq, dest); + dest[2] = mp_obj_new_bound_meth(MP_OBJ_FROM_PTR(&network_ppp_poll_obj), MP_OBJ_FROM_PTR(self)); + dest[3] = mp_load_attr(self->stream, MP_QSTR_IRQ_RXIDLE); + mp_call_method_n_kw(2, 0, dest); +} - self->pcb = pppapi_pppos_create(&self->pppif, ppp_output_callback, ppp_status_cb, self); +static mp_obj_t network_ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + if (n_args != 1 && kwargs->used != 0) { + mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed")); + } + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); - if (self->pcb == NULL) { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("init failed")); - } - self->active = true; - } else { - if (!self->active) { - return mp_const_false; + if (kwargs->used != 0) { + for (size_t i = 0; i < kwargs->alloc; i++) { + if (mp_map_slot_is_filled(kwargs, i)) { + switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { + case MP_QSTR_stream: { + if (kwargs->table[i].value != mp_const_none) { + mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); + } + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_disable(self); + } + self->stream = kwargs->table[i].value; + if (self->state >= STATE_ACTIVE) { + network_ppp_stream_uart_irq_enable(self); + } + break; + } + default: + break; + } } + } + return mp_const_none; + } - if (self->client_task_handle != NULL) { // is connecting or connected? - // Wait for PPPERR_USER, with timeout - pppapi_close(self->pcb, 0); - uint32_t t0 = mp_hal_ticks_ms(); - while (!self->clean_close && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { - mp_hal_delay_ms(10); - } + if (n_args != 2) { + mp_raise_TypeError(MP_ERROR_TEXT("can query only one param")); + } - // Shutdown task - xTaskNotifyGive(self->client_task_handle); - t0 = mp_hal_ticks_ms(); - while (self->client_task_handle != NULL && mp_hal_ticks_ms() - t0 < PPP_CLOSE_TIMEOUT_MS) { - mp_hal_delay_ms(10); + mp_obj_t val = mp_const_none; + + switch (mp_obj_str_get_qstr(args[1])) { + case MP_QSTR_stream: { + val = self->stream; + break; + } + case MP_QSTR_ifname: { + if (self->pcb != NULL) { + struct netif *pppif = ppp_netif(self->pcb); + char ifname[NETIF_NAMESIZE + 1] = {0}; + netif_index_to_name(netif_get_index(pppif), ifname); + if (ifname[0] != 0) { + val = mp_obj_new_str_from_cstr((char *)ifname); } } - - // Release PPP - pppapi_free(self->pcb); - self->pcb = NULL; - self->active = false; - self->connected = false; - self->clean_close = false; + break; } + default: + mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); + } + + return val; +} +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_config_obj, 1, network_ppp_config); + +static mp_obj_t network_ppp_status(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->state == STATE_ERROR) { + return MP_OBJ_NEW_SMALL_INT(-self->error_code); + } else { + return MP_OBJ_NEW_SMALL_INT(self->state); + } +} +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_status_obj, network_ppp_status); + +#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 4, 0) +static u32_t network_ppp_output_callback(ppp_pcb *pcb, const void *data, u32_t len, void *ctx) +#else +static u32_t network_ppp_output_callback(ppp_pcb *pcb, u8_t *data, u32_t len, void *ctx) +#endif +{ + network_ppp_obj_t *self = ctx; + #if PPP_TRACE_IN_OUT + mp_printf(&mp_plat_print, "ppp_out(n=%u,data=", len); + for (size_t i = 0; i < len; ++i) { + mp_printf(&mp_plat_print, "%02x:", ((const uint8_t *)data)[i]); + } + mp_printf(&mp_plat_print, ")\n"); + #endif + mp_obj_t stream = self->stream; + if (stream == mp_const_none) { + return 0; } - return mp_obj_new_bool(self->active); + int err; + // The return value from this output callback is the number of bytes written out. + // If it's less than the requested number of bytes then lwIP will propagate out an error. + return mp_stream_rw(stream, (void *)data, len, &err, MP_STREAM_RW_WRITE); } -static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_active_obj, 1, 2, ppp_active); -static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { - enum { ARG_authmode, ARG_username, ARG_password }; +static mp_obj_t network_ppp_connect(size_t n_args, const mp_obj_t *args, mp_map_t *kw_args) { + enum { ARG_security, ARG_user, ARG_key, ARG_authmode, ARG_username, ARG_password }; static const mp_arg_t allowed_args[] = { + { MP_QSTR_security, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = -1} }, + { MP_QSTR_user, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_key, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + // Deprecated arguments for backwards compatibility { MP_QSTR_authmode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = PPPAUTHTYPE_NONE} }, { MP_QSTR_username, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_password, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, @@ -200,17 +294,34 @@ static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw mp_arg_val_t parsed_args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args - 1, args + 1, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, parsed_args); - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); + // Use deprecated arguments as defaults + if (parsed_args[ARG_security].u_int == -1) { + parsed_args[ARG_security].u_int = parsed_args[ARG_authmode].u_int; + } + if (parsed_args[ARG_user].u_obj == mp_const_none) { + parsed_args[ARG_user].u_obj = parsed_args[ARG_username].u_obj; + } + if (parsed_args[ARG_key].u_obj == mp_const_none) { + parsed_args[ARG_key].u_obj = parsed_args[ARG_password].u_obj; + } + + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + + if (self->state == STATE_INACTIVE) { + self->pcb = pppapi_pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self); + if (self->pcb == NULL) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed")); + } + self->state = STATE_ACTIVE; - if (!self->active) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("must be active")); + network_ppp_stream_uart_irq_enable(self); } - if (self->client_task_handle != NULL) { + if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) { mp_raise_OSError(MP_EALREADY); } - switch (parsed_args[ARG_authmode].u_int) { + switch (parsed_args[ARG_security].u_int) { case PPPAUTHTYPE_NONE: case PPPAUTHTYPE_PAP: case PPPAUTHTYPE_CHAP: @@ -219,39 +330,49 @@ static mp_obj_t ppp_connect_py(size_t n_args, const mp_obj_t *args, mp_map_t *kw mp_raise_ValueError(MP_ERROR_TEXT("invalid auth")); } - if (parsed_args[ARG_authmode].u_int != PPPAUTHTYPE_NONE) { - const char *username_str = mp_obj_str_get_str(parsed_args[ARG_username].u_obj); - const char *password_str = mp_obj_str_get_str(parsed_args[ARG_password].u_obj); - pppapi_set_auth(self->pcb, parsed_args[ARG_authmode].u_int, username_str, password_str); + if (parsed_args[ARG_security].u_int != PPPAUTHTYPE_NONE) { + const char *user_str = mp_obj_str_get_str(parsed_args[ARG_user].u_obj); + const char *key_str = mp_obj_str_get_str(parsed_args[ARG_key].u_obj); + pppapi_set_auth(self->pcb, parsed_args[ARG_security].u_int, user_str, key_str); } - if (pppapi_set_default(self->pcb) != ESP_OK) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("set default failed")); + + if (pppapi_set_default(self->pcb) != ERR_OK) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_set_default failed")); } ppp_set_usepeerdns(self->pcb, true); - if (pppapi_connect(self->pcb, 0) != ESP_OK) { - mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("connect failed")); + if (pppapi_connect(self->pcb, 0) != ERR_OK) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("ppp_connect failed")); } - if (xTaskCreatePinnedToCore(pppos_client_task, "ppp", 2048, self, 1, (TaskHandle_t *)&self->client_task_handle, MP_TASK_COREID) != pdPASS) { - mp_raise_msg(&mp_type_RuntimeError, MP_ERROR_TEXT("failed to create worker task")); - } + self->state = STATE_CONNECTING; + + // Do a poll in case there is data waiting on the input stream. + network_ppp_poll(1, args); return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_KW(ppp_connect_obj, 1, ppp_connect_py); +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_connect_obj, 1, network_ppp_connect); -static mp_obj_t ppp_delete(mp_obj_t self_in) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_obj_t args[] = {self, mp_const_false}; - ppp_active(2, args); +static mp_obj_t network_ppp_disconnect(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + if (self->state == STATE_CONNECTING || self->state == STATE_CONNECTED) { + // Initiate close and wait for PPPERR_USER callback. + pppapi_close(self->pcb, 0); + } return mp_const_none; } -MP_DEFINE_CONST_FUN_OBJ_1(ppp_delete_obj, ppp_delete); +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_disconnect_obj, network_ppp_disconnect); + +static mp_obj_t network_ppp_isconnected(mp_obj_t self_in) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(self_in); + return mp_obj_new_bool(self->state == STATE_CONNECTED); +} +static MP_DEFINE_CONST_FUN_OBJ_1(network_ppp_isconnected_obj, network_ppp_isconnected); -static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); +static mp_obj_t network_ppp_ifconfig(size_t n_args, const mp_obj_t *args) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (n_args == 1) { // get const ip_addr_t *dns; @@ -282,11 +403,11 @@ static mp_obj_t ppp_ifconfig(size_t n_args, const mp_obj_t *args) { return mp_const_none; } } -MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(ppp_ifconfig_obj, 1, 2, ppp_ifconfig); +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_ifconfig_obj, 1, 2, network_ppp_ifconfig); -static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { +static mp_obj_t network_ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (kwargs->used == 0) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); if (self->pcb == NULL) { mp_raise_ValueError(MP_ERROR_TEXT("PPP not active")); } @@ -318,94 +439,64 @@ static mp_obj_t ppp_ipconfig(size_t n_args, const mp_obj_t *args, mp_map_t *kwar } return mp_const_none; } -static MP_DEFINE_CONST_FUN_OBJ_KW(ppp_ipconfig_obj, 1, ppp_ipconfig); +static MP_DEFINE_CONST_FUN_OBJ_KW(network_ppp_ipconfig_obj, 1, network_ppp_ipconfig); -static mp_obj_t ppp_status(mp_obj_t self_in) { - return mp_const_none; -} -static MP_DEFINE_CONST_FUN_OBJ_1(ppp_status_obj, ppp_status); - -static mp_obj_t ppp_isconnected(mp_obj_t self_in) { - ppp_if_obj_t *self = MP_OBJ_TO_PTR(self_in); - return mp_obj_new_bool(self->connected); -} -static MP_DEFINE_CONST_FUN_OBJ_1(ppp_isconnected_obj, ppp_isconnected); - -static mp_obj_t ppp_config(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) { - if (n_args != 1 && kwargs->used != 0) { - mp_raise_TypeError(MP_ERROR_TEXT("either pos or kw args are allowed")); - } - ppp_if_obj_t *self = MP_OBJ_TO_PTR(args[0]); - - if (kwargs->used != 0) { - for (size_t i = 0; i < kwargs->alloc; i++) { - if (mp_map_slot_is_filled(kwargs, i)) { - switch (mp_obj_str_get_qstr(kwargs->table[i].key)) { - case MP_QSTR_stream: { - if (kwargs->table[i].value != mp_const_none) { - mp_get_stream_raise(kwargs->table[i].value, MP_STREAM_OP_READ | MP_STREAM_OP_WRITE); - } - self->stream = kwargs->table[i].value; - break; - } - default: - break; - } +static mp_obj_t network_ppp_active(size_t n_args, const mp_obj_t *args) { + network_ppp_obj_t *self = MP_OBJ_TO_PTR(args[0]); + if (n_args > 1) { + if (mp_obj_is_true(args[1])) { + if (self->state >= STATE_ACTIVE) { + return mp_const_true; } - } - return mp_const_none; - } - if (n_args != 2) { - mp_raise_TypeError(MP_ERROR_TEXT("can query only one param")); - } - - mp_obj_t val = mp_const_none; + self->pcb = pppapi_pppos_create(&self->netif, network_ppp_output_callback, network_ppp_status_cb, self); + if (self->pcb == NULL) { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("pppos_create failed")); + } + self->state = STATE_ACTIVE; - switch (mp_obj_str_get_qstr(args[1])) { - case MP_QSTR_stream: { - val = self->stream; - break; - } - case MP_QSTR_ifname: { - if (self->pcb != NULL) { - struct netif *pppif = ppp_netif(self->pcb); - char ifname[NETIF_NAMESIZE + 1] = {0}; - netif_index_to_name(netif_get_index(pppif), ifname); - if (ifname[0] != 0) { - val = mp_obj_new_str_from_cstr((char *)ifname); - } + network_ppp_stream_uart_irq_enable(self); + } else { + if (self->state < STATE_ACTIVE) { + return mp_const_false; } - break; + + network_ppp___del__(MP_OBJ_FROM_PTR(self)); } - default: - mp_raise_ValueError(MP_ERROR_TEXT("unknown config param")); } - - return val; + return mp_obj_new_bool(self->state >= STATE_ACTIVE); } -static MP_DEFINE_CONST_FUN_OBJ_KW(ppp_config_obj, 1, ppp_config); - -static const mp_rom_map_elem_t ppp_if_locals_dict_table[] = { - { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&ppp_active_obj) }, - { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&ppp_connect_obj) }, - { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&ppp_isconnected_obj) }, - { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&ppp_status_obj) }, - { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&ppp_config_obj) }, - { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&ppp_ifconfig_obj) }, - { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&ppp_ipconfig_obj) }, - { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&ppp_delete_obj) }, +static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(network_ppp_active_obj, 1, 2, network_ppp_active); + +static const mp_rom_map_elem_t network_ppp_locals_dict_table[] = { + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&network_ppp___del___obj) }, + { MP_ROM_QSTR(MP_QSTR_config), MP_ROM_PTR(&network_ppp_config_obj) }, + { MP_ROM_QSTR(MP_QSTR_status), MP_ROM_PTR(&network_ppp_status_obj) }, + { MP_ROM_QSTR(MP_QSTR_connect), MP_ROM_PTR(&network_ppp_connect_obj) }, + { MP_ROM_QSTR(MP_QSTR_disconnect), MP_ROM_PTR(&network_ppp_disconnect_obj) }, + { MP_ROM_QSTR(MP_QSTR_isconnected), MP_ROM_PTR(&network_ppp_isconnected_obj) }, + { MP_ROM_QSTR(MP_QSTR_ifconfig), MP_ROM_PTR(&network_ppp_ifconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_ipconfig), MP_ROM_PTR(&network_ppp_ipconfig_obj) }, + { MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&network_ppp_poll_obj) }, + + { MP_ROM_QSTR(MP_QSTR_SEC_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) }, + { MP_ROM_QSTR(MP_QSTR_SEC_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) }, + { MP_ROM_QSTR(MP_QSTR_SEC_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) }, + + // Deprecated interface for backwards compatibility + { MP_ROM_QSTR(MP_QSTR_active), MP_ROM_PTR(&network_ppp_active_obj) }, { MP_ROM_QSTR(MP_QSTR_AUTH_NONE), MP_ROM_INT(PPPAUTHTYPE_NONE) }, { MP_ROM_QSTR(MP_QSTR_AUTH_PAP), MP_ROM_INT(PPPAUTHTYPE_PAP) }, { MP_ROM_QSTR(MP_QSTR_AUTH_CHAP), MP_ROM_INT(PPPAUTHTYPE_CHAP) }, }; -static MP_DEFINE_CONST_DICT(ppp_if_locals_dict, ppp_if_locals_dict_table); +static MP_DEFINE_CONST_DICT(network_ppp_locals_dict, network_ppp_locals_dict_table); MP_DEFINE_CONST_OBJ_TYPE( - ppp_if_type, + esp_network_ppp_lwip_type, MP_QSTR_PPP, MP_TYPE_FLAG_NONE, - locals_dict, &ppp_if_locals_dict + make_new, network_ppp_make_new, + locals_dict, &network_ppp_locals_dict ); #endif diff --git a/ports/esp32/panichandler.c b/ports/esp32/panichandler.c index e6ff98ded2..5211286f84 100644 --- a/ports/esp32/panichandler.c +++ b/ports/esp32/panichandler.c @@ -42,11 +42,20 @@ #endif void __real_esp_panic_handler(void *); -void esp_panic_handler_reconfigure_wdts(uint32_t timeout_ms); void panic_print_str(const char *str); +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) +void esp_panic_handler_reconfigure_wdts(uint32_t timeout_ms); +#else +void esp_panic_handler_feed_wdts(void); +#endif + void MICROPY_WRAP_PANICHANDLER_FUN(__wrap_esp_panic_handler)(void *info) { + #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 4, 2) esp_panic_handler_reconfigure_wdts(1000); + #else + esp_panic_handler_feed_wdts(); + #endif const static char *msg = MICROPY_WRAP_PANICHANDLER_STR( "\r\n" diff --git a/ports/esp32/partitions-16MiB-ota.csv b/ports/esp32/partitions-16MiB-ota.csv deleted file mode 100644 index a6f83bc46b..0000000000 --- a/ports/esp32/partitions-16MiB-ota.csv +++ /dev/null @@ -1,10 +0,0 @@ -# Partition table for MicroPython with OTA support using 16MB flash -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x4000, -otadata, data, ota, 0xd000, 0x2000, -phy_init, data, phy, 0xf000, 0x1000, -ota_0, app, ota_0, 0x10000, 0x270000, -ota_1, app, ota_1, 0x280000, 0x270000, -vfs, data, fat, 0x4f0000, 0xb10000, diff --git a/ports/esp32/partitions-16MiB.csv b/ports/esp32/partitions-16MiB.csv deleted file mode 100644 index ae926c7b94..0000000000 --- a/ports/esp32/partitions-16MiB.csv +++ /dev/null @@ -1,7 +0,0 @@ -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0xE00000, diff --git a/ports/esp32/partitions-2MiB.csv b/ports/esp32/partitions-2MiB.csv index ea6626825c..5449201a7a 100644 --- a/ports/esp32/partitions-2MiB.csv +++ b/ports/esp32/partitions-2MiB.csv @@ -4,4 +4,3 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x160000, -vfs, data, fat, 0x170000, 0x50000, diff --git a/ports/esp32/partitions-32MiB.csv b/ports/esp32/partitions-32MiB.csv deleted file mode 100644 index 31591c9949..0000000000 --- a/ports/esp32/partitions-32MiB.csv +++ /dev/null @@ -1,7 +0,0 @@ -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0x1E00000, diff --git a/ports/esp32/partitions-4MiB-ota.csv b/ports/esp32/partitions-4MiB-ota.csv index 094ad76660..9cbb422799 100644 --- a/ports/esp32/partitions-4MiB-ota.csv +++ b/ports/esp32/partitions-4MiB-ota.csv @@ -7,4 +7,3 @@ otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, ota_0, app, ota_0, 0x10000, 0x180000, ota_1, app, ota_1, 0x190000, 0x180000, -vfs, data, fat, 0x310000, 0x0f0000, diff --git a/ports/esp32/partitions-4MiB-romfs.csv b/ports/esp32/partitions-4MiB-romfs.csv index dd02506e54..29033ff3ab 100644 --- a/ports/esp32/partitions-4MiB-romfs.csv +++ b/ports/esp32/partitions-4MiB-romfs.csv @@ -5,4 +5,3 @@ nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x1D0000, romfs, data, 0x8f, 0x1E0000, 0x20000, -vfs, data, fat, 0x200000, 0x200000, diff --git a/ports/esp32/partitions-4MiB.csv b/ports/esp32/partitions-4MiBplus.csv index 53f0f16744..460e5cc0e9 100644 --- a/ports/esp32/partitions-4MiB.csv +++ b/ports/esp32/partitions-4MiBplus.csv @@ -1,7 +1,10 @@ +# This partition table is for devices with 4MiB or more of flash. +# The first 2MiB is used for bootloader, nvs, phy_init and firmware. +# The remaining flash is for the user filesystem(s). + # Notes: the offset of the partition table itself is set in # $IDF_PATH/components/partition_table/Kconfig.projbuild. # Name, Type, SubType, Offset, Size, Flags nvs, data, nvs, 0x9000, 0x6000, phy_init, data, phy, 0xf000, 0x1000, factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0x200000, diff --git a/ports/esp32/partitions-8MiB.csv b/ports/esp32/partitions-8MiB.csv deleted file mode 100644 index 582d3b50e5..0000000000 --- a/ports/esp32/partitions-8MiB.csv +++ /dev/null @@ -1,7 +0,0 @@ -# Notes: the offset of the partition table itself is set in -# $IDF_PATH/components/partition_table/Kconfig.projbuild. -# Name, Type, SubType, Offset, Size, Flags -nvs, data, nvs, 0x9000, 0x6000, -phy_init, data, phy, 0xf000, 0x1000, -factory, app, factory, 0x10000, 0x1F0000, -vfs, data, fat, 0x200000, 0x600000, diff --git a/ports/esp32/partitions-32MiB-ota.csv b/ports/esp32/partitions-8MiBplus-ota.csv index 7366a2ad8d..09a8e6d702 100644 --- a/ports/esp32/partitions-32MiB-ota.csv +++ b/ports/esp32/partitions-8MiBplus-ota.csv @@ -1,4 +1,7 @@ -# Partition table for MicroPython with OTA support using 32MB flash +# This partition table is for devices with 8MiB or more of flash and OTA support. +# The first 5056kiB is used for bootloader, nvs, phy_init and firmware. +# The remaining flash is for the user filesystem(s). + # Notes: the offset of the partition table itself is set in # $IDF_PATH/components/partition_table/Kconfig.projbuild. # Name, Type, SubType, Offset, Size, Flags @@ -7,4 +10,3 @@ otadata, data, ota, 0xd000, 0x2000, phy_init, data, phy, 0xf000, 0x1000, ota_0, app, ota_0, 0x10000, 0x270000, ota_1, app, ota_1, 0x280000, 0x270000, -vfs, data, fat, 0x4f0000, 0x1B10000, diff --git a/ports/esp32/tools/metrics_esp32.py b/ports/esp32/tools/metrics_esp32.py index 5e65a78c97..9efaae63a9 100755 --- a/ports/esp32/tools/metrics_esp32.py +++ b/ports/esp32/tools/metrics_esp32.py @@ -37,7 +37,7 @@ import sys import subprocess from dataclasses import dataclass -IDF_VERS = ("v5.2.2",) +IDF_VERS = ("v5.4.1",) BUILDS = ( ("ESP32_GENERIC", ""), diff --git a/ports/esp8266/esp_init_data.c b/ports/esp8266/esp_init_data.c index c369ed58f5..a3dd6ffed1 100644 --- a/ports/esp8266/esp_init_data.c +++ b/ports/esp8266/esp_init_data.c @@ -30,7 +30,7 @@ #include "user_interface.h" #include "extmod/misc.h" -NORETURN void call_user_start(void); +MP_NORETURN void call_user_start(void); void ets_printf(const char *fmt, ...); extern char flashchip; diff --git a/ports/esp8266/esp_mphal.h b/ports/esp8266/esp_mphal.h index 0a4f92ac0b..fb2e441c97 100644 --- a/ports/esp8266/esp_mphal.h +++ b/ports/esp8266/esp_mphal.h @@ -27,11 +27,20 @@ #include "user_interface.h" #include "py/ringbuf.h" #include "shared/runtime/interrupt_char.h" +#include "ets_alt_task.h" #include "xtirq.h" #define MICROPY_BEGIN_ATOMIC_SECTION() esp_disable_irq() #define MICROPY_END_ATOMIC_SECTION(state) esp_enable_irq(state) +// During machine.time_pulse_us, feed WDT every now and then. +#define MICROPY_PY_MACHINE_TIME_PULSE_US_HOOK(dt) \ + do { \ + if ((dt & 0xffff) == 0xffff && !ets_loop_dont_feed_sw_wdt) { \ + system_soft_wdt_feed(); \ + } \ + } while (0) + void mp_sched_keyboard_interrupt(void); struct _mp_print_t; diff --git a/ports/esp8266/main.c b/ports/esp8266/main.c index 2dd7c1dece..da712fce9b 100644 --- a/ports/esp8266/main.c +++ b/ports/esp8266/main.c @@ -49,6 +49,64 @@ static char heap[38 * 1024]; +#if MICROPY_HW_HARD_FAULT_DEBUG + +static void format_hex(uint32_t hex, char *buffer) { + static const char table[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' }; + int offset = 7; + uint32_t value = hex; + while (offset >= 0) { + buffer[offset--] = table[value & 0x0F]; + value >>= 4; + } +} + +static void print_reset_info(void) { + struct rst_info *rst_info = system_get_rst_info(); + if ((rst_info->reason == REASON_WDT_RST) || (rst_info->reason == REASON_EXCEPTION_RST) || (rst_info->reason == REASON_SOFT_WDT_RST)) { + char buffer[8]; + mp_hal_stdout_tx_str("\r\n\r\nThe system restarted due to an error.\r\n\r\nReason: "); + switch (rst_info->reason) { + case REASON_WDT_RST: + mp_hal_stdout_tx_str("WDT"); + break; + + case REASON_EXCEPTION_RST: + mp_hal_stdout_tx_str("EXCEPTION"); + break; + + case REASON_SOFT_WDT_RST: + mp_hal_stdout_tx_str("SOFT_WDT"); + break; + + default: + assert(!"Should not ever get here."); + break; + } + mp_hal_stdout_tx_str(" Cause: "); + format_hex(rst_info->exccause, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" EPC1: "); + format_hex(rst_info->epc1, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" EPC2: "); + format_hex(rst_info->epc2, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" EPC3: "); + format_hex(rst_info->epc3, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" Exception Vector address: "); + format_hex(rst_info->excvaddr, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str(" DEPC: "); + format_hex(rst_info->depc, buffer); + mp_hal_stdout_tx_strn(buffer, sizeof(buffer)); + mp_hal_stdout_tx_str("\r\n\r\n"); + } +} + +#endif + static void mp_reset(void) { mp_stack_set_top((void *)0x40000000); mp_stack_set_limit(8192); @@ -114,6 +172,10 @@ void init_done(void) { pyexec_event_repl_init(); #endif + #if MICROPY_HW_HARD_FAULT_DEBUG + print_reset_info(); + #endif + #if !MICROPY_REPL_EVENT_DRIVEN soft_reset: for (;;) { diff --git a/ports/esp8266/modmachine.c b/ports/esp8266/modmachine.c index 23ccf8cebf..d43fe38245 100644 --- a/ports/esp8266/modmachine.c +++ b/ports/esp8266/modmachine.c @@ -33,7 +33,6 @@ #include "os_type.h" #include "osapi.h" #include "etshal.h" -#include "ets_alt_task.h" #include "user_interface.h" // #define MACHINE_WAKE_IDLE (0x01) @@ -71,7 +70,7 @@ static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { system_update_cpu_freq(freq); } -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { system_restart(); // we must not return @@ -114,7 +113,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { } } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { // default to sleep forever uint32_t sleep_us = 0; @@ -327,32 +326,3 @@ MP_DEFINE_CONST_OBJ_TYPE( print, esp_timer_print, locals_dict, &esp_timer_locals_dict ); - -// Custom version of this function that feeds system WDT if necessary -mp_uint_t machine_time_pulse_us(mp_hal_pin_obj_t pin, int pulse_level, mp_uint_t timeout_us) { - int nchanges = 2; - uint32_t start = system_get_time(); // in microseconds - for (;;) { - uint32_t dt = system_get_time() - start; - - // Check if pin changed to wanted value - if (mp_hal_pin_read(pin) == pulse_level) { - if (--nchanges == 0) { - return dt; - } - pulse_level = 1 - pulse_level; - start = system_get_time(); - continue; - } - - // Check for timeout - if (dt >= timeout_us) { - return (mp_uint_t)-nchanges; - } - - // Only feed WDT every now and then, to make sure edge timing is accurate - if ((dt & 0xffff) == 0xffff && !ets_loop_dont_feed_sw_wdt) { - system_soft_wdt_feed(); - } - } -} diff --git a/ports/esp8266/modtime.c b/ports/esp8266/modtime.c index 0903005597..e99d920fde 100644 --- a/ports/esp8266/modtime.c +++ b/ports/esp8266/modtime.c @@ -31,7 +31,7 @@ // Return the localtime as an 8-tuple. static mp_obj_t mp_time_localtime_get(void) { - mp_int_t seconds = pyb_rtc_get_us_since_epoch() / 1000 / 1000; + mp_uint_t seconds = pyb_rtc_get_us_since_epoch() / 1000u / 1000u; timeutils_struct_time_t tm; timeutils_seconds_since_epoch_to_struct_time(seconds, &tm); mp_obj_t tuple[8] = { @@ -50,5 +50,5 @@ static mp_obj_t mp_time_localtime_get(void) { // Returns the number of seconds, as an integer, since the Epoch. static mp_obj_t mp_time_time_get(void) { // get date and time - return mp_obj_new_int(pyb_rtc_get_us_since_epoch() / 1000 / 1000); + return timeutils_obj_from_timestamp(pyb_rtc_get_us_since_epoch() / 1000 / 1000); } diff --git a/ports/esp8266/mpconfigport.h b/ports/esp8266/mpconfigport.h index 83d80a7c96..03f3bb643d 100644 --- a/ports/esp8266/mpconfigport.h +++ b/ports/esp8266/mpconfigport.h @@ -117,6 +117,9 @@ #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ #define MICROPY_ESP8266_APA102 (1) +// Print error information at reboot time if the board crashed. +#define MICROPY_HW_HARD_FAULT_DEBUG (0) + // No blocking wait-for-event on ESP8266, only non-blocking pump of the "OS" event // loop // diff --git a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json index 9f260b3a00..804b4fd734 100644 --- a/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json +++ b/ports/mimxrt/boards/ADAFRUIT_METRO_M7/board.json @@ -15,7 +15,7 @@ "Metro_M7.jpg" ], "mcu": "mimxrt", - "product": "Adafruit Metro M7", + "product": "Metro M7", "thumbnail": "", "url": "https://www.adafruit.com/product/4950", "vendor": "Adafruit" diff --git a/ports/mimxrt/boards/OLIMEX_RT1010/board.json b/ports/mimxrt/boards/OLIMEX_RT1010/board.json index 882b931f8b..a6f0316bc8 100644 --- a/ports/mimxrt/boards/OLIMEX_RT1010/board.json +++ b/ports/mimxrt/boards/OLIMEX_RT1010/board.json @@ -12,8 +12,8 @@ "OLIMEX_RT1010Py.jpg" ], "mcu": "mimxrt", - "product": "Olimex_RT1010Py", + "product": "RT1010-Py", "thumbnail": "", - "url": "https://www.olimex.com/Products/ARM/NXP", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/MicroPython/RT1010-Py", + "vendor": "Olimex" } diff --git a/ports/mimxrt/cyw43_configport.h b/ports/mimxrt/cyw43_configport.h index cf2f5d4b9f..b1dd6fbe6a 100644 --- a/ports/mimxrt/cyw43_configport.h +++ b/ports/mimxrt/cyw43_configport.h @@ -28,12 +28,8 @@ #define MICROPY_INCLUDED_MIMXRT_CYW43_CONFIGPORT_H // The board-level config will be included here, so it can set some CYW43 values. -#include "py/mpconfig.h" -#include "py/mperrno.h" -#include "py/mphal.h" -#include "extmod/modnetwork.h" #include "extmod/mpbthci.h" -#include "pendsv.h" +#include "extmod/cyw43_config_common.h" #include "sdio.h" #define CYW43_USE_SPI (0) @@ -61,55 +57,15 @@ #define CYW43_BT_UART_BAUDRATE_DOWNLOAD_FIRMWARE MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE #endif -#define CYW43_IOCTL_TIMEOUT_US (1000000) -#define CYW43_SLEEP_MAX (50) -#define CYW43_NETUTILS (1) #define CYW43_CLEAR_SDIO_INT (1) -#define CYW43_EPERM MP_EPERM // Operation not permitted -#define CYW43_EIO MP_EIO // I/O error -#define CYW43_EINVAL MP_EINVAL // Invalid argument -#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out - -#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER -#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT -#define CYW43_THREAD_LOCK_CHECK - -#define CYW43_HOST_NAME mod_network_hostname_data - #define CYW43_SDPCM_SEND_COMMON_WAIT __WFI(); #define CYW43_DO_IOCTL_WAIT __WFI(); -#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) - -#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT -#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT -#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE -#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP -#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN - -#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 -#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR - -#define cyw43_hal_ticks_us mp_hal_ticks_us -#define cyw43_hal_ticks_ms mp_hal_ticks_ms - -#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t -#define cyw43_hal_pin_read mp_hal_pin_read -#define cyw43_hal_pin_low mp_hal_pin_low -#define cyw43_hal_pin_high mp_hal_pin_high - -#define cyw43_hal_get_mac mp_hal_get_mac -#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii -#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac - #define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate #define cyw43_hal_uart_write mp_bluetooth_hci_uart_write #define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar -#define cyw43_delay_us mp_hal_delay_us -#define cyw43_delay_ms mp_hal_delay_ms - #define cyw43_bluetooth_controller_init mp_bluetooth_hci_controller_init #define cyw43_bluetooth_controller_deinit mp_bluetooth_hci_controller_deinit #define cyw43_bluetooth_controller_woken mp_bluetooth_hci_controller_woken @@ -135,7 +91,7 @@ #define CYW43_PIN_RFSW_VDD pin_WL_RFSW_VDD #endif -#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) +#undef cyw43_hal_pin_config // mp_hal_pin_config not yet implemented on this port static inline void cyw43_hal_pin_config(cyw43_hal_pin_obj_t pin, uint8_t mode, uint8_t pull, uint8_t alt) { machine_pin_set_mode(pin, mode); @@ -175,6 +131,4 @@ static inline int cyw43_sdio_transfer_cmd53(bool write, uint32_t block_size, uin return sdio_transfer_cmd53(write, block_size, arg, len, buf); } -#define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK - #endif // MICROPY_INCLUDED_MIMXRT_CYW43_CONFIGPORT_H diff --git a/ports/mimxrt/modmachine.c b/ports/mimxrt/modmachine.c index 204699bcc4..ad078ff3ac 100644 --- a/ports/mimxrt/modmachine.c +++ b/ports/mimxrt/modmachine.c @@ -87,7 +87,7 @@ static mp_obj_t mp_machine_unique_id(void) { return mp_obj_new_bytes(id, sizeof(id)); } -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { WDOG_TriggerSystemSoftwareReset(WDOG1); while (true) { ; @@ -131,7 +131,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { mp_raise_NotImplementedError(NULL); } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { if (n_args != 0) { mp_int_t seconds = mp_obj_get_int(args[0]) / 1000; if (seconds > 0) { @@ -159,7 +159,7 @@ NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { } } -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { #if defined(MICROPY_BOARD_ENTER_BOOTLOADER) // If a board has a custom bootloader, call it first. MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); diff --git a/ports/mimxrt/modtime.c b/ports/mimxrt/modtime.c index a2ccf1b273..3bcfac411c 100644 --- a/ports/mimxrt/modtime.c +++ b/ports/mimxrt/modtime.c @@ -51,14 +51,7 @@ static mp_obj_t mp_time_localtime_get(void) { static mp_obj_t mp_time_time_get(void) { snvs_lp_srtc_datetime_t t; SNVS_LP_SRTC_GetDatetime(SNVS, &t); - // EPOCH is 1970 for this port, which leads to the following trouble: - // timeutils_seconds_since_epoch() calls timeutils_seconds_since_2000(), and - // timeutils_seconds_since_2000() subtracts 2000 from year, but uses - // an unsigned number for seconds, That causes an underrun, which is not - // fixed by adding the TIMEUTILS_SECONDS_1970_TO_2000. - // Masking it to 32 bit for year < 2000 fixes it. - return mp_obj_new_int_from_ull( + return timeutils_obj_from_timestamp( timeutils_seconds_since_epoch(t.year, t.month, t.day, t.hour, t.minute, t.second) - & (t.year < 2000 ? 0xffffffff : 0xffffffffffff) ); } diff --git a/ports/mimxrt/mpconfigport.h b/ports/mimxrt/mpconfigport.h index 9a7dfc630f..e1c605f452 100644 --- a/ports/mimxrt/mpconfigport.h +++ b/ports/mimxrt/mpconfigport.h @@ -178,20 +178,12 @@ extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_HW_NIC_ETH #endif -#if MICROPY_PY_NETWORK_CYW43 -extern const struct _mp_obj_type_t mp_network_cyw43_type; -#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, -#else -#define MICROPY_HW_NIC_CYW43 -#endif - #ifndef MICROPY_BOARD_NETWORK_INTERFACES #define MICROPY_BOARD_NETWORK_INTERFACES #endif #define MICROPY_PORT_NETWORK_INTERFACES \ MICROPY_HW_NIC_ETH \ - MICROPY_HW_NIC_CYW43 \ MICROPY_BOARD_NETWORK_INTERFACES \ #ifndef MICROPY_BOARD_ROOT_POINTERS diff --git a/ports/minimal/main.c b/ports/minimal/main.c index 5f472c1afd..b9e9034bf5 100644 --- a/ports/minimal/main.c +++ b/ports/minimal/main.c @@ -87,7 +87,7 @@ void nlr_jump_fail(void *val) { } } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { while (1) { ; } diff --git a/ports/nrf/Makefile b/ports/nrf/Makefile index 59e74dce42..7b16974f97 100644 --- a/ports/nrf/Makefile +++ b/ports/nrf/Makefile @@ -129,14 +129,7 @@ CFLAGS_MCU_m4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 -mfpu=fpv4-s CFLAGS_MCU_m0 = $(CFLAGS_CORTEX_M) -fshort-enums -mtune=cortex-m0 -mcpu=cortex-m0 -mfloat-abi=soft -# linker wrap does not work with lto on older gcc/binutils: https://sourceware.org/bugzilla/show_bug.cgi?id=24406 -GCC_VERSION = $(shell arm-none-eabi-gcc -dumpversion) -GCC_MAJOR_VERS = $(word 1,$(subst ., ,$(GCC_VERSION))) -ifeq ($(shell test $(GCC_MAJOR_VERS) -ge 10; echo $$?),0) LTO ?= 1 -else -LTO ?= 0 -endif ifeq ($(LTO),1) CFLAGS += -flto @@ -268,7 +261,6 @@ SRC_C += $(addprefix lib/tinyusb/src/,\ portable/nordic/nrf5x/dcd_nrf5x.c \ ) -LDFLAGS += -Wl,--wrap=dcd_event_handler endif DRIVERS_SRC_C += $(addprefix modules/,\ diff --git a/ports/nrf/boards/ACTINIUS_ICARUS/board.json b/ports/nrf/boards/ACTINIUS_ICARUS/board.json index c1d9f1c4ef..09ed3fcba3 100644 --- a/ports/nrf/boards/ACTINIUS_ICARUS/board.json +++ b/ports/nrf/boards/ACTINIUS_ICARUS/board.json @@ -8,7 +8,7 @@ "icarus-v1.4-front-shadow-p-800.jpg" ], "mcu": "nrf91", - "product": "actinius_icarus", + "product": "Icarus", "thumbnail": "", "url": "https://www.actinius.com/icarus", "vendor": "Actinius" diff --git a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json index 9079fbd666..72b5135611 100644 --- a/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json +++ b/ports/nrf/boards/ARDUINO_NANO_33_BLE_SENSE/board.json @@ -15,7 +15,7 @@ "ABX00031_01.iso_998x749.jpg" ], "mcu": "nrf52", - "product": "Arduino Nano 33 BLE Sense", + "product": "Nano 33 BLE Sense", "thumbnail": "", "url": "https://store.arduino.cc/products/arduino-nano-33-ble-sense", "vendor": "Arduino" diff --git a/ports/nrf/boards/ARDUINO_PRIMO/board.json b/ports/nrf/boards/ARDUINO_PRIMO/board.json index f7afed0ced..236acfd448 100644 --- a/ports/nrf/boards/ARDUINO_PRIMO/board.json +++ b/ports/nrf/boards/ARDUINO_PRIMO/board.json @@ -8,8 +8,8 @@ "arduino_primo.jpg" ], "mcu": "nrf52", - "product": "arduino_primo", + "product": "Primo", "thumbnail": "", - "url": "", + "url": "https://docs.arduino.cc/retired/boards/arduino-primo/", "vendor": "Arduino" } diff --git a/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json b/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json index 5b6e5747d5..e910ea8db2 100644 --- a/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json +++ b/ports/nrf/boards/BLUEIO_TAG_EVIM/board.json @@ -8,7 +8,7 @@ "blyst-nano-mod-4_jpg_project-body.jpg" ], "mcu": "nrf52", - "product": "blueio_tag_evim", + "product": "BLUEIO Tag EVIM", "thumbnail": "", "url": "https://www.i-syst.com/index.php/products/blyst-nano", "vendor": "I-SYST" diff --git a/ports/nrf/boards/DVK_BL652/board.json b/ports/nrf/boards/DVK_BL652/board.json index 5c9cfad26e..d0942ca256 100644 --- a/ports/nrf/boards/DVK_BL652/board.json +++ b/ports/nrf/boards/DVK_BL652/board.json @@ -8,8 +8,8 @@ "BL652-SA_JPG-500.jpg" ], "mcu": "nrf52", - "product": "dvk_bl652", + "product": "DVK-BL652", "thumbnail": "", - "url": "https://www.lairdconnect.com/wireless-modules/bluetooth-modules/bluetooth-5-modules/bl652-series-bluetooth-v5-nfc-module", - "vendor": "Laird Connectivity" + "url": "https://www.ezurio.com/wireless-modules/bluetooth-modules/bluetooth-5-modules/bl652-series-bluetooth-v5-nfc-module", + "vendor": "Ezurio" } diff --git a/ports/nrf/boards/EVK_NINA_B1/board.json b/ports/nrf/boards/EVK_NINA_B1/board.json index 657b0fa06d..ef9c6ae5be 100644 --- a/ports/nrf/boards/EVK_NINA_B1/board.json +++ b/ports/nrf/boards/EVK_NINA_B1/board.json @@ -8,7 +8,7 @@ "EVK-NINA-B1_.jpg" ], "mcu": "nrf52", - "product": "evk_nina_b1", + "product": "EVK-NINA-B1", "thumbnail": "", "url": "https://www.u-blox.com/en/product/evk-nina-b1", "vendor": "u-blox" diff --git a/ports/nrf/boards/EVK_NINA_B3/board.json b/ports/nrf/boards/EVK_NINA_B3/board.json index 54e3dc3585..6311b17338 100644 --- a/ports/nrf/boards/EVK_NINA_B3/board.json +++ b/ports/nrf/boards/EVK_NINA_B3/board.json @@ -8,7 +8,7 @@ "EVK-NINA-B3-top.jpg" ], "mcu": "nrf52", - "product": "evk_nina_b3", + "product": "EVK-NINA-B3", "thumbnail": "", "url": "https://www.u-blox.com/en/product/evk-nina-b3", "vendor": "u-blox" diff --git a/ports/nrf/boards/IBK_BLYST_NANO/board.json b/ports/nrf/boards/IBK_BLYST_NANO/board.json index 562c33607b..ddbbef66f5 100644 --- a/ports/nrf/boards/IBK_BLYST_NANO/board.json +++ b/ports/nrf/boards/IBK_BLYST_NANO/board.json @@ -8,7 +8,7 @@ "blyst-nano-fingertip-close_jpg_content-body-gallery.jpg" ], "mcu": "nrf52", - "product": "ibk_blyst_nano", + "product": "IBK BLYST Nano", "thumbnail": "", "url": "https://www.i-syst.com/products/blyst-nano", "vendor": "I-SYST" diff --git a/ports/nrf/boards/IDK_BLYST_NANO/board.json b/ports/nrf/boards/IDK_BLYST_NANO/board.json index 199721698d..cb692f44cd 100644 --- a/ports/nrf/boards/IDK_BLYST_NANO/board.json +++ b/ports/nrf/boards/IDK_BLYST_NANO/board.json @@ -8,7 +8,7 @@ "blyst-nano-fingertip-close_jpg_content-body-gallery.jpg" ], "mcu": "nrf52", - "product": "idk_blyst_nano", + "product": "IDK BLYST Nano", "thumbnail": "", "url": "https://www.i-syst.com/products/blyst-nano", "vendor": "I-SYST" diff --git a/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json b/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json index 99d0caad23..102102b6c2 100644 --- a/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json +++ b/ports/nrf/boards/NRF52840_MDK_USB_DONGLE/board.json @@ -8,7 +8,7 @@ "dongle_pcba_case.jpg" ], "mcu": "nrf52", - "product": "nrf52840-mdk-usb-dongle", + "product": "nrf52840 MDK USB Dongle", "thumbnail": "", "url": "https://wiki.makerdiary.com/nrf52840-mdk-usb-dongle", "vendor": "Makerdiary" diff --git a/ports/nrf/boards/PCA10028/mpconfigboard.h b/ports/nrf/boards/PCA10028/mpconfigboard.h index 3dc8ed3459..df2e4e85d9 100644 --- a/ports/nrf/boards/PCA10028/mpconfigboard.h +++ b/ports/nrf/boards/PCA10028/mpconfigboard.h @@ -60,3 +60,8 @@ #define MICROPY_HW_SPI0_MISO (28) #define HELP_TEXT_BOARD_LED "1,2,3,4" + +// The JLink CDC on the PCA10028 cannot accept more than 64 incoming bytes at a time. +// That makes the UART REPL unreliable in general. But it can be improved to some +// extent by setting the raw-paste buffer size to that limit of 64. +#define MICROPY_REPL_STDIN_BUFFER_MAX (64) diff --git a/ports/nrf/boards/PCA10040/mpconfigboard.h b/ports/nrf/boards/PCA10040/mpconfigboard.h index b965bf319d..2b1c4c7ef5 100644 --- a/ports/nrf/boards/PCA10040/mpconfigboard.h +++ b/ports/nrf/boards/PCA10040/mpconfigboard.h @@ -64,3 +64,8 @@ #define MICROPY_HW_PWM2_NAME "PWM2" #define HELP_TEXT_BOARD_LED "1,2,3,4" + +// The JLink CDC on the PCA10040 cannot accept more than 64 incoming bytes at a time. +// That makes the UART REPL unreliable in general. But it can be improved to some +// extent by setting the raw-paste buffer size to that limit of 64. +#define MICROPY_REPL_STDIN_BUFFER_MAX (64) diff --git a/ports/nrf/boards/SEEED_XIAO_NRF52/board.json b/ports/nrf/boards/SEEED_XIAO_NRF52/board.json index d40355bb2e..b00044a56a 100644 --- a/ports/nrf/boards/SEEED_XIAO_NRF52/board.json +++ b/ports/nrf/boards/SEEED_XIAO_NRF52/board.json @@ -16,8 +16,8 @@ "XIAO_nrf52840_front.jpg" ], "mcu": "nrf52", - "product": "SEEED XIAO nRF52840 Sense", + "product": "XIAO nRF52840 Sense", "thumbnail": "", - "url": "https://www.seeedstudio.com", + "url": "https://www.seeedstudio.com/Seeed-XIAO-BLE-Sense-nRF52840-p-5253.html", "vendor": "Seeed Studio" } diff --git a/ports/nrf/boards/WT51822_S4AT/board.json b/ports/nrf/boards/WT51822_S4AT/board.json index 89d008c6f5..2ec42ac414 100644 --- a/ports/nrf/boards/WT51822_S4AT/board.json +++ b/ports/nrf/boards/WT51822_S4AT/board.json @@ -8,8 +8,8 @@ "WT51822-S4AT.jpg" ], "mcu": "nrf51", - "product": "wt51822_s4at", + "product": "WT51822-S4AT", "thumbnail": "", - "url": "http://www.wireless-tag.com/portfolio/wt51822-s4at-2/", + "url": "https://shop.wireless-tag.com/products/esp32-c3-mini-1-10pcs-espressif-esp32-c3-mini-1-4mb-flash-pcb-antenna-15-gpios-wifi-ble-5-module-esp32-c3-module-on-esp32-c3-chip", "vendor": "Wireless-Tag" } diff --git a/ports/nrf/main.c b/ports/nrf/main.c index 29550bd77a..21a71c7c9e 100644 --- a/ports/nrf/main.c +++ b/ports/nrf/main.c @@ -39,6 +39,8 @@ #include "py/stackctrl.h" #include "py/gc.h" #include "py/compile.h" +#include "py/persistentcode.h" +#include "extmod/misc.h" #include "extmod/modmachine.h" #include "shared/runtime/pyexec.h" #include "readline.h" @@ -107,7 +109,7 @@ void do_str(const char *src, mp_parse_input_kind_t input_kind) { extern uint32_t _heap_start; extern uint32_t _heap_end; -void NORETURN _start(void) { +void MP_NORETURN _start(void) { // Hook for a board to run code at start up, for example check if a // bootloader should be entered instead of the main application. MICROPY_BOARD_STARTUP(); @@ -171,7 +173,8 @@ soft_reset: MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL), MP_OBJ_NEW_SMALL_INT(MICROPY_HW_UART_REPL_BAUD), }; - MP_STATE_VM(dupterm_objs[0]) = MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, make_new)((mp_obj_t)&machine_uart_type, MP_ARRAY_SIZE(args), 0, args); + mp_obj_t uart = MP_OBJ_TYPE_GET_SLOT(&machine_uart_type, make_new)((mp_obj_t)&machine_uart_type, MP_ARRAY_SIZE(args), 0, args); + mp_os_dupterm_obj.fun.var(1, &uart); } #endif @@ -353,7 +356,7 @@ void HardFault_Handler(void) { #endif } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { while (1) { ; } @@ -369,3 +372,16 @@ void MP_WEAK __assert_func(const char *file, int line, const char *func, const c printf("Assertion '%s' failed, at file %s:%d\n", expr, file, line); __fatal_error("Assertion failed"); } + +#if MICROPY_EMIT_MACHINE_CODE +void *nrf_native_code_commit(void *buf, unsigned int len, void *reloc) { + (void)len; + if (reloc) { + // Native code in RAM must execute from the IRAM region at 0x00800000, and so relocations + // to text must also point to this region. The MICROPY_MAKE_POINTER_CALLABLE macro will + // adjust the `buf` address from RAM to IRAM. + mp_native_relocate(reloc, buf, (uintptr_t)MICROPY_MAKE_POINTER_CALLABLE(buf) & ~1); + } + return buf; +} +#endif diff --git a/ports/nrf/modules/machine/modmachine.c b/ports/nrf/modules/machine/modmachine.c index de1d0e3124..f543265479 100644 --- a/ports/nrf/modules/machine/modmachine.c +++ b/ports/nrf/modules/machine/modmachine.c @@ -85,8 +85,6 @@ #define MICROPY_PY_MACHINE_EXTRA_GLOBALS \ { MP_ROM_QSTR(MP_QSTR_info), MP_ROM_PTR(&machine_info_obj) }, \ - { MP_ROM_QSTR(MP_QSTR_enable_irq), MP_ROM_PTR(&machine_enable_irq_obj) }, \ - { MP_ROM_QSTR(MP_QSTR_disable_irq), MP_ROM_PTR(&machine_disable_irq_obj) }, \ { MP_ROM_QSTR(MP_QSTR_sleep), MP_ROM_PTR(&machine_lightsleep_obj) }, \ { MP_ROM_QSTR(MP_QSTR_Pin), MP_ROM_PTR(&pin_type) }, \ \ @@ -181,11 +179,11 @@ static mp_obj_t mp_machine_unique_id(void) { } // Resets the board in a manner similar to pushing the external RESET button. -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { NVIC_SystemReset(); } -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); for (;;) { } @@ -199,7 +197,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { __WFE(); } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { mp_machine_reset(); } @@ -214,24 +212,3 @@ static mp_obj_t mp_machine_get_freq(void) { static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { mp_raise_NotImplementedError(NULL); } - -static mp_obj_t machine_enable_irq(void) { - #ifndef BLUETOOTH_SD - __enable_irq(); - #else - - #endif - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_0(machine_enable_irq_obj, machine_enable_irq); - -// Resets the board in a manner similar to pushing the external RESET button. -static mp_obj_t machine_disable_irq(void) { - #ifndef BLUETOOTH_SD - __disable_irq(); - #else - - #endif - return mp_const_none; -} -MP_DEFINE_CONST_FUN_OBJ_0(machine_disable_irq_obj, machine_disable_irq); diff --git a/ports/nrf/modules/machine/uart.c b/ports/nrf/modules/machine/uart.c index 8d5a73e095..4c75bf8209 100644 --- a/ports/nrf/modules/machine/uart.c +++ b/ports/nrf/modules/machine/uart.c @@ -46,6 +46,11 @@ #include "nrfx_uarte.h" #endif +#if defined(NRF52832) +// The nRF52832 cannot write more than 255 bytes at a time. +#define UART_MAX_TX_CHUNK (255) +#endif + typedef struct _machine_uart_buf_t { uint8_t tx_buf[1]; uint8_t rx_buf[1]; @@ -104,6 +109,7 @@ typedef struct _machine_uart_obj_t { uint16_t timeout_char; // timeout waiting between chars (in ms) uint8_t uart_id; bool initialized; // static flag. Initialized to False + bool attached_to_repl; #if MICROPY_PY_MACHINE_UART_IRQ uint16_t mp_irq_trigger; // user IRQ trigger mask uint16_t mp_irq_flags; // user IRQ active IRQ flags @@ -118,6 +124,13 @@ static machine_uart_obj_t machine_uart_obj[] = { }; void uart_init0(void) { + for (int i = 0; i < MP_ARRAY_SIZE(machine_uart_obj); i++) { + machine_uart_obj[i].attached_to_repl = false; + } +} + +void uart_attach_to_repl(machine_uart_obj_t *self, bool attached) { + self->attached_to_repl = attached; } static int uart_find(mp_obj_t id) { @@ -137,14 +150,16 @@ static void uart_event_handler(nrfx_uart_event_t const *p_event, void *p_context if (p_event->type == NRFX_UART_EVT_RX_DONE) { nrfx_uart_rx(self->p_uart, &self->buf.rx_buf[0], 1); int chr = self->buf.rx_buf[0]; - #if !MICROPY_PY_BLE_NUS && MICROPY_KBD_EXCEPTION - if (chr == mp_interrupt_char) { - self->buf.rx_ringbuf.iget = 0; - self->buf.rx_ringbuf.iput = 0; - mp_sched_keyboard_interrupt(); - } else - #endif - { + if (self->attached_to_repl) { + #if MICROPY_KBD_EXCEPTION + if (chr == mp_interrupt_char) { + mp_sched_keyboard_interrupt(); + } else + #endif + { + ringbuf_put((ringbuf_t *)&stdin_ringbuf, chr); + } + } else { ringbuf_put((ringbuf_t *)&self->buf.rx_ringbuf, chr); } #if MICROPY_PY_MACHINE_UART_IRQ @@ -446,17 +461,29 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf, mp_uin #endif machine_uart_obj_t *self = self_in; - nrfx_err_t err = nrfx_uart_tx(self->p_uart, buf, size); - if (err == NRFX_SUCCESS) { + + // Send data out, in chunks if needed. + mp_uint_t remaining = size; + while (remaining) { + #ifdef UART_MAX_TX_CHUNK + mp_uint_t chunk = MIN(UART_MAX_TX_CHUNK, remaining); + #else + mp_uint_t chunk = remaining; + #endif + nrfx_err_t err = nrfx_uart_tx(self->p_uart, buf, chunk); + if (err != NRFX_SUCCESS) { + *errcode = mp_hal_status_to_errno_table[err]; + return MP_STREAM_ERROR; + } while (nrfx_uart_tx_in_progress(self->p_uart)) { MICROPY_EVENT_POLL_HOOK; } - // return number of bytes written - return size; - } else { - *errcode = mp_hal_status_to_errno_table[err]; - return MP_STREAM_ERROR; + buf += chunk; + remaining -= chunk; } + + // return number of bytes written + return size; } static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { diff --git a/ports/nrf/modules/machine/uart.h b/ports/nrf/modules/machine/uart.h index 741473ab7a..85c2092458 100644 --- a/ports/nrf/modules/machine/uart.h +++ b/ports/nrf/modules/machine/uart.h @@ -34,8 +34,7 @@ typedef struct _machine_uart_obj_t machine_uart_obj_t; void uart_init0(void); -void uart_deinit(void); -void uart_irq_handler(mp_uint_t uart_id); +void uart_attach_to_repl(machine_uart_obj_t *self, bool attached); bool uart_rx_any(machine_uart_obj_t *uart_obj); int uart_rx_char(machine_uart_obj_t *uart_obj); diff --git a/ports/nrf/modules/os/modos.c b/ports/nrf/modules/os/modos.c index f000e1eeb6..97ed1e1bad 100644 --- a/ports/nrf/modules/os/modos.c +++ b/ports/nrf/modules/os/modos.c @@ -30,6 +30,7 @@ #include "py/runtime.h" #include "extmod/modmachine.h" #include "drivers/rng.h" +#include "modules/machine/uart.h" #if MICROPY_PY_OS_URANDOM // Return a bytes object with n random bytes, generated by the hardware random number generator. @@ -46,10 +47,17 @@ static MP_DEFINE_CONST_FUN_OBJ_1(mp_os_urandom_obj, mp_os_urandom); #endif #if MICROPY_PY_OS_DUPTERM -// TODO should accept any object with read/write methods. void mp_os_dupterm_stream_detached_attached(mp_obj_t stream_detached, mp_obj_t stream_attached) { - if (!(stream_attached == mp_const_none || mp_obj_get_type(stream_attached) == &machine_uart_type)) { - mp_raise_ValueError(MP_ERROR_TEXT("need a UART object")); + #if MICROPY_PY_MACHINE_UART + if (mp_obj_get_type(stream_detached) == &machine_uart_type) { + uart_attach_to_repl(MP_OBJ_TO_PTR(stream_detached), false); } + #endif + + #if MICROPY_PY_MACHINE_UART + if (mp_obj_get_type(stream_attached) == &machine_uart_type) { + uart_attach_to_repl(MP_OBJ_TO_PTR(stream_attached), true); + } + #endif } #endif // MICROPY_PY_OS_DUPTERM diff --git a/ports/nrf/mpconfigport.h b/ports/nrf/mpconfigport.h index 7cc8a66d98..d52b5745d4 100644 --- a/ports/nrf/mpconfigport.h +++ b/ports/nrf/mpconfigport.h @@ -181,6 +181,7 @@ #define MICROPY_PY_MACHINE_RESET (1) #define MICROPY_PY_MACHINE_BARE_METAL_FUNCS (1) #define MICROPY_PY_MACHINE_BOOTLOADER (1) +#define MICROPY_PY_MACHINE_DISABLE_IRQ_ENABLE_IRQ (1) #define MICROPY_PY_MACHINE_PULSE (0) #define MICROPY_PY_MACHINE_SOFTI2C (MICROPY_PY_MACHINE_I2C) @@ -320,7 +321,17 @@ // type definitions for the specific machine -#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((mp_uint_t)(p) | 1)) +#if defined(NRF52832) || defined(NRF52840) +// On nRF52, the physical SRAM is mapped to 0x20000000 for data access and 0x00800000 +// for instruction access. So convert addresses to make them executable. +#define MICROPY_PERSISTENT_CODE_TRACK_FUN_DATA (1) +#define MICROPY_PERSISTENT_CODE_TRACK_BSS_RODATA (0) +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)(((uintptr_t)(p) - 0x20000000 + 0x00800000) | 1)) +void *nrf_native_code_commit(void *, unsigned int, void *); +#define MP_PLAT_COMMIT_EXEC(buf, len, reloc) nrf_native_code_commit(buf, len, reloc) +#else +#define MICROPY_MAKE_POINTER_CALLABLE(p) ((void *)((uintptr_t)(p) | 1)) +#endif #define MP_SSIZE_MAX (0x7fffffff) diff --git a/ports/nrf/mphalport.c b/ports/nrf/mphalport.c index 9bb51deb12..3a3d3ad7a6 100644 --- a/ports/nrf/mphalport.c +++ b/ports/nrf/mphalport.c @@ -202,7 +202,7 @@ const byte mp_hal_status_to_errno_table[4] = { [HAL_TIMEOUT] = MP_ETIMEDOUT, }; -NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { mp_raise_OSError(mp_hal_status_to_errno_table[status]); } diff --git a/ports/nrf/mphalport.h b/ports/nrf/mphalport.h index a0bca58a62..12e881d7b6 100644 --- a/ports/nrf/mphalport.h +++ b/ports/nrf/mphalport.h @@ -35,6 +35,22 @@ #include "nrfx_config.h" #include "shared/runtime/interrupt_char.h" +// Entering a critical section. +#ifndef BLUETOOTH_SD +#define MICROPY_BEGIN_ATOMIC_SECTION() disable_irq() +#define MICROPY_END_ATOMIC_SECTION(state) enable_irq(state) +#endif + +static inline void enable_irq(mp_uint_t state) { + __set_PRIMASK(state); +} + +static inline mp_uint_t disable_irq(void) { + mp_uint_t state = __get_PRIMASK(); + __disable_irq(); + return state; +} + typedef enum { HAL_OK = 0x00, @@ -47,7 +63,7 @@ extern const unsigned char mp_hal_status_to_errno_table[4]; extern ringbuf_t stdin_ringbuf; -NORETURN void mp_hal_raise(HAL_StatusTypeDef status); +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status); void mp_hal_set_interrupt_char(int c); // -1 to disable int mp_hal_stdin_rx_chr(void); diff --git a/ports/pic16bit/main.c b/ports/pic16bit/main.c index b1fe7321f0..437f91ca7c 100644 --- a/ports/pic16bit/main.c +++ b/ports/pic16bit/main.c @@ -121,7 +121,7 @@ void nlr_jump_fail(void *val) { } } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { while (1) { ; } diff --git a/ports/powerpc/main.c b/ports/powerpc/main.c index 4b668c861c..bbd2082b42 100644 --- a/ports/powerpc/main.c +++ b/ports/powerpc/main.c @@ -130,7 +130,7 @@ void nlr_jump_fail(void *val) { } } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { while (1) { ; } diff --git a/ports/qemu/mcu/arm/errorhandler.c b/ports/qemu/mcu/arm/errorhandler.c index a0e7ef930f..a647c0e153 100644 --- a/ports/qemu/mcu/arm/errorhandler.c +++ b/ports/qemu/mcu/arm/errorhandler.c @@ -63,7 +63,7 @@ static const char *EXCEPTION_NAMES_TABLE[] = { // R0-R15, PSR, Kind uintptr_t registers_copy[18] = { 0 }; -__attribute__((naked)) NORETURN void exception_handler(uintptr_t kind) { +__attribute__((naked)) MP_NORETURN void exception_handler(uintptr_t kind) { // Save registers __asm volatile ( "ldr r1, =registers_copy \n" @@ -137,39 +137,39 @@ __attribute__((naked)) NORETURN void exception_handler(uintptr_t kind) { for (;;) {} } -__attribute__((naked)) NORETURN void NMI_Handler(void) { +__attribute__((naked)) MP_NORETURN void NMI_Handler(void) { exception_handler(NMI); } -__attribute__((naked)) NORETURN void HardFault_Handler(void) { +__attribute__((naked)) MP_NORETURN void HardFault_Handler(void) { exception_handler(HARD_FAULT); } -__attribute__((naked)) NORETURN void MemManage_Handler(void) { +__attribute__((naked)) MP_NORETURN void MemManage_Handler(void) { exception_handler(MEM_MANAGE); } -__attribute__((naked)) NORETURN void BusFault_Handler(void) { +__attribute__((naked)) MP_NORETURN void BusFault_Handler(void) { exception_handler(BUS_FAULT); } -__attribute__((naked)) NORETURN void UsageFault_Handler(void) { +__attribute__((naked)) MP_NORETURN void UsageFault_Handler(void) { exception_handler(USAGE_FAULT); } -__attribute__((naked)) NORETURN void SVC_Handler(void) { +__attribute__((naked)) MP_NORETURN void SVC_Handler(void) { exception_handler(SV_CALL); } -__attribute__((naked)) NORETURN void DebugMon_Handler(void) { +__attribute__((naked)) MP_NORETURN void DebugMon_Handler(void) { exception_handler(DEBUG_MONITOR); } -__attribute__((naked)) NORETURN void PendSV_Handler(void) { +__attribute__((naked)) MP_NORETURN void PendSV_Handler(void) { exception_handler(PENDING_SV); } -__attribute__((naked)) NORETURN void SysTick_Handler(void) { +__attribute__((naked)) MP_NORETURN void SysTick_Handler(void) { exception_handler(SYSTEM_TICK); } diff --git a/ports/qemu/mcu/arm/startup.c b/ports/qemu/mcu/arm/startup.c index dbb75e3392..a7e9efb9fb 100644 --- a/ports/qemu/mcu/arm/startup.c +++ b/ports/qemu/mcu/arm/startup.c @@ -52,7 +52,7 @@ __attribute__((naked)) void Reset_Handler(void) { _start(); } -NORETURN void Default_Handler(void) { +MP_NORETURN void Default_Handler(void) { for (;;) { } } diff --git a/ports/renesas-ra/Makefile b/ports/renesas-ra/Makefile index ec47510d98..fd74c60a85 100644 --- a/ports/renesas-ra/Makefile +++ b/ports/renesas-ra/Makefile @@ -157,9 +157,6 @@ LIBSTDCPP_FILE_NAME = "$(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a)" LDFLAGS += -L"$(shell dirname $(LIBSTDCPP_FILE_NAME))" endif -# Hook tinyusb USB interrupt if used to service usb task. -LDFLAGS += --wrap=dcd_event_handler - # Options for mpy-cross MPY_CROSS_FLAGS += -march=armv7m diff --git a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json index 4c4d4e4326..70a33b3515 100644 --- a/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json +++ b/ports/renesas-ra/boards/ARDUINO_PORTENTA_C33/board.json @@ -15,7 +15,7 @@ "ABX00074_01.iso_1000x750.jpg" ], "mcu": "RA6M5", - "product": "Arduino Portenta C33", + "product": "Portenta C33", "thumbnail": "", "url": "https://store.arduino.cc/pages/portenta-c33", "vendor": "Arduino" diff --git a/ports/renesas-ra/machine_uart.c b/ports/renesas-ra/machine_uart.c index b70978ad7a..196e1ccd6d 100644 --- a/ports/renesas-ra/machine_uart.c +++ b/ports/renesas-ra/machine_uart.c @@ -501,7 +501,7 @@ static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uint if (!uart_tx_busy(self)) { return 0; } - MICROPY_EVENT_POLL_HOOK + mp_event_wait_indefinite(); } while (mp_hal_ticks_ms() < timeout); *errcode = MP_ETIMEDOUT; ret = MP_STREAM_ERROR; diff --git a/ports/renesas-ra/main.c b/ports/renesas-ra/main.c index febb7b6d7a..cfc4611ecb 100644 --- a/ports/renesas-ra/main.c +++ b/ports/renesas-ra/main.c @@ -88,7 +88,7 @@ static machine_uart_obj_t machine_uart_repl_obj; static uint8_t machine_uart_repl_rxbuf[MICROPY_HW_UART_REPL_RXBUF]; #endif -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { for (volatile uint delay = 0; delay < 1000000; delay++) { } led_state(1, 1); diff --git a/ports/renesas-ra/modmachine.c b/ports/renesas-ra/modmachine.c index dc38d809dd..c23ce7e469 100644 --- a/ports/renesas-ra/modmachine.c +++ b/ports/renesas-ra/modmachine.c @@ -184,12 +184,12 @@ static mp_obj_t mp_machine_unique_id(void) { } // Resets the pyboard in a manner similar to pushing the external RESET button. -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { powerctrl_mcu_reset(); } // Activate the bootloader without BOOT* pins. -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { #if MICROPY_HW_ENABLE_STORAGE storage_flush(); #endif @@ -232,7 +232,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { powerctrl_enter_stop_mode(); } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { if (n_args != 0) { mp_obj_t args2[2] = {MP_OBJ_NULL, args[0]}; machine_rtc_wakeup(2, args2); diff --git a/ports/renesas-ra/modtime.c b/ports/renesas-ra/modtime.c index cbd639721f..e1358f82bc 100644 --- a/ports/renesas-ra/modtime.c +++ b/ports/renesas-ra/modtime.c @@ -53,5 +53,5 @@ static mp_obj_t mp_time_time_get(void) { rtc_init_finalise(); ra_rtc_t time; ra_rtc_get_time(&time); - return mp_obj_new_int(timeutils_seconds_since_epoch(time.year, time.month, time.date, time.hour, time.minute, time.second)); + return timeutils_obj_from_timestamp(timeutils_seconds_since_epoch(time.year, time.month, time.date, time.hour, time.minute, time.second)); } diff --git a/ports/renesas-ra/mpconfigport.h b/ports/renesas-ra/mpconfigport.h index bd936061fa..8a116269bb 100644 --- a/ports/renesas-ra/mpconfigport.h +++ b/ports/renesas-ra/mpconfigport.h @@ -210,19 +210,11 @@ #define MP_STATE_PORT MP_STATE_VM -#if MICROPY_PY_NETWORK_ESP_HOSTED -extern const struct _mp_obj_type_t mod_network_esp_hosted_type; -#define MICROPY_HW_NIC_ESP_HOSTED { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_esp_hosted_type) }, -#else -#define MICROPY_HW_NIC_ESP_HOSTED -#endif - #ifndef MICROPY_BOARD_NETWORK_INTERFACES #define MICROPY_BOARD_NETWORK_INTERFACES #endif #define MICROPY_PORT_NETWORK_INTERFACES \ - MICROPY_HW_NIC_ESP_HOSTED \ MICROPY_BOARD_NETWORK_INTERFACES \ // Miscellaneous settings @@ -251,28 +243,17 @@ typedef unsigned int mp_uint_t; // must be pointer size typedef long mp_off_t; #if MICROPY_PY_THREAD -#define MICROPY_EVENT_POLL_HOOK \ +#define MICROPY_INTERNAL_EVENT_HOOK \ do { \ - extern void mp_handle_pending(bool); \ - mp_handle_pending(true); \ if (pyb_thread_enabled) { \ MP_THREAD_GIL_EXIT(); \ pyb_thread_yield(); \ MP_THREAD_GIL_ENTER(); \ - } else { \ - __WFI(); \ } \ } while (0); #define MICROPY_THREAD_YIELD() pyb_thread_yield() #else -#define MICROPY_EVENT_POLL_HOOK \ - do { \ - extern void mp_handle_pending(bool); \ - mp_handle_pending(true); \ - __WFI(); \ - } while (0); - #define MICROPY_THREAD_YIELD() #endif diff --git a/ports/renesas-ra/mphalport.c b/ports/renesas-ra/mphalport.c index 1c62c23dd7..455b28af19 100644 --- a/ports/renesas-ra/mphalport.c +++ b/ports/renesas-ra/mphalport.c @@ -61,7 +61,7 @@ const byte mp_hal_status_to_errno_table[4] = { [HAL_TIMEOUT] = MP_ETIMEDOUT, }; -NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { mp_raise_OSError(mp_hal_status_to_errno_table[status]); } @@ -104,7 +104,7 @@ int mp_hal_stdin_rx_chr(void) { return dupterm_c; } #endif - MICROPY_EVENT_POLL_HOOK + mp_event_wait_indefinite(); } } diff --git a/ports/renesas-ra/mphalport.h b/ports/renesas-ra/mphalport.h index 0819abeaf6..4f8be705d1 100644 --- a/ports/renesas-ra/mphalport.h +++ b/ports/renesas-ra/mphalport.h @@ -35,6 +35,14 @@ #define MICROPY_PY_PENDSV_ENTER uint32_t atomic_state = raise_irq_pri(IRQ_PRI_PENDSV) #define MICROPY_PY_PENDSV_EXIT restore_irq_pri(atomic_state) +// Port level Wait-for-Event macro +// +// Do not use this macro directly, include py/runtime.h and +// call mp_event_wait_indefinite() or mp_event_wait_ms(timeout). +// Uses WFI which will wake up from regular systick interrupt if not +// before from any other source. +#define MICROPY_INTERNAL_WFE(TIMEOUT_MS) __WFI() + #define MICROPY_PY_LWIP_ENTER #define MICROPY_PY_LWIP_REENTER #define MICROPY_PY_LWIP_EXIT @@ -49,7 +57,7 @@ static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) { return -mp_hal_status_to_errno_table[status]; } -NORETURN void mp_hal_raise(HAL_StatusTypeDef status); +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status); void mp_hal_set_interrupt_char(int c); // -1 to disable static inline void mp_hal_wake_main_task_from_isr(void) { diff --git a/ports/renesas-ra/powerctrl.c b/ports/renesas-ra/powerctrl.c index 548d31679a..04c39b3511 100644 --- a/ports/renesas-ra/powerctrl.c +++ b/ports/renesas-ra/powerctrl.c @@ -174,7 +174,7 @@ const lpm_instance_t g_lpm_standby = { #endif -NORETURN void powerctrl_mcu_reset(void) { +MP_NORETURN void powerctrl_mcu_reset(void) { #if BSP_TZ_SECURE_BUILD R_BSP_NonSecureEnter(); #else @@ -185,7 +185,7 @@ NORETURN void powerctrl_mcu_reset(void) { } } -NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) { +MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) { while (1) { ; } @@ -245,7 +245,7 @@ void powerctrl_enter_stop_mode(void) { enable_irq(irq_state); } -NORETURN void powerctrl_enter_standby_mode(void) { +MP_NORETURN void powerctrl_enter_standby_mode(void) { rtc_init_finalise(); #if defined(MICROPY_BOARD_ENTER_STANDBY) diff --git a/ports/renesas-ra/powerctrl.h b/ports/renesas-ra/powerctrl.h index 34c40ea1ad..37932b1002 100644 --- a/ports/renesas-ra/powerctrl.h +++ b/ports/renesas-ra/powerctrl.h @@ -32,11 +32,11 @@ void SystemClock_Config(void); -NORETURN void powerctrl_mcu_reset(void); -NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr); +MP_NORETURN void powerctrl_mcu_reset(void); +MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr); void powerctrl_check_enter_bootloader(void); void powerctrl_enter_stop_mode(void); -NORETURN void powerctrl_enter_standby_mode(void); +MP_NORETURN void powerctrl_enter_standby_mode(void); #endif // MICROPY_INCLUDED_RA_POWERCTRL_H diff --git a/ports/renesas-ra/systick.c b/ports/renesas-ra/systick.c index 6fa02a2b6e..cb528d91d2 100644 --- a/ports/renesas-ra/systick.c +++ b/ports/renesas-ra/systick.c @@ -97,22 +97,24 @@ void HAL_Delay(uint32_t Delay) { // Core delay function that does an efficient sleep and may switch thread context. // If IRQs are enabled then we must have the GIL. -void mp_hal_delay_ms(mp_uint_t Delay) { +void mp_hal_delay_ms(mp_uint_t ms) { if (query_irq() == IRQ_STATE_ENABLED) { // IRQs enabled, so can use systick counter to do the delay uint32_t start = uwTick; + mp_uint_t elapsed = 0; // Wraparound of tick is taken care of by 2's complement arithmetic. do { // This macro will execute the necessary idle behaviour. It may // raise an exception, switch threads or enter sleep mode (waiting for // (at least) the SysTick interrupt). - MICROPY_EVENT_POLL_HOOK - } while (uwTick - start < Delay); + mp_event_wait_ms(ms - elapsed); + elapsed = uwTick - start; + } while (elapsed < ms); } else { // IRQs disabled, so need to use a busy loop for the delay. // To prevent possible overflow of the counter we use a double loop. volatile uint32_t count_1ms; - while (Delay-- > 0) { + while (ms-- > 0) { count_1ms = (MICROPY_HW_MCU_PCLK / 1000 / 10); while (count_1ms-- > 0) { __asm__ __volatile__ ("nop"); diff --git a/ports/renesas-ra/uart.c b/ports/renesas-ra/uart.c index c319b4c0a8..91714bf05b 100644 --- a/ports/renesas-ra/uart.c +++ b/ports/renesas-ra/uart.c @@ -42,7 +42,7 @@ typedef int (*KEYEX_CB)(uint32_t d); -extern void NORETURN __fatal_error(const char *msg); +extern void MP_NORETURN __fatal_error(const char *msg); #if MICROPY_KBD_EXCEPTION extern int mp_interrupt_char; static KEYEX_CB keyex_cb[MICROPY_HW_MAX_UART] = {(KEYEX_CB)NULL}; @@ -477,7 +477,7 @@ bool uart_rx_wait(machine_uart_obj_t *self, uint32_t timeout) { if (HAL_GetTick() - start >= timeout) { return false; // timeout } - MICROPY_EVENT_POLL_HOOK + mp_event_wait_ms(1); } } @@ -498,7 +498,7 @@ bool uart_tx_wait(machine_uart_obj_t *self, uint32_t timeout) { if (HAL_GetTick() - start >= timeout) { return false; // timeout } - MICROPY_EVENT_POLL_HOOK + mp_event_wait_ms(1); } } diff --git a/ports/rp2/CMakeLists.txt b/ports/rp2/CMakeLists.txt index 53d00c7dfd..49ba8d77d6 100644 --- a/ports/rp2/CMakeLists.txt +++ b/ports/rp2/CMakeLists.txt @@ -84,6 +84,9 @@ endif() list(APPEND GIT_SUBMODULES lib/mbedtls) list(APPEND GIT_SUBMODULES lib/tinyusb) +# Workaround for pico-sdk host toolchain issue, see directory for details +list(APPEND CMAKE_MODULE_PATH "${MICROPY_PORT_DIR}/tools_patch") + # Include component cmake fragments include(${MICROPY_DIR}/py/py.cmake) include(${MICROPY_DIR}/extmod/extmod.cmake) @@ -522,10 +525,23 @@ target_compile_options(${MICROPY_TARGET} PRIVATE target_link_options(${MICROPY_TARGET} PRIVATE -Wl,--defsym=__micropy_c_heap_size__=${MICROPY_C_HEAP_SIZE} - -Wl,--wrap=dcd_event_handler -Wl,--wrap=runtime_init_clocks ) +if(PICO_FLASH_SIZE_BYTES GREATER 0) + target_link_options(${MICROPY_TARGET} PRIVATE + -Wl,--defsym=__micropy_flash_size__=${PICO_FLASH_SIZE_BYTES} + ) +elseif(PICO_RP2040) + target_link_options(${MICROPY_TARGET} PRIVATE + -Wl,--defsym=__micropy_flash_size__=2048k # Default to 2MB + ) +elseif(PICO_RP2350) + target_link_options(${MICROPY_TARGET} PRIVATE + -Wl,--defsym=__micropy_flash_size__=4096k # Default to 4MB + ) +endif() + if(PICO_RP2350) target_link_options(${MICROPY_TARGET} PRIVATE -Wl,--defsym=__micropy_extra_stack__=4096 @@ -594,14 +610,18 @@ endif() # todo this is a bit brittle, but we want to move a few source files into RAM (which requires # a linker script modification) until we explicitly add macro calls around the function # defs to move them into RAM. -if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM) +if (NOT MICROPY_BOARD_LINKER_SCRIPT) if(PICO_RP2040) - pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2040.ld) + set(MICROPY_BOARD_LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2040.ld) elseif(PICO_RP2350) - pico_set_linker_script(${MICROPY_TARGET} ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2350.ld) + set(MICROPY_BOARD_LINKER_SCRIPT ${CMAKE_CURRENT_LIST_DIR}/memmap_mp_rp2350.ld) endif() endif() +if (PICO_ON_DEVICE AND NOT PICO_NO_FLASH AND NOT PICO_COPY_TO_RAM) + pico_set_linker_script(${MICROPY_TARGET} ${MICROPY_BOARD_LINKER_SCRIPT}) +endif() + pico_add_extra_outputs(${MICROPY_TARGET}) pico_find_compiler_with_triples(PICO_COMPILER_SIZE "${PICO_GCC_TRIPLE}" size) @@ -638,9 +658,13 @@ if(NOT PICO_NUM_EXT_GPIOS) set(PICO_NUM_EXT_GPIOS 10) endif() -if(EXISTS "${MICROPY_BOARD_DIR}/pins.csv") - set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_DIR}/pins.csv") - set(GEN_PINS_CSV_ARG --board-csv "${GEN_PINS_BOARD_CSV}") +if(NOT MICROPY_BOARD_PINS) + set(MICROPY_BOARD_PINS "${MICROPY_BOARD_DIR}/pins.csv") +endif() + +if(EXISTS "${MICROPY_BOARD_PINS}") + set(GEN_PINS_BOARD_CSV "${MICROPY_BOARD_PINS}") + set(GEN_PINS_CSV_ARG --board-csv "${MICROPY_BOARD_PINS}") endif() target_sources(${MICROPY_TARGET} PRIVATE diff --git a/ports/rp2/Makefile b/ports/rp2/Makefile index 76b5698d2c..2895faaca6 100644 --- a/ports/rp2/Makefile +++ b/ports/rp2/Makefile @@ -67,6 +67,9 @@ all: clean: $(RM) -rf $(BUILD) +deploy: all + $(Q)picotool load -x $(BUILD)/firmware.elf + # First ensure that pico-sdk is initialised, then run CMake with the # UPDATE_SUBMODULES flag to update necessary submodules for this board. # diff --git a/ports/rp2/README.md b/ports/rp2/README.md index 911d797fe0..41fca97f95 100644 --- a/ports/rp2/README.md +++ b/ports/rp2/README.md @@ -47,8 +47,10 @@ pass the board name to the build; e.g. for Raspberry Pi Pico W: ## Deploying firmware to the device Firmware can be deployed to the device by putting it into bootloader mode -(hold down BOOTSEL while powering on or resetting) and then copying -`firmware.uf2` to the USB mass storage device that appears. +(hold down BOOTSEL while powering on or resetting) and then either copying +`firmware.uf2` to the USB mass storage device that appears, or using +`picotool load -x firmware.elf`. The latter command can be accessed +conveniently via `make deploy`. If MicroPython is already installed then the bootloader can be entered by executing `import machine; machine.bootloader()` at the REPL. diff --git a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json index 5639aaa2a4..5914445ed8 100644 --- a/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json +++ b/ports/rp2/boards/ARDUINO_NANO_RP2040_CONNECT/board.json @@ -17,7 +17,7 @@ "ABX00052_01.iso_999x750.jpg" ], "mcu": "rp2040", - "product": "Arduino Nano RP2040 Connect", + "product": "Nano RP2040 Connect", "thumbnail": "", "url": "https://store-usa.arduino.cc/products/arduino-nano-rp2040-connect", "vendor": "Arduino" diff --git a/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json b/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json index df9dd5d0de..108d632678 100644 --- a/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json +++ b/ports/rp2/boards/GARATRONIC_PYBSTICK26_RP2040/board.json @@ -13,7 +13,7 @@ "pybstick-rp2040-26-broches-micropython-c.jpg" ], "mcu": "rp2040", - "product": "PYBSTICK26 RP2040", + "product": "RP2040 PYBStick", "thumbnail": "", "url": "https://shop.mchobby.be/product.php?id_product=2331", "vendor": "McHobby" diff --git a/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json b/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json index 5358db627e..4b12cfda65 100644 --- a/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json +++ b/ports/rp2/boards/POLOLU_3PI_2040_ROBOT/board.json @@ -15,7 +15,7 @@ "pololu_3pi_2040_robot.jpg" ], "mcu": "rp2040", - "product": "Pololu 3pi+ 2040 Robot", + "product": "3pi+ 2040 Robot", "thumbnail": "", "url": "https://www.pololu.com/3pi", "vendor": "Pololu" diff --git a/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json b/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json index 7ef383a9c4..1ec15ad591 100644 --- a/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json +++ b/ports/rp2/boards/POLOLU_ZUMO_2040_ROBOT/board.json @@ -16,7 +16,7 @@ "pololu_zumo_2040_robot.jpg" ], "mcu": "rp2040", - "product": "Pololu Zumo 2040 Robot", + "product": "Zumo 2040 Robot", "thumbnail": "", "url": "https://www.pololu.com/zumo", "vendor": "Pololu" diff --git a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json index 66cea79dde..5ee6bcb754 100644 --- a/ports/rp2/boards/SPARKFUN_PROMICRO/board.json +++ b/ports/rp2/boards/SPARKFUN_PROMICRO/board.json @@ -14,7 +14,7 @@ "18288-SparkFun_Pro_Micro_-_RP2040-01.jpg" ], "mcu": "rp2040", - "product": "Pro Micro RP2040", + "product": "Pro Micro - RP2040", "thumbnail": "", "url": "https://www.sparkfun.com/products/18288", "vendor": "SparkFun" diff --git a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json index e756c9bff4..ebd68bc5d9 100644 --- a/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json +++ b/ports/rp2/boards/SPARKFUN_THINGPLUS/board.json @@ -17,7 +17,7 @@ "17745-SparkFun_Thing_Plus_-_RP2040-01a.jpg" ], "mcu": "rp2040", - "product": "Thing Plus RP2040", + "product": "Thing Plus - RP2040", "thumbnail": "", "url": "https://www.sparkfun.com/products/17745", "vendor": "SparkFun" diff --git a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h index 56071e1873..931391d972 100644 --- a/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h +++ b/ports/rp2/boards/SPARKFUN_XRP_CONTROLLER_BETA/mpconfigboard.h @@ -24,5 +24,9 @@ int mp_hal_is_pin_reserved(int n); #define MICROPY_HW_PIN_RESERVED(i) mp_hal_is_pin_reserved(i) +// Set the default I2C to I2C1 on pins 18 and 19 which route to the qwiic connector +#undef PICO_DEFAULT_I2C +#define PICO_DEFAULT_I2C (1) + #define MICROPY_HW_I2C1_SDA (18) #define MICROPY_HW_I2C1_SCL (19) diff --git a/ports/rp2/boards/W5100S_EVB_PICO/board.json b/ports/rp2/boards/W5100S_EVB_PICO/board.json index daaf1cf8ac..3d052de420 100644 --- a/ports/rp2/boards/W5100S_EVB_PICO/board.json +++ b/ports/rp2/boards/W5100S_EVB_PICO/board.json @@ -13,8 +13,8 @@ "W5100S-EVB-Pico.jpg" ], "mcu": "rp2040", - "product": "Wiznet W5100S-EVB-Pico", + "product": "W5100S-EVB-Pico", "thumbnail": "", - "url": "https://www.wiznet.io/product-item/w5100s-evb-pico/", - "vendor": "Wiznet" + "url": "https://docs.wiznet.io/Product/iEthernet/W5100S/w5100s-evb-pico", + "vendor": "WIZnet" } diff --git a/ports/rp2/boards/W5500_EVB_PICO/board.json b/ports/rp2/boards/W5500_EVB_PICO/board.json index e7625c3436..d4298014c0 100644 --- a/ports/rp2/boards/W5500_EVB_PICO/board.json +++ b/ports/rp2/boards/W5500_EVB_PICO/board.json @@ -13,8 +13,8 @@ "W5500-EVB-Pico.jpg" ], "mcu": "rp2040", - "product": "Wiznet W5500-EVB-Pico", + "product": "W5500-EVB-Pico", "thumbnail": "", "url": "https://docs.wiznet.io/Product/iEthernet/W5500/w5500-evb-pico", - "vendor": "Wiznet" + "vendor": "WIZnet" } diff --git a/ports/rp2/boards/WEACTSTUDIO/board.json b/ports/rp2/boards/WEACTSTUDIO/board.json index 8881b40e73..58d89ddf2e 100644 --- a/ports/rp2/boards/WEACTSTUDIO/board.json +++ b/ports/rp2/boards/WEACTSTUDIO/board.json @@ -12,7 +12,7 @@ "weact_rp2040.jpg" ], "mcu": "rp2040", - "product": "WeAct Studio RP2040", + "product": "Studio RP2040", "url": "https://github.com/WeActTC/WeActStudio.RP2040CoreBoard", "variants": { "FLASH_2M": "2 MiB Flash", diff --git a/ports/rp2/cyw43_configport.h b/ports/rp2/cyw43_configport.h index 5523305742..2da0b1f8a4 100644 --- a/ports/rp2/cyw43_configport.h +++ b/ports/rp2/cyw43_configport.h @@ -26,34 +26,27 @@ #ifndef MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H #define MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H -// The board-level config will be included here, so it can set some CYW43 values. -#include <stdio.h> -#include "py/mpconfig.h" -#include "py/mperrno.h" -#include "py/mphal.h" -#include "py/runtime.h" -#include "extmod/modnetwork.h" -#include "lwip/apps/mdns.h" -#include "pendsv.h" +#include "extmod/cyw43_config_common.h" #define CYW43_INCLUDE_LEGACY_F1_OVERFLOW_WORKAROUND_VARIABLES (1) #define CYW43_WIFI_NVRAM_INCLUDE_FILE "wifi_nvram_43439.h" -#define CYW43_IOCTL_TIMEOUT_US (1000000) -#define CYW43_SLEEP_MAX (10) -#define CYW43_NETUTILS (1) +#define CYW43_SLEEP_MAX (10) // Unclear why rp2 port overrides the default here #define CYW43_USE_OTP_MAC (1) -#define CYW43_PRINTF(...) mp_printf(MP_PYTHON_PRINTER, __VA_ARGS__) -#define CYW43_EPERM MP_EPERM // Operation not permitted -#define CYW43_EIO MP_EIO // I/O error -#define CYW43_EINVAL MP_EINVAL // Invalid argument -#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out +static inline bool cyw43_poll_is_pending(void) { + return pendsv_is_pending(PENDSV_DISPATCH_CYW43); +} + +static inline void cyw43_yield(void) { + if (!cyw43_poll_is_pending()) { + best_effort_wfe_or_timeout(make_timeout_time_ms(1)); + } +} -#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER -#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT -#define CYW43_THREAD_LOCK_CHECK +#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook(); -#define CYW43_HOST_NAME mod_network_hostname_data +// set in SDK board header +#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT #if CYW43_PIN_WL_DYNAMIC @@ -100,36 +93,6 @@ uint cyw43_get_pin_wl(cyw43_pin_index_t pin_id); cyw43_yield(); \ } \ -#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) - -#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT -#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT -#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE -#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP -#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN - -#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 - -// set in SDK board header -#define CYW43_NUM_GPIOS CYW43_WL_GPIO_COUNT - -#define CYW43_POST_POLL_HOOK cyw43_post_poll_hook(); - -#define cyw43_hal_ticks_us mp_hal_ticks_us -#define cyw43_hal_ticks_ms mp_hal_ticks_ms - -#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t -#define cyw43_hal_pin_config mp_hal_pin_config -#define cyw43_hal_pin_read mp_hal_pin_read -#define cyw43_hal_pin_low mp_hal_pin_low -#define cyw43_hal_pin_high mp_hal_pin_high - -#define cyw43_hal_get_mac mp_hal_get_mac -#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii -#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac - -#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) - // Bluetooth requires dynamic memory allocation to load its firmware (the allocation // call is made from pico-sdk). This allocation is always done at thread-level, not // from an IRQ, so is safe to delegate to the MicroPython GC heap. @@ -140,47 +103,4 @@ uint cyw43_get_pin_wl(cyw43_pin_index_t pin_id); #define cyw43_free m_tracked_free #endif -void cyw43_post_poll_hook(void); -static inline bool cyw43_poll_is_pending(void) { - return pendsv_is_pending(PENDSV_DISPATCH_CYW43); -} - -static inline void cyw43_yield(void) { - if (!cyw43_poll_is_pending()) { - best_effort_wfe_or_timeout(make_timeout_time_ms(1)); - } -} - -static inline void cyw43_delay_us(uint32_t us) { - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - } -} - -static inline void cyw43_delay_ms(uint32_t ms) { - // PendSV may be disabled via CYW43_THREAD_ENTER, so this delay is a busy loop. - uint32_t us = ms * 1000; - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - mp_event_handle_nowait(); - } -} - -#define CYW43_EVENT_POLL_HOOK mp_event_handle_nowait() - -#if LWIP_MDNS_RESPONDER == 1 - -// Hook for any additional TCP/IP initialization than needs to be done. -// Called after the netif specified by `itf` has been set up. -#ifndef CYW43_CB_TCPIP_INIT_EXTRA -#define CYW43_CB_TCPIP_INIT_EXTRA(self, itf) mdns_resp_add_netif(&self->netif[itf], mod_network_hostname_data) -#endif - -// Hook for any additional TCP/IP deinitialization than needs to be done. -// Called before the netif specified by `itf` is removed. -#ifndef CYW43_CB_TCPIP_DEINIT_EXTRA -#define CYW43_CB_TCPIP_DEINIT_EXTRA(self, itf) mdns_resp_remove_netif(&self->netif[itf]) -#endif - -#endif #endif // MICROPY_INCLUDED_RP2_CYW43_CONFIGPORT_H diff --git a/ports/rp2/machine_pin.c b/ports/rp2/machine_pin.c index d9ca3f8ce3..7a2de0c0bc 100644 --- a/ports/rp2/machine_pin.c +++ b/ports/rp2/machine_pin.c @@ -46,15 +46,6 @@ #define GPIO_IRQ_ALL (0xf) -// Macros to access the state of the hardware. -#define GPIO_GET_FUNCSEL(id) ((iobank0_hw->io[(id)].ctrl & IO_BANK0_GPIO0_CTRL_FUNCSEL_BITS) >> IO_BANK0_GPIO0_CTRL_FUNCSEL_LSB) -#define GPIO_IS_OUT(id) (sio_hw->gpio_oe & (1 << (id))) -#define GPIO_IS_PULL_UP(id) (padsbank0_hw->io[(id)] & PADS_BANK0_GPIO0_PUE_BITS) -#define GPIO_IS_PULL_DOWN(id) (padsbank0_hw->io[(id)] & PADS_BANK0_GPIO0_PDE_BITS) - -// Open drain behaviour is simulated. -#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1 << (id))) - #ifndef MICROPY_HW_PIN_RESERVED #define MICROPY_HW_PIN_RESERVED(i) (0) #endif @@ -89,7 +80,11 @@ static const mp_irq_methods_t machine_pin_irq_methods; static const int num_intr_regs = sizeof(iobank0_hw->intr) / sizeof(iobank0_hw->intr[0]); // Mask with "1" indicating that the corresponding pin is in simulated open-drain mode. +#if NUM_BANK0_GPIOS > 32 uint64_t machine_pin_open_drain_mask; +#else +uint32_t machine_pin_open_drain_mask; +#endif #if MICROPY_HW_PIN_EXT_COUNT static inline bool is_ext_pin(__unused const machine_pin_obj_t *self) { @@ -198,13 +193,13 @@ const machine_pin_obj_t *machine_pin_find(mp_obj_t pin) { static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_pin_obj_t *self = self_in; - uint funcsel = GPIO_GET_FUNCSEL(self->id); + uint funcsel = gpio_get_function(self->id); qstr mode_qst; if (!is_ext_pin(self)) { if (funcsel == GPIO_FUNC_SIO) { if (GPIO_IS_OPEN_DRAIN(self->id)) { mode_qst = MP_QSTR_OPEN_DRAIN; - } else if (GPIO_IS_OUT(self->id)) { + } else if (gpio_is_dir_out(self->id)) { mode_qst = MP_QSTR_OUT; } else { mode_qst = MP_QSTR_IN; @@ -214,11 +209,11 @@ static void machine_pin_print(const mp_print_t *print, mp_obj_t self_in, mp_prin } mp_printf(print, "Pin(%q, mode=%q", self->name, mode_qst); bool pull_up = false; - if (GPIO_IS_PULL_UP(self->id)) { + if (gpio_is_pulled_up(self->id)) { mp_printf(print, ", pull=%q", MP_QSTR_PULL_UP); pull_up = true; } - if (GPIO_IS_PULL_DOWN(self->id)) { + if (gpio_is_pulled_down(self->id)) { if (pull_up) { mp_printf(print, "|%q", MP_QSTR_PULL_DOWN); } else { @@ -298,7 +293,7 @@ static mp_obj_t machine_pin_obj_init_helper(const machine_pin_obj_t *self, size_ mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("invalid pin af: %d"), af); } gpio_set_function(self->id, af); - machine_pin_open_drain_mask &= ~(1ULL << self->id); + GPIO_DISABLE_OPEN_DRAIN(self->id); } } @@ -411,7 +406,7 @@ static mp_obj_t machine_pin_toggle(mp_obj_t self_in) { machine_pin_ext_set(self, self->last_output_value ^ 1); #endif } else if (GPIO_IS_OPEN_DRAIN(self->id)) { - if (GPIO_IS_OUT(self->id)) { + if (gpio_is_dir_out(self->id)) { gpio_set_dir(self->id, GPIO_IN); } else { gpio_set_dir(self->id, GPIO_OUT); diff --git a/ports/rp2/machine_timer.c b/ports/rp2/machine_timer.c index 6f8b04f10e..ffb4c70243 100644 --- a/ports/rp2/machine_timer.c +++ b/ports/rp2/machine_timer.c @@ -27,6 +27,7 @@ #include "py/runtime.h" #include "py/mperrno.h" #include "py/mphal.h" +#include "py/gc.h" #include "pico/time.h" #define ALARM_ID_INVALID (-1) @@ -40,13 +41,36 @@ typedef struct _machine_timer_obj_t { uint32_t mode; uint64_t delta_us; // for periodic mode mp_obj_t callback; + bool ishard; } machine_timer_obj_t; const mp_obj_type_t machine_timer_type; static int64_t alarm_callback(alarm_id_t id, void *user_data) { machine_timer_obj_t *self = user_data; - mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self)); + + if (self->ishard) { + // When executing code within a handler we must lock the scheduler to + // prevent any scheduled callbacks from running, and lock the GC to + // prevent any memory allocations. + mp_sched_lock(); + gc_lock(); + nlr_buf_t nlr; + if (nlr_push(&nlr) == 0) { + mp_call_function_1(self->callback, MP_OBJ_FROM_PTR(self)); + nlr_pop(); + } else { + // Uncaught exception; disable the callback so it doesn't run again. + self->mode = TIMER_MODE_ONE_SHOT; + mp_printf(MICROPY_ERROR_PRINTER, "uncaught exception in timer callback\n"); + mp_obj_print_exception(MICROPY_ERROR_PRINTER, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + gc_unlock(); + mp_sched_unlock(); + } else { + mp_sched_schedule(self->callback, MP_OBJ_FROM_PTR(self)); + } + if (self->mode == TIMER_MODE_ONE_SHOT) { return 0; } else { @@ -66,13 +90,14 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr } static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, }; + enum { ARG_mode, ARG_callback, ARG_period, ARG_tick_hz, ARG_freq, ARG_hard, }; static const mp_arg_t allowed_args[] = { { MP_QSTR_mode, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = TIMER_MODE_PERIODIC} }, { MP_QSTR_callback, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_period, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0xffffffff} }, { MP_QSTR_tick_hz, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1000} }, { MP_QSTR_freq, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, + { MP_QSTR_hard, MP_ARG_KW_ONLY | MP_ARG_BOOL, {.u_bool = false} }, }; // Parse args @@ -96,6 +121,7 @@ static mp_obj_t machine_timer_init_helper(machine_timer_obj_t *self, size_t n_ar } self->callback = args[ARG_callback].u_obj; + self->ishard = args[ARG_hard].u_bool; self->alarm_id = alarm_pool_add_alarm_in_us(self->pool, self->delta_us, alarm_callback, self, true); if (self->alarm_id == -1) { mp_raise_OSError(MP_ENOMEM); diff --git a/ports/rp2/memmap_mp_rp2040.ld b/ports/rp2/memmap_mp_rp2040.ld index a5799cd88b..5c8d9f4718 100644 --- a/ports/rp2/memmap_mp_rp2040.ld +++ b/ports/rp2/memmap_mp_rp2040.ld @@ -23,7 +23,7 @@ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 2048k + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = __micropy_flash_size__ RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 256k SCRATCH_X(rwx) : ORIGIN = 0x20040000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20041000, LENGTH = 4k diff --git a/ports/rp2/memmap_mp_rp2350.ld b/ports/rp2/memmap_mp_rp2350.ld index 1e1cbbfd70..1c4770efe1 100644 --- a/ports/rp2/memmap_mp_rp2350.ld +++ b/ports/rp2/memmap_mp_rp2350.ld @@ -23,7 +23,7 @@ MEMORY { - FLASH(rx) : ORIGIN = 0x10000000, LENGTH = 4096k + FLASH(rx) : ORIGIN = 0x10000000, LENGTH = __micropy_flash_size__ RAM(rwx) : ORIGIN = 0x20000000, LENGTH = 512k SCRATCH_X(rwx) : ORIGIN = 0x20080000, LENGTH = 4k SCRATCH_Y(rwx) : ORIGIN = 0x20081000, LENGTH = 4k diff --git a/ports/rp2/modmachine.c b/ports/rp2/modmachine.c index 58a3a8ae4d..1f1b0e2f59 100644 --- a/ports/rp2/modmachine.c +++ b/ports/rp2/modmachine.c @@ -65,7 +65,7 @@ static mp_obj_t mp_machine_unique_id(void) { return mp_obj_new_bytes(id.id, sizeof(id.id)); } -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { watchdog_reboot(0, SRAM_END, 0); for (;;) { __wfi(); @@ -82,7 +82,7 @@ static mp_int_t mp_machine_reset_cause(void) { return reset_cause; } -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { MICROPY_BOARD_ENTER_BOOTLOADER(n_args, args); rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_ENABLE << ROSC_CTRL_ENABLE_LSB; reset_usb_boot(0, 0); @@ -137,6 +137,12 @@ static void mp_machine_idle(void) { MICROPY_INTERNAL_WFE(1); } +static void alarm_sleep_callback(uint alarm_id) { +} + +// Set this to 1 to enable some debug of the interrupt that woke the device +#define DEBUG_LIGHTSLEEP 0 + static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { mp_int_t delay_ms = 0; bool use_timer_alarm = false; @@ -167,6 +173,16 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { } #endif + #if MICROPY_PY_THREAD + static bool in_lightsleep; + if (in_lightsleep) { + // The other CPU is also in machine.lightsleep() + MICROPY_END_ATOMIC_SECTION(my_interrupts); + return; + } + in_lightsleep = true; + #endif + #if MICROPY_HW_ENABLE_USBDEV // Only disable the USB clock if a USB host has not configured the device // or if going to DORMANT mode. @@ -206,6 +222,15 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { // Disable ROSC. rosc_hw->ctrl = ROSC_CTRL_ENABLE_VALUE_DISABLE << ROSC_CTRL_ENABLE_LSB; + #if DEBUG_LIGHTSLEEP + #if PICO_RP2040 + uint32_t pending_intr = 0; + #else + uint32_t pending_intr[2] = { 0 }; + #endif + #endif + + bool alarm_armed = false; if (n_args == 0) { #if MICROPY_PY_NETWORK_CYW43 gpio_set_dormant_irq_enabled(CYW43_PIN_WL_HOST_WAKE, GPIO_IRQ_LEVEL_HIGH, true); @@ -214,16 +239,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { } else { uint32_t save_sleep_en0 = clocks_hw->sleep_en0; uint32_t save_sleep_en1 = clocks_hw->sleep_en1; - bool timer3_enabled = irq_is_enabled(3); - - const uint32_t alarm_num = 3; - const uint32_t irq_num = TIMER_ALARM_IRQ_NUM(timer_hw, alarm_num); if (use_timer_alarm) { - // Make sure ALARM3/IRQ3 is enabled on _this_ core - if (!timer3_enabled) { - irq_set_enabled(irq_num, true); - } - hw_set_bits(&timer_hw->inte, 1u << alarm_num); // Use timer alarm to wake. clocks_hw->sleep_en0 = 0x0; #if PICO_RP2040 @@ -233,8 +249,11 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { #else #error Unknown processor #endif - timer_hw->intr = 1u << alarm_num; // clear any IRQ - timer_hw->alarm[alarm_num] = timer_hw->timerawl + delay_ms * 1000; + hardware_alarm_claim(MICROPY_HW_LIGHTSLEEP_ALARM_NUM); + hardware_alarm_set_callback(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, alarm_sleep_callback); + if (hardware_alarm_set_target(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, make_timeout_time_ms(delay_ms)) == PICO_OK) { + alarm_armed = true; + } } else { // TODO: Use RTC alarm to wake. clocks_hw->sleep_en0 = 0x0; @@ -264,10 +283,17 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { #endif // Go into low-power mode. - __wfi(); + if (alarm_armed) { + __wfi(); - if (!timer3_enabled) { - irq_set_enabled(irq_num, false); + #if DEBUG_LIGHTSLEEP + #if PICO_RP2040 + pending_intr = nvic_hw->ispr; + #else + pending_intr[0] = nvic_hw->ispr[0]; + pending_intr[1] = nvic_hw->ispr[1]; + #endif + #endif } clocks_hw->sleep_en0 = save_sleep_en0; clocks_hw->sleep_en1 = save_sleep_en1; @@ -282,9 +308,37 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { // Re-sync mp_hal_time_ns() counter with aon timer. mp_hal_time_ns_set_from_rtc(); + + // Note: This must be done after MICROPY_END_ATOMIC_SECTION + if (use_timer_alarm) { + if (alarm_armed) { + hardware_alarm_cancel(MICROPY_HW_LIGHTSLEEP_ALARM_NUM); + } + hardware_alarm_set_callback(MICROPY_HW_LIGHTSLEEP_ALARM_NUM, NULL); + hardware_alarm_unclaim(MICROPY_HW_LIGHTSLEEP_ALARM_NUM); + + #if DEBUG_LIGHTSLEEP + // Check irq.h for the list of IRQ's + // for rp2040 00000042: TIMER_IRQ_1 woke the device as expected + // 00000020: USBCTRL_IRQ woke the device (probably early) + // For rp2350 00000000:00000002: TIMER0_IRQ_1 woke the device as expected + // 00000000:00004000: USBCTRL_IRQ woke the device (probably early) + #if PICO_RP2040 + mp_printf(MP_PYTHON_PRINTER, "lightsleep: pending_intr=%08lx\n", pending_intr); + #else + mp_printf(MP_PYTHON_PRINTER, "lightsleep: pending_intr=%08lx:%08lx\n", pending_intr[1], pending_intr[0]); + #endif + #endif + } + + #if MICROPY_PY_THREAD + // Clearing the flag here is atomic, and we know we're the ones who set it + // (higher up, inside the critical section) + in_lightsleep = false; + #endif } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { mp_machine_lightsleep(n_args, args); mp_machine_reset(); } diff --git a/ports/rp2/mpconfigport.h b/ports/rp2/mpconfigport.h index 3d65737266..35afea4fac 100644 --- a/ports/rp2/mpconfigport.h +++ b/ports/rp2/mpconfigport.h @@ -198,8 +198,9 @@ #define MICROPY_PY_LWIP_SOCK_RAW (MICROPY_PY_LWIP) // Hardware timer alarm index. Available range 0-3. -// Number 3 is currently used by pico-sdk (PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM) +// Number 3 is currently used by pico-sdk alarm pool (PICO_TIME_DEFAULT_ALARM_POOL_HARDWARE_ALARM_NUM) #define MICROPY_HW_SOFT_TIMER_ALARM_NUM (2) +#define MICROPY_HW_LIGHTSLEEP_ALARM_NUM (1) // fatfs configuration #define MICROPY_FATFS_ENABLE_LFN (2) @@ -241,46 +242,11 @@ #endif #endif -#if MICROPY_PY_NETWORK_CYW43 -extern const struct _mp_obj_type_t mp_network_cyw43_type; -#define MICROPY_HW_NIC_CYW43 \ - { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_IDLE), MP_ROM_INT(CYW43_LINK_DOWN) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_CONNECTING), MP_ROM_INT(CYW43_LINK_JOIN) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_WRONG_PASSWORD), MP_ROM_INT(CYW43_LINK_BADAUTH) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_NO_AP_FOUND), MP_ROM_INT(CYW43_LINK_NONET) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_CONNECT_FAIL), MP_ROM_INT(CYW43_LINK_FAIL) }, \ - { MP_ROM_QSTR(MP_QSTR_STAT_GOT_IP), MP_ROM_INT(CYW43_LINK_UP) }, -#else -#define MICROPY_HW_NIC_CYW43 -#endif - -#if MICROPY_PY_NETWORK_NINAW10 -// This Network interface requires the extended socket state. -#ifndef MICROPY_PY_SOCKET_EXTENDED_STATE -#define MICROPY_PY_SOCKET_EXTENDED_STATE (1) -#endif -extern const struct _mp_obj_type_t mod_network_nic_type_nina; -#define MICROPY_HW_NIC_NINAW10 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mod_network_nic_type_nina) }, -#else -#define MICROPY_HW_NIC_NINAW10 -#endif - -#if MICROPY_PY_NETWORK_WIZNET5K -extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k; -#define MICROPY_HW_NIC_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) }, -#else -#define MICROPY_HW_NIC_WIZNET5K -#endif - #ifndef MICROPY_BOARD_NETWORK_INTERFACES #define MICROPY_BOARD_NETWORK_INTERFACES #endif #define MICROPY_PORT_NETWORK_INTERFACES \ - MICROPY_HW_NIC_CYW43 \ - MICROPY_HW_NIC_NINAW10 \ - MICROPY_HW_NIC_WIZNET5K \ MICROPY_BOARD_NETWORK_INTERFACES \ // Additional entries for use with pendsv_schedule_dispatch. diff --git a/ports/rp2/mphalport.c b/ports/rp2/mphalport.c index caecb69509..b581b3b59f 100644 --- a/ports/rp2/mphalport.c +++ b/ports/rp2/mphalport.c @@ -266,17 +266,7 @@ void soft_timer_init(void) { } void mp_wfe_or_timeout(uint32_t timeout_ms) { - soft_timer_entry_t timer; - - // Note the timer doesn't have an associated callback, it just exists to create a - // hardware interrupt to wake the CPU - soft_timer_static_init(&timer, SOFT_TIMER_MODE_ONE_SHOT, 0, NULL); - soft_timer_insert(&timer, timeout_ms); - - __wfe(); - - // Clean up the timer node if it's not already - soft_timer_remove(&timer); + best_effort_wfe_or_timeout(delayed_by_ms(get_absolute_time(), timeout_ms)); } int mp_hal_is_pin_reserved(int n) { diff --git a/ports/rp2/mphalport.h b/ports/rp2/mphalport.h index 956db3ec70..5012b682b9 100644 --- a/ports/rp2/mphalport.h +++ b/ports/rp2/mphalport.h @@ -123,7 +123,18 @@ static inline mp_uint_t mp_hal_get_cpu_freq(void) { #define MP_HAL_PIN_PULL_UP (1) #define MP_HAL_PIN_PULL_DOWN (2) +// Open drain behaviour is simulated. +#if NUM_BANK0_GPIOS > 32 extern uint64_t machine_pin_open_drain_mask; +#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1ULL << id)) +#define GPIO_ENABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask |= (1ULL << id)) +#define GPIO_DISABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask &= ~(1ULL << id)) +#else +extern uint32_t machine_pin_open_drain_mask; +#define GPIO_IS_OPEN_DRAIN(id) (machine_pin_open_drain_mask & (1U << id)) +#define GPIO_ENABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask |= (1U << id)) +#define GPIO_DISABLE_OPEN_DRAIN(id) (machine_pin_open_drain_mask &= ~(1U << id)) +#endif mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in); @@ -133,13 +144,13 @@ static inline unsigned int mp_hal_pin_name(mp_hal_pin_obj_t pin) { static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) { gpio_set_dir(pin, GPIO_IN); - machine_pin_open_drain_mask &= ~(1 << pin); + GPIO_DISABLE_OPEN_DRAIN(pin); gpio_set_function(pin, GPIO_FUNC_SIO); } static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) { gpio_set_dir(pin, GPIO_OUT); - machine_pin_open_drain_mask &= ~(1 << pin); + GPIO_DISABLE_OPEN_DRAIN(pin); gpio_set_function(pin, GPIO_FUNC_SIO); } @@ -151,7 +162,7 @@ static inline void mp_hal_pin_open_drain_with_value(mp_hal_pin_obj_t pin, int v) gpio_put(pin, 0); gpio_set_dir(pin, GPIO_OUT); } - machine_pin_open_drain_mask |= 1 << pin; + GPIO_ENABLE_OPEN_DRAIN(pin); gpio_set_function(pin, GPIO_FUNC_SIO); } diff --git a/ports/rp2/mpnetworkport.c b/ports/rp2/mpnetworkport.c index e1e1567828..fed34be380 100644 --- a/ports/rp2/mpnetworkport.c +++ b/ports/rp2/mpnetworkport.c @@ -31,6 +31,7 @@ #if MICROPY_PY_LWIP #include "shared/runtime/softtimer.h" +#include "lwip/netif.h" #include "lwip/timeouts.h" // Poll lwIP every 64ms by default @@ -39,6 +40,9 @@ // Soft timer for running lwIP in the background. static soft_timer_entry_t mp_network_soft_timer; +// Callback for change of netif state +NETIF_DECLARE_EXT_CALLBACK(netif_callback) + #if MICROPY_PY_NETWORK_CYW43 #include "lib/cyw43-driver/src/cyw43.h" #include "lib/cyw43-driver/src/cyw43_stats.h" @@ -137,17 +141,48 @@ static void mp_network_soft_timer_callback(soft_timer_entry_t *self) { #if MICROPY_PY_NETWORK_WIZNET5K wiznet5k_poll(); #endif + + // Only keep the timer running if any TCP sockets are active, or any netif is up + struct netif *netif; + extern void *tcp_active_pcbs; + bool keep_running = (tcp_active_pcbs != NULL); + if (!keep_running) { + NETIF_FOREACH(netif) { + if (netif->flags & NETIF_FLAG_LINK_UP) { + keep_running = true; + break; + } + } + } + + // Periodic timer will re-queue as soon as this handler exits, + // one shot timer will not + mp_network_soft_timer.mode = keep_running ? SOFT_TIMER_MODE_PERIODIC : SOFT_TIMER_MODE_ONE_SHOT; } +static void mp_network_netif_status_cb(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args); + void mod_network_lwip_init(void) { soft_timer_static_init( &mp_network_soft_timer, - SOFT_TIMER_MODE_PERIODIC, + SOFT_TIMER_MODE_ONE_SHOT, LWIP_TICK_RATE_MS, mp_network_soft_timer_callback ); - soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS); + if (netif_callback.callback_fn == NULL) { + netif_add_ext_callback(&netif_callback, mp_network_netif_status_cb); + } +} + +static void mp_network_netif_status_cb(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args) { + // Start the network soft timer any time an interface comes up, unless + // it's already running + if (reason == LWIP_NSC_LINK_CHANGED && args->link_changed.state + && mp_network_soft_timer.mode == SOFT_TIMER_MODE_ONE_SHOT) { + mp_network_soft_timer.mode = SOFT_TIMER_MODE_PERIODIC; + soft_timer_reinsert(&mp_network_soft_timer, LWIP_TICK_RATE_MS); + } } #endif // MICROPY_PY_LWIP diff --git a/ports/rp2/rp2_dma.c b/ports/rp2/rp2_dma.c index 78f69e6452..94c61e226e 100644 --- a/ports/rp2/rp2_dma.c +++ b/ports/rp2/rp2_dma.c @@ -315,7 +315,7 @@ static mp_obj_t rp2_dma_pack_ctrl(size_t n_pos_args, const mp_obj_t *pos_args, m // Pack keyword settings into a control register value, using either the default for this // DMA channel or the provided defaults rp2_dma_obj_t *self = MP_OBJ_TO_PTR(pos_args[0]); - mp_uint_t value = DEFAULT_DMA_CONFIG | ((self->channel & 0xf) << 11); + mp_uint_t value = DEFAULT_DMA_CONFIG | ((self->channel & 0xf) << DMA_CH0_CTRL_TRIG_CHAIN_TO_LSB); if (n_pos_args > 1) { mp_raise_TypeError(MP_ERROR_TEXT("pack_ctrl only takes keyword arguments")); diff --git a/ports/rp2/rp2_flash.c b/ports/rp2/rp2_flash.c index a9beabf051..d6b9e13653 100644 --- a/ports/rp2/rp2_flash.c +++ b/ports/rp2/rp2_flash.c @@ -49,6 +49,19 @@ static_assert(MICROPY_HW_ROMFS_BYTES % 4096 == 0, "ROMFS size must be a multiple of 4K"); static_assert(MICROPY_HW_FLASH_STORAGE_BYTES % 4096 == 0, "Flash storage size must be a multiple of 4K"); +#ifndef MICROPY_HW_FLASH_MAX_FREQ +// Emulate Pico SDK's SYS_CLK_HZ / PICO_FLASH_SPI_CLKDIV behaviour by default. +// On RP2040 if PICO_USE_FASTEST_SUPPORTED_CLOCK is set then SYS_CLK_HZ can be +// 200MHz, potentially putting timings derived from PICO_FLASH_SPI_CLKDIV +// out of range. +#ifdef PICO_FLASH_SPI_CLKDIV +#define MICROPY_HW_FLASH_MAX_FREQ (SYS_CLK_HZ / PICO_FLASH_SPI_CLKDIV) +#else +// A default PICO_FLASH_SPI_CLKDIV of 4 is set in boot2_generic_03h.S +#define MICROPY_HW_FLASH_MAX_FREQ (SYS_CLK_HZ / 4) +#endif +#endif + #ifndef MICROPY_HW_FLASH_STORAGE_BASE #define MICROPY_HW_FLASH_STORAGE_BASE (PICO_FLASH_SIZE_BYTES - MICROPY_HW_FLASH_STORAGE_BYTES) #endif @@ -104,9 +117,8 @@ static bool use_multicore_lockout(void) { // and core1 locked out if relevant. static void __no_inline_not_in_flash_func(rp2_flash_set_timing_internal)(int clock_hz) { - // Use the minimum divisor assuming a 133MHz flash. - const int max_flash_freq = 133000000; - int divisor = (clock_hz + max_flash_freq - 1) / max_flash_freq; + // Use the minimum divisor based upon our target MICROPY_HW_FLASH_MAX_FREQ + int divisor = (clock_hz + MICROPY_HW_FLASH_MAX_FREQ - 1) / MICROPY_HW_FLASH_MAX_FREQ; #if PICO_RP2350 // Make sure flash is deselected - QMI doesn't appear to have a busy flag(!) diff --git a/ports/rp2/tools_patch/Findpioasm.cmake b/ports/rp2/tools_patch/Findpioasm.cmake new file mode 100644 index 0000000000..72e6bf6fb6 --- /dev/null +++ b/ports/rp2/tools_patch/Findpioasm.cmake @@ -0,0 +1,58 @@ +# Finds (or builds) the pioasm executable +# +# This will define the following imported targets +# +# pioasm +# + +# This is a temporary patched copy of pico-sdk file Findpioasm.cmake to work around +# a host toolchain issue with GCC 15.1: +# https://github.com/raspberrypi/pico-sdk/issues/2448 + +if (NOT TARGET pioasm) + # todo we would like to use pckgconfig to look for it first + # see https://pabloariasal.github.io/2018/02/19/its-time-to-do-cmake-right/ + + include(ExternalProject) + + set(PIOASM_SOURCE_DIR ${PICO_SDK_PATH}/tools/pioasm) + set(PIOASM_BINARY_DIR ${CMAKE_BINARY_DIR}/pioasm) + set(PIOASM_INSTALL_DIR ${CMAKE_BINARY_DIR}/pioasm-install CACHE PATH "Directory where pioasm has been installed" FORCE) + + set(pioasmBuild_TARGET pioasmBuild) + set(pioasm_TARGET pioasm) + + if (NOT TARGET ${pioasmBuild_TARGET}) + pico_message_debug("PIOASM will need to be built") +# message("Adding external project ${pioasmBuild_Target} in ${CMAKE_CURRENT_LIST_DIR}}") + ExternalProject_Add(${pioasmBuild_TARGET} + PREFIX pioasm + SOURCE_DIR ${PIOASM_SOURCE_DIR} + BINARY_DIR ${PIOASM_BINARY_DIR} + INSTALL_DIR ${PIOASM_INSTALL_DIR} + CMAKE_ARGS + "--no-warn-unused-cli" + "-DCMAKE_MAKE_PROGRAM:FILEPATH=${CMAKE_MAKE_PROGRAM}" + "-DPIOASM_FLAT_INSTALL=1" + "-DCMAKE_INSTALL_PREFIX=${PIOASM_INSTALL_DIR}" + "-DCMAKE_RULE_MESSAGES=OFF" # quieten the build + "-DCMAKE_INSTALL_MESSAGE=NEVER" # quieten the install + # Toolchain workaround follows + "-DCMAKE_CXX_FLAGS=-include cstdint" + CMAKE_CACHE_ARGS "-DPIOASM_EXTRA_SOURCE_FILES:STRING=${PIOASM_EXTRA_SOURCE_FILES}" + BUILD_ALWAYS 1 # force dependency checking + EXCLUDE_FROM_ALL TRUE + ) + endif() + + if (CMAKE_HOST_WIN32) + set(pioasm_EXECUTABLE ${PIOASM_INSTALL_DIR}/pioasm/pioasm.exe) + else() + set(pioasm_EXECUTABLE ${PIOASM_INSTALL_DIR}/pioasm/pioasm) + endif() + add_executable(${pioasm_TARGET} IMPORTED GLOBAL) + set_property(TARGET ${pioasm_TARGET} PROPERTY IMPORTED_LOCATION + ${pioasm_EXECUTABLE}) + + add_dependencies(${pioasm_TARGET} ${pioasmBuild_TARGET}) +endif() diff --git a/ports/samd/Makefile b/ports/samd/Makefile index 005664f178..bec530d803 100644 --- a/ports/samd/Makefile +++ b/ports/samd/Makefile @@ -113,8 +113,6 @@ LIBSTDCPP_FILE_NAME = "$(shell $(CXX) $(CXXFLAGS) -print-file-name=libstdc++.a)" LDFLAGS += -L"$(shell dirname $(LIBSTDCPP_FILE_NAME))" endif -LDFLAGS += --wrap=dcd_event_handler - MPY_CROSS_FLAGS += -march=$(MPY_CROSS_MCU_ARCH) SRC_C += \ diff --git a/ports/samd/boards/MINISAM_M4/mpconfigboard.h b/ports/samd/boards/MINISAM_M4/mpconfigboard.h index 6d908bdcb8..30a7a80454 100644 --- a/ports/samd/boards/MINISAM_M4/mpconfigboard.h +++ b/ports/samd/boards/MINISAM_M4/mpconfigboard.h @@ -7,4 +7,4 @@ #define MICROPY_HW_DEFAULT_I2C_ID (2) #define MICROPY_HW_DEFAULT_SPI_ID (1) -#define MICROPY_HW_QSPIFLASH GD25Q16C +#define MICROPY_HW_QSPIFLASH W25Q16JV_IQ diff --git a/ports/samd/boards/SAMD_GENERIC_D21X18/board.json b/ports/samd/boards/SAMD_GENERIC_D21X18/board.json index a14730bd1b..c00d78addd 100644 --- a/ports/samd/boards/SAMD_GENERIC_D21X18/board.json +++ b/ports/samd/boards/SAMD_GENERIC_D21X18/board.json @@ -11,6 +11,7 @@ ], "mcu": "samd21", "vendor": "Microchip", + "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d", "product": "Generic SAMD21J18", "thumbnail": "" } diff --git a/ports/samd/boards/SAMD_GENERIC_D51X19/board.json b/ports/samd/boards/SAMD_GENERIC_D51X19/board.json index 21cb114bf6..2be5f20851 100644 --- a/ports/samd/boards/SAMD_GENERIC_D51X19/board.json +++ b/ports/samd/boards/SAMD_GENERIC_D51X19/board.json @@ -11,6 +11,7 @@ ], "mcu": "samd51", "vendor": "Microchip", + "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d", "product": "Generic SAMD51P19", "thumbnail": "" } diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/board.json b/ports/samd/boards/SAMD_GENERIC_D51X20/board.json index ae4f83472e..8384484327 100644 --- a/ports/samd/boards/SAMD_GENERIC_D51X20/board.json +++ b/ports/samd/boards/SAMD_GENERIC_D51X20/board.json @@ -11,6 +11,7 @@ ], "mcu": "samd51", "vendor": "Microchip", + "url": "https://www.microchip.com/en-us/products/microcontrollers-and-microprocessors/32-bit-mcus/sam-32-bit-mcus/sam-d", "product": "Generic SAMD51P20", "thumbnail": "" } diff --git a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk index ddba3dcbba..b240c2587f 100644 --- a/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk +++ b/ports/samd/boards/SAMD_GENERIC_D51X20/mpconfigboard.mk @@ -1,13 +1,11 @@ MCU_SERIES = SAMD51 CMSIS_MCU = SAMD51P20A -LD_FILES = boards/samd51x19a.ld sections.ld +LD_FILES = boards/samd51x20a.ld sections.ld TEXT0 = 0x4000 - # The ?='s allow overriding in mpconfigboard.mk. # MicroPython settings # The size of a MCU flash filesystem will be -# 1008k - MICROPY_HW_CODESIZE - MICROPY_HW_VFSROMSIZE +# 1008k - MICROPY_HW_CODESIZE # The default for MICROPY_HW_VFSROMSIZE is 64K MICROPY_HW_CODESIZE ?= 752K -MICROPY_HW_VFSROMSIZE ?= 128K diff --git a/ports/samd/boards/SEEED_XIAO_SAMD21/board.json b/ports/samd/boards/SEEED_XIAO_SAMD21/board.json index 4d19f6b945..0d6b2fc30d 100644 --- a/ports/samd/boards/SEEED_XIAO_SAMD21/board.json +++ b/ports/samd/boards/SEEED_XIAO_SAMD21/board.json @@ -10,7 +10,7 @@ "seeeduino-xiao.jpg" ], "mcu": "samd21", - "product": "Seeeduino XIAO (SAMD21)", + "product": "XIAO SAMD21", "thumbnail": "", "url": "https://www.seeedstudio.com/Seeeduino-XIAO-Arduino-Microcontroller-SAMD21-Cortex-M0+-p-4426.html", "vendor": "Seeed Studio" diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json new file mode 100644 index 0000000000..789bd97730 --- /dev/null +++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/board.json @@ -0,0 +1,21 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Battery Charging", + "DAC", + "External Flash", + "USB", + "RGB LED" + ], + "images": [ + "sparkfun_readboard_turbo.jpg" + ], + "mcu": "samd21", + "product": "SparkFun RedBoard Turbo", + "thumbnail": "", + "url": "https://www.sparkfun.com/products/14812", + "vendor": "SparkFun" +} diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h new file mode 100644 index 0000000000..cd0ec51acc --- /dev/null +++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.h @@ -0,0 +1,11 @@ +#define MICROPY_HW_BOARD_NAME "SparkFun RedBoard Turbo" +#define MICROPY_HW_MCU_NAME "SAMD21G18A" + +#define MICROPY_HW_XOSC32K (1) + +#define MICROPY_HW_SPIFLASH (1) +#define MICROPY_HW_SPIFLASH_ID (5) + +#define MICROPY_HW_DEFAULT_UART_ID (0) +#define MICROPY_HW_DEFAULT_I2C_ID (3) +#define MICROPY_HW_DEFAULT_SPI_ID (4) diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk new file mode 100644 index 0000000000..6ea327a650 --- /dev/null +++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/mpconfigboard.mk @@ -0,0 +1,8 @@ +MCU_SERIES = SAMD21 +CMSIS_MCU = SAMD21G18A +LD_FILES = boards/samd21x18a.ld sections.ld +TEXT0 = 0x2000 + +# The ?='s allow overriding in mpconfigboard.mk. +# MicroPython settings +MICROPY_HW_CODESIZE ?= 232K diff --git a/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv new file mode 100644 index 0000000000..f065fc8917 --- /dev/null +++ b/ports/samd/boards/SPARKFUN_REDBOARD_TURBO/pins.csv @@ -0,0 +1,47 @@ +# The lines contain pairs of Pin name and Pin number. +# Pin names must be valid Python identifiers. +# Pin numbers have the form Pxnn, with x being A, B, C or D. +# Lines starting with # or empty lines are ignored. + +USB_DM,PA24 +USB_DP,PA25 + +SWCLK,PA30 +SWDIO,PA31 + +D0,PA11 +D1,PA10 +D2,PA14 +D3,PA09 +D4,PA08 +D5,PA15 +D6,PA20 +D7,PA21 +D8,PA06 +D9,PA07 +D10,PA18 +D11,PA16 +D12,PA19 +D13,PA17 +A0,PA02 +A1,PB08 +A2,PB09 +A3,PA04 +A4,PA05 +A5,PB02 +AREF,PA03 +RX,PA11 +TX,PA10 +SCL,PA23 +SDA,PA22 +MOSI,PB10 +MISO,PA12 +SCK,PB11 +FLASH_MOSI,PB22 +FLASH_MISO,PB03 +FLASH_SCK,PB23 +FLASH_CS,PA13 +NEOPIXEL,PA30 + +LED_TX,PA27 +LED_RX,PA31 diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json new file mode 100644 index 0000000000..e5c5411a0c --- /dev/null +++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/board.json @@ -0,0 +1,19 @@ +{ + "deploy": [ + "../deploy.md" + ], + "docs": "", + "features": [ + "Battery Charging", + "DAC", + "USB" + ], + "images": [ + "sparkfun_sam21_dev_breakout.jpg" + ], + "mcu": "samd21", + "product": "SparkFun SAMD21 Dev Breakout", + "thumbnail": "", + "url": "https://www.sparkfun.com/products/13672", + "vendor": "SparkFun" +} diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h new file mode 100644 index 0000000000..0679a9f4e4 --- /dev/null +++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.h @@ -0,0 +1,8 @@ +#define MICROPY_HW_BOARD_NAME "SparkFun SAMD21 Dev Breakout" +#define MICROPY_HW_MCU_NAME "SAMD21G18A" + +#define MICROPY_HW_XOSC32K (1) + +#define MICROPY_HW_DEFAULT_UART_ID (0) +#define MICROPY_HW_DEFAULT_I2C_ID (3) +#define MICROPY_HW_DEFAULT_SPI_ID (4) diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk new file mode 100644 index 0000000000..8696c966bc --- /dev/null +++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/mpconfigboard.mk @@ -0,0 +1,4 @@ +MCU_SERIES = SAMD21 +CMSIS_MCU = SAMD21G18A +LD_FILES = boards/samd21x18a.ld sections.ld +TEXT0 = 0x2000 diff --git a/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv new file mode 100644 index 0000000000..794be31800 --- /dev/null +++ b/ports/samd/boards/SPARKFUN_SAMD21_DEV_BREAKOUT/pins.csv @@ -0,0 +1,45 @@ +# The lines contain pairs of Pin name and Pin number. +# Pin names must be valid Python identifiers. +# Pin numbers have the form Pxnn, with x being A, B, C or D. +# Lines starting with # or empty lines are ignored. + +USB_DM,PA24 +USB_DP,PA25 + +SWCLK,PA30 +SWDIO,PA31 + +D0,PA11 +D1,PA10 +D2,PA14 +D3,PA09 +D4,PA08 +D5,PA15 +D6,PA20 +D7,PA21 +D8,PA06 +D9,PA07 +D10,PA18 +D11,PA16 +D12,PA19 +D13,PA17 +A0,PA02 +A1,PB08 +A2,PB09 +A3,PA04 +A4,PA05 +A5,PB02 +AREF,PA03 +RX,PA11 +TX,PA10 +SCL,PA23 +SDA,PA22 +MOSI,PB10 +MISO,PA12 +SCK,PB11 +D38,PA13 +D30,PB22 +D31,PB23 + +LED_TX,PA27 +LED_RX,PB03 diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json index ee9ca9d368..6e89cebadd 100644 --- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json +++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/board.json @@ -13,8 +13,8 @@ "sparkfun_samd51_thing_plus.jpg" ], "mcu": "samd51", - "product": "Sparkfun SAMD51 Thing Plus", + "product": "SAMD51 Thing Plus", "thumbnail": "", "url": "https://www.sparkfun.com/products/14713", - "vendor": "Sparkfun" + "vendor": "SparkFun" } diff --git a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h index fe2226a59e..9e0db7875f 100644 --- a/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h +++ b/ports/samd/boards/SPARKFUN_SAMD51_THING_PLUS/mpconfigboard.h @@ -1,4 +1,4 @@ -#define MICROPY_HW_BOARD_NAME "Sparkfun SAMD51 Thing Plus" +#define MICROPY_HW_BOARD_NAME "SparkFun SAMD51 Thing Plus" #define MICROPY_HW_MCU_NAME "SAMD51J20A" #define MICROPY_HW_XOSC32K (1) diff --git a/ports/samd/machine_i2c.c b/ports/samd/machine_i2c.c index 03af3d29ed..172518523d 100644 --- a/ports/samd/machine_i2c.c +++ b/ports/samd/machine_i2c.c @@ -37,9 +37,9 @@ #include "genhdr/pins.h" #include "clock_config.h" -#define DEFAULT_I2C_FREQ (400000) -#define RISETIME_NS (200) -#define I2C_TIMEOUT (100) +#define DEFAULT_I2C_FREQ (400000) +#define RISETIME_NS (200) +#define DEFAULT_I2C_TIMEOUT (50000) #define IS_BUS_BUSY (i2c->I2CM.STATUS.bit.BUSSTATE == 3) #define NACK_RECVD (i2c->I2CM.STATUS.bit.RXNACK == 1) @@ -67,6 +67,7 @@ typedef struct _machine_i2c_obj_t { uint8_t state; uint32_t freq; uint32_t timeout; + uint32_t timer; size_t len; uint8_t *buf; } machine_i2c_obj_t; @@ -88,7 +89,7 @@ void common_i2c_irq_handler(int i2c_id) { if (self->len > 0) { *(self->buf)++ = i2c->I2CM.DATA.reg; self->len--; - self->timeout = I2C_TIMEOUT; + self->timer = self->timeout; } if (self->len > 0) { // no ACK at the last byte PREPARE_ACK; // Send ACK @@ -105,7 +106,7 @@ void common_i2c_irq_handler(int i2c_id) { } else if (self->len > 0) { // data to be sent i2c->I2CM.DATA.bit.DATA = *(self->buf)++; self->len--; - self->timeout = I2C_TIMEOUT; + self->timer = self->timeout; } else { // No data left, if there was any. self->state = state_done; i2c->I2CM.INTFLAG.reg |= SERCOM_I2CM_INTFLAG_MB; @@ -120,12 +121,13 @@ void common_i2c_irq_handler(int i2c_id) { static void machine_i2c_print(const mp_print_t *print, mp_obj_t self_in, mp_print_kind_t kind) { machine_i2c_obj_t *self = MP_OBJ_TO_PTR(self_in); - mp_printf(print, "I2C(%u, freq=%u, scl=\"%q\", sda=\"%q\")", - self->id, self->freq, pin_find_by_id(self->scl)->name, pin_find_by_id(self->sda)->name); + mp_printf(print, "I2C(%u, freq=%u, scl=\"%q\", sda=\"%q\", timeout=%u)", + self->id, self->freq, pin_find_by_id(self->scl)->name, pin_find_by_id(self->sda)->name, + self->timeout * 1000); } mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { - enum { ARG_id, ARG_freq, ARG_scl, ARG_sda }; + enum { ARG_id, ARG_freq, ARG_scl, ARG_sda, ARG_timeout }; static const mp_arg_t allowed_args[] = { #if MICROPY_HW_DEFAULT_I2C_ID < 0 { MP_QSTR_id, MP_ARG_REQUIRED | MP_ARG_INT, {.u_int = -1} }, @@ -140,6 +142,7 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n { MP_QSTR_scl, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, { MP_QSTR_sda, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_rom_obj = MP_ROM_NONE} }, #endif + { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = DEFAULT_I2C_TIMEOUT} }, }; // Parse args. @@ -168,6 +171,8 @@ mp_obj_t machine_i2c_make_new(const mp_obj_type_t *type, size_t n_args, size_t n } MP_STATE_PORT(sercom_table[self->id]) = self; self->freq = args[ARG_freq].u_int; + // The unit for ARG_timeout is us, but the code uses ms. + self->timeout = args[ARG_timeout].u_int / 1000; // Configure the Pin mux. mp_hal_set_pin_mux(self->scl, scl_pad_config.alt_fct); @@ -224,13 +229,13 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si machine_i2c_obj_t *self = (machine_i2c_obj_t *)self_in; Sercom *i2c = self->instance; - self->timeout = I2C_TIMEOUT; + self->timer = self->timeout; self->len = len; self->buf = buf; // Wait a while if the bus is busy - while (IS_BUS_BUSY && self->timeout) { + while (IS_BUS_BUSY && self->timer) { MICROPY_EVENT_POLL_HOOK - if (--self->timeout == 0) { + if (--self->timer == 0) { return -MP_ETIMEDOUT; } } @@ -242,9 +247,9 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si i2c->I2CM.ADDR.bit.ADDR = (addr << 1) | READ_MODE; // Transfer the data - self->timeout = I2C_TIMEOUT; - while (self->state == state_busy && self->timeout) { - self->timeout--; + self->timer = self->timeout; + while (self->state == state_busy && self->timer) { + self->timer--; MICROPY_EVENT_POLL_HOOK } i2c->I2CM.INTENCLR.reg = SERCOM_I2CM_INTENSET_MB | SERCOM_I2CM_INTENSET_SB | SERCOM_I2CM_INTENSET_ERROR; @@ -256,7 +261,7 @@ static int machine_i2c_transfer_single(mp_obj_base_t *self_in, uint16_t addr, si } else if (self->state == state_buserr) { SET_STOP_STATE; return -MP_EIO; - } else if (self->timeout == 0) { + } else if (self->timer == 0) { SET_STOP_STATE; return -MP_ETIMEDOUT; } diff --git a/ports/samd/machine_rtc.c b/ports/samd/machine_rtc.c index 74c2266d60..4b03488b71 100644 --- a/ports/samd/machine_rtc.c +++ b/ports/samd/machine_rtc.c @@ -133,6 +133,7 @@ static mp_obj_t machine_rtc_datetime_helper(size_t n_args, const mp_obj_t *args, } #endif + mp_hal_time_ns_set_from_rtc(); return mp_const_none; } } diff --git a/ports/samd/main.c b/ports/samd/main.c index 2bbaf63e6e..a7da95582f 100644 --- a/ports/samd/main.c +++ b/ports/samd/main.c @@ -28,6 +28,7 @@ #include "py/runtime.h" #include "py/gc.h" #include "py/mperrno.h" +#include "py/mphal.h" #include "py/stackctrl.h" #include "shared/readline/readline.h" #include "shared/runtime/gchelper.h" @@ -45,6 +46,7 @@ extern void sercom_deinit_all(void); void samd_main(void) { mp_stack_set_top(&_estack); mp_stack_set_limit(&_estack - &_sstack - 1024); + mp_hal_time_ns_set_from_rtc(); for (;;) { gc_init(&_sheap, &_eheap); diff --git a/ports/samd/mcu/samd51/mpconfigmcu.h b/ports/samd/mcu/samd51/mpconfigmcu.h index 831949bab5..8cce90b886 100644 --- a/ports/samd/mcu/samd51/mpconfigmcu.h +++ b/ports/samd/mcu/samd51/mpconfigmcu.h @@ -23,7 +23,7 @@ unsigned long trng_random_u32(void); #define MICROPY_FATFS_MAX_SS (4096) #define MICROPY_FATFS_LFN_CODE_PAGE 437 /* 1=SFN/ANSI 437=LFN/U.S.(OEM) */ -#define VFS_BLOCK_SIZE_BYTES (1536) // +#define VFS_BLOCK_SIZE_BYTES (2048) // #ifndef MICROPY_HW_UART_TXBUF #define MICROPY_HW_UART_TXBUF (1) diff --git a/ports/samd/modmachine.c b/ports/samd/modmachine.c index 65dbf8a7f5..c20ce8d641 100644 --- a/ports/samd/modmachine.c +++ b/ports/samd/modmachine.c @@ -42,7 +42,7 @@ #define DBL_TAP_ADDR ((volatile uint32_t *)(HSRAM_ADDR + HSRAM_SIZE - 4)) #endif // A board may define a DPL_TAP_ADDR_ALT, which will be set as well -// Needed at the moment for Sparkfun SAMD51 Thing Plus +// Needed at the moment for SparkFun SAMD51 Thing Plus #define DBL_TAP_MAGIC_LOADER 0xf01669ef #define DBL_TAP_MAGIC_RESET 0xf02669ef @@ -65,7 +65,7 @@ extern bool EIC_occured; extern uint32_t _dbl_tap_addr; -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { *DBL_TAP_ADDR = DBL_TAP_MAGIC_RESET; #ifdef DBL_TAP_ADDR_ALT *DBL_TAP_ADDR_ALT = DBL_TAP_MAGIC_RESET; @@ -73,7 +73,7 @@ NORETURN static void mp_machine_reset(void) { NVIC_SystemReset(); } -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { *DBL_TAP_ADDR = DBL_TAP_MAGIC_LOADER; #ifdef DBL_TAP_ADDR_ALT *DBL_TAP_ADDR_ALT = DBL_TAP_MAGIC_LOADER; @@ -164,7 +164,7 @@ static void mp_machine_lightsleep(size_t n_args, const mp_obj_t *args) { set_cpu_freq(freq); } -NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { +MP_NORETURN static void mp_machine_deepsleep(size_t n_args, const mp_obj_t *args) { mp_machine_lightsleep(n_args, args); mp_machine_reset(); } diff --git a/ports/samd/modtime.c b/ports/samd/modtime.c index 83072dd16f..0bed3cb83a 100644 --- a/ports/samd/modtime.c +++ b/ports/samd/modtime.c @@ -28,6 +28,8 @@ #include "shared/timeutils/timeutils.h" #include "modmachine.h" +static uint64_t time_us_64_offset_from_epoch; + // Return the localtime as an 8-tuple. static mp_obj_t mp_time_localtime_get(void) { timeutils_struct_time_t tm; @@ -54,3 +56,15 @@ static mp_obj_t mp_time_time_get(void) { return mp_obj_new_int_from_uint(timeutils_mktime( tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec)); } + +void mp_hal_time_ns_set_from_rtc(void) { + timeutils_struct_time_t tm; + rtc_gettime(&tm); + uint64_t time_us = (uint64_t)timeutils_mktime(tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec) * 1000000ULL; + time_us_64_offset_from_epoch = time_us - mp_hal_ticks_us_64(); +} + +uint64_t mp_hal_time_ns(void) { + return (time_us_64_offset_from_epoch + mp_hal_ticks_us_64()) * 1000ULL; +} diff --git a/ports/samd/mphalport.h b/ports/samd/mphalport.h index 6c9e525bb9..69e2b60640 100644 --- a/ports/samd/mphalport.h +++ b/ports/samd/mphalport.h @@ -47,6 +47,7 @@ extern int mp_interrupt_char; extern ringbuf_t stdin_ringbuf; extern volatile uint32_t systick_ms; uint64_t mp_hal_ticks_us_64(void); +void mp_hal_time_ns_set_from_rtc(void); void mp_hal_set_interrupt_char(int c); @@ -94,10 +95,6 @@ static inline mp_uint_t mp_hal_ticks_cpu(void) { } #endif -static inline uint64_t mp_hal_time_ns(void) { - return mp_hal_ticks_us_64() * 1000; -} - // C-level pin HAL #include "py/obj.h" diff --git a/ports/samd/samd_qspiflash.c b/ports/samd/samd_qspiflash.c index e25ddf21ef..f314a95519 100644 --- a/ports/samd/samd_qspiflash.c +++ b/ports/samd/samd_qspiflash.c @@ -105,7 +105,6 @@ static const external_flash_device possible_devices[] = { #define EXTERNAL_FLASH_DEVICE_COUNT MP_ARRAY_SIZE(possible_devices) static external_flash_device const *flash_device; -static external_flash_device generic_config = GENERIC; extern const mp_obj_type_t samd_qspiflash_type; // The QSPIflash object is a singleton @@ -248,15 +247,6 @@ static uint8_t get_baud(int32_t freq_mhz) { return baud; } -int get_sfdp_table(uint8_t *table, int maxlen) { - uint8_t header[16]; - read_memory_single(QSPI_CMD_READ_SFDP_PARAMETER, 0, header, sizeof(header)); - int len = MIN(header[11] * 4, maxlen); - int addr = header[12] + (header[13] << 8) + (header[14] << 16); - read_memory_single(QSPI_CMD_READ_SFDP_PARAMETER, addr, table, len); - return len; -} - static mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) { mp_arg_check_num(n_args, n_kw, 0, 0, false); @@ -297,19 +287,6 @@ static mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args uint8_t jedec_ids[3]; read_command(QSPI_CMD_READ_JEDEC_ID, jedec_ids, sizeof(jedec_ids)); - // Read the common sfdp table - // Check the device addr length, support of 1-1-4 mode and get the sector size - uint8_t sfdp_table[128]; - int len = get_sfdp_table(sfdp_table, sizeof(sfdp_table)); - if (len >= 29) { - self->sectorsize = 1 << sfdp_table[28]; - bool addr4b = ((sfdp_table[2] >> 1) & 0x03) == 0x02; - bool supports_qspi_114 = (sfdp_table[2] & 0x40) != 0; - if (addr4b || !supports_qspi_114) { - mp_raise_ValueError(MP_ERROR_TEXT("QSPI mode not supported")); - } - } - // Check, if the flash device is known and get it's properties. flash_device = NULL; for (uint8_t i = 0; i < EXTERNAL_FLASH_DEVICE_COUNT; i++) { @@ -321,15 +298,8 @@ static mp_obj_t samd_qspiflash_make_new(const mp_obj_type_t *type, size_t n_args break; } } - - // If the flash device is not known, try generic config options if (flash_device == NULL) { - if (jedec_ids[0] == 0xc2) { // Macronix devices - generic_config.quad_enable_bit_mask = 0x04; - generic_config.single_status_byte = true; - } - generic_config.total_size = 1 << jedec_ids[2]; - flash_device = &generic_config; + mp_raise_ValueError(MP_ERROR_TEXT("QSPI device not supported")); } self->size = flash_device->total_size; diff --git a/ports/samd/samd_spiflash.c b/ports/samd/samd_spiflash.c index 8ada7e9609..63dc0a8304 100644 --- a/ports/samd/samd_spiflash.c +++ b/ports/samd/samd_spiflash.c @@ -67,6 +67,19 @@ typedef struct _spiflash_obj_t { extern const mp_obj_type_t samd_spiflash_type; +typedef struct _spiflash_jedec_id_t { + uint32_t jedec_id; + uint32_t mask; + uint32_t size; +} spiflash_jedec_id_t; + +spiflash_jedec_id_t jedec_id_table[] = { + { 0x1f8401, 0xffff00, 512 * 1024 }, // Adesto/Renesas 4 MBit + { 0x1f2400, 0xffff00, 512 * 1024 }, // Adesto 4 MBit + { 0x1f4501, 0xffff00, 1024 * 1024 }, // Adesto/Renesas/Atmel 8 MBit + { 0xc84013, 0xffffff, 512 * 1024 }, // Gigadevices 4 MBit +}; + // The SPIflash object is a singleton static spiflash_obj_t spiflash_obj = { { &samd_spiflash_type }, NULL, 0, false, PAGE_SIZE, SECTOR_SIZE, NULL, 0 @@ -124,6 +137,7 @@ static void write_enable(spiflash_obj_t *self) { mp_hal_pin_write(self->cs, 1); } +#if !defined(MICROPY_HW_SPIFLASH_SIZE) // Write status register 1 static void write_sr1(spiflash_obj_t *self, uint8_t value) { uint8_t msg[2]; @@ -134,6 +148,7 @@ static void write_sr1(spiflash_obj_t *self, uint8_t value) { spi_transfer(self->spi, 2, msg, NULL); mp_hal_pin_write(self->cs, 1); } +#endif static void get_sfdp(spiflash_obj_t *self, uint32_t addr, uint8_t *buffer, int size) { uint8_t dummy[1]; @@ -170,32 +185,46 @@ static mp_obj_t spiflash_make_new(const mp_obj_type_t *type, size_t n_args, size // Get the flash size from the device ID (default) uint8_t id[3]; get_id(self, id); - bool read_sfdp = true; - - if (id[1] == 0x84 && id[2] == 1) { // Adesto - self->size = 512 * 1024; - } else if (id[0] == 0x1f && id[1] == 0x45 && id[2] == 1) { // Adesto/Renesas 8 MBit - self->size = 1024 * 1024; - read_sfdp = false; - self->sectorsize = 4096; - self->addr_is_32bit = false; - // Globally unlock the sectors, which are locked after power on. - write_enable(self); - write_sr1(self, 0); - } else { - self->size = 1 << id[2]; + + #if defined(MICROPY_HW_SPIFLASH_SIZE) + self->size = MICROPY_HW_SPIFLASH_SIZE; + #else + // Assume as default that the size is coded into the last JEDEC ID byte. + self->size = 1 << id[2]; + // Look for specific flash devices with different encoding. + uint32_t jedec_id = (id[0] << 16) | (id[1] << 8) | id[2]; + for (int i = 0; i < MP_ARRAY_SIZE(jedec_id_table); i++) { + if (jedec_id_table[i].jedec_id == (jedec_id & jedec_id_table[i].mask)) { + self->size = jedec_id_table[i].size; + // Globally unlock the sectors, which may be locked after power on. + write_enable(self); + write_sr1(self, 0); + break; + } } + #endif - // Get the addr_is_32bit flag and the sector size - if (read_sfdp) { - uint8_t buffer[128]; - get_sfdp(self, 0, buffer, 16); // get the header + // Get the flash size, addr_is_32bit flag and sector size from SFDP, if present. + uint8_t buffer[128]; + get_sfdp(self, 0, buffer, 16); // get the header + if (*(uint32_t *)buffer == 0x50444653) { // Header signature "SFDP" int len = MIN(buffer[11] * 4, sizeof(buffer)); if (len >= 29) { int addr = buffer[12] + (buffer[13] << 8) + (buffer[14] << 16); get_sfdp(self, addr, buffer, len); // Get the JEDEC mandatory table self->sectorsize = 1 << buffer[28]; self->addr_is_32bit = ((buffer[2] >> 1) & 0x03) != 0; + #if !defined(MICROPY_HW_SPIFLASH_SIZE) + // Get the bit size from the SFDP data + uint32_t size = *(uint32_t *)(buffer + 4); + if (size & 0x8000000) { + // Byte size is 2 ** lower_31_bits / 8 + self->size = 1 << ((size & 0x7fffffff) >> 3); + } else { + // Byte size is lower_31_bits / 8 + 1 + self->size = ((size & 0x7fffffff) >> 3) + 1; + } + #endif } } self->commands = self->addr_is_32bit ? _COMMANDS_32BIT : _COMMANDS_24BIT; diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile index 8ac9a8af03..37d70dcdbf 100644 --- a/ports/stm32/Makefile +++ b/ports/stm32/Makefile @@ -60,6 +60,7 @@ include $(TOP)/extmod/extmod.mk GIT_SUBMODULES += lib/libhydrogen lib/stm32lib +CROSS_COMPILE ?= arm-none-eabi- LD_DIR=boards USBDEV_DIR=usbdev #USBHOST_DIR=usbhost @@ -101,9 +102,6 @@ GEN_STMCONST_HDR = $(HEADER_BUILD)/modstm_const.h GEN_STMCONST_MPZ = $(HEADER_BUILD)/modstm_mpz.h CMSIS_MCU_HDR = $(STM32LIB_CMSIS_ABS)/Include/$(CMSIS_MCU_LOWER).h -# Select the cross compile prefix -CROSS_COMPILE ?= arm-none-eabi- - INC += -I. INC += -I$(TOP) INC += -I$(BUILD) @@ -123,7 +121,15 @@ CFLAGS += -DSTM32_HAL_H='<stm32$(MCU_SERIES)xx_hal.h>' CFLAGS += -DMBOOT_VTOR=$(MBOOT_TEXT0_ADDR) CFLAGS += -DMICROPY_HW_VTOR=$(TEXT0_ADDR) +ifeq ($(MCU_SERIES),n6) +ifeq ($(USE_MBOOT),1) +CFLAGS += -DMICROPY_HW_RUNS_FROM_EXT_FLASH=1 +endif +# as doesn't recognise -mcpu=cortex-m55 +AFLAGS += -march=armv8.1-m.main +else AFLAGS += $(filter -mcpu=%,$(CFLAGS_MCU_$(MCU_SERIES))) +endif # Configure for nan-boxing object model if requested ifeq ($(NANBOX),1) @@ -300,6 +306,7 @@ SRC_C += \ adc.c \ sdio.c \ subghz.c \ + xspi.c \ $(wildcard $(BOARD_DIR)/*.c) SRC_O += \ @@ -316,6 +323,13 @@ CFLAGS += -DUSE_HAL_DRIVER SRC_O += \ resethandler_m3.o \ shared/runtime/gchelper_thumb2.o +else ifeq ($(MCU_SERIES),n6) +SRC_O += shared/runtime/gchelper_thumb2.o +ifeq ($(USE_MBOOT),1) +SRC_O += resethandler_iram.o +else +SRC_O += resethandler.o +endif else SRC_O += \ system_stm32.o \ @@ -329,8 +343,6 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_adc_ex.c \ hal_cortex.c \ hal_dma.c \ - hal_flash.c \ - hal_flash_ex.c \ hal_gpio.c \ hal_i2c.c \ hal_pwr.c \ @@ -347,7 +359,14 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ ll_utils.c \ ) -ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7 l0 l1 l4 wb)) +ifneq ($(MCU_SERIES),n6) +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ + hal_flash.c \ + hal_flash_ex.c \ + ) +endif + +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7 l0 l1 l4 n6 wb)) HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_pcd.c \ hal_pcd_ex.c \ @@ -355,7 +374,15 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ ) endif -ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h5 h7 l4)) +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),n6)) +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ + hal_bsec.c \ + hal_rif.c \ + hal_xspi.c \ + ) +endif + +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h5 h7 l4 n6)) HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_sd.c \ ll_sdmmc.c \ @@ -380,7 +407,7 @@ $(BUILD)/$(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_hal_mmc.o: CFLAGS += -Wno endif endif -ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7)) +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 g0 g4 h5 h7 n6)) HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_dma_ex.c \ ) @@ -417,6 +444,12 @@ HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ ) endif +ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),h7)) +HAL_SRC_C += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ + hal_uart_ex.c \ + ) +endif + USBDEV_SRC_C += $(addprefix $(USBDEV_DIR)/,\ core/src/usbd_core.c \ core/src/usbd_ctlreq.c \ @@ -490,6 +523,12 @@ all: $(TOP)/lib/stm32lib/README.md all_main $(BUILD)/firmware.hex ifeq ($(MBOOT_ENABLE_PACKING),1) all_main: $(BUILD)/firmware.pack.dfu +else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),n6)) +ifeq ($(USE_MBOOT),1) +all_main: $(BUILD)/firmware.dfu +else +all_main: $(BUILD)/firmware-trusted.bin +endif else all_main: $(BUILD)/firmware.dfu endif @@ -550,7 +589,7 @@ define GENERATE_HEX $(Q)$(OBJCOPY) -O ihex $(2) $(1) endef -.PHONY: deploy deploy-stlink deploy-openocd +.PHONY: deploy deploy-stlink deploy-openocd deploy-trusted ifeq ($(MBOOT_ENABLE_PACKING),1) deploy: $(BUILD)/firmware.pack.dfu @@ -560,6 +599,9 @@ deploy: $(BUILD)/firmware.dfu $(call RUN_DFU,$^) endif +deploy-trusted: $(BUILD)/firmware-trusted.bin + $(STM32_CUBE_PROGRAMMER)/bin/STM32_Programmer.sh -c port=SWD mode=HOTPLUG ap=1 -el $(DKEL) -w $^ 0x70000000 -hardRst + # A board should specify TEXT0_ADDR if to use a different location than the # default for the firmware memory location. A board can also optionally define # TEXT1_ADDR to split the firmware into two sections; see below for details. @@ -614,6 +656,10 @@ $(BUILD)/firmware.hex: $(BUILD)/firmware.elf $(BUILD)/firmware.elf: $(OBJ) $(call GENERATE_ELF,$@,$^) +$(BUILD)/firmware-trusted.bin: $(BUILD)/firmware.bin + /bin/rm -f $@ + $(STM32_CUBE_PROGRAMMER)/bin/STM32_SigningTool_CLI -bin $^ -nk -of 0x80000000 -t fsbl -o $@ -hv $(STM32_N6_HEADER_VERSION) + # List of sources for qstr extraction SRC_QSTR += $(SRC_C) $(SRC_CXX) $(SHARED_SRC_C) $(GEN_PINS_SRC) diff --git a/ports/stm32/adc.c b/ports/stm32/adc.c index 3549fc29a9..f47e9eaad7 100644 --- a/ports/stm32/adc.c +++ b/ports/stm32/adc.c @@ -51,7 +51,7 @@ /// val = adc.read_core_vref() # read MCU VREF /* ADC definitions */ -#if defined(STM32H5) +#if defined(STM32H5) || defined(STM32N6) // STM32H5 features two ADC instances, ADCx and pin_adc_table are set dynamically #define PIN_ADC_MASK (PIN_ADC1 | PIN_ADC2) #else @@ -107,12 +107,12 @@ #define ADC_CAL2 ((uint16_t *)(ADC_CAL_ADDRESS + 4)) #define ADC_CAL_BITS (12) -#elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) +#elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L1) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) #define ADC_SCALE_V (((float)VREFINT_CAL_VREF) / 1000.0f) -#define ADC_CAL_ADDRESS VREFINT_CAL_ADDR -#define ADC_CAL1 TEMPSENSOR_CAL1_ADDR -#define ADC_CAL2 TEMPSENSOR_CAL2_ADDR +#define ADC_CAL_ADDRESS (VREFINT_CAL_ADDR) +#define ADC_CAL1 (TEMPSENSOR_CAL1_ADDR) +#define ADC_CAL2 (TEMPSENSOR_CAL2_ADDR) #define ADC_CAL_BITS (12) // UM2319/UM2570, __HAL_ADC_CALC_TEMPERATURE: 'corresponds to a resolution of 12 bits' #elif defined(STM32H7) @@ -123,22 +123,6 @@ #define ADC_CAL2 ((uint16_t *)(0x1FF1E840)) #define ADC_CAL_BITS (16) -#elif defined(STM32L1) - -#define ADC_SCALE_V (VREFINT_CAL_VREF / 1000.0f) -#define ADC_CAL_ADDRESS (VREFINT_CAL_ADDR) -#define ADC_CAL1 (TEMPSENSOR_CAL1_ADDR) -#define ADC_CAL2 (TEMPSENSOR_CAL2_ADDR) -#define ADC_CAL_BITS (12) - -#elif defined(STM32L4) || defined(STM32WB) - -#define ADC_SCALE_V (VREFINT_CAL_VREF / 1000.0f) -#define ADC_CAL_ADDRESS (VREFINT_CAL_ADDR) -#define ADC_CAL1 (TEMPSENSOR_CAL1_ADDR) -#define ADC_CAL2 (TEMPSENSOR_CAL2_ADDR) -#define ADC_CAL_BITS (12) - #else #error Unsupported processor @@ -182,6 +166,9 @@ #define VBAT_DIV (3) #elif defined(STM32L152xE) // STM32L152xE does not have vbat. +#elif defined(STM32N6) +// ADC2 VINP 16 +#define VBAT_DIV (4) #else #error Unsupported processor #endif @@ -263,7 +250,7 @@ static bool is_adcx_channel(int channel) { handle.Instance = ADCx; return __HAL_ADC_IS_CHANNEL_INTERNAL(channel) || IS_ADC_CHANNEL(&handle, __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel)); - #elif defined(STM32H5) + #elif defined(STM32H5) || defined(STM32N6) // The first argument to the IS_ADC_CHANNEL macro is unused. return __HAL_ADC_IS_CHANNEL_INTERNAL(channel) || IS_ADC_CHANNEL(NULL, __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel)); @@ -276,7 +263,7 @@ static void adc_wait_for_eoc_or_timeout(ADC_HandleTypeDef *adcHandle, int32_t ti uint32_t tickstart = HAL_GetTick(); #if defined(STM32F4) || defined(STM32F7) || defined(STM32L1) while ((adcHandle->Instance->SR & ADC_FLAG_EOC) != ADC_FLAG_EOC) { - #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) while (READ_BIT(adcHandle->Instance->ISR, ADC_FLAG_EOC) != ADC_FLAG_EOC) { #else #error Unsupported processor @@ -295,7 +282,7 @@ static void adcx_clock_enable(ADC_HandleTypeDef *adch) { __HAL_RCC_ADC_CONFIG(RCC_ADCCLKSOURCE_CLKP); #elif defined(STM32G0) __HAL_RCC_ADC_CLK_ENABLE(); - #elif defined(STM32G4) + #elif defined(STM32G4) || defined(STM32N6) __HAL_RCC_ADC12_CLK_ENABLE(); #elif defined(STM32H5) __HAL_RCC_ADC_CLK_ENABLE(); @@ -368,6 +355,15 @@ static void adcx_init_periph(ADC_HandleTypeDef *adch, uint32_t resolution) { adch->Init.OversamplingMode = DISABLE; adch->Init.DataAlign = ADC_DATAALIGN_RIGHT; adch->Init.DMAContinuousRequests = DISABLE; + #elif defined(STM32N6) + adch->Init.GainCompensation = 0; + adch->Init.ScanConvMode = ADC_SCAN_DISABLE; + adch->Init.LowPowerAutoWait = DISABLE; + adch->Init.SamplingMode = ADC_SAMPLING_MODE_NORMAL; + adch->Init.ConversionDataManagement = ADC_CONVERSIONDATA_DR; + adch->Init.Overrun = ADC_OVR_DATA_OVERWRITTEN; + adch->Init.LeftBitShift = ADC_LEFTBITSHIFT_NONE; + adch->Init.OversamplingMode = DISABLE; #else #error Unsupported processor #endif @@ -400,7 +396,7 @@ static void adc_init_single(pyb_obj_adc_t *adc_obj, ADC_TypeDef *adc) { static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) { ADC_ChannelConfTypeDef sConfig; - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) sConfig.Rank = ADC_REGULAR_RANK_1; if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel) == 0) { channel = __HAL_ADC_DECIMAL_NB_TO_CHANNEL(channel); @@ -432,7 +428,7 @@ static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) { sConfig.SamplingTime = ADC_SAMPLETIME_384CYCLES; } else { - sConfig.SamplingTime = ADC_SAMPLETIME_384CYCLES; + sConfig.SamplingTime = ADC_SAMPLETIME_16CYCLES; } #elif defined(STM32G0) if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) { @@ -449,6 +445,18 @@ static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) sConfig.SingleDiff = ADC_SINGLE_ENDED; sConfig.OffsetNumber = ADC_OFFSET_NONE; sConfig.Offset = 0; + #elif defined(STM32N6) + if (__HAL_ADC_IS_CHANNEL_INTERNAL(channel)) { + sConfig.SamplingTime = ADC_SAMPLETIME_246CYCLES_5; + } else { + sConfig.SamplingTime = ADC_SAMPLETIME_11CYCLES_5; + } + sConfig.SingleDiff = ADC_SINGLE_ENDED; + sConfig.OffsetNumber = ADC_OFFSET_NONE; + sConfig.Offset = 0; + sConfig.OffsetSignedSaturation = DISABLE; + sConfig.OffsetSaturation = DISABLE; + sConfig.OffsetSign = ADC_OFFSET_SIGN_POSITIVE; #else #error Unsupported processor #endif @@ -464,10 +472,13 @@ static void adc_config_channel(ADC_HandleTypeDef *adc_handle, uint32_t channel) static uint32_t adc_read_channel(ADC_HandleTypeDef *adcHandle) { uint32_t value; - #if defined(STM32G4) - // For STM32G4 there is errata 2.7.7, "Wrong ADC result if conversion done late after - // calibration or previous conversion". According to the errata, this can be avoided - // by performing two consecutive ADC conversions and keeping the second result. + #if defined(STM32G4) || defined(STM32WB) + // For STM32G4 errata 2.7.7 / STM32WB errata 2.7.1: + // "Wrong ADC result if conversion done late after calibration or previous conversion" + // states an incorrect reading is returned if more than 1ms has elapsed since the last + // reading or calibration. According to the errata, this can be avoided by performing + // two consecutive ADC conversions and keeping the second result. + // Note: On STM32WB55 @ 64Mhz each ADC read takes ~ 3us. for (uint8_t i = 0; i < 2; i++) #endif { @@ -523,7 +534,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ // 1st argument is the pin name mp_obj_t pin_obj = args[0]; - #if defined(STM32H5) + #if defined(STM32H5) || defined(STM32N6) // STM32H5 has two ADC instances where some pins are only available on ADC1 or ADC2 (but not both). // Assume we're using a channel of ADC1. Can be overridden for ADC2 later in this function. ADC_TypeDef *adc = ADC1; @@ -540,7 +551,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ // No ADC function on the given pin. mp_raise_msg_varg(&mp_type_ValueError, MP_ERROR_TEXT("Pin(%q) doesn't have ADC capabilities"), pin->name); } - #if defined(STM32H5) + #if defined(STM32H5) || defined(STM32N6) if ((pin->adc_num & PIN_ADC2) == PIN_ADC2) { adc = ADC2; pin_adc_table = pin_adc2; @@ -555,7 +566,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ } // If this channel corresponds to a pin then configure the pin in ADC mode. - #if defined(STM32H5) + #if defined(STM32H5) || defined(STM32N6) if (channel < num_adc_pins) { const machine_pin_obj_t *pin = pin_adc_table[channel]; if (pin != NULL) { @@ -576,7 +587,7 @@ static mp_obj_t adc_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_ o->base.type = &pyb_adc_type; o->pin_name = pin_obj; o->channel = channel; - #if defined(STM32H5) + #if defined(STM32H5) || defined(STM32N6) adc_init_single(o, adc); #else adc_init_single(o, ADCx); @@ -667,7 +678,7 @@ static mp_obj_t adc_read_timed(mp_obj_t self_in, mp_obj_t buf_in, mp_obj_t freq_ // for subsequent samples we can just set the "start sample" bit #if defined(STM32F4) || defined(STM32F7) || defined(STM32L1) self->handle.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART; - #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) SET_BIT(self->handle.Instance->CR, ADC_CR_ADSTART); #else #error Unsupported processor @@ -777,7 +788,7 @@ static mp_obj_t adc_read_timed_multi(mp_obj_t adc_array_in, mp_obj_t buf_array_i // ADC is started: set the "start sample" bit #if defined(STM32F4) || defined(STM32F7) || defined(STM32L1) adc->handle.Instance->CR2 |= (uint32_t)ADC_CR2_SWSTART; - #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) SET_BIT(adc->handle.Instance->CR, ADC_CR_ADSTART); #else #error Unsupported processor @@ -911,6 +922,8 @@ int adc_read_core_temp(ADC_HandleTypeDef *adcHandle) { } else { return 0; } + #elif defined(STM32N6) + int32_t raw_value = 0; // TODO #else int32_t raw_value = adc_config_and_read_ref(adcHandle, ADC_CHANNEL_TEMPSENSOR); #endif @@ -922,6 +935,10 @@ int adc_read_core_temp(ADC_HandleTypeDef *adcHandle) { static volatile float adc_refcor = 1.0f; float adc_read_core_temp_float(ADC_HandleTypeDef *adcHandle) { + #if defined(STM32N6) + return 0.0f; // TODO + #else + #if defined(STM32G4) || defined(STM32L1) || defined(STM32L4) // Update the reference correction factor before reading tempsensor // because TS_CAL1 and TS_CAL2 of STM32G4,L1/L4 are at VDDA=3.0V @@ -936,7 +953,7 @@ float adc_read_core_temp_float(ADC_HandleTypeDef *adcHandle) { return 0; } float core_temp_avg_slope = (*ADC_CAL2 - *ADC_CAL1) / 100.0f; - #elif defined(STM32H5) + #elif defined(STM32H5) || defined(STM32WB) int32_t raw_value = adc_config_and_read_ref(adcHandle, ADC_CHANNEL_TEMPSENSOR); float core_temp_avg_slope = (*ADC_CAL2 - *ADC_CAL1) / 100.0f; #else @@ -944,6 +961,8 @@ float adc_read_core_temp_float(ADC_HandleTypeDef *adcHandle) { float core_temp_avg_slope = (*ADC_CAL2 - *ADC_CAL1) / 80.0f; #endif return (((float)raw_value * adc_refcor - *ADC_CAL1) / core_temp_avg_slope) + 30.0f; + + #endif } float adc_read_core_vbat(ADC_HandleTypeDef *adcHandle) { diff --git a/ports/stm32/adc.h b/ports/stm32/adc.h index 0518cdcd9a..0adc9ac2be 100644 --- a/ports/stm32/adc.h +++ b/ports/stm32/adc.h @@ -48,7 +48,7 @@ static inline void adc_deselect_vbat(ADC_TypeDef *adc, uint32_t channel) { adc_common = ADC_COMMON_REGISTER(0); #elif defined(STM32F7) adc_common = ADC123_COMMON; - #elif defined(STM32G4) || defined(STM32H5) + #elif defined(STM32G4) || defined(STM32H5) || defined(STM32N6) adc_common = ADC12_COMMON; #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) adc_common = ADC12_COMMON; diff --git a/ports/stm32/boardctrl.c b/ports/stm32/boardctrl.c index 8f0d066f37..ea95c8d2d5 100644 --- a/ports/stm32/boardctrl.c +++ b/ports/stm32/boardctrl.c @@ -35,7 +35,7 @@ #include "led.h" #include "usrsw.h" -NORETURN void boardctrl_fatal_error(const char *msg) { +MP_NORETURN void boardctrl_fatal_error(const char *msg) { for (volatile uint delay = 0; delay < 10000000; delay++) { } led_state(1, 1); diff --git a/ports/stm32/boardctrl.h b/ports/stm32/boardctrl.h index 8f4ce30eff..cb5380c298 100644 --- a/ports/stm32/boardctrl.h +++ b/ports/stm32/boardctrl.h @@ -114,7 +114,7 @@ typedef struct _boardctrl_state_t { bool log_soft_reset; } boardctrl_state_t; -NORETURN void boardctrl_fatal_error(const char *msg); +MP_NORETURN void boardctrl_fatal_error(const char *msg); void boardctrl_maybe_enter_mboot(size_t n_args, const void *args); void boardctrl_before_soft_reset_loop(boardctrl_state_t *state); void boardctrl_top_soft_reset_loop(boardctrl_state_t *state); @@ -122,5 +122,6 @@ int boardctrl_run_boot_py(boardctrl_state_t *state); int boardctrl_run_main_py(boardctrl_state_t *state); void boardctrl_start_soft_reset(boardctrl_state_t *state); void boardctrl_end_soft_reset(boardctrl_state_t *state); +void boardctrl_enter_standby(void); #endif // MICROPY_INCLUDED_STM32_BOARDCTRL_H diff --git a/ports/stm32/boards/ARDUINO_GIGA/board.json b/ports/stm32/boards/ARDUINO_GIGA/board.json index 53c636c774..a99e747017 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/board.json +++ b/ports/stm32/boards/ARDUINO_GIGA/board.json @@ -15,7 +15,7 @@ "ABX00063_01.front_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Giga", + "product": "Giga", "thumbnail": "", "url": "https://store.arduino.cc/products/giga-r1-wifi", "vendor": "Arduino" diff --git a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h index cef45d730c..44f6ce66bc 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_GIGA/mpconfigboard.h @@ -31,6 +31,7 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENABLE_MMCARD (0) #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) #define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1) +#define MICROPY_GC_SPLIT_HEAP (1) // ROMFS config #define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) diff --git a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld index e7bb950dbe..dceb1a7489 100644 --- a/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld +++ b/ports/stm32/boards/ARDUINO_GIGA/stm32h747.ld @@ -12,6 +12,7 @@ MEMORY SRAM2 (xrw) : ORIGIN = 0x30020000, LENGTH = 128K /* SRAM2 D2 */ SRAM3 (xrw) : ORIGIN = 0x30040000, LENGTH = 32K /* SRAM3 D2 */ SRAM4 (xrw) : ORIGIN = 0x38000000, LENGTH = 64K /* SRAM4 D3 */ + SDRAM (xrw) : ORIGIN = 0x60000000, LENGTH = 8M /* SDRAM */ FLASH (rx) : ORIGIN = 0x08000000, LENGTH = 2048K /* Total available flash */ FLASH_BL (rx) : ORIGIN = 0x08000000, LENGTH = 128K /* Arduino bootloader */ FLASH_FS (r) : ORIGIN = 0x08020000, LENGTH = 128K /* filesystem */ @@ -53,3 +54,27 @@ _openamp_shm_region_start = ORIGIN(SRAM4); _openamp_shm_region_end = ORIGIN(SRAM4) + LENGTH(SRAM4); INCLUDE common_blifs.ld + +SECTIONS +{ + /* GC blocks addresses and sizes */ + .gc.blocks.table (READONLY) : { + . = ALIGN(4); + _gc_blocks_table_start = .; + + LONG (ORIGIN(SRAM1)); + LONG (128K); + + LONG (ORIGIN(SDRAM) + 0M); + LONG (2M); + + LONG (ORIGIN(SDRAM) + 2M); + LONG (2M); + + LONG (ORIGIN(SDRAM) + 4M); + LONG (4M); + + _gc_blocks_table_end = .; + . = ALIGN(4); + } > FLASH_TEXT +} diff --git a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json index a4c81d69d4..129d9f2fc7 100644 --- a/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json +++ b/ports/stm32/boards/ARDUINO_NICLA_VISION/board.json @@ -15,7 +15,7 @@ "ABX00051_01.iso_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Nicla Vision", + "product": "Nicla Vision", "thumbnail": "", "url": "https://store.arduino.cc/products/nicla-vision", "vendor": "Arduino" diff --git a/ports/stm32/boards/ARDUINO_OPTA/board.json b/ports/stm32/boards/ARDUINO_OPTA/board.json index 3955d7df31..dd6c39a191 100644 --- a/ports/stm32/boards/ARDUINO_OPTA/board.json +++ b/ports/stm32/boards/ARDUINO_OPTA/board.json @@ -14,7 +14,7 @@ "AFX00002_01.iso_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Opta WiFi", + "product": "Opta WiFi", "thumbnail": "", "url": "https://store.arduino.cc/products/opta-wifi", "vendor": "Arduino" diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json index f39d7d4c47..c97b9f165c 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/board.json @@ -16,7 +16,7 @@ "ABX00042_01.iso_1000x750.jpg" ], "mcu": "stm32h7", - "product": "Arduino Portenta H7", + "product": "Portenta H7", "thumbnail": "", "url": "https://store.arduino.cc/products/portenta-h7", "vendor": "Arduino" diff --git a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h index 90a1469056..a9ecf38fbf 100644 --- a/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h +++ b/ports/stm32/boards/ARDUINO_PORTENTA_H7/mpconfigboard.h @@ -30,7 +30,6 @@ typedef unsigned int mp_uint_t; // must be pointer size #define MICROPY_HW_ENABLE_SDCARD (1) #define MICROPY_HW_ENABLE_MMCARD (0) #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) -#define MICROPY_HW_TIM_IS_RESERVED(id) (id == 1) // ROMFS config #define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (1) diff --git a/ports/stm32/boards/HYDRABUS/board.json b/ports/stm32/boards/HYDRABUS/board.json index 20a2ac5eae..24df6ac99f 100644 --- a/ports/stm32/boards/HYDRABUS/board.json +++ b/ports/stm32/boards/HYDRABUS/board.json @@ -10,6 +10,6 @@ "mcu": "stm32f4", "product": "HydraBus v1.0", "thumbnail": "", - "url": "", + "url": "https://github.com/hydrabus/hydrabus", "vendor": "HydraBus" } diff --git a/ports/stm32/boards/LIMIFROG/board.json b/ports/stm32/boards/LIMIFROG/board.json index f516963a5b..36c80f8f72 100644 --- a/ports/stm32/boards/LIMIFROG/board.json +++ b/ports/stm32/boards/LIMIFROG/board.json @@ -10,6 +10,6 @@ "mcu": "stm32l4", "product": "LimiFrog", "thumbnail": "", - "url": "", + "url": "https://github.com/LimiFrog", "vendor": "LimiFrog" } diff --git a/ports/stm32/boards/NUCLEO_N657X0/bdev.c b/ports/stm32/boards/NUCLEO_N657X0/bdev.c new file mode 100644 index 0000000000..2180d46174 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_N657X0/bdev.c @@ -0,0 +1,42 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 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 "py/obj.h" +#include "storage.h" +#include "xspi.h" + +#if MICROPY_HW_SPIFLASH_ENABLE_CACHE +#error "Cannot enable MICROPY_HW_SPIFLASH_ENABLE_CACHE" +#endif + +// External SPI flash uses hardware XSPI interface. +const mp_spiflash_config_t spiflash_config = { + .bus_kind = MP_SPIFLASH_BUS_QSPI, + .bus.u_qspi.data = (void *)&xspi_flash2, + .bus.u_qspi.proto = &xspi_proto, +}; + +spi_bdev_t spi_bdev; diff --git a/ports/stm32/boards/NUCLEO_N657X0/board.c b/ports/stm32/boards/NUCLEO_N657X0/board.c new file mode 100644 index 0000000000..fe5f2f1cc8 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_N657X0/board.c @@ -0,0 +1,122 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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 "py/mphal.h" +#include "boardctrl.h" +#include "xspi.h" + +// Values for OTP fuses for VDDIO3, to select low voltage mode (<2.5V). +// See RM0486, Section 5, Table 18. +#define BSEC_HW_CONFIG_ID (124U) +#define BSEC_HWS_HSLV_VDDIO3 (1U << 15) + +static void board_config_vdd(void) { + // TODO: move some of the below code to a common location for all N6 boards? + + // Enable PWR, BSEC and SYSCFG clocks. + LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG); + + // Program high speed IO optimization fuses if they aren't already set. + uint32_t fuse; + BSEC_HandleTypeDef hbsec = { .Instance = BSEC }; + const uint32_t mask = BSEC_HWS_HSLV_VDDIO3; + if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) { + fuse = 0; + } else if ((fuse & mask) != mask) { + // Program the fuse, and read back the set value. + if (HAL_BSEC_OTP_Program(&hbsec, BSEC_HW_CONFIG_ID, fuse | mask, HAL_BSEC_NORMAL_PROG) != HAL_OK) { + fuse = 0; + } else if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) { + fuse = 0; + } + } + + // Enable Vdd ADC, needed for the ADC to work. + LL_PWR_EnableVddADC(); + + // Configure VDDIO2. + LL_PWR_EnableVddIO2(); + LL_PWR_SetVddIO2VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3); + SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation + + // Configure VDDIO3. Only enable 1.8V mode if the fuse is set. + LL_PWR_EnableVddIO3(); + if (fuse & BSEC_HWS_HSLV_VDDIO3) { + LL_PWR_SetVddIO3VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_1V8); + } + SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation + + // Configure VDDIO4. + LL_PWR_EnableVddIO4(); + LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3); + SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation + + // Enable VDD for ADC and USB. + LL_PWR_EnableVddADC(); + LL_PWR_EnableVddUSB(); +} + +void mboot_board_early_init(void) { + board_config_vdd(); + xspi_init(); +} + +void board_early_init(void) { + #if !MICROPY_HW_RUNS_FROM_EXT_FLASH + // Firmware runs directly from SRAM, so configure VDD and enable XSPI flash. + board_config_vdd(); + xspi_init(); + #endif +} + +void board_leave_standby(void) { + // TODO: move some of the below code to a common location for all N6 boards? + + // Enable PWR, BSEC and SYSCFG clocks. + LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG); + + // Configure VDDIO2. + LL_PWR_EnableVddIO2(); + LL_PWR_SetVddIO2VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3); + SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation + + // Configure VDDIO3 (1.8V mode selection is retained). + LL_PWR_EnableVddIO3(); + SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation + + // Configure VDDIO4. + LL_PWR_EnableVddIO4(); + LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3); + SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation + + // Enable VDD for ADC and USB. + LL_PWR_EnableVddADC(); + LL_PWR_EnableVddUSB(); +} diff --git a/ports/stm32/boards/NUCLEO_N657X0/board.md b/ports/stm32/boards/NUCLEO_N657X0/board.md new file mode 100644 index 0000000000..3360c5db6c --- /dev/null +++ b/ports/stm32/boards/NUCLEO_N657X0/board.md @@ -0,0 +1,17 @@ +The mboot bootloader must first be built and deployed to this board. Make sure that +CN9 is in position 1-2 to select STLK as the 5V power source, that JP1 is in position +1-2 (lower position) and JP2 is in position 2-3 (upper position). Then plug in a USB +cable into the ST-LINK port CN10. This will allow mboot firmware to be programmed to +the external SPI flash via ST's tools, eg: + + make -C ports/stm32/mboot BOARD=NUCLEO_N657X0 deploy-trusted + +Once mboot is installed, change CN9 to position 3-4 to select USB as the 5V power +source, change JP2 back to position 1-2 (lower position) and change the USB cable to +CN8. mboot will present a USB DFU device on this USB port, and the red LED2 should be +blinking at 1Hz to indicate that mboot is active. If it's not active then hold the +USER button and press NRST, and wait until all three LEDs are on, then release USER. +Now mboot will be active. + +Once the USB DFU port can be seen, the firmware below can be programmed as usual with +any DFU loader. diff --git a/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h new file mode 100644 index 0000000000..ccc3fa051f --- /dev/null +++ b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h @@ -0,0 +1,103 @@ +#define MICROPY_HW_BOARD_NAME "NUCLEO-N657X0" +#define MICROPY_HW_MCU_NAME "STM32N657X0" + +#define MICROPY_GC_STACK_ENTRY_TYPE uint32_t +#define MICROPY_ALLOC_GC_STACK_SIZE (128) +#define MICROPY_FATFS_EXFAT (1) + +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) +#define MICROPY_HW_HAS_SWITCH (1) +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_DAC (0) +#define MICROPY_HW_ENABLE_USB (1) +#define MICROPY_PY_PYB_LEGACY (0) + +#define MICROPY_BOARD_EARLY_INIT board_early_init +#define MICROPY_BOARD_LEAVE_STANDBY board_leave_standby() + +// HSE is 48MHz, this gives a CPU frequency of 800MHz. +#define MICROPY_HW_CLK_PLLM (6) +#define MICROPY_HW_CLK_PLLN (100) +#define MICROPY_HW_CLK_PLLP1 (1) +#define MICROPY_HW_CLK_PLLP2 (1) +#define MICROPY_HW_CLK_PLLFRAC (0) + +// The LSE is a 32kHz crystal. +#define MICROPY_HW_RTC_USE_LSE (1) +#define MICROPY_HW_RTC_USE_US (1) + +// External SPI flash, MX25UM51245GXDI00. +#define MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2 (29) + +// SPI flash, block device config. +#define MICROPY_HW_BDEV_SPIFLASH (&spi_bdev) +#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev) +#define MICROPY_HW_BDEV_SPIFLASH_CONFIG (&spiflash_config) +#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES (4 * 1024 * 1024) +#define MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES (60 * 1024 * 1024) + +// UART buses +#define MICROPY_HW_UART1_TX (pyb_pin_UART1_TX) +#define MICROPY_HW_UART1_RX (pyb_pin_UART1_RX) +#define MICROPY_HW_UART3_TX (pyb_pin_UART3_TX) +#define MICROPY_HW_UART3_RX (pyb_pin_UART3_RX) +#define MICROPY_HW_UART_REPL (PYB_UART_1) +#define MICROPY_HW_UART_REPL_BAUD (115200) + +// I2C buses +#define MICROPY_HW_I2C1_SCL (pyb_pin_I2C1_SCL) +#define MICROPY_HW_I2C1_SDA (pyb_pin_I2C1_SDA) + +// SPI buses +#define MICROPY_HW_SPI5_NSS (pyb_pin_SPI5_CS) +#define MICROPY_HW_SPI5_SCK (pyb_pin_SPI5_SCK) +#define MICROPY_HW_SPI5_MISO (pyb_pin_SPI5_MISO) +#define MICROPY_HW_SPI5_MOSI (pyb_pin_SPI5_MOSI) + +// USER2 is floating, and pressing the button makes the input go high. +#define MICROPY_HW_USRSW_PIN (pyb_pin_BUTTON) +#define MICROPY_HW_USRSW_PULL (GPIO_PULLDOWN) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_RISING) +#define MICROPY_HW_USRSW_PRESSED (1) + +// LEDs +#define MICROPY_HW_LED1 (pyb_pin_LED_RED) +#define MICROPY_HW_LED2 (pyb_pin_LED_GREEN) +#define MICROPY_HW_LED3 (pyb_pin_LED_BLUE) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) + +// USB config +#define MICROPY_HW_USB_HS (1) +#define MICROPY_HW_USB_HS_IN_FS (1) +#define MICROPY_HW_USB_MAIN_DEV (USB_PHY_HS_ID) + +/******************************************************************************/ +// Bootloader configuration + +#define MBOOT_BOARD_EARLY_INIT(initial_r0) mboot_board_early_init() + +#define MBOOT_SPIFLASH_CS (pyb_pin_XSPIM_P2_CS) +#define MBOOT_SPIFLASH_SCK (pyb_pin_XSPIM_P2_SCK) +#define MBOOT_SPIFLASH_MOSI (pyb_pin_XSPIM_P2_IO0) +#define MBOOT_SPIFLASH_MISO (pyb_pin_XSPIM_P2_IO1) +#define MBOOT_SPIFLASH_ADDR (0x70000000) +#define MBOOT_SPIFLASH_BYTE_SIZE (64 * 1024 * 1024) +#define MBOOT_SPIFLASH_LAYOUT "/0x70000000/16384*4Kg" +#define MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE (1) +#define MBOOT_SPIFLASH_SPIFLASH (&spi_bdev.spiflash) +#define MBOOT_SPIFLASH_CONFIG (&spiflash_config) + +/******************************************************************************/ +// Function and variable declarations + +extern const struct _mp_spiflash_config_t spiflash_config; +extern struct _spi_bdev_t spi_bdev; + +void mboot_board_early_init(void); +void mboot_board_entry_init(void); + +void board_early_init(void); +void board_leave_standby(void); diff --git a/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk new file mode 100644 index 0000000000..fa64cb1706 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk @@ -0,0 +1,26 @@ +# Without mboot, the main firmware must fit in 512k flash, will be copied to SRAM by +# the hardware bootloader, and will run from SRAM. With mboot, the main firmware can +# be much larger and will run from flash via XSPI in memory-mapped mode. +USE_MBOOT ?= 1 + +MCU_SERIES = n6 +CMSIS_MCU = STM32N657xx +AF_FILE = boards/stm32n657_af.csv +ifeq ($(BUILDING_MBOOT),1) +SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_fsbl.o +else +SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_s.o +endif +STM32_N6_HEADER_VERSION = 2.1 +DKEL = $(STM32_CUBE_PROGRAMMER)/bin/ExternalLoader/MX25UM51245G_STM32N6570-NUCLEO.stldr + +ifeq ($(USE_MBOOT),1) +LD_FILES = boards/stm32n657x0.ld boards/common_n6_flash.ld +TEXT0_ADDR = 0x70080000 +else +LD_FILES = boards/stm32n657x0.ld boards/common_basic.ld +TEXT0_ADDR = 0x34180400 +endif + +# MicroPython settings +MICROPY_FLOAT_IMPL = double diff --git a/ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h b/ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h new file mode 100644 index 0000000000..ac38dac748 --- /dev/null +++ b/ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h @@ -0,0 +1,5 @@ +// This board does not use any security settings, so can just stay in secure +// mode without configuring the SAU. + +static inline void TZ_SAU_Setup(void) { +} diff --git a/ports/stm32/boards/NUCLEO_N657X0/pins.csv b/ports/stm32/boards/NUCLEO_N657X0/pins.csv new file mode 100644 index 0000000000..033f0a552e --- /dev/null +++ b/ports/stm32/boards/NUCLEO_N657X0/pins.csv @@ -0,0 +1,62 @@ +D0,PD9 +D1,PD8 +D2,PD0 +D3,PE9 +D4,PE0 +D5,PE10 +D6,PD5 +D7,PE11 +D8,PD12 +D9,PD7 +D10,PA3 +D11,PG2 +D12,PG1 +D13,PE15 +D14,PC1 +D15,PH9 + +# Ax header pins are connected directly to the following digital IO +A0D,PF5 +A1D,PC10 +A2D,PF6 +A3D,PA2 +A4D,PC12 +A5D,PH2 + +# Ax header pins are connected to the following analog IO via an op-amp in voltage-follower mode running at 1.8V +A0,PA8 +A1,PA9 +A2,PA10 +A3,PA12 +A4,PF3 +A5,PG15 + +-UART1_TX,PE5 +-UART1_RX,PE6 +-UART3_TX,PD8 +-UART3_RX,PD9 + +-I2C1_SCL,PH9 +-I2C1_SDA,PC1 + +-SPI5_CS,PA3 +-SPI5_SCK,PE15 +-SPI5_MISO,PG1 +-SPI5_MOSI,PG2 + +-BUTTON,PC13 +LED_BLUE,PG8 +LED_RED,PG10 +LED_GREEN,PG0 + +-XSPIM_P2_DQS,PN0 +-XSPIM_P2_CS,PN1 +-XSPIM_P2_IO0,PN2 +-XSPIM_P2_IO1,PN3 +-XSPIM_P2_IO2,PN4 +-XSPIM_P2_IO3,PN5 +-XSPIM_P2_SCK,PN6 +-XSPIM_P2_IO4,PN8 +-XSPIM_P2_IO5,PN9 +-XSPIM_P2_IO6,PN10 +-XSPIM_P2_IO7,PN11 diff --git a/ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h b/ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h new file mode 100644 index 0000000000..4012d56e5a --- /dev/null +++ b/ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h @@ -0,0 +1,18 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2019 Damien P. George + */ +#ifndef MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H + +// Oscillator values in Hz +#define HSE_VALUE (48000000) +#define LSE_VALUE (32768) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#include "boards/stm32n6xx_hal_conf_base.h" + +#endif // MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H diff --git a/ports/stm32/boards/OLIMEX_E407/board.json b/ports/stm32/boards/OLIMEX_E407/board.json index c14755c564..408df5a320 100644 --- a/ports/stm32/boards/OLIMEX_E407/board.json +++ b/ports/stm32/boards/OLIMEX_E407/board.json @@ -8,8 +8,8 @@ "olimex_e407.jpg" ], "mcu": "stm32f4", - "product": "E407", + "product": "STM32-E407", "thumbnail": "", - "url": "", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/ARM/ST/STM32-E407", + "vendor": "Olimex" } diff --git a/ports/stm32/boards/OLIMEX_H407/board.json b/ports/stm32/boards/OLIMEX_H407/board.json index 9ecc860ddc..27d2940513 100644 --- a/ports/stm32/boards/OLIMEX_H407/board.json +++ b/ports/stm32/boards/OLIMEX_H407/board.json @@ -8,8 +8,8 @@ "olimex_h407.jpg" ], "mcu": "stm32f4", - "product": "H407", + "product": "STM32-H407", "thumbnail": "", - "url": "", - "vendor": "OLIMEX" + "url": "https://www.olimex.com/Products/ARM/ST/STM32-H407", + "vendor": "Olimex" } diff --git a/ports/stm32/boards/OPENMV_N6/bdev.c b/ports/stm32/boards/OPENMV_N6/bdev.c new file mode 100644 index 0000000000..c6c918bd67 --- /dev/null +++ b/ports/stm32/boards/OPENMV_N6/bdev.c @@ -0,0 +1,41 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2025 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 "storage.h" +#include "xspi.h" + +#if MICROPY_HW_SPIFLASH_ENABLE_CACHE +#error "Cannot enable MICROPY_HW_SPIFLASH_ENABLE_CACHE" +#endif + +// External SPI flash uses hardware XSPI interface. +const mp_spiflash_config_t spiflash_config = { + .bus_kind = MP_SPIFLASH_BUS_QSPI, + .bus.u_qspi.data = (void *)&xspi_flash2, + .bus.u_qspi.proto = &xspi_proto, +}; + +spi_bdev_t spi_bdev; diff --git a/ports/stm32/boards/OPENMV_N6/board.c b/ports/stm32/boards/OPENMV_N6/board.c new file mode 100644 index 0000000000..1f82d10bac --- /dev/null +++ b/ports/stm32/boards/OPENMV_N6/board.c @@ -0,0 +1,131 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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 "py/mphal.h" +#include "boardctrl.h" +#include "xspi.h" + +// Values for OTP fuses for VDDIO2/3, to select low voltage mode (<2.5V). +// See RM0486, Section 5, Table 18. +#define BSEC_HW_CONFIG_ID (124U) +#define BSEC_HWS_HSLV_VDDIO3 (1U << 15) +#define BSEC_HWS_HSLV_VDDIO2 (1U << 16) + +#define OMV_BOOT_MAGIC_ADDR (0x3401FFFCU) +#define OMV_BOOT_MAGIC_VALUE (0xB00710ADU) + +void mboot_board_early_init(void) { + // TODO: move some of the below code to a common location for all N6 boards? + + // Enable PWR, BSEC and SYSCFG clocks. + LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG); + + // Program high speed IO optimization fuses if they aren't already set. + uint32_t fuse; + BSEC_HandleTypeDef hbsec = { .Instance = BSEC }; + const uint32_t mask = BSEC_HWS_HSLV_VDDIO2 | BSEC_HWS_HSLV_VDDIO3; + if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) { + fuse = 0; + } else if ((fuse & mask) != mask) { + // Program the fuse, and read back the set value. + if (HAL_BSEC_OTP_Program(&hbsec, BSEC_HW_CONFIG_ID, fuse | mask, HAL_BSEC_NORMAL_PROG) != HAL_OK) { + fuse = 0; + } else if (HAL_BSEC_OTP_Read(&hbsec, BSEC_HW_CONFIG_ID, &fuse) != HAL_OK) { + fuse = 0; + } + } + + // Enable Vdd ADC, needed for the ADC to work. + LL_PWR_EnableVddADC(); + + // Configure VDDIO2. Only enable 1.8V mode if the fuse is set. + LL_PWR_EnableVddIO2(); + if (fuse & BSEC_HWS_HSLV_VDDIO2) { + LL_PWR_SetVddIO2VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_1V8); + } + SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation + + // Configure VDDIO3. Only enable 1.8V mode if the fuse is set. + LL_PWR_EnableVddIO3(); + if (fuse & BSEC_HWS_HSLV_VDDIO3) { + LL_PWR_SetVddIO3VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_1V8); + } + SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation + + // Configure VDDIO4. + LL_PWR_EnableVddIO4(); + LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3); + SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation + + // Enable VDD for ADC and USB. + LL_PWR_EnableVddADC(); + LL_PWR_EnableVddUSB(); + + // Enable XSPI in memory-mapped mode. + xspi_init(); +} + +void board_enter_bootloader(unsigned int n_args, const void *args) { + // Support both OpenMV bootloader and mboot. + *((uint32_t *)OMV_BOOT_MAGIC_ADDR) = OMV_BOOT_MAGIC_VALUE; + SCB_CleanDCache(); + boardctrl_maybe_enter_mboot(n_args, args); +} + +void board_early_init(void) { + // TODO: if (HAL_PWREx_ConfigSupply(PWR_EXTERNAL_SOURCE_SUPPLY ) != HAL_OK) + + LL_PWR_EnableWakeUpPin(LL_PWR_WAKEUP_PIN3 | LL_PWR_WAKEUP_PIN2); + LL_PWR_SetWakeUpPinPolarityLow(LL_PWR_WAKEUP_PIN3 | LL_PWR_WAKEUP_PIN2); +} + +void board_leave_standby(void) { + // TODO: move some of the below code to a common location for all N6 boards? + + // Enable PWR, BSEC and SYSCFG clocks. + LL_AHB4_GRP1_EnableClock(LL_AHB4_GRP1_PERIPH_PWR); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_BSEC); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG); + + // Configure VDDIO2 (1.8V mode selection is retained). + LL_PWR_EnableVddIO2(); + SYSCFG->VDDIO2CCCR |= SYSCFG_VDDIO2CCCR_EN; // enable IO compensation + + // Configure VDDIO3 (1.8V mode selection is retained). + LL_PWR_EnableVddIO3(); + SYSCFG->VDDIO3CCCR |= SYSCFG_VDDIO3CCCR_EN; // enable IO compensation + + // Configure VDDIO4. + LL_PWR_EnableVddIO4(); + LL_PWR_SetVddIO4VoltageRange(LL_PWR_VDDIO_VOLTAGE_RANGE_3V3); + SYSCFG->VDDIO4CCCR |= SYSCFG_VDDIO4CCCR_EN; // enable IO compensation + + // Enable VDD for ADC and USB. + LL_PWR_EnableVddADC(); + LL_PWR_EnableVddUSB(); +} diff --git a/ports/stm32/boards/OPENMV_N6/board.ld b/ports/stm32/boards/OPENMV_N6/board.ld new file mode 100644 index 0000000000..e9ded785f2 --- /dev/null +++ b/ports/stm32/boards/OPENMV_N6/board.ld @@ -0,0 +1,39 @@ +/* + Linker script for OPENMV_N6. + + Note: upper 512k of SRAM2 is copied from external flash upon reset. +*/ + +/* Specify the memory areas */ +MEMORY +{ + FLEXRAM_S (xrw) : ORIGIN = 0x34000000, LENGTH = 80K + SRAM2_S_RAM (xrw) : ORIGIN = 0x34100000, LENGTH = 1024K + SRAM2_S_FSBL (xrw) : ORIGIN = 0x34180400, LENGTH = 511K /* mboot firmware, not needed after mboot exits */ + EXT_FLASH (rx) : ORIGIN = 0x70080000, LENGTH = 3584K + EXT_FLASH_FS (rx) : ORIGIN = 0x70400000, LENGTH = 4M + EXT_FLASH_ROMFS (rx) : ORIGIN = 0x70800000, LENGTH = 24M +} + +REGION_ALIAS("IRAM", FLEXRAM_S); +REGION_ALIAS("RAM", SRAM2_S_RAM); +REGION_ALIAS("FLASH_APP", EXT_FLASH); + +/* produce a link error if there is not this amount of RAM for these sections */ +_minimum_stack_size = 2K; +_minimum_heap_size = 16K; + +/* Define the stack. The stack is full descending so begins just above last byte + of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ +_estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve; +_sstack = _estack - 16K; /* tunable */ + +/* RAM extents for the garbage collector */ +_ram_start = ORIGIN(RAM); +_ram_end = ORIGIN(RAM) + LENGTH(RAM); +_heap_start = _ebss; /* heap starts just after statically allocated memory */ +_heap_end = _sstack; + +/* ROMFS location */ +_micropy_hw_romfs_part0_start = ORIGIN(EXT_FLASH_ROMFS); +_micropy_hw_romfs_part0_size = LENGTH(EXT_FLASH_ROMFS); diff --git a/ports/stm32/boards/OPENMV_N6/manifest.py b/ports/stm32/boards/OPENMV_N6/manifest.py new file mode 100644 index 0000000000..62990220f3 --- /dev/null +++ b/ports/stm32/boards/OPENMV_N6/manifest.py @@ -0,0 +1,3 @@ +include("$(PORT_DIR)/boards/manifest.py") +require("bundle-networking") +require("aioble") diff --git a/ports/stm32/boards/OPENMV_N6/mpconfigboard.h b/ports/stm32/boards/OPENMV_N6/mpconfigboard.h new file mode 100644 index 0000000000..ed7bb548a1 --- /dev/null +++ b/ports/stm32/boards/OPENMV_N6/mpconfigboard.h @@ -0,0 +1,167 @@ +#define MICROPY_HW_BOARD_NAME "OpenMV N6" +#define MICROPY_HW_MCU_NAME "STM32N657X0" + +#define MICROPY_GC_STACK_ENTRY_TYPE uint32_t +#define MICROPY_ALLOC_GC_STACK_SIZE (128) +#define MICROPY_FATFS_EXFAT (1) + +#define MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE (0) +#define MICROPY_HW_HAS_SWITCH (0) +#define MICROPY_HW_HAS_FLASH (1) +#define MICROPY_HW_SDCARD_MOUNT_AT_BOOT (0) +#define MICROPY_HW_ENABLE_RNG (1) +#define MICROPY_HW_ENABLE_RTC (1) +#define MICROPY_HW_ENABLE_DAC (0) +#define MICROPY_HW_ENABLE_USB (1) +#define MICROPY_HW_ENABLE_SDCARD (1) +#define MICROPY_PY_PYB_LEGACY (0) + +#define MICROPY_BOARD_ENTER_BOOTLOADER board_enter_bootloader +#define MICROPY_BOARD_EARLY_INIT board_early_init +#define MICROPY_BOARD_LEAVE_STANDBY board_leave_standby() + +// HSE is 48MHz, this gives a CPU frequency of 800MHz. +#define MICROPY_HW_CLK_PLLM (6) +#define MICROPY_HW_CLK_PLLN (100) +#define MICROPY_HW_CLK_PLLP1 (1) +#define MICROPY_HW_CLK_PLLP2 (1) +#define MICROPY_HW_CLK_PLLFRAC (0) + +// The LSE is a 32kHz crystal. +#define MICROPY_HW_RTC_USE_LSE (1) +#define MICROPY_HW_RTC_USE_US (1) + +// External SPI flash. +#define MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2 (28) // 256Mbit + +// ROMFS config +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI (1) +#define MICROPY_HW_ROMFS_XSPI_SPIBDEV_OBJ (&spi_bdev) +#define MICROPY_HW_ROMFS_ENABLE_PART0 (1) + +// SPI flash, block device config. +#define MICROPY_HW_BDEV_SPIFLASH (&spi_bdev) +#define MICROPY_HW_BDEV_SPIFLASH_EXTENDED (&spi_bdev) +#define MICROPY_HW_BDEV_SPIFLASH_CONFIG (&spiflash_config) +#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES (4 * 1024 * 1024) +#define MICROPY_HW_BDEV_SPIFLASH_SIZE_BYTES (4 * 1024 * 1024) + +// UART buses +#define MICROPY_HW_UART2_TX (pyb_pin_BT_TXD) +#define MICROPY_HW_UART2_RX (pyb_pin_BT_RXD) +#define MICROPY_HW_UART2_RTS (pyb_pin_BT_RTS) +#define MICROPY_HW_UART2_CTS (pyb_pin_BT_CTS) +#define MICROPY_HW_UART3_TX (pyb_pin_UART3_TX) +#define MICROPY_HW_UART3_RX (pyb_pin_UART3_RX) +#define MICROPY_HW_UART4_TX (pyb_pin_UART4_TX) +#define MICROPY_HW_UART4_RX (pyb_pin_UART4_RX) +#define MICROPY_HW_UART7_TX (pyb_pin_UART7_TX) +#define MICROPY_HW_UART7_RX (pyb_pin_UART7_RX) + +// I2C buses +#define MICROPY_HW_I2C2_SCL (pyb_pin_I2C2_SCL) +#define MICROPY_HW_I2C2_SDA (pyb_pin_I2C2_SDA) +#define MICROPY_HW_I2C4_SCL (pyb_pin_I2C4_SCL) +#define MICROPY_HW_I2C4_SDA (pyb_pin_I2C4_SDA) + +// SPI buses +#define MICROPY_HW_SPI2_NSS (pyb_pin_SPI2_CS) +#define MICROPY_HW_SPI2_SCK (pyb_pin_SPI2_SCK) +#define MICROPY_HW_SPI2_MISO (pyb_pin_SPI2_MISO) +#define MICROPY_HW_SPI2_MOSI (pyb_pin_SPI2_MOSI) +#define MICROPY_HW_SPI4_NSS (pyb_pin_SPI4_CS) +#define MICROPY_HW_SPI4_SCK (pyb_pin_SPI4_SCK) +#define MICROPY_HW_SPI4_MISO (pyb_pin_SPI4_MISO) +#define MICROPY_HW_SPI4_MOSI (pyb_pin_SPI4_MOSI) + +// USER is pulled high, and pressing the button makes the input go low. +#define MICROPY_HW_USRSW_PIN (pyb_pin_BUTTON) +#define MICROPY_HW_USRSW_PULL (GPIO_NOPULL) +#define MICROPY_HW_USRSW_EXTI_MODE (GPIO_MODE_IT_FALLING) +#define MICROPY_HW_USRSW_PRESSED (0) + +// LEDs +#define MICROPY_HW_LED1 (pyb_pin_LED_RED) +#define MICROPY_HW_LED2 (pyb_pin_LED_GREEN) +#define MICROPY_HW_LED3 (pyb_pin_LED_BLUE) +#define MICROPY_HW_LED_ON(pin) (mp_hal_pin_low(pin)) +#define MICROPY_HW_LED_OFF(pin) (mp_hal_pin_high(pin)) + +// SD Card SDMMC +// SD_VSELECT: low(default)=3.3V IO, high=1.8V IO +// SD_RESET: drive low to turn off SD VCC (pulled high by default) +// SD_DETECT: pulled high in hardware, goes low when SD inserted +#define MICROPY_HW_SDCARD_SDMMC (1) +#define MICROPY_HW_SDCARD_CK (pyb_pin_SD_SDIO_CK) +#define MICROPY_HW_SDCARD_CMD (pyb_pin_SD_SDIO_CMD) +#define MICROPY_HW_SDCARD_D0 (pyb_pin_SD_SDIO_D0) +#define MICROPY_HW_SDCARD_D1 (pyb_pin_SD_SDIO_D1) +#define MICROPY_HW_SDCARD_D2 (pyb_pin_SD_SDIO_D2) +#define MICROPY_HW_SDCARD_D3 (pyb_pin_SD_SDIO_D3) +#define MICROPY_HW_SDCARD_DETECT_PIN (pyb_pin_SD_DETECT) +#define MICROPY_HW_SDCARD_DETECT_PULL (GPIO_NOPULL) +#define MICROPY_HW_SDCARD_DETECT_PRESENT (GPIO_PIN_RESET) + +// WiFi SDMMC +#define MICROPY_HW_SDIO_SDMMC (2) +#define MICROPY_HW_SDIO_CK (pyb_pin_WL_SDIO_CK) +#define MICROPY_HW_SDIO_CMD (pyb_pin_WL_SDIO_CMD) +#define MICROPY_HW_SDIO_D0 (pyb_pin_WL_SDIO_D0) +#define MICROPY_HW_SDIO_D1 (pyb_pin_WL_SDIO_D1) +#define MICROPY_HW_SDIO_D2 (pyb_pin_WL_SDIO_D2) +#define MICROPY_HW_SDIO_D3 (pyb_pin_WL_SDIO_D3) + +// USB config +#define MICROPY_HW_USB_HS (1) +#define MICROPY_HW_USB_HS_IN_FS (1) +#define MICROPY_HW_USB_MAIN_DEV (USB_PHY_HS_ID) +#define MICROPY_HW_USB_VID 0x37C5 +#define MICROPY_HW_USB_PID 0x1206 +#define MICROPY_HW_USB_PID_CDC (MICROPY_HW_USB_PID) +#define MICROPY_HW_USB_PID_MSC (MICROPY_HW_USB_PID) +#define MICROPY_HW_USB_PID_CDC_MSC (MICROPY_HW_USB_PID) +#define MICROPY_HW_USB_PID_CDC_HID (MICROPY_HW_USB_PID) +#define MICROPY_HW_USB_PID_CDC_MSC_HID (MICROPY_HW_USB_PID) + +// Murata 1YN configuration +#define CYW43_CHIPSET_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/w43439_sdio_1yn_7_95_59_combined.h" +#define CYW43_WIFI_NVRAM_INCLUDE_FILE "lib/cyw43-driver/firmware/wifi_nvram_1yn.h" +#define CYW43_BT_FIRMWARE_INCLUDE_FILE "lib/cyw43-driver/firmware/cyw43_btfw_1yn.h" + +// Bluetooth config +#define MICROPY_HW_BLE_UART_ID (PYB_UART_2) +#define MICROPY_HW_BLE_UART_BAUDRATE (115200) +#define MICROPY_HW_BLE_UART_BAUDRATE_SECONDARY (3000000) +#define MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE (3000000) + +/******************************************************************************/ +// Bootloader configuration + +#define MBOOT_BOARD_EARLY_INIT(initial_r0) mboot_board_early_init() + +#define MBOOT_FSLOAD (1) +#define MBOOT_VFS_FAT (1) + +#define MBOOT_SPIFLASH_CS (pyb_pin_XSPIM_P2_CS) +#define MBOOT_SPIFLASH_SCK (pyb_pin_XSPIM_P2_SCK) +#define MBOOT_SPIFLASH_MOSI (pyb_pin_XSPIM_P2_IO0) +#define MBOOT_SPIFLASH_MISO (pyb_pin_XSPIM_P2_IO1) +#define MBOOT_SPIFLASH_ADDR (0x70000000) +#define MBOOT_SPIFLASH_BYTE_SIZE (32 * 1024 * 1024) +#define MBOOT_SPIFLASH_LAYOUT "/0x70000000/8192*4Kg" +#define MBOOT_SPIFLASH_ERASE_BLOCKS_PER_PAGE (1) +#define MBOOT_SPIFLASH_SPIFLASH (&spi_bdev.spiflash) +#define MBOOT_SPIFLASH_CONFIG (&spiflash_config) + +/******************************************************************************/ +// Function and variable declarations + +extern const struct _mp_spiflash_config_t spiflash_config; +extern struct _spi_bdev_t spi_bdev; + +void mboot_board_early_init(void); +void mboot_board_entry_init(void); + +void board_enter_bootloader(unsigned int n_args, const void *args); +void board_early_init(void); +void board_leave_standby(void); diff --git a/ports/stm32/boards/OPENMV_N6/mpconfigboard.mk b/ports/stm32/boards/OPENMV_N6/mpconfigboard.mk new file mode 100644 index 0000000000..0283a486c1 --- /dev/null +++ b/ports/stm32/boards/OPENMV_N6/mpconfigboard.mk @@ -0,0 +1,30 @@ +# This board requires a bootloader, either mboot or OpenMV's bootloader. +USE_MBOOT = 1 + +MCU_SERIES = n6 +CMSIS_MCU = STM32N657xx +AF_FILE = boards/stm32n657_af.csv +ifeq ($(BUILDING_MBOOT),1) +SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_fsbl.o +else +SYSTEM_FILE = $(STM32LIB_CMSIS_BASE)/Source/Templates/system_stm32$(MCU_SERIES)xx_s.o +endif +STM32_N6_HEADER_VERSION = 2.3 +DKEL = $(STM32_CUBE_PROGRAMMER)/bin/ExternalLoader/MX25UM51245G_STM32N6570-NUCLEO.stldr + +LD_FILES = boards/OPENMV_N6/board.ld boards/common_n6_flash.ld +TEXT0_ADDR = 0x70080000 + +# MicroPython settings +MICROPY_FLOAT_IMPL = double +MICROPY_PY_BLUETOOTH ?= 1 +MICROPY_BLUETOOTH_NIMBLE ?= 1 +MICROPY_BLUETOOTH_BTSTACK ?= 0 +MICROPY_PY_LWIP ?= 1 +MICROPY_PY_NETWORK_CYW43 ?= 1 +MICROPY_PY_SSL ?= 1 +MICROPY_SSL_MBEDTLS ?= 1 +MICROPY_VFS_LFS2 ?= 1 + +# Board specific frozen modules +FROZEN_MANIFEST ?= $(BOARD_DIR)/manifest.py diff --git a/ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h b/ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h new file mode 100644 index 0000000000..ac38dac748 --- /dev/null +++ b/ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h @@ -0,0 +1,5 @@ +// This board does not use any security settings, so can just stay in secure +// mode without configuring the SAU. + +static inline void TZ_SAU_Setup(void) { +} diff --git a/ports/stm32/boards/OPENMV_N6/pins.csv b/ports/stm32/boards/OPENMV_N6/pins.csv new file mode 100644 index 0000000000..b05b8b57f9 --- /dev/null +++ b/ports/stm32/boards/OPENMV_N6/pins.csv @@ -0,0 +1,142 @@ +,PA0 +,PA1 +,PA2 +,PA3 +,PA4 +,PA5 +,PA6 +,PA7 +,PA8 +,PA9 +,PA10 +SPI2_CS,PA11 +SPI2_SCK,PA12 +UART4_RX,PA11 +UART4_TX,PA12 +P3,PA11 +P2,PA12 +,PA13 +,PA14 +,PA15 +,PB0 +,PB1 +,PB2 +,PB3 +,PB4 +,PB5 +SPI4_MISO,PB6 +SPI4_MOSI,PB7 +,PB8 +,PB9 +I2C2_SCL,PB10 +I2C2_SDA,PB11 +UART3_TX,PB10 +UART3_RX,PB11 +P4,PB10 +P5,PB11 +,PB12 +,PB13 +,PB14 +,PB15 +,PC0 +,PC1 +,PC2 +,PC3 +,PC4 +,PC5 +,PC6 +,PC7 +,PC8 +,PC9 +,PC10 +,PC11 +,PC12 +P11,PC13 +,PC14 +,PC15 +,PD0 +,PD1 +,PD2 +,PD3 +,PD4 +,PD5 +P10,PD6 +SPI2_MOSI,PD7 +P0,PD7 +,PD8 +,PD9 +,PD10 +SPI2_MISO,PD11 +P1,PD11 +,PD12 +P8,PD13 +,PD14 +,PD15 +,PE0 +,PE1 +,PE2 +,PE3 +,PE4 +,PE5 +,PE6 +UART7_RX,PE7 +UART7_TX,PE8 +,PE9 +,PE10 +SPI4_CS,PE11 +SPI4_SCK,PE12 +I2C4_SCL,PE13 +I2C4_SDA,PE14 +,PE15 +P6,PG0 +P9,PG12 +P7,PG13 +,PG15 + +BUTTON,PF4 +LED_RED,PG10 +LED_GREEN,PA7 +LED_BLUE,PB1 + +-XSPIM_P2_DQS,PN0 +-XSPIM_P2_CS,PN1 +-XSPIM_P2_IO0,PN2 +-XSPIM_P2_IO1,PN3 +-XSPIM_P2_IO2,PN4 +-XSPIM_P2_IO3,PN5 +-XSPIM_P2_SCK,PN6 +-XSPIM_P2_NCLK,PN7 +-XSPIM_P2_IO4,PN8 +-XSPIM_P2_IO5,PN9 +-XSPIM_P2_IO6,PN10 +-XSPIM_P2_IO7,PN11 +-FLASH_RESET,PN12 + +-WL_REG_ON,PB12 +-WL_HOST_WAKE,PB14 +-WL_SDIO_D0,PB8 +-WL_SDIO_D1,PG8 +-WL_SDIO_D2,PB9 +-WL_SDIO_D3,PB4 +-WL_SDIO_CMD,PA0 +-WL_SDIO_CK,PD2 +-WL_I2S_SDO,PG14 +-WL_I2S_WS,PB15 +-WL_I2S_SCLK,PB13 +-BT_RXD,PF6 +-BT_TXD,PD5 +-BT_CTS,PG5 +-BT_RTS,PF3 +-BT_REG_ON,PD10 +-BT_HOST_WAKE,PD14 +-BT_DEV_WAKE,PD15 + +-SD_SDIO_D0,PC8 +-SD_SDIO_D1,PC9 +-SD_SDIO_D2,PC10 +-SD_SDIO_D3,PC11 +-SD_SDIO_CK,PC12 +-SD_SDIO_CMD,PH2 +-SD_RESET,PC7 +-SD_DETECT,PC6 +-SD_VSELECT,PG6 diff --git a/ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h b/ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h new file mode 100644 index 0000000000..4012d56e5a --- /dev/null +++ b/ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h @@ -0,0 +1,18 @@ +/* This file is part of the MicroPython project, http://micropython.org/ + * The MIT License (MIT) + * Copyright (c) 2019 Damien P. George + */ +#ifndef MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H +#define MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H + +// Oscillator values in Hz +#define HSE_VALUE (48000000) +#define LSE_VALUE (32768) + +// Oscillator timeouts in ms +#define HSE_STARTUP_TIMEOUT (100) +#define LSE_STARTUP_TIMEOUT (5000) + +#include "boards/stm32n6xx_hal_conf_base.h" + +#endif // MICROPY_INCLUDED_STM32N6XX_HAL_CONF_H diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json index 0bd3573d64..5efd188618 100644 --- a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json +++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/board.json @@ -8,8 +8,8 @@ "sparkfun_micromod_stm32.jpg" ], "mcu": "stm32f4", - "product": "Micromod STM32", + "product": "MicroMod STM32", "thumbnail": "", - "url": "", - "vendor": "Sparkfun" + "url": "https://www.sparkfun.com/products/17713", + "vendor": "SparkFun" } diff --git a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h index d2f44cf6cc..1e17905bb0 100644 --- a/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h +++ b/ports/stm32/boards/SPARKFUN_MICROMOD_STM32/mpconfigboard.h @@ -1,4 +1,4 @@ -// The Sparkfun MicroMod spec uses a zero-based peripheral numbering scheme. +// The SparkFun MicroMod spec uses a zero-based peripheral numbering scheme. // In cases where the 0th peripheral is the default, the "0" is omitted from // the name (e.g. "I2C" instead of "I2C0"). // diff --git a/ports/stm32/boards/common_n6_flash.ld b/ports/stm32/boards/common_n6_flash.ld new file mode 100644 index 0000000000..a1f1fa531f --- /dev/null +++ b/ports/stm32/boards/common_n6_flash.ld @@ -0,0 +1,57 @@ +/* Memory layout for N6 when the application runs from external flash in XIP mode. + + FLASH_APP .isr_vector + FLASH_APP .text + FLASH_APP .data + + RAM .data + RAM .bss + RAM .heap + RAM .stack +*/ + +ENTRY(Reset_Handler) + +REGION_ALIAS("FLASH_COMMON", FLASH_APP); + +/* define output sections */ +SECTIONS +{ + .isr_vector : + { + _siram = .; + + /* This ISR is used for normal application mode. */ + . = ALIGN(1024); + KEEP(*(.isr_vector)); + + /* This ISR is used when waking from STANDBY. */ + . = ALIGN(1024); + KEEP(*(.rodata.iram_bootloader_isr_vector)); + + /* Need to place in RAM all the code necessary to write to + * flash, and to resume from STANDBY. */ + *(.*.iram_bootloader_reset); + *(.*.memcpy); + *(.*.mp_hal_gpio_clock_enable); + *(.*.mp_hal_pin_config); + *(.*.mp_hal_pin_config_speed); + *drivers/memory/spiflash.o(.text.* .rodata.*) + *xspi.o(.text.* .rodata.*); + *boards*(.rodata.spiflash_config*) + *boards*(.*.board_leave_standby); + *(*.rodata.pin_N*_obj); + *(.text.LL_AHB4_GRP1_EnableClock); + *(.text.LL_APB4_GRP2_EnableClock); + + . = ALIGN(4); + _eiram = .; + } >IRAM AT> FLASH_COMMON + + INCLUDE common_text.ld + INCLUDE common_extratext_data_in_flash.ld + INCLUDE common_bss_heap_stack.ld +} + +/* Used by the start-up code to initialise data */ +_siiram = LOADADDR(.isr_vector); diff --git a/ports/stm32/boards/common_text.ld b/ports/stm32/boards/common_text.ld index 16eea43bae..d95467babc 100644 --- a/ports/stm32/boards/common_text.ld +++ b/ports/stm32/boards/common_text.ld @@ -12,3 +12,11 @@ . = ALIGN(4); _etext = .; /* define a global symbol at end of code */ } >FLASH_COMMON + +/* Secure Gateway stubs */ +.gnu.sgstubs : +{ + . = ALIGN(4); + *(.gnu.sgstubs*) + . = ALIGN(4); +} >FLASH_COMMON diff --git a/ports/stm32/boards/make-pins.py b/ports/stm32/boards/make-pins.py index 1b89fd6415..6f8a0a659d 100755 --- a/ports/stm32/boards/make-pins.py +++ b/ports/stm32/boards/make-pins.py @@ -215,7 +215,7 @@ class Stm32Pin(boardgen.Pin): def validate_cpu_pin_name(cpu_pin_name): boardgen.Pin.validate_cpu_pin_name(cpu_pin_name) - if not re.match("P[A-K][0-9]+(_C)?$", cpu_pin_name): + if not re.match("P[A-O][0-9]+(_C)?$", cpu_pin_name): raise boardgen.PinGeneratorError("Invalid cpu pin name '{}'".format(cpu_pin_name)) diff --git a/ports/stm32/boards/pllvalues.py b/ports/stm32/boards/pllvalues.py index d8856bfecd..ae042d999c 100644 --- a/ports/stm32/boards/pllvalues.py +++ b/ports/stm32/boards/pllvalues.py @@ -293,7 +293,7 @@ def main(): break # Relax constraint on PLLQ being 48MHz on MCUs which have separate PLLs for 48MHz - relax_pll48 = mcu_series.startswith(("stm32f413", "stm32f7", "stm32h5", "stm32h7")) + relax_pll48 = mcu_series.startswith(("stm32f413", "stm32f7", "stm32h5", "stm32h7", "stm32n6")) hse_valid_plls = compute_pll_table(hse, relax_pll48) if hsi is not None: diff --git a/ports/stm32/boards/stm32g474_af.csv b/ports/stm32/boards/stm32g474_af.csv index 5853f5f900..59a0e91a14 100644 --- a/ports/stm32/boards/stm32g474_af.csv +++ b/ports/stm32/boards/stm32g474_af.csv @@ -2,12 +2,12 @@ Port ,Pin ,AF0 ,AF1 ,AF2 , ,I2C4/SYS_AF ,LPTIM1/TIM2/5/15/16/17,I2C1/3/TIM1/2/3/4/5/8/20/15/COMP1,QUADSPI1/I2C3/4/SAI1/USB/HRTIM1/TIM8/20/15/COMP3,I2C1/2/3/4/TIM1/8/16/17,QUADSPI1/SPI1/2/3/4/I2S2/3/I2C4/UART4/5/TIM8/Infrared,QUADSPI1/SPI2/3/I2S2/3/TIM1/5/8/20/Infrared,USART1/2/3/CAN/COMP7/5/6,I2C3/4/UART4/5/LPUART1/COMP1/2/7/4/5/6/3,CAN/TIM1/8/15/CAN1/2,QUADSPI1/TIM2/3/4/8/17,LPTIM1/TIM1/8/CAN1/3,FMC/LPUART1/SAI1/HRTIM1/TIM1,SAI1SAI1/HRTIM1/OPAMP2,UART4/5/SAI1/TIM2/15/UCPD1,SYS ,ADC PortA,PA0 , ,TIM2_CH1 ,TIM5_CH1 , , , , ,USART2_CTS ,COMP1_OUT ,TIM8_BKIN ,TIM8_ETR , , , ,TIM2_ETR ,EVENTOUT,ADC12_IN1 PortA,PA1 ,RTC_REFIN ,TIM2_CH2 ,TIM5_CH2 , , , , ,USART2_RTS_DE , ,TIM15_CH1N , , , , , ,EVENTOUT,ADC12_IN2 -PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3 , , , , ,USART2_TX ,COMP2_OUT ,TIM15_CH1 ,QUADSPI1_BK1_NCS , ,LPUART1_TX , ,UCPD1_FRSTX ,EVENTOUT,ADC1_IN3 -PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4 ,SAI1_CK1 , , , ,USART2_RX , ,TIM15_CH2 ,QUADSPI1_CLK , ,LPUART1_RX ,SAI1_MCLK_A , ,EVENTOUT,ADC1_IN4 +PortA,PA2 , ,TIM2_CH3 ,TIM5_CH3 , , , , ,USART2_TX ,COMP2_OUT ,TIM15_CH1 ,QUADSPI_BK1_NCS , ,LPUART1_TX , ,UCPD1_FRSTX ,EVENTOUT,ADC1_IN3 +PortA,PA3 , ,TIM2_CH4 ,TIM5_CH4 ,SAI1_CK1 , , , ,USART2_RX , ,TIM15_CH2 ,QUADSPI_CLK , ,LPUART1_RX ,SAI1_MCLK_A , ,EVENTOUT,ADC1_IN4 PortA,PA4 , , ,TIM3_CH2 , , ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_CK , , , , , ,SAI1_FS_B , ,EVENTOUT,ADC2_IN17 PortA,PA5 , ,TIM2_CH1 ,TIM2_ETR , , ,SPI1_SCK , , , , , , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN13 -PortA,PA6 , ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_BKIN ,SPI1_MISO ,TIM1_BKIN , ,COMP1_OUT , ,QUADSPI1_BK1_IO3 , ,LPUART1_CTS , , ,EVENTOUT,ADC2_IN3 -PortA,PA7 , ,TIM17_CH1 ,TIM3_CH2 , ,TIM8_CH1N ,SPI1_MOSI ,TIM1_CH1N , ,COMP2_OUT , ,QUADSPI1_BK1_IO2 , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN4 +PortA,PA6 , ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_BKIN ,SPI1_MISO ,TIM1_BKIN , ,COMP1_OUT , ,QUADSPI_BK1_IO3 , ,LPUART1_CTS , , ,EVENTOUT,ADC2_IN3 +PortA,PA7 , ,TIM17_CH1 ,TIM3_CH2 , ,TIM8_CH1N ,SPI1_MOSI ,TIM1_CH1N , ,COMP2_OUT , ,QUADSPI_BK1_IO2 , , , ,UCPD1_FRSTX ,EVENTOUT,ADC2_IN4 PortA,PA8 ,MCO , ,I2C3_SCL , ,I2C2_SDA ,I2S2_MCK ,TIM1_CH1 ,USART1_CK ,COMP7_OUT , ,TIM4_ETR ,CAN3_RX ,SAI1_CK2 ,HRTIM1_CHA1 ,SAI1_SCK_A ,EVENTOUT,ADC5_IN1 PortA,PA9 , , ,I2C3_SMBA , ,I2C2_SCL ,I2S3_MCK ,TIM1_CH2 ,USART1_TX ,COMP5_OUT ,TIM15_BKIN ,TIM2_CH3 , , ,HRTIM1_CHA2 ,SAI1_FS_A ,EVENTOUT,ADC5_IN2 PortA,PA10, ,TIM17_BKIN , ,USB_CRS_SYNC ,I2C2_SMBA ,SPI2_MISO ,TIM1_CH3 ,USART1_RX ,COMP6_OUT , ,TIM2_CH4 ,TIM8_BKIN ,SAI1_D1 ,HRTIM1_CHB1 ,SAI1_SD_A ,EVENTOUT, @@ -16,9 +16,9 @@ PortA,PA12, ,TIM16_CH1 , PortA,PA13,SWDIOJTMS ,TIM16_CH1N , ,I2C4_SCL ,I2C1_SCL ,IR_OUT , ,USART3_CTS , , ,TIM4_CH3 , , ,SAI1_SD_B , ,EVENTOUT, PortA,PA14,SWCLKJTCK ,LPTIM1_OUT , ,I2C4_SMBA ,I2C1_SDA ,TIM8_CH2 ,TIM1_BKIN ,USART2_TX , , , , , ,SAI1_FS_B , ,EVENTOUT, PortA,PA15,JTDI ,TIM2_CH1 ,TIM8_CH1 , ,I2C1_SCL ,SPI1_NSS ,SPI3_NSS/I2S3_WS ,USART2_RX ,UART4_RTS_DE ,TIM1_BKIN , ,CAN3_TX , ,HRTIM1_FLT2 ,TIM2_ETR ,EVENTOUT, -PortB,PB0 , , ,TIM3_CH3 , ,TIM8_CH2N , ,TIM1_CH2N , , , ,QUADSPI1_BK1_IO1 , , ,HRTIM1_FLT5 ,UCPD1_FRSTX ,EVENTOUT,ADC3_IN12/ADC1_IN15 -PortB,PB1 , , ,TIM3_CH4 , ,TIM8_CH3N , ,TIM1_CH3N , ,COMP4_OUT , ,QUADSPI1_BK1_IO0 , ,LPUART1_RTS_DE ,HRTIM1_SCOUT , ,EVENTOUT,ADC3_IN1/ADC1_IN12 -PortB,PB2 ,RTC_OUT2 ,LPTIM1_OUT ,TIM5_CH1 ,TIM20_CH1 ,I2C3_SMBA , , , , , ,QUADSPI1_BK2_IO1 , , ,HRTIM1_SCIN , ,EVENTOUT,ADC2_IN12 +PortB,PB0 , , ,TIM3_CH3 , ,TIM8_CH2N , ,TIM1_CH2N , , , ,QUADSPI_BK1_IO1 , , ,HRTIM1_FLT5 ,UCPD1_FRSTX ,EVENTOUT,ADC3_IN12/ADC1_IN15 +PortB,PB1 , , ,TIM3_CH4 , ,TIM8_CH3N , ,TIM1_CH3N , ,COMP4_OUT , ,QUADSPI_BK1_IO0 , ,LPUART1_RTS_DE ,HRTIM1_SCOUT , ,EVENTOUT,ADC3_IN1/ADC1_IN12 +PortB,PB2 ,RTC_OUT2 ,LPTIM1_OUT ,TIM5_CH1 ,TIM20_CH1 ,I2C3_SMBA , , , , , ,QUADSPI_BK2_IO1 , , ,HRTIM1_SCIN , ,EVENTOUT,ADC2_IN12 PortB,PB3 ,JTDOTRACESWO,TIM2_CH2 ,TIM4_ETR ,USB_CRS_SYNC ,TIM8_CH1N ,SPI1_SCK ,SPI3_SCK/I2S3_CK ,USART2_TX , , ,TIM3_ETR ,CAN3_RX ,HRTIM1_SCOUT ,HRTIM1_EEV9 ,SAI1_SCK_B ,EVENTOUT, PortB,PB4 ,JTRST ,TIM16_CH1 ,TIM3_CH1 , ,TIM8_CH2N ,SPI1_MISO ,SPI3_MISO ,USART2_RX ,UART5_RTS_DE , ,TIM17_BKIN ,CAN3_TX , ,HRTIM1_EEV7 ,SAI1_MCLK_B ,EVENTOUT, PortB,PB5 , ,TIM16_BKIN ,TIM3_CH2 ,TIM8_CH3N ,I2C1_SMBA ,SPI1_MOSI ,SPI3_MOSI/I2S3_SD ,USART2_CK ,I2C3_SDA ,CAN2_RX ,TIM17_CH1 ,LPTIM1_IN1 ,SAI1_SD_B ,HRTIM1_EEV6 ,UART5_CTS ,EVENTOUT, @@ -26,17 +26,17 @@ PortB,PB6 , ,TIM16_CH1N ,TIM4_CH1 PortB,PB7 , ,TIM17_CH1N ,TIM4_CH2 ,I2C4_SDA ,I2C1_SDA ,TIM8_BKIN , ,USART1_RX ,COMP3_OUT , ,TIM3_CH4 ,LPTIM1_IN2 ,FMC_NL ,HRTIM1_EEV3 ,UART4_CTS ,EVENTOUT, PortB,PB8 , ,TIM16_CH1 ,TIM4_CH3 ,SAI1_CK1 ,I2C1_SCL , , ,USART3_RX ,COMP1_OUT ,CAN1_RX ,TIM8_CH2 , ,TIM1_BKIN ,HRTIM1_EEV8 ,SAI1_MCLK_A ,EVENTOUT, PortB,PB9 , ,TIM17_CH1 ,TIM4_CH4 ,SAI1_D2 ,I2C1_SDA , ,IR_OUT ,USART3_TX ,COMP2_OUT ,CAN1_TX ,TIM8_CH3 , ,TIM1_CH3N ,HRTIM1_EEV5 ,SAI1_FS_A ,EVENTOUT, -PortB,PB10, ,TIM2_CH3 , , , , , ,USART3_TX ,LPUART1_RX , ,QUADSPI1_CLK , ,TIM1_BKIN ,HRTIM1_FLT3 ,SAI1_SCK_A ,EVENTOUT, -PortB,PB11, ,TIM2_CH4 , , , , , ,USART3_RX ,LPUART1_TX , ,QUADSPI1_BK1_NCS , , ,HRTIM1_FLT4 , ,EVENTOUT,ADC12_IN14 +PortB,PB10, ,TIM2_CH3 , , , , , ,USART3_TX ,LPUART1_RX , ,QUADSPI_CLK , ,TIM1_BKIN ,HRTIM1_FLT3 ,SAI1_SCK_A ,EVENTOUT, +PortB,PB11, ,TIM2_CH4 , , , , , ,USART3_RX ,LPUART1_TX , ,QUADSPI_BK1_NCS , , ,HRTIM1_FLT4 , ,EVENTOUT,ADC12_IN14 PortB,PB12, , ,TIM5_ETR , ,I2C2_SMBA ,SPI2_NSS/I2S2_WS ,TIM1_BKIN ,USART3_CK ,LPUART1_RTS_DE ,CAN2_RX , , , ,HRTIM1_CHC1 , ,EVENTOUT,ADC4_IN3/ADC1_IN11 PortB,PB13, , , , , ,SPI2_SCK/I2S2_CK ,TIM1_CH1N ,USART3_CTS ,LPUART1_CTS ,CAN2_TX , , , ,HRTIM1_CHC2 , ,EVENTOUT,ADC3_IN5 PortB,PB14, ,TIM15_CH1 , , , ,SPI2_MISO ,TIM1_CH2N ,USART3_RTS_DE ,COMP4_OUT , , , , ,HRTIM1_CHD1 , ,EVENTOUT,ADC4_IN4/ADC1_IN5 PortB,PB15,RTC_REFIN ,TIM15_CH2 ,TIM15_CH1N ,COMP3_OUT ,TIM1_CH3N ,SPI2_MOSI/I2S2_SD , , , , , , , ,HRTIM1_CHD2 , ,EVENTOUT,ADC4_IN5/ADC2_IN15 PortC,PC0 , ,LPTIM1_IN1 ,TIM1_CH1 , , , , , ,LPUART1_RX , , , , , , ,EVENTOUT,ADC12_IN6 -PortC,PC1 , ,LPTIM1_OUT ,TIM1_CH2 , , , , , ,LPUART1_TX , ,QUADSPI1_BK2_IO0 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN7 -PortC,PC2 , ,LPTIM1_IN2 ,TIM1_CH3 ,COMP3_OUT , , ,TIM20_CH2 , , , ,QUADSPI1_BK2_IO1 , , , , ,EVENTOUT,ADC12_IN8 -PortC,PC3 , ,LPTIM1_ETR ,TIM1_CH4 ,SAI1_D1 , , ,TIM1_BKIN2 , , , ,QUADSPI1_BK2_IO2 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN9 -PortC,PC4 , , ,TIM1_ETR , ,I2C2_SCL , , ,USART1_TX , , ,QUADSPI1_BK2_IO3 , , , , ,EVENTOUT,ADC2_IN5 +PortC,PC1 , ,LPTIM1_OUT ,TIM1_CH2 , , , , , ,LPUART1_TX , ,QUADSPI_BK2_IO0 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN7 +PortC,PC2 , ,LPTIM1_IN2 ,TIM1_CH3 ,COMP3_OUT , , ,TIM20_CH2 , , , ,QUADSPI_BK2_IO1 , , , , ,EVENTOUT,ADC12_IN8 +PortC,PC3 , ,LPTIM1_ETR ,TIM1_CH4 ,SAI1_D1 , , ,TIM1_BKIN2 , , , ,QUADSPI_BK2_IO2 , , ,SAI1_SD_A , ,EVENTOUT,ADC12_IN9 +PortC,PC4 , , ,TIM1_ETR , ,I2C2_SCL , , ,USART1_TX , , ,QUADSPI_BK2_IO3 , , , , ,EVENTOUT,ADC2_IN5 PortC,PC5 , , ,TIM15_BKIN ,SAI1_D3 , , ,TIM1_CH4N ,USART1_RX , , , , , ,HRTIM1_EEV10 , ,EVENTOUT,ADC2_IN11 PortC,PC6 , , ,TIM3_CH1 ,HRTIM1_EEV10 ,TIM8_CH1 , ,I2S2_MCK ,COMP6_OUT ,I2C4_SCL , , , , ,HRTIM1_CHF1 , ,EVENTOUT, PortC,PC7 , , ,TIM3_CH2 ,HRTIM1_FLT5 ,TIM8_CH2 , ,I2S3_MCK ,COMP5_OUT ,I2C4_SDA , , , , ,HRTIM1_CHF2 , ,EVENTOUT, @@ -51,11 +51,11 @@ PortC,PC15, , , PortD,PD0 , , , , , , ,TIM8_CH4N , , ,CAN1_RX , , ,FMC_D2 , , ,EVENTOUT, PortD,PD1 , , , , ,TIM8_CH4 , ,TIM8_BKIN2 , , ,CAN1_TX , , ,FMC_D3 , , ,EVENTOUT, PortD,PD2 , , ,TIM3_ETR , ,TIM8_BKIN ,UART5_RX , , , , , , , , , ,EVENTOUT, -PortD,PD3 , , ,TIM2_CH1/TIM2_ETR , , , , ,USART2_CTS , , ,QUADSPI1_BK2_NCS , ,FMC_CLK , , ,EVENTOUT, -PortD,PD4 , , ,TIM2_CH2 , , , , ,USART2_RTS_DE , , ,QUADSPI1_BK2_IO0 , ,FMC_NOE , , ,EVENTOUT, -PortD,PD5 , , , , , , , ,USART2_TX , , ,QUADSPI1_BK2_IO1 , ,FMC_NWE , , ,EVENTOUT, -PortD,PD6 , , ,TIM2_CH4 ,SAI1_D1 , , , ,USART2_RX , , ,QUADSPI1_BK2_IO2 , ,FMC_NWAIT ,SAI1_SD_A , ,EVENTOUT, -PortD,PD7 , , ,TIM2_CH3 , , , , ,USART2_CK , , ,QUADSPI1_BK2_IO3 , ,FMC_NCE/FMC_NE1 , , ,EVENTOUT, +PortD,PD3 , , ,TIM2_CH1/TIM2_ETR , , , , ,USART2_CTS , , ,QUADSPI_BK2_NCS , ,FMC_CLK , , ,EVENTOUT, +PortD,PD4 , , ,TIM2_CH2 , , , , ,USART2_RTS_DE , , ,QUADSPI_BK2_IO0 , ,FMC_NOE , , ,EVENTOUT, +PortD,PD5 , , , , , , , ,USART2_TX , , ,QUADSPI_BK2_IO1 , ,FMC_NWE , , ,EVENTOUT, +PortD,PD6 , , ,TIM2_CH4 ,SAI1_D1 , , , ,USART2_RX , , ,QUADSPI_BK2_IO2 , ,FMC_NWAIT ,SAI1_SD_A , ,EVENTOUT, +PortD,PD7 , , ,TIM2_CH3 , , , , ,USART2_CK , , ,QUADSPI_BK2_IO3 , ,FMC_NCE/FMC_NE1 , , ,EVENTOUT, PortD,PD8 , , , , , , , ,USART3_TX , , , , ,FMC_D13 , , ,EVENTOUT,ADC4_IN12/ADC5_IN12 PortD,PD9 , , , , , , , ,USART3_RX , , , , ,FMC_D14 , , ,EVENTOUT,ADC4_IN13/ADC5_IN13 PortD,PD10, , , , , , , ,USART3_CK , , , , ,FMC_D15 , , ,EVENTOUT,ADC345_IN7 @@ -74,23 +74,23 @@ PortE,PE6 ,TRACED3 , , PortE,PE7 , , ,TIM1_ETR , , , , , , , , , ,FMC_D4 ,SAI1_SD_B , ,EVENTOUT,ADC3_IN4 PortE,PE8 , ,TIM5_CH3 ,TIM1_CH1N , , , , , , , , , ,FMC_D5 ,SAI1_SCK_B , ,EVENTOUT,ADC345_IN6 PortE,PE9 , ,TIM5_CH4 ,TIM1_CH1 , , , , , , , , , ,FMC_D6 ,SAI1_FS_B , ,EVENTOUT,ADC3_IN2 -PortE,PE10, , ,TIM1_CH2N , , , , , , , ,QUADSPI1_CLK , ,FMC_D7 ,SAI1_MCLK_B , ,EVENTOUT,ADC345_IN14 -PortE,PE11, , ,TIM1_CH2 , , ,SPI4_NSS , , , , ,QUADSPI1_BK1_NCS , ,FMC_D8 , , ,EVENTOUT,ADC345_IN15 -PortE,PE12, , ,TIM1_CH3N , , ,SPI4_SCK , , , , ,QUADSPI1_BK1_IO0 , ,FMC_D9 , , ,EVENTOUT,ADC345_IN16 -PortE,PE13, , ,TIM1_CH3 , , ,SPI4_MISO , , , , ,QUADSPI1_BK1_IO1 , ,FMC_D10 , , ,EVENTOUT,ADC3_IN3 -PortE,PE14, , ,TIM1_CH4 , , ,SPI4_MOSI ,TIM1_BKIN2 , , , ,QUADSPI1_BK1_IO2 , ,FMC_D11 , , ,EVENTOUT,ADC4_IN1 -PortE,PE15, , ,TIM1_BKIN , , , ,TIM1_CH4N ,USART3_RX , , ,QUADSPI1_BK1_IO3 , ,FMC_D12 , , ,EVENTOUT,ADC4_IN2 +PortE,PE10, , ,TIM1_CH2N , , , , , , , ,QUADSPI_CLK , ,FMC_D7 ,SAI1_MCLK_B , ,EVENTOUT,ADC345_IN14 +PortE,PE11, , ,TIM1_CH2 , , ,SPI4_NSS , , , , ,QUADSPI_BK1_NCS , ,FMC_D8 , , ,EVENTOUT,ADC345_IN15 +PortE,PE12, , ,TIM1_CH3N , , ,SPI4_SCK , , , , ,QUADSPI_BK1_IO0 , ,FMC_D9 , , ,EVENTOUT,ADC345_IN16 +PortE,PE13, , ,TIM1_CH3 , , ,SPI4_MISO , , , , ,QUADSPI_BK1_IO1 , ,FMC_D10 , , ,EVENTOUT,ADC3_IN3 +PortE,PE14, , ,TIM1_CH4 , , ,SPI4_MOSI ,TIM1_BKIN2 , , , ,QUADSPI_BK1_IO2 , ,FMC_D11 , , ,EVENTOUT,ADC4_IN1 +PortE,PE15, , ,TIM1_BKIN , , , ,TIM1_CH4N ,USART3_RX , , ,QUADSPI_BK1_IO3 , ,FMC_D12 , , ,EVENTOUT,ADC4_IN2 PortF,PF0 , , , , ,I2C2_SDA ,SPI2_NSS/I2S2_WS ,TIM1_CH3N , , , , , , , , ,EVENTOUT,ADC1_IN10 PortF,PF1 , , , , , ,SPI2_SCK/I2S2_CK , , , , , , , , , ,EVENTOUT,ADC2_IN10 PortF,PF2 , , ,TIM20_CH3 , ,I2C2_SMBA , , , , , , , ,FMC_A2 , , ,EVENTOUT, PortF,PF3 , , ,TIM20_CH4 , ,I2C3_SCL , , , , , , , ,FMC_A3 , , ,EVENTOUT, PortF,PF4 , , ,COMP1_OUT ,TIM20_CH1N ,I2C3_SDA , , , , , , , ,FMC_A4 , , ,EVENTOUT, PortF,PF5 , , ,TIM20_CH2N , , , , , , , , , ,FMC_A5 , , ,EVENTOUT, -PortF,PF6 , ,TIM5_ETR ,TIM4_CH4 ,SAI1_SD_B ,I2C2_SCL , ,TIM5_CH1 ,USART3_RTS , , ,QUADSPI1_BK1_IO3 , , , , ,EVENTOUT, -PortF,PF7 , , ,TIM20_BKIN , , , ,TIM5_CH2 , , , ,QUADSPI1_BK1_IO2 , ,FMC_A1 ,SAI1_MCLK_B , ,EVENTOUT, -PortF,PF8 , , ,TIM20_BKIN2 , , , ,TIM5_CH3 , , , ,QUADSPI1_BK1_IO0 , ,FMC_A24 ,SAI1_SCK_B , ,EVENTOUT, -PortF,PF9 , , ,TIM20_BKIN ,TIM15_CH1 , ,SPI2_SCK ,TIM5_CH4 , , , ,QUADSPI1_BK1_IO1 , ,FMC_A25 ,SAI1_FS_B , ,EVENTOUT, -PortF,PF10, , ,TIM20_BKIN2 ,TIM15_CH2 , ,SPI2_SCK , , , , ,QUADSPI1_CLK , ,FMC_A0 ,SAI1_D3 , ,EVENTOUT, +PortF,PF6 , ,TIM5_ETR ,TIM4_CH4 ,SAI1_SD_B ,I2C2_SCL , ,TIM5_CH1 ,USART3_RTS , , ,QUADSPI_BK1_IO3 , , , , ,EVENTOUT, +PortF,PF7 , , ,TIM20_BKIN , , , ,TIM5_CH2 , , , ,QUADSPI_BK1_IO2 , ,FMC_A1 ,SAI1_MCLK_B , ,EVENTOUT, +PortF,PF8 , , ,TIM20_BKIN2 , , , ,TIM5_CH3 , , , ,QUADSPI_BK1_IO0 , ,FMC_A24 ,SAI1_SCK_B , ,EVENTOUT, +PortF,PF9 , , ,TIM20_BKIN ,TIM15_CH1 , ,SPI2_SCK ,TIM5_CH4 , , , ,QUADSPI_BK1_IO1 , ,FMC_A25 ,SAI1_FS_B , ,EVENTOUT, +PortF,PF10, , ,TIM20_BKIN2 ,TIM15_CH2 , ,SPI2_SCK , , , , ,QUADSPI_CLK , ,FMC_A0 ,SAI1_D3 , ,EVENTOUT, PortF,PF11, , ,TIM20_ETR , , , , , , , , , ,FMC_NE4 , , ,EVENTOUT, PortF,PF12, , ,TIM20_CH1 , , , , , , , , , ,FMC_A6 , , ,EVENTOUT, PortF,PF13, , ,TIM20_CH2 , ,I2C4_SMBA , , , , , , , ,FMC_A7 , , ,EVENTOUT, diff --git a/ports/stm32/boards/stm32h7xx_hal_conf_base.h b/ports/stm32/boards/stm32h7xx_hal_conf_base.h index 670dee383f..1953ba020b 100644 --- a/ports/stm32/boards/stm32h7xx_hal_conf_base.h +++ b/ports/stm32/boards/stm32h7xx_hal_conf_base.h @@ -90,6 +90,7 @@ #include "stm32h7xx_hal_spi.h" #include "stm32h7xx_hal_tim.h" #include "stm32h7xx_hal_uart.h" +#include "stm32h7xx_hal_uart_ex.h" #include "stm32h7xx_hal_usart.h" #include "stm32h7xx_hal_wwdg.h" #include "stm32h7xx_ll_adc.h" diff --git a/ports/stm32/boards/stm32n657_af.csv b/ports/stm32/boards/stm32n657_af.csv new file mode 100644 index 0000000000..35e305a376 --- /dev/null +++ b/ports/stm32/boards/stm32n657_af.csv @@ -0,0 +1,42 @@ +Port ,Pin ,AF0 ,AF1 ,AF2 ,AF3 ,AF4 ,AF5 ,AF6 ,AF7 ,AF8 ,AF9 ,AF10 ,AF11 ,AF12 ,AF13 ,AF14 ,AF15 ,ADC + , ,SYS ,LPTIM1/TIM1/2/16/17,LPTIM3/PDM_SAI1/TIM3/4/5/12/15,I3C1/LPTIM2/3/LPUART1/OCTOSPI/TIM1/8,CEC/DCMI/I2C1/2/3/4/LPTIM1/2/SPI1/I2S1/TIM15/USART1,CEC/I3C1/LPTIM1/SPI1/I2S1/SPI2/I2S2/SPI3/I2S3/SPI4/5/6,I2C4/OCTOSPI/SAI1/SPI3/I2S3/SPI4/UART4/12/USART10/USB_PD,SDMMC1/SPI2/I2S2/SPI3/I2S3/SPI6/UART7/8/12/USART1/2/3/6/10/11,LPUART1/SAI2/SDMMC1/SPI6/UART4/5/8,FDCAN1/2/FMC[NAND16]/FMC[NORmux]/FMC[NOR_RAM]/OCTOSPI/SDMMC2/TIM13/14,CRS/FMC[NAND16]/OCTOSPI/SAI2/SDMMC2/TIM8/USB_,ETH[MII/RMII]/FMC[NAND16]/OCTOSPI/SDMMC2/UART7/9/USB_PD,FMC[NAND16]/FMC[NORmux]/FMC[NOR_RAM]/FMC[SDRAM_16bit]/SDMMC1,DCMI/FMC[NAND16]/FMC[NORmux]/FMC[NOR_RAM]/LPTIM5,LPTIM3/4/5/6/TIM2/UART5,SYS ,ADC +PortA,PA0 , , , , , , , , , , , ,SDMMC2_CMD , , , , ,ADC12_INP0/ADC12_INN1 +PortA,PA3 , , , , , ,SPI5_NSS , , , , , , , , , , , +PortA,PA5 , , , , , , , , , , , , , , , , ,ADC2_INP18 +PortA,PA8 , , , , , , , , , , , , , , , , ,ADC12_INP5 +PortA,PA9 , , , , , , , , , , , , , , , , ,ADC12_INP10 +PortA,PA10, , , , , , , , , , , , , , , , ,ADC12_INP11/ADC12_INN10 +PortA,PA11, , , , , , , , ,UART4_RX , , , , , , , ,ADC12_INP12/ADC12_INN11 +PortA,PA12, , , , , , , , ,UART4_TX , , , , , , , ,ADC12_INP13/ADC12_INN12 +PortB,PB4 , , , , , , , , , , , ,SDMMC2_D3 , , , , , +PortB,PB8 , , , , , , , , , , , ,SDMMC2_D0 , , , , , +PortB,PB9 , , , , , , , , , , , ,SDMMC2_D2 , , , , , +PortB,PB10, ,TIM2_CH3 , , , , , ,USART3_TX , , , , , , , , , +PortB,PB11, ,TIM2_CH4 , , , , , ,USART3_RX , , , , , , , , , +PortC,PC8 , , , , , , , , , , ,SDMMC1_D0 , , , , , , +PortC,PC9 , , , , , , , , , , ,SDMMC1_D1 , , , , , , +PortC,PC10, , , , , , , , , , ,SDMMC1_D2 , , , , , , +PortC,PC11, , , , , , , , , , ,SDMMC1_D3 , , , , , , +PortC,PC12, , , , , , , , , , ,SDMMC1_CK , , , , , , +PortD,PD2 , , , , , , , , , , , ,SDMMC2_CK , , , , , +PortD,PD5 , , , , , , , ,USART2_TX , , , , , , , , , +PortD,PD6 , , , , ,TIM15_CH2 , , , , , , , , , , , , +PortD,PD8 , , , , , , , ,USART3_TX , , , , , , , , , +PortD,PD9 , , , , , , , ,USART3_RX , , , , , , , , , +PortD,PD13, , ,TIM4_CH2 , , , , , , , , , , , , , , +PortE,PE5 , , , , , , , ,USART1_TX , , , , , , , , , +PortE,PE6 , , , , , , , ,USART1_RX , , , , , , , , , +PortE,PE7 , , , , , , , , ,UART7_RX , , , , , , , , +PortE,PE8 , , , , , , , , ,UART7_TX , , , , , , , , +PortE,PE15, , , , , ,SPI5_SCK , , , , , , , , , , , +PortF,PF3 , , , , , , , ,USART2_RTS , , , , , , , , ,ADC1_INP16 +PortF,PF6 , , , , , , , ,USART2_RX , , , , , , , , , +PortG,PG0 , , ,TIM12_CH1 , , , , , , , , , , , , , , +PortG,PG1 , , , , , ,SPI5_MISO , , , , , , , , , , , +PortG,PG2 , , , , , ,SPI5_MOSI , , , , , , , , , , , +PortG,PG5 , , , , , , , ,USART2_CTS , , , , , , , , , +PortG,PG8 , , , , , , , , , , , ,SDMMC2_D1 , , , , , +PortG,PG12, ,TIM17_CH1 , , , , , , , , , , , , , , , +PortG,PG13, , ,TIM4_CH1 , , , , , , , , , , , , , , +PortG,PG15, , , , , , , , , , , , , , , , ,ADC12_INP7/ADC12_INN3 +PortC,PH2 , , , , , , , , , , ,SDMMC1_CMD , , , , , , diff --git a/ports/stm32/boards/stm32n657x0.ld b/ports/stm32/boards/stm32n657x0.ld new file mode 100644 index 0000000000..242d113b30 --- /dev/null +++ b/ports/stm32/boards/stm32n657x0.ld @@ -0,0 +1,34 @@ +/* + GNU linker script for STM32N657x0 + + Note: upper 512k of SRAM2 is copied from external flash upon reset. +*/ + +/* Specify the memory areas */ +MEMORY +{ + FLEXRAM_S (xrw) : ORIGIN = 0x34000000, LENGTH = 80K + SRAM2_S_RAM (xrw) : ORIGIN = 0x34100000, LENGTH = 512K /* only use first half, second half may contain firmware */ + SRAM2_S_FSBL (xrw) : ORIGIN = 0x34180400, LENGTH = 511K /* firmware loaded from SPI flash upon reset */ + EXT_FLASH (rx) : ORIGIN = 0x70080000, LENGTH = 1536K +} + +REGION_ALIAS("IRAM", FLEXRAM_S); +REGION_ALIAS("RAM", SRAM2_S_RAM); +REGION_ALIAS("FLASH", SRAM2_S_FSBL); +REGION_ALIAS("FLASH_APP", EXT_FLASH); + +/* produce a link error if there is not this amount of RAM for these sections */ +_minimum_stack_size = 2K; +_minimum_heap_size = 16K; + +/* Define the stack. The stack is full descending so begins just above last byte + of RAM. Note that EABI requires the stack to be 8-byte aligned for a call. */ +_estack = ORIGIN(RAM) + LENGTH(RAM) - _estack_reserve; +_sstack = _estack - 16K; /* tunable */ + +/* RAM extents for the garbage collector */ +_ram_start = ORIGIN(RAM); +_ram_end = ORIGIN(RAM) + LENGTH(RAM); +_heap_start = _ebss; /* heap starts just after statically allocated memory */ +_heap_end = _sstack; diff --git a/ports/stm32/boards/stm32n6xx_hal_conf_base.h b/ports/stm32/boards/stm32n6xx_hal_conf_base.h new file mode 100644 index 0000000000..641a003d8b --- /dev/null +++ b/ports/stm32/boards/stm32n6xx_hal_conf_base.h @@ -0,0 +1,215 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024 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. + */ +#ifndef MICROPY_INCLUDED_STM32N6XX_HAL_CONF_BASE_H +#define MICROPY_INCLUDED_STM32N6XX_HAL_CONF_BASE_H + +// Enable various HAL modules +#define HAL_MODULE_ENABLED +#define HAL_ADC_MODULE_ENABLED +#define HAL_BSEC_MODULE_ENABLED +#define HAL_CACHEAXI_MODULE_ENABLED +#define HAL_CORTEX_MODULE_ENABLED +#define HAL_CRC_MODULE_ENABLED +#define HAL_CRYP_MODULE_ENABLED +#define HAL_CSI_MODULE_ENABLED +#define HAL_DCMI_MODULE_ENABLED +#define HAL_DCMIPP_MODULE_ENABLED +#define HAL_DMA_MODULE_ENABLED +#define HAL_DMA2D_MODULE_ENABLED +#define HAL_DTS_MODULE_ENABLED +#define HAL_ETH_MODULE_ENABLED +#define HAL_EXTI_MODULE_ENABLED +#define HAL_FDCAN_MODULE_ENABLED +#define HAL_GFXMMU_MODULE_ENABLED +#define HAL_GFXTIM_MODULE_ENABLED +#define HAL_GPIO_MODULE_ENABLED +#define HAL_GPU2D_MODULE_ENABLED +#define HAL_HASH_MODULE_ENABLED +#define HAL_HCD_MODULE_ENABLED +#define HAL_I2C_MODULE_ENABLED +#define HAL_I3C_MODULE_ENABLED +#define HAL_ICACHE_MODULE_ENABLED +#define HAL_IRDA_MODULE_ENABLED +#define HAL_IWDG_MODULE_ENABLED +#define HAL_JPEG_MODULE_ENABLED +#define HAL_LPTIM_MODULE_ENABLED +#define HAL_LTDC_MODULE_ENABLED +#define HAL_MCE_MODULE_ENABLED +#define HAL_MDF_MODULE_ENABLED +#define HAL_MDIOS_MODULE_ENABLED +#define HAL_MMC_MODULE_ENABLED +#define HAL_NAND_MODULE_ENABLED +#define HAL_NOR_MODULE_ENABLED +#define HAL_PCD_MODULE_ENABLED +#define HAL_PKA_MODULE_ENABLED +#define HAL_PSSI_MODULE_ENABLED +#define HAL_PWR_MODULE_ENABLED +#define HAL_RAMCFG_MODULE_ENABLED +#define HAL_RCC_MODULE_ENABLED +#define HAL_RIF_MODULE_ENABLED +#define HAL_RNG_MODULE_ENABLED +#define HAL_RTC_MODULE_ENABLED +#define HAL_SAI_MODULE_ENABLED +#define HAL_SD_MODULE_ENABLED +#define HAL_SDRAM_MODULE_ENABLED +#define HAL_SMARTCARD_MODULE_ENABLED +#define HAL_SMBUS_MODULE_ENABLED +#define HAL_SPDIFRX_MODULE_ENABLED +#define HAL_SPI_MODULE_ENABLED +#define HAL_SRAM_MODULE_ENABLED +#define HAL_TIM_MODULE_ENABLED +#define HAL_UART_MODULE_ENABLED +#define HAL_USART_MODULE_ENABLED +#define HAL_WWDG_MODULE_ENABLED +#define HAL_XSPI_MODULE_ENABLED + +// Oscillator values in Hz +#define HSI_VALUE (64000000UL) +#define LSI_VALUE (32000UL) +#define MSI_VALUE (4000000UL) + +// SysTick has the highest priority +#define TICK_INT_PRIORITY (0x00) + +// Miscellaneous HAL settings +#define VDD_VALUE 3300UL +#define USE_RTOS 0 +#define USE_SD_TRANSCEIVER 0 +#define USE_SPI_CRC 1 + +// Disable dynamic callback registration +#define USE_HAL_ADC_REGISTER_CALLBACKS 0U /* ADC register callback disabled */ +#define USE_HAL_CACHEAXI_REGISTER_CALLBACKS 0U /* CACHEAXI register callback disabled */ +#define USE_HAL_CRYP_REGISTER_CALLBACKS 0U /* CRYP register callback disabled */ +#define USE_HAL_DCMI_REGISTER_CALLBACKS 0U /* DCMI register callback disabled */ +#define USE_HAL_DCMIPP_REGISTER_CALLBACKS 0U /* DCMIPP register callback disabled */ +#define USE_HAL_DMA2D_REGISTER_CALLBACKS 0U /* DMA2D register callback disabled */ +#define USE_HAL_DTS_REGISTER_CALLBACKS 0U /* DTS register callback disabled */ +#define USE_HAL_ETH_REGISTER_CALLBACKS 0U /* ETH register callback disabled */ +#define USE_HAL_FDCAN_REGISTER_CALLBACKS 0U /* FDCAN register callback disabled */ +#define USE_HAL_GFXMMU_REGISTER_CALLBACKS 0U /* GFXMMU register callback disabled */ +#define USE_HAL_GFXTIM_REGISTER_CALLBACKS 0U /* GFXTIM register callback disabled */ +#define USE_HAL_HASH_REGISTER_CALLBACKS 0U /* HASH register callback disabled */ +#define USE_HAL_HCD_REGISTER_CALLBACKS 0U /* HCD register callback disabled */ +#define USE_HAL_I2C_REGISTER_CALLBACKS 0U /* I2C register callback disabled */ +#define USE_HAL_I3C_REGISTER_CALLBACKS 0U /* I3C register callback disabled */ +#define USE_HAL_IWDG_REGISTER_CALLBACKS 0U /* IWDG register callback disabled */ +#define USE_HAL_IRDA_REGISTER_CALLBACKS 0U /* IRDA register callback disabled */ +#define USE_HAL_LPTIM_REGISTER_CALLBACKS 0U /* LPTIM register callback disabled */ +#define USE_HAL_LTDC_REGISTER_CALLBACKS 0U /* LTDC register callback disabled */ +#define USE_HAL_MCE_REGISTER_CALLBACKS 0U /* MCE register callback disabled */ +#define USE_HAL_MDF_REGISTER_CALLBACKS 0U /* MDF register callback disabled */ +#define USE_HAL_MMC_REGISTER_CALLBACKS 0U /* MMC register callback disabled */ +#define USE_HAL_NAND_REGISTER_CALLBACKS 0U /* NAND register callback disabled */ +#define USE_HAL_NOR_REGISTER_CALLBACKS 0U /* NOR register callback disabled */ +#define USE_HAL_PCD_REGISTER_CALLBACKS 0U /* PCD register callback disabled */ +#define USE_HAL_PKA_REGISTER_CALLBACKS 0U /* PKA register callback disabled */ +#define USE_HAL_PSSI_REGISTER_CALLBACKS 0U /* PSSI register callback disabled */ +#define USE_HAL_RAMCFG_REGISTER_CALLBACKS 0U /* RAMCFG register callback disabled */ +#define USE_HAL_RNG_REGISTER_CALLBACKS 0U /* RNG register callback disabled */ +#define USE_HAL_RTC_REGISTER_CALLBACKS 0U /* RTC register callback disabled */ +#define USE_HAL_SAI_REGISTER_CALLBACKS 0U /* SAI register callback disabled */ +#define USE_HAL_SD_REGISTER_CALLBACKS 0U /* SD register callback disabled */ +#define USE_HAL_SDRAM_REGISTER_CALLBACKS 0U /* SDRAM register callback disabled */ +#define USE_HAL_SMARTCARD_REGISTER_CALLBACKS 0U /* SMARTCARD register callback disabled */ +#define USE_HAL_SMBUS_REGISTER_CALLBACKS 0U /* SMBUS register callback disabled */ +#define USE_HAL_SPDIFRX_REGISTER_CALLBACKS 0U /* SPDIFRX register callback disabled */ +#define USE_HAL_SPI_REGISTER_CALLBACKS 0U /* SPI register callback disabled */ +#define USE_HAL_SRAM_REGISTER_CALLBACKS 0U /* SRAM register callback disabled */ +#define USE_HAL_TIM_REGISTER_CALLBACKS 0U /* TIM register callback disabled */ +#define USE_HAL_UART_REGISTER_CALLBACKS 0U /* UART register callback disabled */ +#define USE_HAL_USART_REGISTER_CALLBACKS 0U /* USART register callback disabled */ +#define USE_HAL_WWDG_REGISTER_CALLBACKS 0U /* WWDG register callback disabled */ +#define USE_HAL_XSPI_REGISTER_CALLBACKS 0U /* XSPI register callback disabled */ + +// Include various HAL modules for convenience +#include "stm32n6xx_hal_rcc.h" +#include "stm32n6xx_hal_gpio.h" +#include "stm32n6xx_hal_rif.h" +#include "stm32n6xx_hal_dma.h" +#include "stm32n6xx_hal_cacheaxi.h" +#include "stm32n6xx_hal_cortex.h" +#include "stm32n6xx_hal_adc.h" +#include "stm32n6xx_hal_bsec.h" +#include "stm32n6xx_hal_crc.h" +#include "stm32n6xx_hal_cryp.h" +#include "stm32n6xx_hal_dcmi.h" +#include "stm32n6xx_hal_dcmipp.h" +#include "stm32n6xx_hal_dma2d.h" +#include "stm32n6xx_hal_dts.h" +#include "stm32n6xx_hal_eth.h" +#include "stm32n6xx_hal_exti.h" +#include "stm32n6xx_hal_fdcan.h" +#include "stm32n6xx_hal_gfxmmu.h" +#include "stm32n6xx_hal_gfxtim.h" +#include "stm32n6xx_hal_gpio.h" +#include "stm32n6xx_hal_gpu2d.h" +#include "stm32n6xx_hal_hash.h" +#include "stm32n6xx_hal_hcd.h" +#include "stm32n6xx_hal_i2c.h" +#include "stm32n6xx_hal_i3c.h" +#include "stm32n6xx_hal_icache.h" +#include "stm32n6xx_hal_irda.h" +#include "stm32n6xx_hal_iwdg.h" +#include "stm32n6xx_hal_jpeg.h" +#include "stm32n6xx_hal_lptim.h" +#include "stm32n6xx_hal_ltdc.h" +#include "stm32n6xx_hal_mce.h" +#include "stm32n6xx_hal_mdf.h" +#include "stm32n6xx_hal_mdios.h" +#include "stm32n6xx_hal_mmc.h" +#include "stm32n6xx_hal_nand.h" +#include "stm32n6xx_hal_nor.h" +#include "stm32n6xx_hal_nand.h" +#include "stm32n6xx_hal_pcd.h" +#include "stm32n6xx_hal_pka.h" +#include "stm32n6xx_hal_pssi.h" +#include "stm32n6xx_hal_pwr.h" +#include "stm32n6xx_hal_ramcfg.h" +#include "stm32n6xx_hal_rng.h" +#include "stm32n6xx_hal_rtc.h" +#include "stm32n6xx_hal_sai.h" +#include "stm32n6xx_hal_sd.h" +#include "stm32n6xx_hal_sdram.h" +#include "stm32n6xx_hal_smartcard.h" +#include "stm32n6xx_hal_smbus.h" +#include "stm32n6xx_hal_spdifrx.h" +#include "stm32n6xx_hal_spi.h" +#include "stm32n6xx_hal_sram.h" +#include "stm32n6xx_hal_tim.h" +#include "stm32n6xx_hal_uart.h" +#include "stm32n6xx_hal_usart.h" +#include "stm32n6xx_hal_wwdg.h" +#include "stm32n6xx_hal_xspi.h" +#include "stm32n6xx_ll_lpuart.h" +#include "stm32n6xx_ll_pwr.h" +#include "stm32n6xx_ll_rtc.h" +#include "stm32n6xx_ll_usart.h" + +// HAL parameter assertions are disabled +#define assert_param(expr) ((void)0) + +#endif // MICROPY_INCLUDED_STM32N6XX_HAL_CONF_BASE_H diff --git a/ports/stm32/cyw43_configport.h b/ports/stm32/cyw43_configport.h index c26c554761..6528dbc625 100644 --- a/ports/stm32/cyw43_configport.h +++ b/ports/stm32/cyw43_configport.h @@ -27,14 +27,9 @@ #ifndef MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H #define MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H -// The board-level config will be included here, so it can set some CYW43 values. -#include "py/mpconfig.h" -#include "py/mperrno.h" -#include "py/mphal.h" -#include "extmod/modnetwork.h" -#include "extmod/mpbthci.h" #include "extint.h" -#include "pendsv.h" +#include "extmod/mpbthci.h" +#include "extmod/cyw43_config_common.h" #include "sdio.h" #define CYW43_USE_SPI (0) @@ -62,50 +57,12 @@ #define CYW43_BT_UART_BAUDRATE_DOWNLOAD_FIRMWARE MICROPY_HW_BLE_UART_BAUDRATE_DOWNLOAD_FIRMWARE #endif -#define CYW43_IOCTL_TIMEOUT_US (1000000) -#define CYW43_SLEEP_MAX (50) -#define CYW43_NETUTILS (1) #define CYW43_CLEAR_SDIO_INT (1) -#define CYW43_EPERM MP_EPERM // Operation not permitted -#define CYW43_EIO MP_EIO // I/O error -#define CYW43_EINVAL MP_EINVAL // Invalid argument -#define CYW43_ETIMEDOUT MP_ETIMEDOUT // Connection timed out - -#define CYW43_THREAD_ENTER MICROPY_PY_LWIP_ENTER -#define CYW43_THREAD_EXIT MICROPY_PY_LWIP_EXIT -#define CYW43_THREAD_LOCK_CHECK - -#define CYW43_HOST_NAME mod_network_hostname_data - #define CYW43_SDPCM_SEND_COMMON_WAIT __WFI(); #define CYW43_DO_IOCTL_WAIT __WFI(); #define CYW43_HAL_UART_READCHAR_BLOCKING_WAIT __WFI() -#define CYW43_ARRAY_SIZE(a) MP_ARRAY_SIZE(a) - -#define CYW43_HAL_PIN_MODE_INPUT MP_HAL_PIN_MODE_INPUT -#define CYW43_HAL_PIN_MODE_OUTPUT MP_HAL_PIN_MODE_OUTPUT -#define CYW43_HAL_PIN_PULL_NONE MP_HAL_PIN_PULL_NONE -#define CYW43_HAL_PIN_PULL_UP MP_HAL_PIN_PULL_UP -#define CYW43_HAL_PIN_PULL_DOWN MP_HAL_PIN_PULL_DOWN - -#define CYW43_HAL_MAC_WLAN0 MP_HAL_MAC_WLAN0 -#define CYW43_HAL_MAC_BDADDR MP_HAL_MAC_BDADDR - -#define cyw43_hal_ticks_us mp_hal_ticks_us -#define cyw43_hal_ticks_ms mp_hal_ticks_ms - -#define cyw43_hal_pin_obj_t mp_hal_pin_obj_t -#define cyw43_hal_pin_config mp_hal_pin_config -#define cyw43_hal_pin_read mp_hal_pin_read -#define cyw43_hal_pin_low mp_hal_pin_low -#define cyw43_hal_pin_high mp_hal_pin_high - -#define cyw43_hal_get_mac mp_hal_get_mac -#define cyw43_hal_get_mac_ascii mp_hal_get_mac_ascii -#define cyw43_hal_generate_laa_mac mp_hal_generate_laa_mac - #define cyw43_hal_uart_set_baudrate mp_bluetooth_hci_uart_set_baudrate #define cyw43_hal_uart_write mp_bluetooth_hci_uart_write #define cyw43_hal_uart_readchar mp_bluetooth_hci_uart_readchar @@ -132,24 +89,6 @@ #define CYW43_PIN_RFSW_SELECT pyb_pin_WL_GPIO_1 #endif -#define cyw43_schedule_internal_poll_dispatch(func) pendsv_schedule_dispatch(PENDSV_DISPATCH_CYW43, func) - -void cyw43_post_poll_hook(void); - -static inline void cyw43_delay_us(uint32_t us) { - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - } -} - -static inline void cyw43_delay_ms(uint32_t ms) { - uint32_t us = ms * 1000; - uint32_t start = mp_hal_ticks_us(); - while (mp_hal_ticks_us() - start < us) { - MICROPY_EVENT_POLL_HOOK; - } -} - static inline void cyw43_hal_pin_config_irq_falling(cyw43_hal_pin_obj_t pin, int enable) { if (enable) { extint_set(pin, GPIO_MODE_IT_FALLING); @@ -184,6 +123,4 @@ static inline int cyw43_sdio_transfer_cmd53(bool write, uint32_t block_size, uin return sdio_transfer_cmd53(write, block_size, arg, len, buf); } -#define CYW43_EVENT_POLL_HOOK MICROPY_EVENT_POLL_HOOK - #endif // MICROPY_INCLUDED_STM32_CYW43_CONFIGPORT_H diff --git a/ports/stm32/dma.c b/ports/stm32/dma.c index df8a408cbf..c252770740 100644 --- a/ports/stm32/dma.c +++ b/ports/stm32/dma.c @@ -81,7 +81,7 @@ typedef union { struct _dma_descr_t { #if defined(STM32F4) || defined(STM32F7) || defined(STM32H7) DMA_Stream_TypeDef *instance; - #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) + #elif defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) DMA_Channel_TypeDef *instance; #else #error "Unsupported Processor" @@ -93,7 +93,7 @@ struct _dma_descr_t { // Default parameters to dma_init() shared by spi and i2c; Channel and Direction // vary depending on the peripheral instance so they get passed separately -#if defined(STM32H5) +#if defined(STM32H5) || defined(STM32N6) static const DMA_InitTypeDef dma_init_struct_spi_i2c = { .Request = 0, // set by dma_init_handle .BlkHWRequest = DMA_BREQ_SINGLE_BURST, @@ -157,7 +157,7 @@ static const DMA_InitTypeDef dma_init_struct_i2s = { }; #endif -#if ENABLE_SDIO && !defined(STM32H5) && !defined(STM32H7) +#if ENABLE_SDIO && !defined(STM32H5) && !defined(STM32H7) && !defined(STM32N6) // Parameters to dma_init() for SDIO tx and rx. static const DMA_InitTypeDef dma_init_struct_sdio = { #if defined(STM32F4) || defined(STM32F7) @@ -735,9 +735,13 @@ const dma_descr_t dma_SPI_1_RX = { GPDMA1_Channel0, GPDMA1_REQUEST_SPI1_RX, dma_ const dma_descr_t dma_SPI_1_TX = { GPDMA1_Channel1, GPDMA1_REQUEST_SPI1_TX, dma_id_1, &dma_init_struct_spi_i2c }; const dma_descr_t dma_SPI_2_RX = { GPDMA1_Channel2, GPDMA1_REQUEST_SPI2_RX, dma_id_2, &dma_init_struct_spi_i2c }; const dma_descr_t dma_SPI_2_TX = { GPDMA1_Channel3, GPDMA1_REQUEST_SPI2_TX, dma_id_3, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_3_RX = { GPDMA1_Channel4, GPDMA1_REQUEST_SPI3_RX, dma_id_4, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_3_TX = { GPDMA1_Channel5, GPDMA1_REQUEST_SPI3_TX, dma_id_5, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_4_RX = { GPDMA1_Channel6, GPDMA1_REQUEST_SPI4_RX, dma_id_6, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_4_TX = { GPDMA1_Channel7, GPDMA1_REQUEST_SPI4_TX, dma_id_7, &dma_init_struct_spi_i2c }; #if MICROPY_HW_ENABLE_DAC -const dma_descr_t dma_DAC_1_TX = { GPDMA1_Channel4, GPDMA1_REQUEST_DAC1_CH1, dma_id_4, &dma_init_struct_dac }; -const dma_descr_t dma_DAC_2_TX = { GPDMA1_Channel5, GPDMA1_REQUEST_DAC1_CH2, dma_id_5, &dma_init_struct_dac }; +const dma_descr_t dma_DAC_1_TX = { GPDMA2_Channel0, GPDMA1_REQUEST_DAC1_CH1, dma_id_8, &dma_init_struct_dac }; +const dma_descr_t dma_DAC_2_TX = { GPDMA2_Channel1, GPDMA1_REQUEST_DAC1_CH2, dma_id_9, &dma_init_struct_dac }; #endif static const uint8_t dma_irqn[NSTREAM] = { @@ -826,6 +830,46 @@ static const uint8_t dma_irqn[NSTREAM] = { DMA2_Stream7_IRQn, }; +#elif defined(STM32N6) + +#define NCONTROLLERS (1) +#define NSTREAMS_PER_CONTROLLER (16) +#define NSTREAM (NCONTROLLERS * NSTREAMS_PER_CONTROLLER) + +#define DMA_SUB_INSTANCE_AS_UINT8(dma_channel) (dma_channel) + +#define DMA1_ENABLE_MASK (0xffff) // Bits in dma_enable_mask corresponding to GPDMA1 + +const dma_descr_t dma_SPI_1_RX = { GPDMA1_Channel0, GPDMA1_REQUEST_SPI1_RX, dma_id_0, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_1_TX = { GPDMA1_Channel1, GPDMA1_REQUEST_SPI1_TX, dma_id_1, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_2_RX = { GPDMA1_Channel2, GPDMA1_REQUEST_SPI2_RX, dma_id_2, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_2_TX = { GPDMA1_Channel3, GPDMA1_REQUEST_SPI2_TX, dma_id_3, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_3_RX = { GPDMA1_Channel4, GPDMA1_REQUEST_SPI3_RX, dma_id_4, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_3_TX = { GPDMA1_Channel5, GPDMA1_REQUEST_SPI3_TX, dma_id_5, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_4_RX = { GPDMA1_Channel6, GPDMA1_REQUEST_SPI4_RX, dma_id_6, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_4_TX = { GPDMA1_Channel7, GPDMA1_REQUEST_SPI4_TX, dma_id_7, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_5_RX = { GPDMA1_Channel8, GPDMA1_REQUEST_SPI5_RX, dma_id_8, &dma_init_struct_spi_i2c }; +const dma_descr_t dma_SPI_5_TX = { GPDMA1_Channel9, GPDMA1_REQUEST_SPI5_TX, dma_id_9, &dma_init_struct_spi_i2c }; + +static const uint8_t dma_irqn[NSTREAM] = { + GPDMA1_Channel0_IRQn, + GPDMA1_Channel1_IRQn, + GPDMA1_Channel2_IRQn, + GPDMA1_Channel3_IRQn, + GPDMA1_Channel4_IRQn, + GPDMA1_Channel5_IRQn, + GPDMA1_Channel6_IRQn, + GPDMA1_Channel7_IRQn, + GPDMA1_Channel8_IRQn, + GPDMA1_Channel9_IRQn, + GPDMA1_Channel10_IRQn, + GPDMA1_Channel11_IRQn, + GPDMA1_Channel12_IRQn, + GPDMA1_Channel13_IRQn, + GPDMA1_Channel14_IRQn, + GPDMA1_Channel15_IRQn, +}; + #endif static DMA_HandleTypeDef *dma_handle[NSTREAM] = {NULL}; @@ -849,6 +893,10 @@ volatile dma_idle_count_t dma_idle; #define __HAL_RCC_DMA2_CLK_ENABLE __HAL_RCC_GPDMA2_CLK_ENABLE #define __HAL_RCC_DMA1_CLK_DISABLE __HAL_RCC_GPDMA1_CLK_DISABLE #define __HAL_RCC_DMA2_CLK_DISABLE __HAL_RCC_GPDMA2_CLK_DISABLE +#elif defined(STM32N6) +#define DMA1_IS_CLK_ENABLED() (__HAL_RCC_GPDMA1_IS_CLK_ENABLED()) +#define __HAL_RCC_DMA1_CLK_ENABLE __HAL_RCC_GPDMA1_CLK_ENABLE +#define __HAL_RCC_DMA1_CLK_DISABLE __HAL_RCC_GPDMA1_CLK_DISABLE #else #define DMA1_IS_CLK_ENABLED() ((RCC->AHB1ENR & RCC_AHB1ENR_DMA1EN) != 0) #define DMA2_IS_CLK_ENABLED() ((RCC->AHB1ENR & RCC_AHB1ENR_DMA2EN) != 0) @@ -1181,10 +1229,11 @@ void DMA2_Channel8_IRQHandler(void) { } #endif -#elif defined(STM32H5) +#elif defined(STM32H5) || defined(STM32N6) #define DEFINE_IRQ_HANDLER(periph, channel, id) \ void GPDMA##periph##_Channel##channel##_IRQHandler(void) { \ + MP_STATIC_ASSERT(GPDMA##periph##_Channel##channel##_IRQn > 0); \ IRQ_ENTER(GPDMA##periph##_Channel##channel##_IRQn); \ if (dma_handle[id] != NULL) { \ HAL_DMA_IRQHandler(dma_handle[id]); \ @@ -1200,6 +1249,7 @@ DEFINE_IRQ_HANDLER(1, 4, dma_id_4) DEFINE_IRQ_HANDLER(1, 5, dma_id_5) DEFINE_IRQ_HANDLER(1, 6, dma_id_6) DEFINE_IRQ_HANDLER(1, 7, dma_id_7) +#if defined(STM32H5) DEFINE_IRQ_HANDLER(2, 0, dma_id_8) DEFINE_IRQ_HANDLER(2, 1, dma_id_9) DEFINE_IRQ_HANDLER(2, 2, dma_id_10) @@ -1208,6 +1258,16 @@ DEFINE_IRQ_HANDLER(2, 4, dma_id_12) DEFINE_IRQ_HANDLER(2, 5, dma_id_13) DEFINE_IRQ_HANDLER(2, 6, dma_id_14) DEFINE_IRQ_HANDLER(2, 7, dma_id_15) +#else +DEFINE_IRQ_HANDLER(1, 8, dma_id_8) +DEFINE_IRQ_HANDLER(1, 9, dma_id_9) +DEFINE_IRQ_HANDLER(1, 10, dma_id_10) +DEFINE_IRQ_HANDLER(1, 11, dma_id_11) +DEFINE_IRQ_HANDLER(1, 12, dma_id_12) +DEFINE_IRQ_HANDLER(1, 13, dma_id_13) +DEFINE_IRQ_HANDLER(1, 14, dma_id_14) +DEFINE_IRQ_HANDLER(1, 15, dma_id_15) +#endif #elif defined(STM32L0) @@ -1420,7 +1480,7 @@ void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint3 dma->Instance = dma_descr->instance; dma->Init = *dma_descr->init; dma->Init.Direction = dir; - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) dma->Init.Request = dma_descr->sub_instance; #else #if !defined(STM32F0) && !defined(STM32L1) @@ -1428,7 +1488,7 @@ void dma_init_handle(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint3 #endif #endif - #if defined(STM32H5) + #if defined(STM32H5) || defined(STM32N6) // Configure src/dest settings based on the DMA direction. if (dir == DMA_MEMORY_TO_PERIPH) { dma->Init.SrcInc = DMA_SINC_INCREMENTED; @@ -1459,13 +1519,24 @@ void dma_init(DMA_HandleTypeDef *dma, const dma_descr_t *dma_descr, uint32_t dir dma_enable_clock(dma_id); - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) // Always reset and configure the H7 and G0/G4/H7/L0/L4/WB/WL DMA peripheral // (dma->State is set to HAL_DMA_STATE_RESET by memset above) // TODO: understand how L0/L4 DMA works so this is not needed HAL_DMA_DeInit(dma); dma->Parent = data; // HAL_DMA_DeInit may clear Parent, so set it again HAL_DMA_Init(dma); + + #if defined(STM32N6) + // Configure security attributes. + HAL_DMA_ConfigChannelAttributes(dma, DMA_CHANNEL_PRIV | DMA_CHANNEL_SEC | DMA_CHANNEL_SRC_SEC | DMA_CHANNEL_DEST_SEC); + // Configure data handling. + DMA_DataHandlingConfTypeDef config; + config.DataExchange = DMA_EXCHANGE_NONE; + config.DataAlignment = DMA_DATA_RIGHTALIGN_ZEROPADDED; + HAL_DMAEx_ConfigDataHandling(dma, &config); + #endif + NVIC_SetPriority(IRQn_NONNEG(dma_irqn[dma_id]), IRQ_PRI_DMA); #else // if this stream was previously configured for this channel/request and direction then we @@ -1736,7 +1807,7 @@ void dma_nohal_start(const dma_descr_t *descr, uint32_t src_addr, uint32_t dst_a dma->CCR |= DMA_CCR_EN; } -#elif defined(STM32G0) || defined(STM32WB) || defined(STM32WL) +#elif defined(STM32G0) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) // These functions are currently not implemented or needed for this MCU. diff --git a/ports/stm32/dma.h b/ports/stm32/dma.h index 7ba04baf52..f05b22b5d0 100644 --- a/ports/stm32/dma.h +++ b/ports/stm32/dma.h @@ -147,6 +147,19 @@ extern const dma_descr_t dma_I2C_4_RX; extern const dma_descr_t dma_SPI_SUBGHZ_TX; extern const dma_descr_t dma_SPI_SUBGHZ_RX; +#elif defined(STM32N6) + +extern const dma_descr_t dma_SPI_1_RX; +extern const dma_descr_t dma_SPI_1_TX; +extern const dma_descr_t dma_SPI_2_RX; +extern const dma_descr_t dma_SPI_2_TX; +extern const dma_descr_t dma_SPI_3_RX; +extern const dma_descr_t dma_SPI_3_TX; +extern const dma_descr_t dma_SPI_4_RX; +extern const dma_descr_t dma_SPI_4_TX; +extern const dma_descr_t dma_SPI_5_RX; +extern const dma_descr_t dma_SPI_5_TX; + #endif // API that configures the DMA via the HAL. diff --git a/ports/stm32/extint.c b/ports/stm32/extint.c index 5b5658809b..9c3c243253 100644 --- a/ports/stm32/extint.c +++ b/ports/stm32/extint.c @@ -92,7 +92,7 @@ #define EXTI_SWIER_BB(line) (*(__IO uint32_t *)(PERIPH_BB_BASE + ((EXTI_OFFSET + offsetof(EXTI_TypeDef, SWIER)) * 32) + ((line) * 4))) #endif -#if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) +#if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) // The L4 MCU supports 40 Events/IRQs lines of the type configurable and direct. // Here we only support configurable line types. Details, see page 330 of RM0351, Rev 1. // The USB_FS_WAKUP event is a direct type and there is no support for it. @@ -170,7 +170,7 @@ static const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = { ADC1_COMP_IRQn, #endif - #elif defined(STM32H5) + #elif defined(STM32N6) || defined(STM32H5) EXTI0_IRQn, EXTI1_IRQn, @@ -189,6 +189,13 @@ static const uint8_t nvic_irq_channel[EXTI_NUM_VECTORS] = { EXTI14_IRQn, EXTI15_IRQn, + #if defined(STM32N6) + 0, + RTC_S_IRQn, + RTC_IRQn, + TAMP_IRQn, + #endif + #else EXTI0_IRQn, EXTI1_IRQn, EXTI2_IRQn, EXTI3_IRQn, EXTI4_IRQn, @@ -307,7 +314,7 @@ void EXTI15_10_IRQHandler(void) { IRQ_EXIT(EXTI15_10_IRQn); } -#elif defined(STM32H5) +#elif defined(STM32H5) || defined(STM32N6) DEFINE_EXTI_IRQ_HANDLER(0) DEFINE_EXTI_IRQ_HANDLER(1) @@ -440,10 +447,10 @@ void extint_register_pin(const machine_pin_obj_t *pin, uint32_t mode, bool hard_ #if !defined(STM32H5) && !defined(STM32WB) && !defined(STM32WL) __HAL_RCC_SYSCFG_CLK_ENABLE(); #endif - #if defined(STM32G0) || defined(STM32H5) + #if defined(STM32G0) || defined(STM32H5) || defined(STM32N6) EXTI->EXTICR[line >> 2] = - (EXTI->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03)))) - | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (4 * (line & 0x03))); + (EXTI->EXTICR[line >> 2] & ~(0xff << (8 * (line & 0x03)))) + | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (8 * (line & 0x03))); #else SYSCFG->EXTICR[line >> 2] = (SYSCFG->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03)))) @@ -480,13 +487,13 @@ void extint_set(const machine_pin_obj_t *pin, uint32_t mode) { pyb_extint_callback_arg[line] = MP_OBJ_FROM_PTR(pin); // Route the GPIO to EXTI - #if !defined(STM32H5) && !defined(STM32WB) && !defined(STM32WL) + #if !defined(STM32H5) && !defined(STM32N6) && !defined(STM32WB) && !defined(STM32WL) __HAL_RCC_SYSCFG_CLK_ENABLE(); #endif - #if defined(STM32G0) || defined(STM32H5) + #if defined(STM32G0) || defined(STM32H5) || defined(STM32N6) EXTI->EXTICR[line >> 2] = - (EXTI->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03)))) - | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (4 * (line & 0x03))); + (EXTI->EXTICR[line >> 2] & ~(0xff << (8 * (line & 0x03)))) + | ((uint32_t)(GPIO_GET_INDEX(pin->gpio)) << (8 * (line & 0x03))); #else SYSCFG->EXTICR[line >> 2] = (SYSCFG->EXTICR[line >> 2] & ~(0x0f << (4 * (line & 0x03)))) @@ -526,7 +533,7 @@ void extint_enable(uint line) { if (pyb_extint_mode[line] == EXTI_Mode_Interrupt) { #if defined(STM32H7) EXTI_D1->IMR1 |= (1 << line); - #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WB) || defined(STM32WL) + #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) EXTI->IMR1 |= (1 << line); #else EXTI->IMR |= (1 << line); @@ -534,7 +541,7 @@ void extint_enable(uint line) { } else { #if defined(STM32H7) EXTI_D1->EMR1 |= (1 << line); - #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WB) || defined(STM32WL) + #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) EXTI->EMR1 |= (1 << line); #else EXTI->EMR |= (1 << line); @@ -560,7 +567,7 @@ void extint_disable(uint line) { #if defined(STM32H7) EXTI_D1->IMR1 &= ~(1 << line); EXTI_D1->EMR1 &= ~(1 << line); - #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WB) || defined(STM32WL) + #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) EXTI->IMR1 &= ~(1 << line); EXTI->EMR1 &= ~(1 << line); #else @@ -582,7 +589,7 @@ void extint_swint(uint line) { return; } // we need 0 to 1 transition to trigger the interrupt - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) EXTI->SWIER1 &= ~(1 << line); EXTI->SWIER1 |= (1 << line); #else @@ -662,7 +669,7 @@ static MP_DEFINE_CONST_FUN_OBJ_1(extint_obj_swint_obj, extint_obj_swint); static mp_obj_t extint_regs(void) { const mp_print_t *print = &mp_plat_print; - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) mp_printf(print, "EXTI_IMR1 %08x\n", (unsigned int)EXTI->IMR1); mp_printf(print, "EXTI_IMR2 %08x\n", (unsigned int)EXTI->IMR2); mp_printf(print, "EXTI_EMR1 %08x\n", (unsigned int)EXTI->EMR1); @@ -673,7 +680,7 @@ static mp_obj_t extint_regs(void) { mp_printf(print, "EXTI_FTSR2 %08x\n", (unsigned int)EXTI->FTSR2); mp_printf(print, "EXTI_SWIER1 %08x\n", (unsigned int)EXTI->SWIER1); mp_printf(print, "EXTI_SWIER2 %08x\n", (unsigned int)EXTI->SWIER2); - #if defined(STM32G0) || defined(STM32H5) + #if defined(STM32G0) || defined(STM32N6) || defined(STM32H5) mp_printf(print, "EXTI_RPR1 %08x\n", (unsigned int)EXTI->RPR1); mp_printf(print, "EXTI_FPR1 %08x\n", (unsigned int)EXTI->FPR1); mp_printf(print, "EXTI_RPR2 %08x\n", (unsigned int)EXTI->RPR2); diff --git a/ports/stm32/extint.h b/ports/stm32/extint.h index d5abb04d65..801dcf62b5 100644 --- a/ports/stm32/extint.h +++ b/ports/stm32/extint.h @@ -46,7 +46,7 @@ #if defined(STM32F0) || defined(STM32G4) || defined(STM32L1) || defined(STM32L4) || defined(STM32WL) #define EXTI_RTC_TIMESTAMP (19) #define EXTI_RTC_WAKEUP (20) -#elif defined(STM32H5) +#elif defined(STM32H5) || defined(STM32N6) #define EXTI_RTC_WAKEUP (17) #define EXTI_RTC_TAMP (19) #elif defined(STM32H7) || defined(STM32WB) @@ -55,8 +55,6 @@ #elif defined(STM32G0) #define EXTI_RTC_WAKEUP (19) #define EXTI_RTC_TIMESTAMP (21) -#elif defined(STM32H5) -#define EXTI_RTC_WAKEUP (17) #else #define EXTI_RTC_TIMESTAMP (21) #define EXTI_RTC_WAKEUP (22) diff --git a/ports/stm32/flash.c b/ports/stm32/flash.c index 7668b53a43..85bcee5a97 100644 --- a/ports/stm32/flash.c +++ b/ports/stm32/flash.c @@ -30,6 +30,8 @@ #include "py/mphal.h" #include "flash.h" +#if !defined(STM32N6) + #if defined(STM32F0) #define FLASH_FLAG_ALL_ERRORS (FLASH_FLAG_EOP | FLASH_FLAG_WRPERR | FLASH_FLAG_PGERR) @@ -509,3 +511,5 @@ int flash_write(uint32_t flash_dest, const uint32_t *src, uint32_t num_word32) { return mp_hal_status_to_neg_errno(status); } + +#endif diff --git a/ports/stm32/i2cslave.h b/ports/stm32/i2cslave.h index a4b2957de3..cc4e7f9be9 100644 --- a/ports/stm32/i2cslave.h +++ b/ports/stm32/i2cslave.h @@ -51,6 +51,16 @@ static inline void i2c_slave_init(i2c_slave_t *i2c, int irqn, int irq_pri, int a RCC->APB1LENR |= 1 << (RCC_APB1LENR_I2C1EN_Pos + i2c_idx); volatile uint32_t tmp = RCC->APB1LENR; // Delay after enabling clock (void)tmp; + #elif defined(STM32N6) + if (i2c_idx == 3) { + RCC->APB4ENR1 |= RCC_APB4ENR1_I2C4EN; + volatile uint32_t tmp = RCC->APB4ENR1; // Delay after enabling clock + (void)tmp; + } else { + RCC->APB1ENR1 |= 1 << (RCC_APB1ENR1_I2C1EN_Pos + i2c_idx); + volatile uint32_t tmp = RCC->APB1ENR1; // Delay after enabling clock + (void)tmp; + } #elif defined(STM32WB) RCC->APB1ENR1 |= 1 << (RCC_APB1ENR1_I2C1EN_Pos + i2c_idx); volatile uint32_t tmp = RCC->APB1ENR1; // Delay after enabling clock diff --git a/ports/stm32/irq.h b/ports/stm32/irq.h index 58e6d0a804..dfe901ff74 100644 --- a/ports/stm32/irq.h +++ b/ports/stm32/irq.h @@ -155,6 +155,9 @@ static inline void restore_irq_pri(uint32_t state) { // SDIO must be higher priority than DMA for SDIO DMA transfers to work. #define IRQ_PRI_SDIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 4, 0) +// SPI must be higher priority than DMA for SPI DMA transfers to work. +#define IRQ_PRI_SPI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 4, 0) + // DMA should be higher priority than USB, since USB Mass Storage calls // into the sdcard driver which waits for the DMA to complete. #define IRQ_PRI_DMA NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 5, 0) @@ -172,8 +175,6 @@ static inline void restore_irq_pri(uint32_t state) { #define IRQ_PRI_SUBGHZ_RADIO NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0) -#define IRQ_PRI_SPI NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 8, 0) - #define IRQ_PRI_HSEM NVIC_EncodePriority(NVIC_PRIORITYGROUP_4, 10, 0) // Interrupt priority for non-special timers. diff --git a/ports/stm32/lwip_inc/lwipopts.h b/ports/stm32/lwip_inc/lwipopts.h index 9e2402c8dc..ad1143845f 100644 --- a/ports/stm32/lwip_inc/lwipopts.h +++ b/ports/stm32/lwip_inc/lwipopts.h @@ -10,6 +10,15 @@ #define LWIP_RAND() rng_get() +// Increase memory for lwIP to get better performance. +#if defined(STM32N6) +#define MEM_SIZE (16 * 1024) +#define TCP_MSS (1460) +#define TCP_WND (8 * TCP_MSS) +#define TCP_SND_BUF (8 * TCP_MSS) +#define MEMP_NUM_TCP_SEG (32) +#endif + // Include common lwIP configuration. #include "extmod/lwip-include/lwipopts_common.h" diff --git a/ports/stm32/machine_adc.c b/ports/stm32/machine_adc.c index 8f07075914..63cd4e089d 100644 --- a/ports/stm32/machine_adc.c +++ b/ports/stm32/machine_adc.c @@ -30,7 +30,7 @@ #include "py/mphal.h" #include "adc.h" -#if defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) +#if defined(STM32F0) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) #define ADC_V2 (1) #else #define ADC_V2 (0) @@ -80,11 +80,14 @@ #define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_12CYCLES_5 #define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_160CYCLES_5 #elif defined(STM32L1) -#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_384CYCLES +#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_16CYCLES #define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_384CYCLES #elif defined(STM32L4) || defined(STM32WB) #define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_12CYCLES_5 #define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_247CYCLES_5 +#elif defined(STM32N6) +#define ADC_SAMPLETIME_DEFAULT ADC_SAMPLETIME_11CYCLES_5 +#define ADC_SAMPLETIME_DEFAULT_INT ADC_SAMPLETIME_246CYCLES_5 #endif // Timeout for waiting for end-of-conversion @@ -127,6 +130,8 @@ static uint32_t adc_ll_channel(uint32_t channel_id) { case MACHINE_ADC_INT_CH_TEMPSENSOR: #if defined(STM32G4) adc_ll_ch = ADC_CHANNEL_TEMPSENSOR_ADC1; + #elif defined(STM32N6) + adc_ll_ch = ADC_CHANNEL_0; // TODO #else adc_ll_ch = ADC_CHANNEL_TEMPSENSOR; #endif @@ -183,7 +188,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { if (adc == ADC1) { #if defined(STM32H5) __HAL_RCC_ADC_CLK_ENABLE(); - #elif defined(STM32G4) || defined(STM32H7) + #elif defined(STM32G4) || defined(STM32H7) || defined(STM32N6) __HAL_RCC_ADC12_CLK_ENABLE(); #else __HAL_RCC_ADC1_CLK_ENABLE(); @@ -193,7 +198,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { if (adc == ADC2) { #if defined(STM32H5) __HAL_RCC_ADC_CLK_ENABLE(); - #elif defined(STM32G4) || defined(STM32H7) + #elif defined(STM32G4) || defined(STM32H7) || defined(STM32N6) __HAL_RCC_ADC12_CLK_ENABLE(); #else __HAL_RCC_ADC2_CLK_ENABLE(); @@ -225,13 +230,15 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { ADC3_COMMON->CCR = 3 << ADC_CCR_CKMODE_Pos; #elif defined(STM32L0) ADC1_COMMON->CCR = 0; // ADCPR=PCLK/2 + #elif defined(STM32L1) + ADC1_COMMON->CCR = 1 << ADC_CCR_ADCPRE_Pos; // ADCPRE=2 #elif defined(STM32WB) ADC1_COMMON->CCR = 0 << ADC_CCR_PRESC_Pos | 0 << ADC_CCR_CKMODE_Pos; // PRESC=1, MODE=ASYNC #elif defined(STM32WL) ADC_COMMON->CCR = 0 << ADC_CCR_PRESC_Pos; // PRESC=1 #endif - #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) + #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) if (adc->CR & ADC_CR_DEEPPWD) { adc->CR = 0; // disable deep powerdown } @@ -255,6 +262,11 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { LL_ADC_StartCalibration(adc); #elif defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB) LL_ADC_StartCalibration(adc, LL_ADC_SINGLE_ENDED); + #elif defined(STM32N6) + ADC_HandleTypeDef hadc; + hadc.Instance = adc; + hadc.State = 0; + HAL_ADCEx_Calibration_Start(&hadc, ADC_SINGLE_ENDED); #else LL_ADC_StartCalibration(adc, LL_ADC_CALIB_OFFSET_LINEARITY, LL_ADC_SINGLE_ENDED); #endif @@ -310,11 +322,17 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) { uint32_t cfgr = res << ADC_CFGR_RES_Pos; adc->CFGR = (adc->CFGR & ~cfgr_clr) | cfgr; + #elif defined(STM32N6) + + uint32_t cfgr1_clr = ADC_CFGR1_CONT | ADC_CFGR1_EXTEN; + uint32_t cfgr1 = res << ADC_CFGR1_RES_Pos; + adc->CFGR1 = (adc->CFGR1 & ~cfgr1_clr) | cfgr1; + #endif } static int adc_get_bits(ADC_TypeDef *adc) { - #if defined(STM32F0) || defined(STM32G0) || defined(STM32L0) || defined(STM32WL) + #if defined(STM32F0) || defined(STM32G0) || defined(STM32L0) || defined(STM32N6) || defined(STM32WL) uint32_t res = (adc->CFGR1 & ADC_CFGR1_RES) >> ADC_CFGR1_RES_Pos; #elif defined(STM32F4) || defined(STM32F7) || defined(STM32L1) uint32_t res = (adc->CR1 & ADC_CR1_RES) >> ADC_CR1_RES_Pos; @@ -385,8 +403,34 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp } *smpr = (*smpr & ~(7 << (channel * 3))) | sample_time << (channel * 3); // select sample time - #elif defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB) - #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) + #elif defined(STM32L1) + + ADC_Common_TypeDef *adc_common = ADC1_COMMON; + if (channel == ADC_CHANNEL_VREFINT || channel == ADC_CHANNEL_TEMPSENSOR) { + adc_common->CCR |= ADC_CCR_TSVREFE; + if (channel == ADC_CHANNEL_TEMPSENSOR) { + adc_stabilisation_delay_us(ADC_TEMPSENSOR_DELAY_US); + } + } + + adc->SQR1 = (1 - 1) << ADC_SQR1_L_Pos; + adc->SQR5 = (channel & 0x1f) << ADC_SQR5_SQ1_Pos; + + __IO uint32_t *smpr; + if (channel >= 20) { + smpr = &adc->SMPR1; + channel -= 20; + } else if (channel >= 10) { + smpr = &adc->SMPR2; + channel -= 10; + } else { + smpr = &adc->SMPR3; + } + *smpr = (*smpr & ~(7 << (channel * 3))) | sample_time << (channel * 3); // select sample time + + #elif defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) + + #if defined(STM32G4) || defined(STM32H5) || defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) || defined(STM32N6) ADC_Common_TypeDef *adc_common = ADC12_COMMON; #elif defined(STM32H7) #if defined(ADC_VER_V5_V90) @@ -404,6 +448,7 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp #endif if (channel == ADC_CHANNEL_VREFINT) { adc_common->CCR |= ADC_CCR_VREFEN; + #if !defined(STM32N6) #if defined(STM32G4) } else if (channel == ADC_CHANNEL_TEMPSENSOR_ADC1) { adc_common->CCR |= ADC_CCR_VSENSESEL; @@ -412,19 +457,20 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp adc_common->CCR |= ADC_CCR_TSEN; #endif adc_stabilisation_delay_us(ADC_TEMPSENSOR_DELAY_US); + #endif } else if (channel == ADC_CHANNEL_VBAT) { #if defined(STM32G4) adc_common->CCR |= ADC_CCR_VBATSEL; #else adc_common->CCR |= ADC_CCR_VBATEN; #endif - #if defined(STM32H5) + #if defined(STM32H5) || defined(STM32N6) } else if (channel == ADC_CHANNEL_VDDCORE) { adc->OR |= ADC_OR_OP0; // Enable Vddcore channel on ADC2 #endif } - #if defined(STM32G4) || defined(STM32H5) - // G4 and H5 use encoded literals for internal channels -> extract ADC channel for following code + #if defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WB) + // MCU uses encoded literals for internal channels -> extract ADC channel for following code if (__LL_ADC_IS_CHANNEL_INTERNAL(channel)) { channel = __LL_ADC_CHANNEL_TO_DECIMAL_NB(channel); } @@ -445,10 +491,13 @@ static void adc_config_channel(ADC_TypeDef *adc, uint32_t channel, uint32_t samp static uint32_t adc_read_channel(ADC_TypeDef *adc) { uint32_t value; - #if defined(STM32G4) - // For STM32G4 there is errata 2.7.7, "Wrong ADC result if conversion done late after - // calibration or previous conversion". According to the errata, this can be avoided - // by performing two consecutive ADC conversions and keeping the second result. + #if defined(STM32G4) || defined(STM32WB) + // For STM32G4 errata 2.7.7 / STM32WB errata 2.7.1: + // "Wrong ADC result if conversion done late after calibration or previous conversion" + // states an incorrect reading is returned if more than 1ms has elapsed since the last + // reading or calibration. According to the errata, this can be avoided by performing + // two consecutive ADC conversions and keeping the second result. + // Note: On STM32WB55 @ 64Mhz each ADC read takes ~ 3us. for (uint8_t i = 0; i < 2; i++) #endif { diff --git a/ports/stm32/machine_uart.c b/ports/stm32/machine_uart.c index 8444b29989..8f1faea4b6 100644 --- a/ports/stm32/machine_uart.c +++ b/ports/stm32/machine_uart.c @@ -399,7 +399,7 @@ static bool mp_machine_uart_txdone(machine_uart_obj_t *self) { // Send a break condition. static void mp_machine_uart_sendbreak(machine_uart_obj_t *self) { - #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) self->uartx->RQR = USART_RQR_SBKRQ; // write-only register #else self->uartx->CR1 |= USART_CR1_SBK; diff --git a/ports/stm32/main.c b/ports/stm32/main.c index e8395013b9..137e132817 100644 --- a/ports/stm32/main.c +++ b/ports/stm32/main.c @@ -306,11 +306,30 @@ static bool init_sdcard_fs(void) { } #endif +#if defined(STM32N6) +static void risaf_init(void) { + RIMC_MasterConfig_t rimc_master = {0}; + + __HAL_RCC_RIFSC_CLK_ENABLE(); + LL_AHB3_GRP1_EnableClockLowPower(LL_AHB3_GRP1_PERIPH_RIFSC | LL_AHB3_GRP1_PERIPH_RISAF); + + rimc_master.MasterCID = RIF_CID_1; + rimc_master.SecPriv = RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV; + + HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_SDMMC1, &rimc_master); + HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_SDMMC1, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV); + HAL_RIF_RIMC_ConfigMasterAttributes(RIF_MASTER_INDEX_SDMMC2, &rimc_master); + HAL_RIF_RISC_SetSlaveSecureAttributes(RIF_RISC_PERIPH_INDEX_SDMMC2, RIF_ATTRIBUTE_SEC | RIF_ATTRIBUTE_PRIV); +} +#endif + void stm32_main(uint32_t reset_mode) { // Low-level MCU initialisation. stm32_system_init(); - #if !defined(STM32F0) + // Set VTOR, the location of the interrupt vector table. + // On N6, SystemInit does this, setting VTOR to &g_pfnVectors. + #if !defined(STM32F0) && !defined(STM32N6) #if MICROPY_HW_ENABLE_ISR_UART_FLASH_FUNCS_IN_RAM // Copy IRQ vector table to RAM and point VTOR there extern uint32_t __isr_vector_flash_addr, __isr_vector_ram_start, __isr_vector_ram_end; @@ -325,8 +344,7 @@ void stm32_main(uint32_t reset_mode) { #endif #endif - - #if __CORTEX_M != 33 + #if __CORTEX_M != 33 && __CORTEX_M != 55 // Enable 8-byte stack alignment for IRQ handlers, in accord with EABI SCB->CCR |= SCB_CCR_STKALIGN_Msk; #endif @@ -349,14 +367,18 @@ void stm32_main(uint32_t reset_mode) { __HAL_FLASH_PREFETCH_BUFFER_ENABLE(); #endif - #elif defined(STM32F7) || defined(STM32H7) + #elif defined(STM32F7) || defined(STM32H7) || defined(STM32N6) #if ART_ACCLERATOR_ENABLE __HAL_FLASH_ART_ENABLE(); #endif SCB_EnableICache(); + #if defined(STM32N6) && !defined(NDEBUG) + // Don't enable D-cache on N6 when debugging; see ST Errata ES0620 - Rev 0.2 section 2.1.2. + #else SCB_EnableDCache(); + #endif #elif defined(STM32H5) @@ -376,6 +398,23 @@ void stm32_main(uint32_t reset_mode) { #endif + #if defined(STM32N6) + // SRAM, XSPI needs to remain awake during sleep, eg so DMA from flash works. + LL_MEM_EnableClockLowPower(0xffffffff); + LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_XSPI2 | LL_AHB5_GRP1_PERIPH_XSPIM); + LL_APB4_GRP1_EnableClock(LL_APB4_GRP1_PERIPH_RTC | LL_APB4_GRP1_PERIPH_RTCAPB); + LL_APB4_GRP1_EnableClockLowPower(LL_APB4_GRP1_PERIPH_RTC | LL_APB4_GRP1_PERIPH_RTCAPB); + + // Enable some AHB peripherals during sleep. + LL_AHB1_GRP1_EnableClockLowPower(0xffffffff); // GPDMA1, ADC12 + LL_AHB4_GRP1_EnableClockLowPower(0xffffffff); // GPIOA-Q, PWR, CRC + + // Enable some APB peripherals during sleep. + LL_APB1_GRP1_EnableClockLowPower(0xffffffff); // I2C, I3C, LPTIM, SPI, TIM, UART, WWDG + LL_APB2_GRP1_EnableClockLowPower(0xffffffff); // SAI, SPI, TIM, UART + LL_APB4_GRP1_EnableClockLowPower(0xffffffff); // I2C, LPTIM, LPUART, RTC, SPI + #endif + mpu_init(); #if __CORTEX_M >= 0x03 @@ -389,6 +428,10 @@ void stm32_main(uint32_t reset_mode) { // set the system clock to be HSE SystemClock_Config(); + #if defined(STM32N6) + risaf_init(); + #endif + #if defined(STM32F4) || defined(STM32F7) #if defined(__HAL_RCC_DTCMRAMEN_CLK_ENABLE) // The STM32F746 doesn't really have CCM memory, but it does have DTCM, @@ -521,6 +564,23 @@ soft_reset: // GC init gc_init(MICROPY_HEAP_START, MICROPY_HEAP_END); + // Add additional GC blocks (if enabled). + #if MICROPY_GC_SPLIT_HEAP + typedef struct { + uint8_t *addr; + uint32_t size; + } gc_blocks_table_t; + + extern const gc_blocks_table_t _gc_blocks_table_start; + extern const gc_blocks_table_t _gc_blocks_table_end; + + for (gc_blocks_table_t const *block = &_gc_blocks_table_start; block < &_gc_blocks_table_end; block++) { + if (block->size) { + gc_add(block->addr, block->addr + block->size); + } + } + #endif + #if MICROPY_ENABLE_PYSTACK static mp_obj_t pystack[384]; mp_pystack_init(pystack, &pystack[384]); diff --git a/ports/stm32/mboot/Makefile b/ports/stm32/mboot/Makefile index 87bced1aee..7226dd353f 100755 --- a/ports/stm32/mboot/Makefile +++ b/ports/stm32/mboot/Makefile @@ -34,7 +34,13 @@ include ../../../py/mkenv.mk include $(BOARD_DIR)/mpconfigboard.mk # A board can set MBOOT_TEXT0_ADDR to a custom location where mboot should reside. +ifeq ($(MCU_SERIES),n6) +MBOOT_TEXT0_ADDR ?= 0x34180400 +MBOOT_LD_FILES ?= stm32_memory_n6.ld stm32_sections.ld +else MBOOT_TEXT0_ADDR ?= 0x08000000 +MBOOT_LD_FILES ?= stm32_memory.ld stm32_sections.ld +endif # The string in MBOOT_VERSION (default defined in version.c if not defined by a # board) will be stored in the final MBOOT_VERSION_ALLOCATED_BYTES bytes of mboot flash. @@ -42,6 +48,7 @@ MBOOT_TEXT0_ADDR ?= 0x08000000 MBOOT_VERSION_ALLOCATED_BYTES ?= 64 MBOOT_VERSION_INCLUDE_OPTIONS ?= 1 # if set to 1, this will append build options to version string (see version.c) +CROSS_COMPILE ?= arm-none-eabi- USBDEV_DIR=usbdev DFU=$(TOP)/tools/dfu.py PYDFU ?= $(TOP)/tools/pydfu.py @@ -53,8 +60,6 @@ OPENOCD_CONFIG ?= boards/openocd_stm32f4.cfg include ../stm32.mk -CROSS_COMPILE ?= arm-none-eabi- - INC += -I. INC += -I.. INC += -I$(TOP) @@ -89,7 +94,6 @@ CFLAGS += -DMBOOT_VERSION=\"$(MBOOT_VERSION)\" endif CFLAGS += -DMBOOT_VERSION_ALLOCATED_BYTES=$(MBOOT_VERSION_ALLOCATED_BYTES) -DMBOOT_VERSION_INCLUDE_OPTIONS=$(MBOOT_VERSION_INCLUDE_OPTIONS) -MBOOT_LD_FILES ?= stm32_memory.ld stm32_sections.ld LDFLAGS += -nostdlib -L . $(addprefix -T,$(MBOOT_LD_FILES)) -Map=$(@:.elf=.map) --cref LDFLAGS += --defsym mboot_version_len=$(MBOOT_VERSION_ALLOCATED_BYTES) LIBS += $(shell $(CC) $(CFLAGS) -print-libgcc-file-name) @@ -137,12 +141,11 @@ SRC_C += \ drivers/bus/softqspi.c \ drivers/memory/spiflash.c \ ports/stm32/flash.c \ - ports/stm32/flashbdev.c \ ports/stm32/i2cslave.c \ ports/stm32/powerctrlboot.c \ ports/stm32/qspi.c \ - ports/stm32/spibdev.c \ ports/stm32/usbd_conf.c \ + ports/stm32/xspi.c \ $(wildcard $(BOARD_DIR)/*.c) SRC_O += \ @@ -169,16 +172,22 @@ SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal.c \ hal_cortex.c \ hal_dma.c \ - hal_flash.c \ - hal_flash_ex.c \ hal_pcd.c \ hal_pcd_ex.c \ hal_pwr_ex.c \ hal_rcc.c \ hal_rcc_ex.c \ + ll_rcc.c \ ll_usb.c \ ) +ifneq ($(MCU_SERIES),n6) +SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ + hal_flash.c \ + hal_flash_ex.c \ + ) +endif + ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f4 f7 h7)) SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ hal_mmc.c \ @@ -187,6 +196,12 @@ SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ ) endif +ifeq ($(MCU_SERIES),n6) +SRC_HAL += $(addprefix $(STM32LIB_HAL_BASE)/Src/stm32$(MCU_SERIES)xx_,\ + hal_bsec.c \ + ) +endif + SRC_USBDEV += $(addprefix ports/stm32/$(USBDEV_DIR)/,\ core/src/usbd_core.c \ core/src/usbd_ctlreq.c \ @@ -206,7 +221,7 @@ $(TOP)/lib/stm32lib/README.md: $(ECHO) "stm32lib submodule not found, fetching it now..." (cd $(TOP) && git submodule update --init lib/stm32lib) -.PHONY: deploy deploy-stlink +.PHONY: deploy deploy-stlink deploy-trusted deploy: $(BUILD)/firmware.dfu $(ECHO) "Writing $< to the board" @@ -216,9 +231,15 @@ deploy-stlink: $(BUILD)/firmware.dfu $(ECHO) "Writing $< to the board via ST-LINK" $(Q)$(STFLASH) write $(BUILD)/firmware.bin $(MBOOT_TEXT0_ADDR) -$(BUILD)/firmware.dfu: $(BUILD)/firmware.elf +deploy-trusted: $(BUILD)/firmware-trusted.bin + $(STM32_CUBE_PROGRAMMER)/bin/STM32_Programmer.sh -c port=SWD mode=HOTPLUG ap=1 -el $(DKEL) -w $^ 0x70000000 -hardRst + +$(BUILD)/firmware.bin: $(BUILD)/firmware.elf $(ECHO) "Create $@" $(Q)$(OBJCOPY) -O binary -j .isr_vector -j .text -j .data -j .mboot_version_text $^ $(BUILD)/firmware.bin + +$(BUILD)/firmware.dfu: $(BUILD)/firmware.bin + $(ECHO) "Create $@" $(Q)$(PYTHON) $(DFU) -b $(MBOOT_TEXT0_ADDR):$(BUILD)/firmware.bin $@ $(BUILD)/firmware.hex: $(BUILD)/firmware.elf @@ -230,6 +251,10 @@ $(BUILD)/firmware.elf: $(OBJ) $(Q)$(LD) $(LDFLAGS) -o $@ $^ $(LIBS) $(Q)$(SIZE) $@ +$(BUILD)/firmware-trusted.bin: $(BUILD)/firmware.bin + /bin/rm -f $@ + $(STM32_CUBE_PROGRAMMER)/bin/STM32_SigningTool_CLI -bin $^ -nk -of 0x80000000 -t fsbl -o $@ -hv $(STM32_N6_HEADER_VERSION) + ######################################### # Rules to generate header files diff --git a/ports/stm32/mboot/adc.c b/ports/stm32/mboot/adc.c index c7b9749244..06db0b59b7 100644 --- a/ports/stm32/mboot/adc.c +++ b/ports/stm32/mboot/adc.c @@ -1,3 +1,5 @@ // Include the main ADC driver, so mboot can use adc_config() and adc_config_and_read_u16(). #include "py/obj.h" +#if MICROPY_PY_MACHINE_ADC #include "../machine_adc.c" +#endif diff --git a/ports/stm32/mboot/main.c b/ports/stm32/mboot/main.c index 01f8892a51..2be8793351 100644 --- a/ports/stm32/mboot/main.c +++ b/ports/stm32/mboot/main.c @@ -41,6 +41,7 @@ #include "sdcard.h" #include "dfu.h" #include "pack.h" +#include "xspi.h" // Whether the bootloader will leave via reset, or direct jump to the application. #ifndef MBOOT_LEAVE_BOOTLOADER_VIA_RESET @@ -176,7 +177,7 @@ void HAL_Delay(uint32_t ms) { mp_hal_delay_ms(ms); } -NORETURN static void __fatal_error(const char *msg) { +MP_NORETURN static void __fatal_error(const char *msg) { NVIC_SystemReset(); for (;;) { } @@ -373,7 +374,7 @@ void SystemClock_Config(void) { #elif defined(STM32G0) #define AHBxENR IOPENR #define AHBxENR_GPIOAEN_Pos RCC_IOPENR_GPIOAEN_Pos -#elif defined(STM32H7) +#elif defined(STM32H7) || defined(STM32N6) #define AHBxENR AHB4ENR #define AHBxENR_GPIOAEN_Pos RCC_AHB4ENR_GPIOAEN_Pos #elif defined(STM32H5) || defined(STM32WB) @@ -424,6 +425,10 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed) { #define MBOOT_SPIFLASH2_LAYOUT "" #endif +#if defined(STM32N6) +#define FLASH_LAYOUT_STR "@Internal Flash " MBOOT_SPIFLASH_LAYOUT MBOOT_SPIFLASH2_LAYOUT +#else + #if defined(STM32F4) \ || defined(STM32F722xx) \ || defined(STM32F723xx) \ @@ -584,12 +589,18 @@ static int mboot_flash_write(uint32_t addr, const uint8_t *src8, size_t len) { return 0; } +#endif + /******************************************************************************/ // Writable address space interface static int do_mass_erase(void) { + #if defined(STM32N6) + return -1; + #else // TODO spiflash erase ? return mboot_flash_mass_erase(); + #endif } #if defined(MBOOT_SPIFLASH_ADDR) || defined(MBOOT_SPIFLASH2_ADDR) @@ -625,7 +636,12 @@ int hw_page_erase(uint32_t addr, uint32_t *next_addr) { } else #endif { + #if defined(STM32N6) + dfu_context.status = DFU_STATUS_ERROR_ADDRESS; + dfu_context.error = MBOOT_ERROR_STR_INVALID_ADDRESS_IDX; + #else ret = mboot_flash_page_erase(addr, next_addr); + #endif } mboot_state_change(MBOOT_STATE_ERASE_END, ret); @@ -678,9 +694,12 @@ int hw_write(uint32_t addr, const uint8_t *src8, size_t len) { ret = mp_spiflash_write(MBOOT_SPIFLASH2_SPIFLASH, addr - MBOOT_SPIFLASH2_ADDR, len, src8); } else #endif + #if !defined(STM32N6) if (flash_is_valid_addr(addr)) { ret = mboot_flash_write(addr, src8, len); - } else { + } else + #endif + { dfu_context.status = DFU_STATUS_ERROR_ADDRESS; dfu_context.error = MBOOT_ERROR_STR_INVALID_ADDRESS_IDX; } @@ -1443,7 +1462,7 @@ static int pyb_usbdd_shutdown(void) { /******************************************************************************/ // main -NORETURN static __attribute__((naked)) void branch_to_application(uint32_t r0, uint32_t bl_addr) { +MP_NORETURN static __attribute__((naked)) void branch_to_application(uint32_t r0, uint32_t bl_addr) { __asm volatile ( "ldr r2, [r1, #0]\n" // get address of stack pointer "msr msp, r2\n" // set stack pointer @@ -1509,7 +1528,7 @@ void stm32_main(uint32_t initial_r0) { // Make sure IRQ vector table points to flash where this bootloader lives. SCB->VTOR = MBOOT_VTOR; - #if __CORTEX_M != 33 + #if __CORTEX_M != 33 && __CORTEX_M != 55 // Enable 8-byte stack alignment for IRQ handlers, in accord with EABI SCB->CCR |= SCB_CCR_STKALIGN_Msk; #endif @@ -1539,6 +1558,12 @@ void stm32_main(uint32_t initial_r0) { SCB_EnableDCache(); #endif + #if defined(STM32N6) + LL_PWR_EnableBkUpAccess(); + initial_r0 = TAMP_S->BKP31R; + TAMP_S->BKP31R = 0; + #endif + MBOOT_BOARD_EARLY_INIT(&initial_r0); #ifdef MBOOT_BOOTPIN_PIN @@ -1748,6 +1773,12 @@ void USB_DRD_FS_IRQHandler(void) { HAL_PCD_IRQHandler(&pcd_fs_handle); } +#elif defined(STM32N6) + +void USB1_OTG_HS_IRQHandler(void) { + HAL_PCD_IRQHandler(&pcd_hs_handle); +} + #elif defined(STM32WB) void USB_LP_IRQHandler(void) { diff --git a/ports/stm32/mboot/mboot.h b/ports/stm32/mboot/mboot.h index 1fabff0080..bc1320c5cd 100644 --- a/ports/stm32/mboot/mboot.h +++ b/ports/stm32/mboot/mboot.h @@ -36,7 +36,7 @@ #define ELEM_DATA_START (&_estack[0]) #define ELEM_DATA_MAX (&_estack[ELEM_DATA_SIZE]) -#define NORETURN __attribute__((noreturn)) +#define MP_NORETURN __attribute__((noreturn)) #define MP_ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0])) // The default UI code in ui.c only works if there is at least one LED configured. diff --git a/ports/stm32/mboot/mphalport.h b/ports/stm32/mboot/mphalport.h index 45bf11d42b..9cac0f70c4 100644 --- a/ports/stm32/mboot/mphalport.h +++ b/ports/stm32/mboot/mphalport.h @@ -239,3 +239,20 @@ void mp_hal_pin_config_speed(uint32_t port_pin, uint32_t speed); #define pin_J13 (GPIOJ_BASE | 13) #define pin_J14 (GPIOJ_BASE | 14) #define pin_J15 (GPIOJ_BASE | 15) + +#define pin_N0 (GPION_BASE | 0) +#define pin_N1 (GPION_BASE | 1) +#define pin_N2 (GPION_BASE | 2) +#define pin_N3 (GPION_BASE | 3) +#define pin_N4 (GPION_BASE | 4) +#define pin_N5 (GPION_BASE | 5) +#define pin_N6 (GPION_BASE | 6) +#define pin_N7 (GPION_BASE | 7) +#define pin_N8 (GPION_BASE | 8) +#define pin_N9 (GPION_BASE | 9) +#define pin_N10 (GPION_BASE | 10) +#define pin_N11 (GPION_BASE | 11) +#define pin_N12 (GPION_BASE | 12) +#define pin_N13 (GPION_BASE | 13) +#define pin_N14 (GPION_BASE | 14) +#define pin_N15 (GPION_BASE | 15) diff --git a/ports/stm32/mboot/stm32_memory_n6.ld b/ports/stm32/mboot/stm32_memory_n6.ld new file mode 100644 index 0000000000..bd2471dbfa --- /dev/null +++ b/ports/stm32/mboot/stm32_memory_n6.ld @@ -0,0 +1,18 @@ +/* + Linker script fragment for mboot on an STM32N6xx MCU. + This defines the memory sections for the bootloader to use. + + On N6, the hardware bootloader loads the first 512k of external flash into + the upper part of SRAM2 AXI S, starting at 0x34180000. The first 1024 bytes + is a header. Then comes the actual code, starting with the vector table. +*/ + +MEMORY +{ + FLASH_BL (rx) : ORIGIN = 0x34180400, LENGTH = 31744 /* AXISRAM2_S */ + RAM (xrw) : ORIGIN = 0x341e0000, LENGTH = 128K /* AXISRAM2_S */ +} + +/* Location of protected flash area which must not be modified, because mboot lives there. */ +_mboot_protected_flash_start = ORIGIN(FLASH_BL); +_mboot_protected_flash_end_exclusive = ORIGIN(FLASH_BL) + LENGTH(FLASH_BL); diff --git a/ports/stm32/mboot/stm32_sections.ld b/ports/stm32/mboot/stm32_sections.ld index 43511f0839..4a6fd44b2b 100644 --- a/ports/stm32/mboot/stm32_sections.ld +++ b/ports/stm32/mboot/stm32_sections.ld @@ -33,6 +33,14 @@ SECTIONS _etext = .; } >FLASH_BL + /* Secure Gateway stubs */ + .gnu.sgstubs : + { + . = ALIGN(4); + *(.gnu.sgstubs*) + . = ALIGN(4); + } >FLASH_BL + /* used by the startup to initialize data */ _sidata = LOADADDR(.data); diff --git a/ports/stm32/modmachine.c b/ports/stm32/modmachine.c index 620ae468cb..8123cd8011 100644 --- a/ports/stm32/modmachine.c +++ b/ports/stm32/modmachine.c @@ -54,7 +54,7 @@ #define RCC_CSR_PORRSTF RCC_CSR_PWRRSTF #endif -#if defined(STM32H5) +#if defined(STM32H5) || defined(STM32N6) #define RCC_SR RSR #define RCC_SR_IWDGRSTF RCC_RSR_IWDGRSTF #define RCC_SR_WWDGRSTF RCC_RSR_WWDGRSTF @@ -135,7 +135,7 @@ void machine_init(void) { reset_cause = PYB_RESET_DEEPSLEEP; PWR->PMCR |= PWR_PMCR_CSSF; } else - #elif defined(STM32H7) + #elif defined(STM32H7) || defined(STM32N6) if (PWR->CPUCR & PWR_CPUCR_SBF || PWR->CPUCR & PWR_CPUCR_STOPF) { // came out of standby or stop mode reset_cause = PYB_RESET_DEEPSLEEP; @@ -291,12 +291,12 @@ static mp_obj_t mp_machine_unique_id(void) { } // Resets the pyboard in a manner similar to pushing the external RESET button. -NORETURN static void mp_machine_reset(void) { +MP_NORETURN static void mp_machine_reset(void) { powerctrl_mcu_reset(); } // Activate the bootloader without BOOT* pins. -NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { +MP_NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { #if MICROPY_HW_ENABLE_USB pyb_usb_dev_deinit(); #endif @@ -323,6 +323,19 @@ NORETURN void mp_machine_bootloader(size_t n_args, const mp_obj_t *args) { // get or set the MCU frequencies static mp_obj_t mp_machine_get_freq(void) { + #if defined(STM32N6) + LL_RCC_ClocksTypeDef clocks; + LL_RCC_GetSystemClocksFreq(&clocks); + mp_obj_t tuple[] = { + mp_obj_new_int(clocks.CPUCLK_Frequency), + mp_obj_new_int(clocks.SYSCLK_Frequency), + mp_obj_new_int(clocks.HCLK_Frequency), + mp_obj_new_int(clocks.PCLK1_Frequency), + mp_obj_new_int(clocks.PCLK2_Frequency), + mp_obj_new_int(clocks.PCLK4_Frequency), + mp_obj_new_int(clocks.PCLK5_Frequency), + }; + #else mp_obj_t tuple[] = { mp_obj_new_int(HAL_RCC_GetSysClockFreq()), mp_obj_new_int(HAL_RCC_GetHCLKFreq()), @@ -331,11 +344,12 @@ static mp_obj_t mp_machine_get_freq(void) { mp_obj_new_int(HAL_RCC_GetPCLK2Freq()), #endif }; + #endif return mp_obj_new_tuple(MP_ARRAY_SIZE(tuple), tuple); } static void mp_machine_set_freq(size_t n_args, const mp_obj_t *args) { - #if defined(STM32F0) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32G0) + #if defined(STM32F0) || defined(STM32L0) || defined(STM32L1) || defined(STM32L4) || defined(STM32G0) || defined(STM32N6) mp_raise_NotImplementedError(MP_ERROR_TEXT("machine.freq set not supported yet")); #else mp_int_t sysclk = mp_obj_get_int(args[0]); diff --git a/ports/stm32/modtime.c b/ports/stm32/modtime.c index ff1495a5d9..87a4536b04 100644 --- a/ports/stm32/modtime.c +++ b/ports/stm32/modtime.c @@ -59,5 +59,5 @@ static mp_obj_t mp_time_time_get(void) { RTC_TimeTypeDef time; HAL_RTC_GetTime(&RTCHandle, &time, RTC_FORMAT_BIN); HAL_RTC_GetDate(&RTCHandle, &date, RTC_FORMAT_BIN); - return mp_obj_new_int(timeutils_seconds_since_epoch(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds)); + return timeutils_obj_from_timestamp(timeutils_seconds_since_epoch(2000 + date.Year, date.Month, date.Date, time.Hours, time.Minutes, time.Seconds)); } diff --git a/ports/stm32/mpconfigboard_common.h b/ports/stm32/mpconfigboard_common.h index e01a4d4b87..9fa9bf7714 100644 --- a/ports/stm32/mpconfigboard_common.h +++ b/ports/stm32/mpconfigboard_common.h @@ -64,8 +64,12 @@ // Whether machine.bootloader() will enter the bootloader via reset, or direct jump. #ifndef MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET +#if defined(STM32N6) +#define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (0) +#else #define MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET (1) #endif +#endif // Whether to enable ROMFS on the internal flash. #ifndef MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH @@ -77,6 +81,11 @@ #define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI (0) #endif +// Whether to enable ROMFS on external XSPI flash. +#ifndef MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI +#define MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI (0) +#endif + // Whether to enable ROMFS partition 0. #ifndef MICROPY_HW_ROMFS_ENABLE_PART0 #define MICROPY_HW_ROMFS_ENABLE_PART0 (0) @@ -465,6 +474,16 @@ #define MICROPY_HW_MAX_UART (5) #define MICROPY_HW_MAX_LPUART (1) +// Configuration for STM32N6 series +#elif defined(STM32N6) + +#define MP_HAL_UNIQUE_ID_ADDRESS (UID_BASE) +#define PYB_EXTI_NUM_VECTORS (20) // only EXTI[15:0], RTC and TAMP currently supported +#define MICROPY_HW_MAX_I2C (4) +#define MICROPY_HW_MAX_TIMER (18) +#define MICROPY_HW_MAX_UART (10) +#define MICROPY_HW_MAX_LPUART (1) + // Configuration for STM32WB series #elif defined(STM32WB) @@ -589,8 +608,16 @@ (op) == BDEV_IOCTL_INIT ? spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (uint32_t)MICROPY_HW_BDEV_SPIFLASH_CONFIG) : \ spi_bdev_ioctl(MICROPY_HW_BDEV_SPIFLASH, (op), (arg)) \ ) -#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(MICROPY_HW_BDEV_SPIFLASH, (dest), (bl), (n)) -#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(MICROPY_HW_BDEV_SPIFLASH, (src), (bl), (n)) +#ifndef MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES +#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES (0) +#endif +#define MICROPY_HW_BDEV_SPIFLASH_OFFSET_BLOCKS (MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / FLASH_BLOCK_SIZE) +#define MICROPY_HW_BDEV_READBLOCKS(dest, bl, n) spi_bdev_readblocks(MICROPY_HW_BDEV_SPIFLASH, (dest), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BLOCKS + (bl), (n)) +#define MICROPY_HW_BDEV_WRITEBLOCKS(src, bl, n) spi_bdev_writeblocks(MICROPY_HW_BDEV_SPIFLASH, (src), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BLOCKS + (bl), (n)) +#endif + +#if defined(STM32N6) +#define MICROPY_FATFS_MAX_SS (4096) #endif // Whether to enable caching for external SPI flash, to allow block writes that are diff --git a/ports/stm32/mpconfigport.h b/ports/stm32/mpconfigport.h index d1d6fe249b..b910188c5a 100644 --- a/ports/stm32/mpconfigport.h +++ b/ports/stm32/mpconfigport.h @@ -76,12 +76,13 @@ #ifndef MICROPY_FLOAT_IMPL // can be configured by each board via mpconfigboard.mk #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #endif +#define MICROPY_TIME_SUPPORT_Y1969_AND_BEFORE (1) #define MICROPY_USE_INTERNAL_ERRNO (1) #define MICROPY_SCHEDULER_STATIC_NODES (1) #define MICROPY_SCHEDULER_DEPTH (8) #define MICROPY_VFS (1) #ifndef MICROPY_VFS_ROM -#define MICROPY_VFS_ROM (MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH || MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI) +#define MICROPY_VFS_ROM (MICROPY_HW_ROMFS_ENABLE_INTERNAL_FLASH || MICROPY_HW_ROMFS_ENABLE_EXTERNAL_QSPI || MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI) #endif // control over Python builtins @@ -205,20 +206,6 @@ extern const struct _mp_obj_type_t network_lan_type; #define MICROPY_HW_NIC_ETH #endif -#if MICROPY_PY_NETWORK_CYW43 -extern const struct _mp_obj_type_t mp_network_cyw43_type; -#define MICROPY_HW_NIC_CYW43 { MP_ROM_QSTR(MP_QSTR_WLAN), MP_ROM_PTR(&mp_network_cyw43_type) }, -#else -#define MICROPY_HW_NIC_CYW43 -#endif - -#if MICROPY_PY_NETWORK_WIZNET5K -extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k; -#define MICROPY_HW_NIC_WIZNET5K { MP_ROM_QSTR(MP_QSTR_WIZNET5K), MP_ROM_PTR(&mod_network_nic_type_wiznet5k) }, -#else -#define MICROPY_HW_NIC_WIZNET5K -#endif - // extra constants #define MICROPY_PORT_CONSTANTS \ MACHINE_BUILTIN_MODULE_CONSTANTS \ @@ -231,8 +218,6 @@ extern const struct _mp_obj_type_t mod_network_nic_type_wiznet5k; #define MICROPY_PORT_NETWORK_INTERFACES \ MICROPY_HW_NIC_ETH \ - MICROPY_HW_NIC_CYW43 \ - MICROPY_HW_NIC_WIZNET5K \ MICROPY_BOARD_NETWORK_INTERFACES \ #define MP_STATE_PORT MP_STATE_VM diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c index dfd50cebd3..fcd08cbd84 100644 --- a/ports/stm32/mphalport.c +++ b/ports/stm32/mphalport.c @@ -20,7 +20,7 @@ const byte mp_hal_status_to_errno_table[4] = { uint8_t mp_hal_unique_id_address[12]; #endif -NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status) { mp_raise_OSError(mp_hal_status_to_errno_table[status]); } @@ -105,7 +105,7 @@ void mp_hal_gpio_clock_enable(GPIO_TypeDef *gpio) { #elif defined(STM32F4) || defined(STM32F7) #define AHBxENR AHB1ENR #define AHBxENR_GPIOAEN_Pos RCC_AHB1ENR_GPIOAEN_Pos - #elif defined(STM32H7) + #elif defined(STM32H7) || defined(STM32N6) #define AHBxENR AHB4ENR #define AHBxENR_GPIOAEN_Pos RCC_AHB4ENR_GPIOAEN_Pos #elif defined(STM32L0) diff --git a/ports/stm32/mphalport.h b/ports/stm32/mphalport.h index e520bc54ae..03b0f8e772 100644 --- a/ports/stm32/mphalport.h +++ b/ports/stm32/mphalport.h @@ -37,7 +37,7 @@ static inline int mp_hal_status_to_neg_errno(HAL_StatusTypeDef status) { return -mp_hal_status_to_errno_table[status]; } -NORETURN void mp_hal_raise(HAL_StatusTypeDef status); +MP_NORETURN void mp_hal_raise(HAL_StatusTypeDef status); void mp_hal_set_interrupt_char(int c); // -1 to disable // Atomic section helpers. diff --git a/ports/stm32/mpu.h b/ports/stm32/mpu.h index a87c04a58d..8713fe8370 100644 --- a/ports/stm32/mpu.h +++ b/ports/stm32/mpu.h @@ -28,7 +28,7 @@ #include "irq.h" -#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32H7) || defined(STM32WB) +#if (defined(STM32F4) && defined(MICROPY_HW_ETH_MDC)) || defined(STM32F7) || defined(STM32G4) || defined(STM32H7) || defined(STM32WB) #define MPU_REGION_ETH (MPU_REGION_NUMBER0) #define MPU_REGION_QSPI1 (MPU_REGION_NUMBER1) @@ -137,18 +137,26 @@ static inline void mpu_config_end(uint32_t irq_state) { enable_irq(irq_state); } -#elif defined(STM32H5) +#elif defined(STM32H5) || defined(STM32N6) #define MPU_REGION_SIG (MPU_REGION_NUMBER0) #define MPU_REGION_ETH (MPU_REGION_NUMBER1) -#define MPU_REGION_LAST_USED (MPU_REGION_NUMBER1) +#define MPU_REGION_DMA_UNCACHED_1 (MPU_REGION_NUMBER2) +#define MPU_REGION_DMA_UNCACHED_2 (MPU_REGION_NUMBER3) +#define MPU_REGION_LAST_USED (MPU_REGION_NUMBER3) #define ST_DEVICE_SIGNATURE_BASE (0x08fff800) #define ST_DEVICE_SIGNATURE_LIMIT (0x08ffffff) // STM32H5 Cortex-M33 MPU works differently from older cores. // Macro only takes region size in bytes, Attributes are coded in mpu_config_region(). +#define MPU_CONFIG_DISABLE (0) #define MPU_CONFIG_ETH(size) (size) +#define MPU_CONFIG_UNCACHED(size) (size) + +#if defined(STM32N6) +#define MPU_REGION_SIZE_32B (32) +#endif static inline void mpu_init(void) { // Configure attribute 0, inner-outer non-cacheable (=0x44). @@ -180,8 +188,12 @@ static inline uint32_t mpu_config_start(void) { } static inline void mpu_config_region(uint32_t region, uint32_t base_addr, uint32_t size) { - if (region == MPU_REGION_ETH) { - // Configure region 1 to make DMA memory non-cacheable. + if (size == 0) { + // Disable MPU for this region. + MPU->RNR = region; + MPU->RLAR &= ~MPU_RLAR_EN_Msk; + } else if (region == MPU_REGION_ETH || region == MPU_REGION_DMA_UNCACHED_1 || region == MPU_REGION_DMA_UNCACHED_2) { + // Configure region to make DMA memory non-cacheable. __DMB(); // Configure attribute 1, inner-outer non-cacheable (=0x44). diff --git a/ports/stm32/pin_defs_stm32.h b/ports/stm32/pin_defs_stm32.h index 6c67b64924..645ec5b2df 100644 --- a/ports/stm32/pin_defs_stm32.h +++ b/ports/stm32/pin_defs_stm32.h @@ -39,6 +39,10 @@ enum { PORT_I, PORT_J, PORT_K, + PORT_L, + PORT_M, + PORT_N, + PORT_O, }; // Must have matching entries in SUPPORTED_FN in boards/make-pins.py diff --git a/ports/stm32/powerctrl.c b/ports/stm32/powerctrl.c index eea009e2d7..a750e8f5be 100644 --- a/ports/stm32/powerctrl.c +++ b/ports/stm32/powerctrl.c @@ -26,10 +26,10 @@ #include "py/mperrno.h" #include "py/mphal.h" +#include "boardctrl.h" #include "powerctrl.h" #include "rtc.h" #include "extmod/modbluetooth.h" -#include "py/mpconfig.h" #ifndef NO_QSTR #include "genhdr/pllfreqtable.h" #endif @@ -46,7 +46,7 @@ static uint32_t __attribute__((unused)) micropy_hw_hse_value = HSE_VALUE; static uint32_t __attribute__((unused)) micropy_hw_clk_pllm = MICROPY_HW_CLK_PLLM; #endif -#if defined(STM32H5) || defined(STM32H7) +#if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) #define RCC_SR RSR #if defined(STM32H747xx) #define RCC_SR_SFTRSTF RCC_RSR_SFT2RSTF @@ -63,7 +63,7 @@ static uint32_t __attribute__((unused)) micropy_hw_clk_pllm = MICROPY_HW_CLK_PLL #define POWERCTRL_GET_VOLTAGE_SCALING() PWR_REGULATOR_VOLTAGE_SCALE0 #elif defined(STM32H723xx) #define POWERCTRL_GET_VOLTAGE_SCALING() LL_PWR_GetRegulVoltageScaling() -#elif defined(STM32H5) +#elif defined(STM32H5) || defined(STM32N6) #define POWERCTRL_GET_VOLTAGE_SCALING() LL_PWR_GetRegulVoltageScaling() #else #define POWERCTRL_GET_VOLTAGE_SCALING() \ @@ -115,7 +115,7 @@ static inline void powerctrl_disable_hsi_if_unused(void) { #endif } -NORETURN void powerctrl_mcu_reset(void) { +MP_NORETURN void powerctrl_mcu_reset(void) { #if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET *BL_STATE_PTR = BL_STATE_INVALID; #if __DCACHE_PRESENT == 1 @@ -125,7 +125,7 @@ NORETURN void powerctrl_mcu_reset(void) { NVIC_SystemReset(); } -NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, uint32_t bl_addr) { +MP_NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, uint32_t bl_addr) { __asm volatile ( "ldr r2, [r1, #0]\n" // get address of stack pointer "msr msp, r2\n" // get stack pointer @@ -135,7 +135,13 @@ NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0, ui MP_UNREACHABLE; } -NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) { +MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr) { + #if defined(STM32N6) + LL_PWR_EnableBkUpAccess(); + TAMP_S->BKP31R = r0; + NVIC_SystemReset(); + #endif + #if MICROPY_HW_ENTER_BOOTLOADER_VIA_RESET // Enter the bootloader via a reset, so everything is reset (including WDT). @@ -169,6 +175,8 @@ void powerctrl_check_enter_bootloader(void) { #endif } +#if !defined(STM32N6) + #if !defined(STM32F0) && !defined(STM32L0) && !defined(STM32WB) && !defined(STM32WL) typedef struct _sysclk_scaling_table_entry_t { @@ -781,6 +789,8 @@ static void powerctrl_low_power_exit_wb55() { #endif // !defined(STM32F0) && !defined(STM32G0) && !defined(STM32L0) && !defined(STM32L1) && !defined(STM32L4) +#endif + void powerctrl_enter_stop_mode(void) { // Disable IRQs so that the IRQ that wakes the device from stop mode is not // executed until after the clocks are reconfigured @@ -809,7 +819,7 @@ void powerctrl_enter_stop_mode(void) { __HAL_RCC_WAKEUPSTOP_CLK_CONFIG(RCC_STOP_WAKEUPCLOCK_MSI); #endif - #if !defined(STM32F0) && !defined(STM32G0) && !defined(STM32G4) && !defined(STM32L0) && !defined(STM32L1) && !defined(STM32L4) && !defined(STM32WB) && !defined(STM32WL) + #if !defined(STM32F0) && !defined(STM32G0) && !defined(STM32G4) && !defined(STM32L0) && !defined(STM32L1) && !defined(STM32L4) && !defined(STM32N6) && !defined(STM32WB) && !defined(STM32WL) // takes longer to wake but reduces stop current HAL_PWREx_EnableFlashPowerDown(); #endif @@ -848,6 +858,8 @@ void powerctrl_enter_stop_mode(void) { #if defined(STM32F7) HAL_PWR_EnterSTOPMode((PWR_CR1_LPDS | PWR_CR1_LPUDS | PWR_CR1_FPDS | PWR_CR1_UDEN), PWR_STOPENTRY_WFI); + #elif defined(STM32N6) + HAL_PWR_EnterSTOPMode(PWR_MAINREGULATOR_ON, PWR_STOPENTRY_WFI); #else HAL_PWR_EnterSTOPMode(PWR_LOWPOWERREGULATOR_ON, PWR_STOPENTRY_WFI); #endif @@ -912,6 +924,19 @@ void powerctrl_enter_stop_mode(void) { while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_PLL1) { } + #elif defined(STM32N6) + + // Enable PLL1, and switch the CPU and system clock source to use PLL1. + LL_RCC_PLL1_Enable(); + while (!LL_RCC_PLL1_IsReady()) { + } + LL_RCC_SetCpuClkSource(LL_RCC_CPU_CLKSOURCE_IC1); + while (LL_RCC_GetCpuClkSource() != LL_RCC_CPU_CLKSOURCE_STATUS_IC1) { + } + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_IC2_IC6_IC11); + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_IC2_IC6_IC11) { + } + #else // defined(STM32H5) // enable PLL @@ -1016,9 +1041,47 @@ void powerctrl_enter_stop_mode(void) { enable_irq(irq_state); } -NORETURN void powerctrl_enter_standby_mode(void) { +#if defined(STM32N6) + +// Upon wake from standby, STM32N6 can resume execution from retained SRAM1. +// Place a small bootloader there which initialises XSPI in memory-mapped mode +// and jumps to the main application entry point. + +#include "xspi.h" + +extern uint32_t _estack; + +void Reset_Handler(void); + +void iram_bootloader_reset(void) { + #if defined(MICROPY_BOARD_LEAVE_STANDBY) + MICROPY_BOARD_LEAVE_STANDBY; + #endif + xspi_init(); + Reset_Handler(); +} + +// Very simple ARM vector table. +const uint32_t iram_bootloader_isr_vector[] = { + (uint32_t)&_estack, + (uint32_t)&iram_bootloader_reset, +}; + +#endif + +MP_NORETURN void powerctrl_enter_standby_mode(void) { rtc_init_finalise(); + #if defined(STM32N6) + // Upon wake from standby, jump to the code at SRAM1. + // A board can reconfigure this in MICROPY_BOARD_ENTER_STANDBY if needed. + LL_PWR_EnableTCMSBRetention(); + LL_PWR_EnableTCMFLXSBRetention(); + LL_APB4_GRP2_EnableClock(LL_APB4_GRP2_PERIPH_SYSCFG); + SCB_CleanDCache(); + SYSCFG->INITSVTORCR = (uint32_t)&iram_bootloader_isr_vector[0]; + #endif + #if defined(MICROPY_BOARD_ENTER_STANDBY) MICROPY_BOARD_ENTER_STANDBY #endif @@ -1039,6 +1102,13 @@ NORETURN void powerctrl_enter_standby_mode(void) { mp_bluetooth_deinit(); #endif + #if defined(STM32N6) + + // Clear all WKUPx flags. + LL_PWR_ClearFlag_WU(); + + #else + // We need to clear the PWR wake-up-flag before entering standby, since // the flag may have been set by a previous wake-up event. Furthermore, // we need to disable the wake-up sources while clearing this flag, so @@ -1135,6 +1205,8 @@ NORETURN void powerctrl_enter_standby_mode(void) { powerctrl_low_power_prep_wb55(); #endif + #endif + // enter standby mode HAL_PWR_EnterSTANDBYMode(); diff --git a/ports/stm32/powerctrl.h b/ports/stm32/powerctrl.h index 5b92405611..724ab58366 100644 --- a/ports/stm32/powerctrl.h +++ b/ports/stm32/powerctrl.h @@ -34,19 +34,25 @@ void stm32_system_init(void); #else static inline void stm32_system_init(void) { SystemInit(); + + #if defined(STM32N6) + // The ROM bootloader uses PLL1 to set the CPU to 400MHz, so update + // the value of SystemCoreClock to reflect the hardware state. + SystemCoreClockUpdate(); + #endif } #endif void SystemClock_Config(void); -NORETURN void powerctrl_mcu_reset(void); -NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr); +MP_NORETURN void powerctrl_mcu_reset(void); +MP_NORETURN void powerctrl_enter_bootloader(uint32_t r0, uint32_t bl_addr); void powerctrl_check_enter_bootloader(void); void powerctrl_config_systick(void); int powerctrl_rcc_clock_config_pll(RCC_ClkInitTypeDef *rcc_init, uint32_t sysclk_mhz, bool need_pllsai); int powerctrl_set_sysclk(uint32_t sysclk, uint32_t ahb, uint32_t apb1, uint32_t apb2); void powerctrl_enter_stop_mode(void); -NORETURN void powerctrl_enter_standby_mode(void); +MP_NORETURN void powerctrl_enter_standby_mode(void); #endif // MICROPY_INCLUDED_STM32_POWERCTRL_H diff --git a/ports/stm32/powerctrlboot.c b/ports/stm32/powerctrlboot.c index 31dae527c1..059d2a45da 100644 --- a/ports/stm32/powerctrlboot.c +++ b/ports/stm32/powerctrlboot.c @@ -47,9 +47,15 @@ void stm32_system_init(void) { #endif void powerctrl_config_systick(void) { + #if defined(STM32N6) + uint32_t systick_source_freq = HAL_RCC_GetCpuClockFreq(); + #else + uint32_t systick_source_freq = HAL_RCC_GetHCLKFreq(); + #endif + // Configure SYSTICK to run at 1kHz (1ms interval) SysTick->CTRL |= SYSTICK_CLKSOURCE_HCLK; - SysTick_Config(HAL_RCC_GetHCLKFreq() / 1000); + SysTick_Config(systick_source_freq / 1000); NVIC_SetPriority(SysTick_IRQn, IRQ_PRI_SYSTICK); #if !BUILDING_MBOOT && (defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32WB)) @@ -410,6 +416,124 @@ void SystemClock_Config(void) { DBGMCU->CR &= ~(DBGMCU_CR_DBG_SLEEP | DBGMCU_CR_DBG_STOP | DBGMCU_CR_DBG_STANDBY); #endif } + +#elif defined(STM32N6) + +void SystemClock_Config(void) { + // Enable HSI. + LL_RCC_HSI_Enable(); + while (!LL_RCC_HSI_IsReady()) { + } + + // Switch the CPU clock source to HSI. + LL_RCC_SetCpuClkSource(LL_RCC_CPU_CLKSOURCE_HSI); + while (LL_RCC_GetCpuClkSource() != LL_RCC_CPU_CLKSOURCE_STATUS_HSI) { + } + + // Switch the system clock source to HSI. + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_HSI); + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_HSI) { + } + + // Disable all ICx clocks. + RCC->DIVENCR = 0x000fffff; + + // This doesn't work, VOSRDY never becomes active. + #if 0 + LL_PWR_SetRegulVoltageScaling(LL_PWR_REGU_VOLTAGE_SCALE0); + while (!LL_PWR_IsActiveFlag_VOSRDY()) { + } + #endif + + // Enable HSE. + LL_RCC_HSE_Enable(); + while (!LL_RCC_HSE_IsReady()) { + } + + // Disable PLL1. + LL_RCC_PLL1_Disable(); + while (LL_RCC_PLL1_IsReady()) { + } + + // Configure PLL1 for use as system clock. + LL_RCC_PLL1_SetSource(LL_RCC_PLLSOURCE_HSE); + LL_RCC_PLL1_DisableBypass(); + LL_RCC_PLL1_DisableFractionalModulationSpreadSpectrum(); + LL_RCC_PLL1_SetM(MICROPY_HW_CLK_PLLM); + LL_RCC_PLL1_SetN(MICROPY_HW_CLK_PLLN); + LL_RCC_PLL1_SetP1(MICROPY_HW_CLK_PLLP1); + LL_RCC_PLL1_SetP2(MICROPY_HW_CLK_PLLP2); + LL_RCC_PLL1_SetFRACN(MICROPY_HW_CLK_PLLFRAC); + LL_RCC_PLL1P_Enable(); + + // Enable PLL1. + LL_RCC_PLL1_Enable(); + while (!LL_RCC_PLL1_IsReady()) { + } + + // Configure IC1, IC2, IC6, IC11. + LL_RCC_IC1_SetSource(LL_RCC_ICCLKSOURCE_PLL1); + LL_RCC_IC1_SetDivider(1); + LL_RCC_IC1_Enable(); + LL_RCC_IC2_SetSource(LL_RCC_ICCLKSOURCE_PLL1); + LL_RCC_IC2_SetDivider(2); + LL_RCC_IC2_Enable(); + LL_RCC_IC6_SetSource(LL_RCC_ICCLKSOURCE_PLL1); + LL_RCC_IC6_SetDivider(1); + LL_RCC_IC6_Enable(); + LL_RCC_IC11_SetSource(LL_RCC_ICCLKSOURCE_PLL1); + LL_RCC_IC11_SetDivider(1); + LL_RCC_IC11_Enable(); + + // Configure IC14 at 100MHz for slower peripherals. + LL_RCC_IC14_SetSource(LL_RCC_ICCLKSOURCE_PLL1); + LL_RCC_IC14_SetDivider(8); + LL_RCC_IC14_Enable(); + + // Enable buses. + LL_BUS_EnableClock(LL_APB5 | LL_APB4 | LL_APB3 | LL_APB2 | LL_APB1 | LL_AHB5 | LL_AHB4 | LL_AHB3 | LL_AHB2 | LL_AHB1); + LL_MISC_EnableClock(LL_PER); + + // Configure bus dividers. + LL_RCC_SetAHBPrescaler(LL_RCC_AHB_DIV_2); + LL_RCC_SetAPB1Prescaler(LL_RCC_APB1_DIV_1); + LL_RCC_SetAPB2Prescaler(LL_RCC_APB2_DIV_1); + LL_RCC_SetAPB4Prescaler(LL_RCC_APB4_DIV_1); + LL_RCC_SetAPB5Prescaler(LL_RCC_APB5_DIV_1); + + // Switch the CPU clock source to IC1 (connected to PLL1). + LL_RCC_SetCpuClkSource(LL_RCC_CPU_CLKSOURCE_IC1); + while (LL_RCC_GetCpuClkSource() != LL_RCC_CPU_CLKSOURCE_STATUS_IC1) { + } + + // Switch the system clock source to IC2/IC6/IC11 (connected to PLL1). + LL_RCC_SetSysClkSource(LL_RCC_SYS_CLKSOURCE_IC2_IC6_IC11); + while (LL_RCC_GetSysClkSource() != LL_RCC_SYS_CLKSOURCE_STATUS_IC2_IC6_IC11) { + } + + // ADC clock configuration, HCLK/2. + LL_RCC_SetADCClockSource(LL_RCC_ADC_CLKSOURCE_HCLK); + LL_RCC_SetADCPrescaler(2 - 1); + + // USB clock configuration. + #if MICROPY_HW_ENABLE_USB + + // Select HSE/2 as output of direct HSE signal. + LL_RCC_HSE_SelectHSEDiv2AsDiv2Clock(); + + // Select HSE/2 for OTG1 clock source. + LL_RCC_SetClockSource(LL_RCC_OTGPHY1_CLKSOURCE_HSE_DIV_2_OSC); + LL_RCC_SetClockSource(LL_RCC_OTGPHY1CKREF_CLKSOURCE_HSE_DIV_2_OSC); + LL_RCC_SetOTGPHYClockSource(LL_RCC_OTGPHY1_CLKSOURCE_HSE_DIV_2_OSC); + LL_RCC_SetOTGPHYCKREFClockSource(LL_RCC_OTGPHY1CKREF_CLKSOURCE_HSE_DIV_2_OSC); + + #endif + + // Reconfigure clock state and SysTick. + SystemCoreClockUpdate(); + powerctrl_config_systick(); +} + #elif defined(STM32WB) void SystemClock_Config(void) { diff --git a/ports/stm32/qspi.c b/ports/stm32/qspi.c index 781aae803e..2ef9a4d018 100644 --- a/ports/stm32/qspi.c +++ b/ports/stm32/qspi.c @@ -213,11 +213,13 @@ static int qspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) { qspi_memory_map(); break; case MP_QSPI_IOCTL_MEMORY_MODIFIED: { + #if defined(__ICACHE_PRESENT) && (__ICACHE_PRESENT == 1U) uintptr_t *addr_len = (uintptr_t *)arg; volatile void *addr = (volatile void *)(QSPI_MAP_ADDR + addr_len[0]); size_t len = addr_len[1]; SCB_InvalidateICache_by_Addr(addr, len); SCB_InvalidateDCache_by_Addr(addr, len); + #endif break; } } diff --git a/ports/stm32/resethandler_iram.s b/ports/stm32/resethandler_iram.s new file mode 100644 index 0000000000..49a8b40068 --- /dev/null +++ b/ports/stm32/resethandler_iram.s @@ -0,0 +1,82 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2018-2025 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. + */ + + .syntax unified + .cpu cortex-m4 + .thumb + + .section .text.Reset_Handler + .global Reset_Handler + .type Reset_Handler, %function + +Reset_Handler: + /* Save the first argument to pass through to stm32_main */ + mov r4, r0 + + /* Load the stack pointer */ + ldr sp, =_estack + + /* Initialise the iram section */ + ldr r1, =_siiram + ldr r2, =_siram + ldr r3, =_eiram + b .iram_copy_entry + nop +.iram_copy_loop: + ldr r0, [r1], #4 /* Should be 4-aligned to be as fast as possible */ + str r0, [r2], #4 +.iram_copy_entry: + cmp r2, r3 + bcc .iram_copy_loop + + /* Initialise the data section */ + ldr r1, =_sidata + ldr r2, =_sdata + ldr r3, =_edata + b .data_copy_entry +.data_copy_loop: + ldr r0, [r1], #4 /* Should be 4-aligned to be as fast as possible */ + str r0, [r2], #4 +.data_copy_entry: + cmp r2, r3 + bcc .data_copy_loop + + /* Zero out the BSS section */ + movs r0, #0 + ldr r1, =_sbss + ldr r2, =_ebss + b .bss_zero_entry +.bss_zero_loop: + str r0, [r1], #4 /* Should be 4-aligned to be as fast as possible */ +.bss_zero_entry: + cmp r1, r2 + bcc .bss_zero_loop + + /* Jump to the main code */ + mov r0, r4 + b stm32_main + + .size Reset_Handler, .-Reset_Handler diff --git a/ports/stm32/rtc.c b/ports/stm32/rtc.c index 8dadc4a88d..b90d17149b 100644 --- a/ports/stm32/rtc.c +++ b/ports/stm32/rtc.c @@ -100,6 +100,10 @@ static bool rtc_need_init_finalise = false; #define RCC_BDCR_LSEBYP RCC_CSR_LSEBYP #endif +#if defined(STM32N6) +#define RCC_DBP_TIMEOUT_VALUE (5) +#endif + void rtc_init_start(bool force_init) { // Enable the RTC APB bus clock, to communicate with the RTC. #if defined(STM32H5) @@ -129,6 +133,32 @@ void rtc_init_start(bool force_init) { if (!force_init) { bool rtc_running = false; + #if defined(STM32N6) + if (LL_RCC_IsEnabledRTC() + && LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSE + && LL_RCC_LSE_IsReady()) { + // LSE is enabled & ready --> no need to (re-)init RTC + rtc_running = true; + // remove Backup Domain write protection + HAL_PWR_EnableBkUpAccess(); + // Clear source Reset Flag + __HAL_RCC_CLEAR_RESET_FLAGS(); + // provide some status information + rtc_info |= 0x40000; + } else if (LL_RCC_IsEnabledRTC() + && LL_RCC_GetRTCClockSource() == LL_RCC_RTC_CLKSOURCE_LSI) { + // LSI configured as the RTC clock source --> no need to (re-)init RTC + rtc_running = true; + // remove Backup Domain write protection + HAL_PWR_EnableBkUpAccess(); + // Clear source Reset Flag + __HAL_RCC_CLEAR_RESET_FLAGS(); + // Turn the LSI on (it may need this even if the RTC is running) + LL_RCC_LSI_Enable(); + // provide some status information + rtc_info |= 0x80000; + } + #else uint32_t bdcr = RCC->BDCR; if ((bdcr & (RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL | RCC_BDCR_LSEON | RCC_BDCR_LSERDY)) == (RCC_BDCR_RTCEN | RCC_BDCR_RTCSEL_0 | RCC_BDCR_LSEON | RCC_BDCR_LSERDY)) { @@ -157,6 +187,7 @@ void rtc_init_start(bool force_init) { // provide some status information rtc_info |= 0x80000; } + #endif if (rtc_running) { // Provide information about the registers that indicated the RTC is running. @@ -296,7 +327,7 @@ static HAL_StatusTypeDef PYB_RCC_OscConfig(RCC_OscInitTypeDef *RCC_OscInitStruct return HAL_TIMEOUT; } } - #elif defined(STM32H5) + #elif defined(STM32H5) || defined(STM32N6) // Wait for Backup domain Write protection disable while (!LL_PWR_IsEnabledBkUpAccess()) { if (HAL_GetTick() - tickstart > RCC_DBP_TIMEOUT_VALUE) { @@ -381,7 +412,7 @@ static HAL_StatusTypeDef PYB_RTC_Init(RTC_HandleTypeDef *hrtc) { #elif defined(STM32F7) hrtc->Instance->OR &= (uint32_t) ~RTC_OR_ALARMTYPE; hrtc->Instance->OR |= (uint32_t)(hrtc->Init.OutPutType); - #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32WL) + #elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32N6) || defined(STM32WL) hrtc->Instance->CR &= (uint32_t) ~RTC_CR_TAMPALRM_TYPE_Msk; hrtc->Instance->CR |= (uint32_t)(hrtc->Init.OutPutType); #else @@ -413,7 +444,14 @@ static void PYB_RTC_MspInit_Kick(RTC_HandleTypeDef *hrtc, bool rtc_use_lse, bool RCC_OscInitTypeDef RCC_OscInitStruct; RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_LSI | RCC_OSCILLATORTYPE_LSE; + #if defined(STM32N6) + RCC_OscInitStruct.PLL1.PLLState = RCC_PLL_NONE; + RCC_OscInitStruct.PLL2.PLLState = RCC_PLL_NONE; + RCC_OscInitStruct.PLL3.PLLState = RCC_PLL_NONE; + RCC_OscInitStruct.PLL4.PLLState = RCC_PLL_NONE; + #else RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE; + #endif #if MICROPY_HW_RTC_USE_BYPASS if (rtc_use_byp) { RCC_OscInitStruct.LSEState = RCC_LSE_BYPASS; @@ -651,6 +689,8 @@ MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_rtc_datetime_obj, 1, 2, pyb_rtc_datetime #define RTC_WKUP_IRQn RTC_IRQn #elif defined(STM32G0) #define RTC_WKUP_IRQn RTC_TAMP_IRQn +#elif defined(STM32N6) +#define RTC_WKUP_IRQn RTC_S_IRQn #endif // wakeup(None) @@ -759,8 +799,9 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { #if defined(STM32G0) || defined(STM32G4) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) EXTI->IMR1 |= 1 << EXTI_RTC_WAKEUP; EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP; - #elif defined(STM32H5) + #elif defined(STM32H5) || defined(STM32N6) EXTI->IMR1 |= 1 << EXTI_RTC_WAKEUP; + EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP; #elif defined(STM32H7) EXTI_D1->IMR1 |= 1 << EXTI_RTC_WAKEUP; EXTI->RTSR1 |= 1 << EXTI_RTC_WAKEUP; @@ -772,8 +813,8 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { // clear interrupt flags #if defined(STM32G0) || defined(STM32G4) || defined(STM32WL) RTC->ICSR &= ~RTC_ICSR_WUTWF; - #elif defined(STM32H5) - RTC->SCR = RTC_SCR_CWUTF; + #elif defined(STM32H5) || defined(STM32N6) + LL_RTC_ClearFlag_WUT(RTC); #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) RTC->SR &= ~RTC_SR_WUTF; #else @@ -783,7 +824,7 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { EXTI->PR1 = 1 << EXTI_RTC_WAKEUP; #elif defined(STM32H7) EXTI_D1->PR1 = 1 << EXTI_RTC_WAKEUP; - #elif defined(STM32G0) || defined(STM32H5) + #elif defined(STM32G0) || defined(STM32H5) || defined(STM32N6) // Do nothing #else EXTI->PR = 1 << EXTI_RTC_WAKEUP; @@ -799,7 +840,7 @@ mp_obj_t pyb_rtc_wakeup(size_t n_args, const mp_obj_t *args) { RTC->WPR = 0xff; // disable external interrupts on line EXTI_RTC_WAKEUP - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) EXTI->IMR1 &= ~(1 << EXTI_RTC_WAKEUP); #elif defined(STM32H7) EXTI_D1->IMR1 |= 1 << EXTI_RTC_WAKEUP; diff --git a/ports/stm32/sdcard.c b/ports/stm32/sdcard.c index 706d6315c4..b91fa3a9c2 100644 --- a/ports/stm32/sdcard.c +++ b/ports/stm32/sdcard.c @@ -40,7 +40,7 @@ #if MICROPY_HW_ENABLE_SDCARD || MICROPY_HW_ENABLE_MMCARD -#if defined(STM32F7) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) +#if defined(STM32F7) || defined(STM32H5) || defined(STM32H7) || defined(STM32L4) || defined(STM32N6) // The H7/F7/L4 have 2 SDMMC peripherals, but at the moment this driver only supports // using one of them in a given build, selected by MICROPY_HW_SDCARD_SDMMC. @@ -104,7 +104,7 @@ #define SDIO_HARDWARE_FLOW_CONTROL_DISABLE SDMMC_HARDWARE_FLOW_CONTROL_DISABLE #define SDIO_HARDWARE_FLOW_CONTROL_ENABLE SDMMC_HARDWARE_FLOW_CONTROL_ENABLE -#if defined(STM32H5) || defined(STM32H7) +#if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) #define SDIO_TRANSFER_CLK_DIV SDMMC_NSpeed_CLK_DIV #define SDIO_USE_GPDMA 0 #else @@ -214,7 +214,7 @@ static void sdmmc_msp_init(void) { // enable SDIO clock SDMMC_CLK_ENABLE(); - #if defined(STM32H7) + #if defined(STM32H7) || defined(STM32N6) // Reset SDMMC SDMMC_FORCE_RESET(); SDMMC_RELEASE_RESET(); @@ -270,7 +270,7 @@ static HAL_StatusTypeDef sdmmc_init_sd(void) { // SD device interface configuration sdmmc_handle.sd.Instance = SDIO; sdmmc_handle.sd.Init.ClockEdge = SDIO_CLOCK_EDGE_RISING; - #if !defined(STM32H5) && !defined(STM32H7) + #if !defined(STM32H5) && !defined(STM32H7) && !defined(STM32N6) sdmmc_handle.sd.Init.ClockBypass = SDIO_CLOCK_BYPASS_DISABLE; #endif sdmmc_handle.sd.Init.ClockPowerSave = SDIO_CLOCK_POWER_SAVE_ENABLE; diff --git a/ports/stm32/sdio.c b/ports/stm32/sdio.c index 99d05a5155..de82ceadc5 100644 --- a/ports/stm32/sdio.c +++ b/ports/stm32/sdio.c @@ -77,7 +77,11 @@ static volatile uint8_t *sdmmc_buf_top; #define SDMMC_IRQHandler SDMMC2_IRQHandler #define SDMMC_CLK_ENABLE() __HAL_RCC_SDMMC2_CLK_ENABLE() #define SDMMC_CLK_DISABLE() __HAL_RCC_SDMMC2_CLK_DISABLE() +#if defined(STM32N6) +#define SDMMC_IS_CLK_DISABLED() (!__HAL_RCC_SDMMC2_IS_CLK_ENABLED()) +#else #define SDMMC_IS_CLK_DISABLED() __HAL_RCC_SDMMC2_IS_CLK_DISABLED() +#endif #define STATIC_AF_SDMMC_CK STATIC_AF_SDMMC2_CK #define STATIC_AF_SDMMC_CMD STATIC_AF_SDMMC2_CMD #define STATIC_AF_SDMMC_D0 STATIC_AF_SDMMC2_D0 @@ -96,9 +100,17 @@ static volatile uint8_t *sdmmc_buf_top; #define MICROPY_HW_SDIO_CMD (pin_D2) #endif -#if defined(STM32H7) +#if defined(STM32H7) || defined(STM32N6) static uint32_t safe_divide(uint32_t denom) { + #if defined(STM32N6) + #if MICROPY_HW_SDIO_SDMMC == 1 + uint32_t num = LL_RCC_GetSDMMCClockFreq(LL_RCC_SDMMC1_CLKSOURCE); + #else + uint32_t num = LL_RCC_GetSDMMCClockFreq(LL_RCC_SDMMC2_CLKSOURCE); + #endif + #else uint32_t num = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SDMMC); + #endif uint32_t divres; divres = num / (2U * denom); @@ -119,11 +131,15 @@ void sdio_init(uint32_t irq_pri) { mp_hal_pin_config_alt_static(MICROPY_HW_SDIO_CMD, MP_HAL_PIN_MODE_ALT, MP_HAL_PIN_PULL_UP, STATIC_AF_SDMMC_CMD); SDMMC_CLK_ENABLE(); // enable SDIO peripheral + #if defined(STM32N6) + LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_SDMMC1); + LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_SDMMC2); + #endif SDMMC_TypeDef *SDIO = SDMMC; #if defined(STM32F7) SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | (120 - 2); // 1-bit, 400kHz - #elif defined(STM32H7) + #elif defined(STM32H7) || defined(STM32N6) SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | safe_divide(400000U); // 1-bit, 400kHz #else SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_PWRSAV | (120 / 2); // 1-bit, 400kHz @@ -172,7 +188,7 @@ void sdio_enable_high_speed_4bit(void) { mp_hal_delay_us(10); #if defined(STM32F7) SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0 | SDMMC_CLKCR_BYPASS /*| SDMMC_CLKCR_PWRSAV*/; // 4-bit, 48MHz - #elif defined(STM32H7) + #elif defined(STM32H7) || defined(STM32N6) SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0 | safe_divide(48000000U); // 4-bit, 48MHz #else SDIO->CLKCR = SDMMC_CLKCR_HWFC_EN | SDMMC_CLKCR_WIDBUS_0; // 4-bit, 48MHz @@ -199,7 +215,7 @@ void SDMMC_IRQHandler(void) { sdmmc_irq_state = SDMMC_IRQ_STATE_DONE; return; } - #if defined(STM32H7) + #if defined(STM32H7) || defined(STM32N6) if (!sdmmc_dma) { while (sdmmc_buf_cur < sdmmc_buf_top && (SDMMC->STA & SDMMC_STA_DPSMACT) && !(SDMMC->STA & SDMMC_STA_RXFIFOE)) { *(uint32_t *)sdmmc_buf_cur = SDMMC->FIFO; @@ -413,11 +429,15 @@ int sdio_transfer_cmd53(bool write, uint32_t block_size, uint32_t arg, size_t le dma_nohal_init(&dma_SDIO_0, dma_config); dma_nohal_start(&dma_SDIO_0, dma_src, dma_dest, dma_len); #else + #if defined(STM32N6) + SDMMC->IDMABASER = (uint32_t)buf; + #else SDMMC->IDMABASE0 = (uint32_t)buf; + #endif SDMMC->IDMACTRL = SDMMC_IDMA_IDMAEN; #endif } else { - #if defined(STM32H7) + #if defined(STM32H7) || defined(STM32N6) SDMMC->IDMACTRL = 0; #endif } diff --git a/ports/stm32/spi.c b/ports/stm32/spi.c index 96dd170652..248075579a 100644 --- a/ports/stm32/spi.c +++ b/ports/stm32/spi.c @@ -106,7 +106,7 @@ const spi_t spi_obj[6] = { #error "spi_obj needs updating for new value of MICROPY_HW_SUBGHZSPI_ID" #endif -#if defined(STM32H5) || defined(STM32H7) +#if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) // STM32H5/H7 HAL requires SPI IRQs to be enabled and handled. #if defined(MICROPY_HW_SPI1_SCK) void SPI1_IRQHandler(void) { @@ -176,6 +176,18 @@ void spi_init0(void) { #if defined(MICROPY_HW_SUBGHZSPI_ID) SPIHandleSubGhz.Instance = SUBGHZSPI; #endif + + #if defined(STM32N6) + // SPI1/2/3/6 clock configuration, PCLKx (max 200MHz). + LL_RCC_SetSPIClockSource(LL_RCC_SPI1_CLKSOURCE_PCLK2); + LL_RCC_SetSPIClockSource(LL_RCC_SPI2_CLKSOURCE_PCLK1); + LL_RCC_SetSPIClockSource(LL_RCC_SPI3_CLKSOURCE_PCLK1); + LL_RCC_SetSPIClockSource(LL_RCC_SPI6_CLKSOURCE_PCLK4); + + // SPI4/5 clock configuration, IC14 (max 100MHz). + LL_RCC_SetSPIClockSource(LL_RCC_SPI4_CLKSOURCE_IC14); + LL_RCC_SetSPIClockSource(LL_RCC_SPI5_CLKSOURCE_IC14); + #endif } int spi_find_index(mp_obj_t id) { @@ -256,6 +268,20 @@ static uint32_t spi_get_source_freq(SPI_HandleTypeDef *spi) { } else { return HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_SPI6); } + #elif defined(STM32N6) + if (spi->Instance == SPI1) { + return LL_RCC_GetSPIClockFreq(LL_RCC_SPI1_CLKSOURCE); + } else if (spi->Instance == SPI2) { + return LL_RCC_GetSPIClockFreq(LL_RCC_SPI2_CLKSOURCE); + } else if (spi->Instance == SPI3) { + return LL_RCC_GetSPIClockFreq(LL_RCC_SPI3_CLKSOURCE); + } else if (spi->Instance == SPI4) { + return LL_RCC_GetSPIClockFreq(LL_RCC_SPI4_CLKSOURCE); + } else if (spi->Instance == SPI5) { + return LL_RCC_GetSPIClockFreq(LL_RCC_SPI5_CLKSOURCE); + } else { + return LL_RCC_GetSPIClockFreq(LL_RCC_SPI6_CLKSOURCE); + } #else // !STM32F0, !STM32G0, !STM32H #if defined(SPI2) if (spi->Instance == SPI2) { @@ -455,7 +481,10 @@ int spi_init(const spi_t *self, bool enable_nss_pin) { if (pins[i] == NULL) { continue; } - mp_hal_pin_config_alt(pins[i], mode, pull, AF_FN_SPI, (self - &spi_obj[0]) + 1); + if (!mp_hal_pin_config_alt(pins[i], mode, pull, AF_FN_SPI, (self - &spi_obj[0]) + 1)) { + // Pin does not have SPI alternate function. + return -MP_EINVAL; + } } // init the SPI device @@ -470,7 +499,7 @@ int spi_init(const spi_t *self, bool enable_nss_pin) { dma_invalidate_channel(self->tx_dma_descr); dma_invalidate_channel(self->rx_dma_descr); - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) NVIC_SetPriority(irqn, IRQ_PRI_SPI); HAL_NVIC_EnableIRQ(irqn); #else @@ -724,7 +753,7 @@ void spi_print(const mp_print_t *print, const spi_t *spi_obj, bool legacy) { if (spi->State != HAL_SPI_STATE_RESET) { if (spi->Init.Mode == SPI_MODE_MASTER) { // compute baudrate - #if defined(STM32H5) || defined(STM32H7) + #if defined(STM32H5) || defined(STM32H7) || defined(STM32N6) uint log_prescaler = (spi->Init.BaudRatePrescaler >> 28) + 1; #else uint log_prescaler = (spi->Init.BaudRatePrescaler >> 3) + 1; diff --git a/ports/stm32/spibdev.c b/ports/stm32/spibdev.c index fecd4a9915..d7a75ed240 100644 --- a/ports/stm32/spibdev.c +++ b/ports/stm32/spibdev.c @@ -32,6 +32,16 @@ #if MICROPY_HW_ENABLE_STORAGE +#if MICROPY_HW_RUNS_FROM_EXT_FLASH +// Disable all interrupts. +#define FLASH_WRITE_ENTER uint32_t atomic_state = MICROPY_BEGIN_ATOMIC_SECTION() +#define FLASH_WRITE_EXIT MICROPY_END_ATOMIC_SECTION(atomic_state) +#else +// Prevent cache flushing and USB access. +#define FLASH_WRITE_ENTER uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH) +#define FLASH_WRITE_EXIT restore_irq_pri(basepri) +#endif + int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg) { switch (op) { case BDEV_IOCTL_INIT: @@ -68,6 +78,7 @@ int32_t spi_bdev_ioctl(spi_bdev_t *bdev, uint32_t op, uint32_t arg) { } #if MICROPY_HW_SPIFLASH_ENABLE_CACHE + int spi_bdev_readblocks(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access int ret = mp_spiflash_cached_read(&bdev->spiflash, block_num * FLASH_BLOCK_SIZE, num_blocks * FLASH_BLOCK_SIZE, dest); @@ -87,20 +98,36 @@ int spi_bdev_writeblocks(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_nu return ret; } + +#elif FLASH_BLOCK_SIZE == MP_SPIFLASH_ERASE_BLOCK_SIZE + +int spi_bdev_readblocks(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t num_blocks) { + int ret = spi_bdev_readblocks_raw(bdev, dest, block_num, 0, num_blocks * FLASH_BLOCK_SIZE); + return ret; +} + +int spi_bdev_writeblocks(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_num, uint32_t num_blocks) { + int ret = spi_bdev_eraseblocks_raw(bdev, block_num, num_blocks * FLASH_BLOCK_SIZE); + if (ret == 0) { + ret = spi_bdev_writeblocks_raw(bdev, src, block_num, 0, num_blocks * FLASH_BLOCK_SIZE); + } + return ret; +} + #endif // MICROPY_HW_SPIFLASH_ENABLE_CACHE int spi_bdev_readblocks_raw(spi_bdev_t *bdev, uint8_t *dest, uint32_t block_num, uint32_t block_offset, uint32_t num_bytes) { - uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access + FLASH_WRITE_ENTER; int ret = mp_spiflash_read(&bdev->spiflash, block_num * MP_SPIFLASH_ERASE_BLOCK_SIZE + block_offset, num_bytes, dest); - restore_irq_pri(basepri); + FLASH_WRITE_EXIT; return ret; } int spi_bdev_writeblocks_raw(spi_bdev_t *bdev, const uint8_t *src, uint32_t block_num, uint32_t block_offset, uint32_t num_bytes) { - uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access + FLASH_WRITE_ENTER; int ret = mp_spiflash_write(&bdev->spiflash, block_num * MP_SPIFLASH_ERASE_BLOCK_SIZE + block_offset, num_bytes, src); - restore_irq_pri(basepri); + FLASH_WRITE_EXIT; return ret; } @@ -108,9 +135,9 @@ int spi_bdev_writeblocks_raw(spi_bdev_t *bdev, const uint8_t *src, uint32_t bloc int spi_bdev_eraseblocks_raw(spi_bdev_t *bdev, uint32_t block_num, uint32_t num_bytes) { int ret = 0; while (num_bytes >= MP_SPIFLASH_ERASE_BLOCK_SIZE) { - uint32_t basepri = raise_irq_pri(IRQ_PRI_FLASH); // prevent cache flushing and USB access + FLASH_WRITE_ENTER; ret = mp_spiflash_erase_block(&bdev->spiflash, block_num * MP_SPIFLASH_ERASE_BLOCK_SIZE); - restore_irq_pri(basepri); + FLASH_WRITE_EXIT; if (ret) { break; } diff --git a/ports/stm32/stm32.mk b/ports/stm32/stm32.mk index 718fa8cf06..e6526fc6bd 100644 --- a/ports/stm32/stm32.mk +++ b/ports/stm32/stm32.mk @@ -43,19 +43,17 @@ ifneq ($(BUILDING_MBOOT),1) # Select hardware floating-point support. SUPPORTS_HARDWARE_FP_SINGLE = 0 SUPPORTS_HARDWARE_FP_DOUBLE = 0 -ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx STM32H747xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ)) +ifeq ($(CMSIS_MCU),$(filter $(CMSIS_MCU),STM32F765xx STM32F767xx STM32F769xx STM32H743xx STM32H747xx STM32H750xx STM32H7A3xx STM32H7A3xxQ STM32H7B3xx STM32H7B3xxQ STM32N657xx)) CFLAGS_CORTEX_M += -mfpu=fpv5-d16 -mfloat-abi=hard -mfp16-format=ieee SUPPORTS_HARDWARE_FP_SINGLE = 1 SUPPORTS_HARDWARE_FP_DOUBLE = 1 -else -ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 g0 l0 l1 wl)) +else ifeq ($(MCU_SERIES),$(filter $(MCU_SERIES),f0 g0 l0 l1 wl)) CFLAGS_CORTEX_M += -msoft-float else CFLAGS_CORTEX_M += -mfpu=fpv4-sp-d16 -mfloat-abi=hard -mfp16-format=ieee SUPPORTS_HARDWARE_FP_SINGLE = 1 endif endif -endif # Options for particular MCU series. CFLAGS_MCU_f0 = $(CFLAGS_CORTEX_M) -mtune=cortex-m0 -mcpu=cortex-m0 @@ -68,6 +66,7 @@ CFLAGS_MCU_l1 = $(CFLAGS_CORTEX_M) -mtune=cortex-m3 -mcpu=cortex-m3 CFLAGS_MCU_l4 = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 CFLAGS_MCU_h5 = $(CFLAGS_CORTEX_M) -mtune=cortex-m33 -mcpu=cortex-m33 CFLAGS_MCU_h7 = $(CFLAGS_CORTEX_M) -mtune=cortex-m7 -mcpu=cortex-m7 +CFLAGS_MCU_n6 = $(CFLAGS_CORTEX_M) -mtune=cortex-m55 -mcpu=cortex-m55 -mcmse CFLAGS_MCU_wb = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 CFLAGS_MCU_wl = $(CFLAGS_CORTEX_M) -mtune=cortex-m4 -mcpu=cortex-m4 @@ -81,5 +80,20 @@ MPY_CROSS_MCU_ARCH_l1 = armv7m MPY_CROSS_MCU_ARCH_l4 = armv7m MPY_CROSS_MCU_ARCH_h5 = armv7m MPY_CROSS_MCU_ARCH_h7 = armv7m +MPY_CROSS_MCU_ARCH_n6 = armv7m # really armv8m MPY_CROSS_MCU_ARCH_wb = armv7m MPY_CROSS_MCU_ARCH_wl = armv7m + +# gcc up to 14.2.0 have a known loop-optimisation bug: +# https://gcc.gnu.org/bugzilla/show_bug.cgi?id=116799 +# This bug manifests for Cortex M55 targets, so require a newer compiler on such targets. +ifeq ($(MCU_SERIES),n6) +# Check if GCC version is less than 14.3 +GCC_VERSION := $(shell $(CROSS_COMPILE)gcc -dumpversion | cut -d. -f1-2) +GCC_VERSION_MAJOR := $(shell echo $(GCC_VERSION) | cut -d. -f1) +GCC_VERSION_MINOR := $(shell echo $(GCC_VERSION) | cut -d. -f2) +GCC_VERSION_NUM := $(shell echo $$(($(GCC_VERSION_MAJOR) * 100 + $(GCC_VERSION_MINOR)))) +ifeq ($(shell test $(GCC_VERSION_NUM) -lt 1403 && echo yes),yes) +$(error Error: GCC $(GCC_VERSION) has known issues with Cortex-M55; upgrade to GCC 14.3+ for proper CM55 support) +endif +endif diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c index 4bf509bb94..3639e2f049 100644 --- a/ports/stm32/stm32_it.c +++ b/ports/stm32/stm32_it.c @@ -343,14 +343,22 @@ void OTG_FS_IRQHandler(void) { } #endif #if MICROPY_HW_USB_HS +#if defined(STM32N6) +void USB1_OTG_HS_IRQHandler(void) { + IRQ_ENTER(USB1_OTG_HS_IRQn); + HAL_PCD_IRQHandler(&pcd_hs_handle); + IRQ_EXIT(USB1_OTG_HS_IRQn); +} +#else void OTG_HS_IRQHandler(void) { IRQ_ENTER(OTG_HS_IRQn); HAL_PCD_IRQHandler(&pcd_hs_handle); IRQ_EXIT(OTG_HS_IRQn); } #endif +#endif -#if MICROPY_HW_USB_FS || MICROPY_HW_USB_HS +#if (MICROPY_HW_USB_FS || MICROPY_HW_USB_HS) && !defined(STM32N6) /** * @brief This function handles USB OTG Common FS/HS Wakeup functions. * @param *pcd_handle for FS or HS @@ -421,7 +429,7 @@ void OTG_FS_WKUP_IRQHandler(void) { } #endif -#if MICROPY_HW_USB_HS +#if MICROPY_HW_USB_HS && !defined(STM32N6) /** * @brief This function handles USB OTG HS Wakeup IRQ Handler. * @param None @@ -480,7 +488,7 @@ void ETH_WKUP_IRQHandler(void) { } #endif -#if defined(STM32H5) +#if defined(STM32H5) || defined(STM32N6) void TAMP_IRQHandler(void) { IRQ_ENTER(TAMP_IRQn); Handle_EXTI_Irq(EXTI_RTC_TAMP); @@ -502,6 +510,9 @@ void TAMP_STAMP_IRQHandler(void) { #if defined(STM32H5) void RTC_IRQHandler(void) +#elif defined(STM32N6) +#define RTC_WKUP_IRQn RTC_S_IRQn +void RTC_S_IRQHandler(void) #else void RTC_WKUP_IRQHandler(void) #endif @@ -509,8 +520,8 @@ void RTC_WKUP_IRQHandler(void) IRQ_ENTER(RTC_WKUP_IRQn); #if defined(STM32G0) || defined(STM32G4) || defined(STM32WL) RTC->MISR &= ~RTC_MISR_WUTMF; // clear wakeup interrupt flag - #elif defined(STM32H5) - RTC->SCR = RTC_SCR_CWUTF; // clear wakeup interrupt flag + #elif defined(STM32H5) || defined(STM32N6) + LL_RTC_ClearFlag_WUT(RTC); #elif defined(STM32H7A3xx) || defined(STM32H7A3xxQ) || defined(STM32H7B3xx) || defined(STM32H7B3xxQ) RTC->SR &= ~RTC_SR_WUTF; // clear wakeup interrupt flag #else @@ -520,6 +531,12 @@ void RTC_WKUP_IRQHandler(void) IRQ_EXIT(RTC_WKUP_IRQn); } +#if defined(STM32N6) +void RTC_IRQHandler(void) { + RTC_S_IRQHandler(); +} +#endif + #if defined(STM32F0) || defined(STM32G0) || defined(STM32L0) #if defined(STM32G0) diff --git a/ports/stm32/storage.c b/ports/stm32/storage.c index d810261fbc..d26ac821e5 100644 --- a/ports/stm32/storage.c +++ b/ports/stm32/storage.c @@ -32,6 +32,7 @@ #include "led.h" #include "storage.h" #include "irq.h" +#include "xspi.h" #if MICROPY_HW_ENABLE_STORAGE @@ -44,13 +45,17 @@ static bool storage_is_initialised = false; +#if !defined(STM32N6) static void storage_systick_callback(uint32_t ticks_ms); +#endif void storage_init(void) { if (!storage_is_initialised) { storage_is_initialised = true; + #if !defined(STM32N6) systick_enable_dispatch(SYSTICK_DISPATCH_STORAGE, storage_systick_callback); + #endif MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_INIT, 0); @@ -58,10 +63,12 @@ void storage_init(void) { MICROPY_HW_BDEV2_IOCTL(BDEV_IOCTL_INIT, 0); #endif + #if !defined(STM32N6) // Enable the flash IRQ, which is used to also call our storage IRQ handler // It must go at the same priority as USB (see comment in irq.h). NVIC_SetPriority(FLASH_IRQn, IRQ_PRI_FLASH); HAL_NVIC_EnableIRQ(FLASH_IRQn); + #endif } } @@ -77,6 +84,7 @@ uint32_t storage_get_block_count(void) { #endif } +#if !defined(STM32N6) static void storage_systick_callback(uint32_t ticks_ms) { if (STORAGE_IDLE_TICK(ticks_ms)) { // Trigger a FLASH IRQ to execute at a lower priority @@ -96,6 +104,7 @@ void FLASH_IRQHandler(void) { #endif IRQ_EXIT(FLASH_IRQn); } +#endif void storage_flush(void) { MICROPY_HW_BDEV_IOCTL(BDEV_IOCTL_SYNC, 0); @@ -235,11 +244,11 @@ int storage_write_blocks(const uint8_t *src, uint32_t block_num, uint32_t num_bl // Board defined an external SPI flash for use with extended block protocol #define MICROPY_HW_BDEV_BLOCKSIZE_EXT (MP_SPIFLASH_ERASE_BLOCK_SIZE) #define MICROPY_HW_BDEV_READBLOCKS_EXT(dest, bl, off, len) \ - (spi_bdev_readblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (dest), (bl), (off), (len))) + (spi_bdev_readblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (dest), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / MP_SPIFLASH_ERASE_BLOCK_SIZE + (bl), (off), (len))) #define MICROPY_HW_BDEV_WRITEBLOCKS_EXT(src, bl, off, len) \ - (spi_bdev_writeblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (src), (bl), (off), (len))) + (spi_bdev_writeblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (src), MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / MP_SPIFLASH_ERASE_BLOCK_SIZE + (bl), (off), (len))) #define MICROPY_HW_BDEV_ERASEBLOCKS_EXT(bl, len) \ - (spi_bdev_eraseblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, (bl), (len))) + (spi_bdev_eraseblocks_raw(MICROPY_HW_BDEV_SPIFLASH_EXTENDED, MICROPY_HW_BDEV_SPIFLASH_OFFSET_BYTES / MP_SPIFLASH_ERASE_BLOCK_SIZE + (bl), (len))) #elif (MICROPY_VFS_LFS1 || MICROPY_VFS_LFS2) && MICROPY_HW_ENABLE_INTERNAL_FLASH_STORAGE // Board uses littlefs and internal flash, so enable extended block protocol on internal flash diff --git a/ports/stm32/storage.h b/ports/stm32/storage.h index accf6c3904..75cb0e9c1e 100644 --- a/ports/stm32/storage.h +++ b/ports/stm32/storage.h @@ -28,7 +28,11 @@ #include "drivers/memory/spiflash.h" +#if defined(STM32N6) +#define FLASH_BLOCK_SIZE (4096) +#else #define FLASH_BLOCK_SIZE (512) +#endif #define FLASH_PART1_START_BLOCK (0x100) // Try to match Python-level VFS block protocol where possible for these constants diff --git a/ports/stm32/timer.c b/ports/stm32/timer.c index 9d65b484cd..4ec467d9db 100644 --- a/ports/stm32/timer.c +++ b/ports/stm32/timer.c @@ -261,6 +261,12 @@ uint32_t timer_get_source_freq(uint32_t tim_id) { } } + #elif defined(STM32N6) + + // Timers are clocked either by ck_timg1 or ck_timg2. + // Both of those have the same frequency: sys_bus_ck / prescaler(TIMPRE) + return LL_RCC_GetSystemClockFreq() / (1 << LL_RCC_GetTIMPrescaler()); + #else uint32_t source, clk_div; @@ -846,7 +852,9 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { TIM_ENTRY(1, TIM1_UP_TIM16_IRQn), #endif #endif + TIM_ENTRY(2, TIM2_IRQn), + #if defined(TIM3) #if defined(STM32G0B1xx) || defined(STM32G0C1xx) TIM_ENTRY(3, TIM3_TIM4_IRQn), @@ -854,6 +862,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { TIM_ENTRY(3, TIM3_IRQn), #endif #endif + #if defined(TIM4) #if defined(STM32G0B1xx) || defined(STM32G0C1xx) TIM_ENTRY(3, TIM3_TIM4_IRQn), @@ -861,20 +870,23 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { TIM_ENTRY(4, TIM4_IRQn), #endif #endif + #if defined(TIM5) TIM_ENTRY(5, TIM5_IRQn), #endif + #if defined(TIM6) #if defined(STM32F412Zx) || defined(STM32L1) TIM_ENTRY(6, TIM6_IRQn), #elif defined(STM32G0) TIM_ENTRY(6, TIM6_DAC_LPTIM1_IRQn), - #elif defined(STM32H5) + #elif defined(STM32H5) || defined(STM32N6) TIM_ENTRY(6, TIM6_IRQn), #else TIM_ENTRY(6, TIM6_DAC_IRQn), #endif #endif + #if defined(TIM7) #if defined(STM32G0) TIM_ENTRY(7, TIM7_LPTIM2_IRQn), @@ -894,7 +906,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { #endif #if defined(TIM9) - #if defined(STM32L1) + #if defined(STM32L1) || defined(STM32N6) TIM_ENTRY(9, TIM9_IRQn), #else TIM_ENTRY(9, TIM1_BRK_TIM9_IRQn), @@ -902,7 +914,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { #endif #if defined(TIM10) - #if defined(STM32L1) + #if defined(STM32L1) || defined(STM32N6) TIM_ENTRY(10, TIM10_IRQn), #else TIM_ENTRY(10, TIM1_UP_TIM10_IRQn), @@ -910,7 +922,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { #endif #if defined(TIM11) - #if defined(STM32L1) + #if defined(STM32L1) || defined(STM32N6) TIM_ENTRY(11, TIM11_IRQn), #else TIM_ENTRY(11, TIM1_TRG_COM_TIM11_IRQn), @@ -918,7 +930,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { #endif #if defined(TIM12) - #if defined(STM32H5) + #if defined(STM32H5) || defined(STM32N6) TIM_ENTRY(12, TIM12_IRQn), #else TIM_ENTRY(12, TIM8_BRK_TIM12_IRQn), @@ -926,21 +938,21 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { #endif #if defined(TIM13) - #if defined(STM32H5) + #if defined(STM32H5) || defined(STM32N6) TIM_ENTRY(13, TIM13_IRQn), #else TIM_ENTRY(13, TIM8_UP_TIM13_IRQn), #endif #endif - #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5) + #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32N6) TIM_ENTRY(14, TIM14_IRQn), #elif defined(TIM14) TIM_ENTRY(14, TIM8_TRG_COM_TIM14_IRQn), #endif #if defined(TIM15) - #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) + #if defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) TIM_ENTRY(15, TIM15_IRQn), #else TIM_ENTRY(15, TIM1_BRK_TIM15_IRQn), @@ -950,7 +962,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { #if defined(TIM16) #if defined(STM32G0B1xx) || defined(STM32G0C1xx) TIM_ENTRY(16, TIM16_FDCAN_IT0_IRQn), - #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32WL) + #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WL) TIM_ENTRY(16, TIM16_IRQn), #else TIM_ENTRY(16, TIM1_UP_TIM16_IRQn), @@ -960,7 +972,7 @@ static const uint32_t tim_instance_table[MICROPY_HW_MAX_TIMER] = { #if defined(TIM17) #if defined(STM32G0B1xx) || defined(STM32G0C1xx) TIM_ENTRY(17, TIM17_FDCAN_IT1_IRQn), - #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32WL) + #elif defined(STM32F0) || defined(STM32G0) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WL) TIM_ENTRY(17, TIM17_IRQn), #else TIM_ENTRY(17, TIM1_TRG_COM_TIM17_IRQn), diff --git a/ports/stm32/uart.c b/ports/stm32/uart.c index 91db91395e..9354af4a29 100644 --- a/ports/stm32/uart.c +++ b/ports/stm32/uart.c @@ -91,7 +91,7 @@ #define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_WUFIE) #endif -#elif defined(STM32H7) +#elif defined(STM32H7) || defined(STM32N6) #define USART_CR1_IE_ALL (USART_CR1_IE_BASE | USART_CR1_RXFFIE | USART_CR1_TXFEIE | USART_CR1_EOBIE | USART_CR1_RTOIE | USART_CR1_CMIE) #define USART_CR2_IE_ALL (USART_CR2_IE_BASE) #define USART_CR3_IE_ALL (USART_CR3_IE_BASE | USART_CR3_RXFTIE | USART_CR3_TCBGTIE | USART_CR3_TXFTIE | USART_CR3_WUFIE) @@ -157,6 +157,18 @@ void uart_init0(void) { if (HAL_RCCEx_PeriphCLKConfig(&RCC_PeriphClkInit) != HAL_OK) { MICROPY_BOARD_FATAL_ERROR("HAL_RCCEx_PeriphCLKConfig"); } + #elif defined(STM32N6) + // UART clock configuration, IC14 (max 100MHz). + LL_RCC_SetUSARTClockSource(LL_RCC_USART1_CLKSOURCE_IC14); + LL_RCC_SetUSARTClockSource(LL_RCC_USART2_CLKSOURCE_IC14); + LL_RCC_SetUSARTClockSource(LL_RCC_USART3_CLKSOURCE_IC14); + LL_RCC_SetUARTClockSource(LL_RCC_UART4_CLKSOURCE_IC14); + LL_RCC_SetUARTClockSource(LL_RCC_UART5_CLKSOURCE_IC14); + LL_RCC_SetUSARTClockSource(LL_RCC_USART6_CLKSOURCE_IC14); + LL_RCC_SetUARTClockSource(LL_RCC_UART7_CLKSOURCE_IC14); + LL_RCC_SetUARTClockSource(LL_RCC_UART8_CLKSOURCE_IC14); + LL_RCC_SetUARTClockSource(LL_RCC_UART9_CLKSOURCE_IC14); + LL_RCC_SetUSARTClockSource(LL_RCC_USART10_CLKSOURCE_IC14); #endif } @@ -653,7 +665,7 @@ bool uart_init(machine_uart_obj_t *uart_obj, huart.Init.HwFlowCtl = flow; huart.Init.OverSampling = UART_OVERSAMPLING_16; - #if defined(STM32G4) // H7 and WB also have fifo.. + #if defined(STM32G4) || defined(STM32H7) // WB also has a fifo.. huart.FifoMode = UART_FIFOMODE_ENABLE; #endif @@ -661,7 +673,7 @@ bool uart_init(machine_uart_obj_t *uart_obj, huart.Init.OneBitSampling = UART_ONE_BIT_SAMPLE_DISABLE; #endif - #if defined(STM32H7) || defined(STM32WB) + #if defined(STM32H7) || defined(STM32N6) || defined(STM32WB) // Compute the smallest prescaler that will allow the given baudrate. uint32_t presc = UART_PRESCALER_DIV1; if (uart_obj->uart_id == PYB_LPUART_1) { @@ -689,6 +701,10 @@ bool uart_init(machine_uart_obj_t *uart_obj, uart_obj->is_enabled = true; uart_obj->attached_to_repl = false; + #if defined(STM32F4) || defined(STM32L1) + uart_obj->suppress_idle_irq = true; + #endif + if (bits == UART_WORDLENGTH_9B && parity == UART_PARITY_NONE) { uart_obj->char_mask = 0x1ff; uart_obj->char_width = CHAR_WIDTH_9BIT; @@ -701,6 +717,12 @@ bool uart_init(machine_uart_obj_t *uart_obj, uart_obj->char_width = CHAR_WIDTH_8BIT; } + #if defined(STM32H7) + HAL_UARTEx_SetTxFifoThreshold(&huart, UART_TXFIFO_THRESHOLD_1_8); + HAL_UARTEx_SetRxFifoThreshold(&huart, UART_RXFIFO_THRESHOLD_1_8); + HAL_UARTEx_EnableFifoMode(&huart); + #endif + uart_obj->mp_irq_trigger = 0; uart_obj->mp_irq_obj = NULL; @@ -966,6 +988,29 @@ uint32_t uart_get_source_freq(machine_uart_obj_t *self) { default: break; } + + #elif defined(STM32N6) + + static const uint16_t is_usart = 1 << 10 | 1 << 6 | 1 << 3 | 1 << 2 | 1 << 1; + static const uint32_t clksource[] = { + LL_RCC_USART1_CLKSOURCE, + LL_RCC_USART2_CLKSOURCE, + LL_RCC_USART3_CLKSOURCE, + LL_RCC_UART4_CLKSOURCE, + LL_RCC_UART5_CLKSOURCE, + LL_RCC_USART6_CLKSOURCE, + LL_RCC_UART7_CLKSOURCE, + LL_RCC_UART8_CLKSOURCE, + LL_RCC_UART9_CLKSOURCE, + LL_RCC_USART10_CLKSOURCE, + }; + + if (is_usart & (1 << self->uart_id)) { + uart_clk = LL_RCC_GetUSARTClockFreq(clksource[self->uart_id - 1]); + } else { + uart_clk = LL_RCC_GetUARTClockFreq(clksource[self->uart_id - 1]); + } + #else if (self->uart_id == 1 #if defined(USART6) @@ -991,14 +1036,14 @@ uint32_t uart_get_baudrate(machine_uart_obj_t *self) { #if defined(LPUART1) if (self->uart_id == PYB_LPUART_1) { return LL_LPUART_GetBaudRate(self->uartx, uart_get_source_freq(self) - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) , self->uartx->PRESC #endif ); } #endif return LL_USART_GetBaudRate(self->uartx, uart_get_source_freq(self), - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) self->uartx->PRESC, #endif LL_USART_OVERSAMPLING_16); @@ -1008,7 +1053,7 @@ void uart_set_baudrate(machine_uart_obj_t *self, uint32_t baudrate) { #if defined(LPUART1) if (self->uart_id == PYB_LPUART_1) { LL_LPUART_SetBaudRate(self->uartx, uart_get_source_freq(self), - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) LL_LPUART_PRESCALER_DIV1, #endif baudrate); @@ -1016,7 +1061,7 @@ void uart_set_baudrate(machine_uart_obj_t *self, uint32_t baudrate) { } #endif LL_USART_SetBaudRate(self->uartx, uart_get_source_freq(self), - #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) LL_USART_PRESCALER_DIV1, #endif LL_USART_OVERSAMPLING_16, baudrate); @@ -1067,7 +1112,7 @@ int uart_rx_char(machine_uart_obj_t *self) { return data; } else { // no buffering - #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L0) || defined(STM32L4) || defined(STM32H7) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) int data = self->uartx->RDR & self->char_mask; self->uartx->ICR = USART_ICR_ORECF; // clear ORE if it was set return data; @@ -1141,6 +1186,9 @@ size_t uart_tx_data(machine_uart_obj_t *self, const void *src_in, size_t num_cha // timeout_char by FIFO size + 1. // STM32G4 has 8 words FIFO. timeout = (8 + 1) * self->timeout_char; + #elif defined(STM32H7) + // STM32H7 has 16 words FIFO. + timeout = (16 + 1) * self->timeout_char; #else // The timeout specified here is for waiting for the TX data register to // become empty (ie between chars), as well as for the final char to be @@ -1219,7 +1267,7 @@ void uart_irq_handler(mp_uint_t uart_id) { uint16_t next_head = (self->read_buf_head + 1) % self->read_buf_len; if (next_head != self->read_buf_tail) { // only read data if room in buf - #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32WB) || defined(STM32WL) + #if defined(STM32F0) || defined(STM32F7) || defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32H7) || defined(STM32L0) || defined(STM32L4) || defined(STM32N6) || defined(STM32WB) || defined(STM32WL) int data = self->uartx->RDR; // clears UART_FLAG_RXNE #else self->mp_irq_flags = self->uartx->SR; // resample to get any new flags since next read of DR will clear SR @@ -1265,6 +1313,9 @@ void uart_irq_handler(mp_uint_t uart_id) { self->uartx->CR1 &= ~USART_CR1_RXNEIE; } } + if (self->suppress_idle_irq) { + self->mp_irq_flags &= ~USART_SR_IDLE; + } #else self->uartx->ICR = self->mp_irq_flags & (USART_ICR_IDLECF | USART_ICR_ORECF); #endif @@ -1273,6 +1324,14 @@ void uart_irq_handler(mp_uint_t uart_id) { if (self->mp_irq_trigger & self->mp_irq_flags) { mp_irq_handler(self->mp_irq_obj); } + + #if defined(STM32F4) || defined(STM32L1) + if (did_clear_sr) { + self->suppress_idle_irq = false; + } else { + self->suppress_idle_irq = true; + } + #endif } static mp_uint_t uart_irq_trigger(mp_obj_t self_in, mp_uint_t new_trigger) { diff --git a/ports/stm32/uart.h b/ports/stm32/uart.h index de4b70cdea..d92434c641 100644 --- a/ports/stm32/uart.h +++ b/ports/stm32/uart.h @@ -26,6 +26,7 @@ #ifndef MICROPY_INCLUDED_STM32_UART_H #define MICROPY_INCLUDED_STM32_UART_H +#include "py/mphal.h" #include "shared/runtime/mpirq.h" typedef enum { @@ -63,6 +64,9 @@ typedef struct _machine_uart_obj_t { pyb_uart_t uart_id : 8; bool is_static : 1; bool is_enabled : 1; + #if defined(STM32F4) || defined(STM32L1) + bool suppress_idle_irq : 1; // whether the RX idle IRQ is suppressed (F4/L1 only) + #endif bool attached_to_repl; // whether the UART is attached to REPL byte char_width; // 0 for 7,8 bit chars, 1 for 9 bit chars uint16_t char_mask; // 0x7f for 7 bit, 0xff for 8 bit, 0x1ff for 9 bit diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c index af9dd1d70e..2d70dcb261 100644 --- a/ports/stm32/usb.c +++ b/ports/stm32/usb.c @@ -67,7 +67,7 @@ #define MAX_ENDPOINT(dev_id) ((dev_id) == USB_PHY_FS_ID ? 3 : 5) #elif defined(STM32F7) #define MAX_ENDPOINT(dev_id) ((dev_id) == USB_PHY_FS_ID ? 5 : 8) -#elif defined(STM32H7) +#elif defined(STM32H7) || defined(STM32N6) #define MAX_ENDPOINT(dev_id) (8) #endif diff --git a/ports/stm32/usbd_conf.c b/ports/stm32/usbd_conf.c index 829037ba93..7a9e63c9f3 100644 --- a/ports/stm32/usbd_conf.c +++ b/ports/stm32/usbd_conf.c @@ -51,6 +51,11 @@ PCD_HandleTypeDef pcd_hs_handle; #define USB_OTG_FS USB #endif +#if defined(STM32N6) +#define USB_OTG_HS USB1_OTG_HS +#define OTG_HS_IRQn USB1_OTG_HS_IRQn +#endif + /******************************************************************************* PCD BSP Routines *******************************************************************************/ @@ -191,6 +196,10 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { mp_hal_pin_config(pin_A12, MP_HAL_PIN_MODE_ANALOG, MP_HAL_PIN_PULL_NONE, 0); mp_hal_pin_config_speed(pin_A12, GPIO_SPEED_FREQ_VERY_HIGH); + #elif defined(STM32N6) + + // These MCUs have dedicated USB pins. + #else // Other MCUs have an alternate function for GPIO's to be in USB mode. @@ -220,6 +229,23 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { mp_hal_pin_config(MICROPY_HW_USB_OTG_ID_PIN, MP_HAL_PIN_MODE_ALT_OPEN_DRAIN, MP_HAL_PIN_PULL_UP, otg_alt); #endif + #if defined(STM32N6) + + __HAL_RCC_USB1_OTG_HS_FORCE_RESET(); + __HAL_RCC_USB1_OTG_HS_PHY_FORCE_RESET(); + __HAL_RCC_USB1_OTG_HS_PHY_RELEASE_RESET(); + __HAL_RCC_USB1_OTG_HS_RELEASE_RESET(); + + LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_OTG1); + LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_OTGPHY1); + LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_OTG1); + LL_AHB5_GRP1_EnableClockLowPower(LL_AHB5_GRP1_PERIPH_OTGPHY1); + + // Select 24MHz clock. + MODIFY_REG(USB1_HS_PHYC->USBPHYC_CR, USB_USBPHYC_CR_FSEL, 2 << USB_USBPHYC_CR_FSEL_Pos); + + #else + // Enable calling WFI and correct function of the embedded USB_FS_IN_HS phy __HAL_RCC_USB_OTG_HS_ULPI_CLK_SLEEP_DISABLE(); __HAL_RCC_USB_OTG_HS_CLK_SLEEP_ENABLE(); @@ -235,6 +261,8 @@ void HAL_PCD_MspInit(PCD_HandleTypeDef *hpcd) { __HAL_RCC_USB_OTG_HS_CLK_ENABLE(); + #endif + #else // !MICROPY_HW_USB_HS_IN_FS // Configure USB HS GPIOs @@ -283,7 +311,12 @@ void HAL_PCD_MspDeInit(PCD_HandleTypeDef *hpcd) { #if MICROPY_HW_USB_HS if (hpcd->Instance == USB_OTG_HS) { /* Disable USB FS Clocks */ + #if defined(STM32N6) + LL_AHB5_GRP1_DisableClock(LL_AHB5_GRP1_PERIPH_OTG1); + LL_AHB5_GRP1_DisableClock(LL_AHB5_GRP1_PERIPH_OTGPHY1); + #else __USB_OTG_HS_CLK_DISABLE(); + #endif } #endif @@ -517,7 +550,7 @@ USBD_StatusTypeDef USBD_LL_Init(USBD_HandleTypeDef *pdev, int high_speed, const #if MICROPY_HW_USB_HS_IN_FS - #if defined(STM32F723xx) || defined(STM32F733xx) + #if defined(STM32F723xx) || defined(STM32F733xx) || defined(STM32N6) pcd_hs_handle.Init.phy_itface = USB_OTG_HS_EMBEDDED_PHY; #else pcd_hs_handle.Init.phy_itface = PCD_PHY_EMBEDDED; diff --git a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h index 2c90ce165e..34f0412534 100644 --- a/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h +++ b/ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h @@ -11,7 +11,7 @@ // Work out if we should support USB high-speed device mode #if MICROPY_HW_USB_HS \ - && (!MICROPY_HW_USB_HS_IN_FS || defined(STM32F723xx) || defined(STM32F733xx)) + && (!MICROPY_HW_USB_HS_IN_FS || defined(STM32F723xx) || defined(STM32F733xx) || defined(STM32N6)) #define USBD_SUPPORT_HS_MODE (1) #else #define USBD_SUPPORT_HS_MODE (0) @@ -31,7 +31,11 @@ #else #define CDC_DATA_MAX_PACKET_SIZE CDC_DATA_FS_MAX_PACKET_SIZE #endif +#if defined(STM32N6) +#define MSC_MEDIA_PACKET (4096) // must be at least the SPI flash erase size +#else #define MSC_MEDIA_PACKET (2048) // was 8192; how low can it go whilst still working? +#endif #define HID_DATA_FS_MAX_PACKET_SIZE (64) // endpoint IN & OUT packet size // Maximum number of LUN that can be exposed on the MSC interface diff --git a/ports/stm32/vfs_rom_ioctl.c b/ports/stm32/vfs_rom_ioctl.c index 7592aa22d6..5dbc855861 100644 --- a/ports/stm32/vfs_rom_ioctl.c +++ b/ports/stm32/vfs_rom_ioctl.c @@ -33,6 +33,7 @@ #include "flash.h" #include "qspi.h" #include "storage.h" +#include "xspi.h" #if MICROPY_VFS_ROM_IOCTL @@ -142,6 +143,18 @@ mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { return MP_OBJ_NEW_SMALL_INT(4); } #endif + + #if MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI + if (xspi_is_valid_addr(&xspi_flash2, dest)) { + dest -= xspi_get_xip_base(&xspi_flash2); + dest_max -= xspi_get_xip_base(&xspi_flash2); + int ret = spi_bdev_eraseblocks_raw(MICROPY_HW_ROMFS_XSPI_SPIBDEV_OBJ, dest / MP_SPIFLASH_ERASE_BLOCK_SIZE, dest_max - dest + MP_SPIFLASH_ERASE_BLOCK_SIZE - 1); + if (ret < 0) { + return MP_OBJ_NEW_SMALL_INT(ret); + } + return MP_OBJ_NEW_SMALL_INT(4); + } + #endif } if (cmd == MP_VFS_ROM_IOCTL_WRITE) { @@ -170,6 +183,14 @@ mp_obj_t mp_vfs_rom_ioctl(size_t n_args, const mp_obj_t *args) { return MP_OBJ_NEW_SMALL_INT(ret); } #endif + + #if MICROPY_HW_ROMFS_ENABLE_EXTERNAL_XSPI + if (xspi_is_valid_addr(&xspi_flash2, dest)) { + dest -= xspi_get_xip_base(&xspi_flash2); + int ret = spi_bdev_writeblocks_raw(MICROPY_HW_ROMFS_XSPI_SPIBDEV_OBJ, bufinfo.buf, 0, dest, bufinfo.len); + return MP_OBJ_NEW_SMALL_INT(ret); + } + #endif } return MP_OBJ_NEW_SMALL_INT(-MP_EINVAL); diff --git a/ports/stm32/xspi.c b/ports/stm32/xspi.c new file mode 100644 index 0000000000..b113110c05 --- /dev/null +++ b/ports/stm32/xspi.c @@ -0,0 +1,599 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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. + */ + +// This XSPI driver is currently configured to run in 1-line (SPI) mode. +// It uses the mp_qspi_proto_t QSPI protocol and translates quad-commands +// into 1-line commands. + +#include <string.h> +#include "py/mperrno.h" +#include "py/mphal.h" +#include "xspi.h" + +#if defined(MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2) + +#ifndef MICROPY_HW_XSPI_PRESCALER +#define MICROPY_HW_XSPI_PRESCALER (4) // F_CLK = F_AHB/4 +#endif + +#ifndef MICROPY_HW_XSPI_CS_HIGH_CYCLES +#define MICROPY_HW_XSPI_CS_HIGH_CYCLES (2) // nCS stays high for 4 cycles +#endif + +// Currently hard-coded to use XSPI2 instance. +#define XSPIx (XSPI2) + +// For XSPI2, PN0 through PN12. +#define XSPI2_AF (9) + +typedef struct _xspi_flash_t { + XSPI_TypeDef *xspi; + uintptr_t xip_base; +} xspi_flash_t; + +const xspi_flash_t xspi_flash1 = { + .xspi = XSPI1, + .xip_base = 0x90000000, +}; + +const xspi_flash_t xspi_flash2 = { + .xspi = XSPI2, + .xip_base = 0x70000000, +}; + +static bool xspi_dtr_enabled = false; + +#ifdef pyb_pin_FLASH_RESET +// Can't rely on SysTick being available, so use a busy loop for delays. +// The timing here is approximate and assumes a CPU frequency of 800MHz. +static void xspi_delay_us(unsigned int us) { + while (us--) { + for (unsigned int i = 0; i < 800; ++i) { + __NOP(); + } + } +} +#endif + +static inline void mp_hal_pin_config_alt_speed(mp_hal_pin_obj_t pin, uint32_t pull, uint32_t alt, uint32_t speed) { + mp_hal_pin_config(pin, MP_HAL_PIN_MODE_ALT, pull, alt); + mp_hal_pin_config_speed(pin, speed); +} + +static int xspi_read_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, uint8_t *dest); +static int xspi_write_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src); +static int xspi_read_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, uint32_t num_dummy, size_t len, uint8_t *dest); +static int xspi_write_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src); +static void xspi_memory_map_111(void); +static void xspi_memory_map_888(void); +static void xspi_memory_map_exit(void); + +void xspi_init(void) { + // Configure XSPI pins. + mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_CS, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH); + mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_SCK, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH); + mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_DQS, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH); + mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO0, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH); + mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO1, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH); + mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO2, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH); + mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO3, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH); + mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO4, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH); + mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO5, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH); + mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO6, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH); + mp_hal_pin_config_alt_speed(pyb_pin_XSPIM_P2_IO7, MP_HAL_PIN_PULL_NONE, XSPI2_AF, MP_HAL_PIN_SPEED_VERY_HIGH); + + LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPIM); + LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPI1); + LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPI2); + LL_AHB5_GRP1_ForceReset(LL_AHB5_GRP1_PERIPH_XSPI3); + + LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPIM); + LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPI1); + LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPI2); + LL_AHB5_GRP1_ReleaseReset(LL_AHB5_GRP1_PERIPH_XSPI3); + + LL_RCC_SetXSPIClockSource(LL_RCC_XSPI1_CLKSOURCE_HCLK); + LL_RCC_SetXSPIClockSource(LL_RCC_XSPI2_CLKSOURCE_HCLK); + + LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_XSPIM); + LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_XSPI1); + LL_AHB5_GRP1_EnableClock(LL_AHB5_GRP1_PERIPH_XSPI2); + + // Configure XSPIM in direct mode. + XSPI1->CR &= ~XSPI_CR_EN; + XSPI2->CR &= ~XSPI_CR_EN; + XSPIM->CR = 0; + + // Configure the XSPIx peripheral. + + XSPIx->CR = + 3 << XSPI_CR_FTHRES_Pos // 4 byte must be available to read/write + | 0 << XSPI_CR_MSEL_Pos // FLASH 0 selected + | 0 << XSPI_CR_CSSEL_Pos // use NCS1 as chip select + | 0 << XSPI_CR_DMM_Pos // dual-memory mode disabled + | 1 << XSPI_CR_TCEN_Pos // time-out counter enabled + | 0 << XSPI_CR_DMAEN_Pos // DMA disabled + | 0 << XSPI_CR_ABORT_Pos // no abort request + | 0 << XSPI_CR_EN_Pos // disabled + ; + + XSPIx->DCR1 = + 1 << XSPI_DCR1_MTYP_Pos // Macronix mode + | (MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2 - 3 - 1) << XSPI_DCR1_DEVSIZE_Pos + | (MICROPY_HW_XSPI_CS_HIGH_CYCLES - 1) << XSPI_DCR1_CSHT_Pos + | 0 << XSPI_DCR1_FRCK_Pos // CLK is not free running + | 0 << XSPI_DCR1_CKMODE_Pos // CLK idles at low state + ; + + XSPIx->DCR2 = + 0 << XSPI_DCR2_WRAPSIZE_Pos // separate wrap reads are not supported by the memory + | (MICROPY_HW_XSPI_PRESCALER - 1) << XSPI_DCR2_PRESCALER_Pos + ; + + XSPIx->DCR3 = + 0 + // 10 << XSPI_DCR3_CSBOUND_Pos // transaction boundary at 1024 + ; + + XSPIx->DCR4 = + 0 << XSPI_DCR4_REFRESH_Pos // refresh disabled (it's non-volatile memory) + ; + + XSPIx->TCR = 0; + + // Enable the XSPI peripheral. + XSPIx->CR |= XSPI_CR_EN; + + // XSPIM init + XSPI1->CR &= ~(1 << XSPI_CR_EN_Pos); + XSPI2->CR &= ~(1 << XSPI_CR_EN_Pos); + XSPIM->CR = 0; // can also be (1 << 4) to pass through CS signal + XSPIx->CR |= 1 << XSPI_CR_EN_Pos; + + #ifdef pyb_pin_FLASH_RESET + // Reset SPI flash to make sure it's in a known state (SPI mode). + mp_hal_pin_output(pyb_pin_FLASH_RESET); + mp_hal_pin_low(pyb_pin_FLASH_RESET); + xspi_delay_us(1000); + mp_hal_pin_high(pyb_pin_FLASH_RESET); + xspi_delay_us(10000); + #endif + + // Enable memory-mapped mode. + // Can select either SPI or DTR mode. + if (1) { + xspi_switch_to_dtr(); + xspi_memory_map_888(); + } else { + xspi_memory_map_111(); + } +} + +uint32_t xspi_get_xip_base(const xspi_flash_t *self) { + return self->xip_base; +} + +bool xspi_is_valid_addr(const xspi_flash_t *self, uint32_t addr) { + return self->xip_base <= addr && addr < self->xip_base + 256 * 1024 * 1024; +} + +static int xspi_read_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, uint8_t *dest) { + uint32_t admode = addr_enabled ? 1 : 0; + XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 1 << XSPI_CR_FMODE_Pos; // indirect read mode + XSPIx->CCR = + 1 << XSPI_CCR_DMODE_Pos // data on 1 line + | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size + | admode << XSPI_CCR_ADMODE_Pos // address on 1 line, or disabled + | 1 << XSPI_CCR_IMODE_Pos // instruction on 1 line + ; + XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // 0 dummy cycles + XSPIx->DLR = len - 1; // number of bytes to read + XSPIx->IR = cmd; // read opcode (triggers the start of the transaction if address disabled) + if (addr_enabled) { + XSPIx->AR = addr; // triggers the start of the transaction + } + + #if 0 // untested code + // Read in the data 4 bytes at a time if dest is aligned. + if (((uintptr_t)dest & 3) == 0) { + while (len >= 4) { + while (!(XSPIx->SR & XSPI_SR_FTF)) { + if (XSPIx->SR & XSPI_SR_TEF) { + return -MP_EIO; + } + } + *(uint32_t *)dest = XSPIx->DR; + dest += 4; + len -= 4; + } + } + #endif + + // Read in data 1 byte at a time. + while (len--) { + while (!((XSPIx->SR >> XSPI_SR_FLEVEL_Pos) & 0x3f)) { + if (XSPIx->SR & XSPI_SR_TEF) { + return -MP_EIO; + } + } + *dest++ = *(volatile uint8_t *)&XSPIx->DR; + } + + XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag + + return 0; +} + +static int xspi_write_111_ext(uint8_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src) { + uint32_t dmode = len == 0 ? 0 : 1; + uint32_t admode = addr_enabled ? 1 : 0; + + // Configure and start the transfer. + // Transfer starts with IR write if no address or data, with AR write if no data, + // otherwise with DR write. + + XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 0 << XSPI_CR_FMODE_Pos; // indirect write mode + XSPIx->CCR = + dmode << XSPI_CCR_DMODE_Pos // data on 1 line, or disabled + | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size + | admode << XSPI_CCR_ADMODE_Pos // address on 1 line, or disabled + | 1 << XSPI_CCR_IMODE_Pos // instruction on 1 line + ; + XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // 0 dummy cycles + if (len != 0) { + XSPIx->DLR = len - 1; + } + XSPIx->IR = cmd; // write opcode + if (addr_enabled) { + XSPIx->AR = addr; // address + } + + // Write out the data one byte at a time + while (len--) { + while (!(XSPIx->SR & XSPI_SR_FTF)) { + if (XSPIx->SR & XSPI_SR_TEF) { + return -MP_EIO; + } + } + *(volatile uint8_t *)&XSPIx->DR = *src++; + } + + // Wait for write to finish + while (!(XSPIx->SR & XSPI_SR_TCF)) { + if (XSPIx->SR & XSPI_SR_TEF) { + return -MP_EIO; + } + } + + XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag + + // Wait for peripheral to return to idle. + while (XSPIx->SR & XSPI_SR_BUSY) { + } + + return 0; +} + +static int xspi_read_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, uint32_t num_dummy, size_t len, uint8_t *dest) { + uint32_t admode = addr_enabled ? 4 : 0; + + // Configure and start the transfer. + // Transfer starts with IR write if no address, otherwise with AR write. + + XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 1 << XSPI_CR_FMODE_Pos; // indirect read mode + XSPIx->CCR = + 1 << XSPI_CCR_DQSE_Pos // DQS enabled + | 1 << XSPI_CCR_DDTR_Pos // data DTR enabled + | 4 << XSPI_CCR_DMODE_Pos // data on 8 lines + | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size + | 1 << XSPI_CCR_ADDTR_Pos // address DTR enabled + | admode << XSPI_CCR_ADMODE_Pos // address on 8 lines, or disabled + | 1 << XSPI_CCR_ISIZE_Pos // 16-bit instruction + | 1 << XSPI_CCR_IDTR_Pos // instruction DTR enabled + | 4 << XSPI_CCR_IMODE_Pos // instruction on 8 lines + ; + XSPIx->TCR = num_dummy << XSPI_TCR_DCYC_Pos; // N dummy cycles + XSPIx->DLR = len - 1; + XSPIx->IR = cmd; // read opcode + if (addr_enabled) { + XSPIx->AR = addr; // address + } + + // Read in data 1 byte at a time. + while (len--) { + while (!((XSPIx->SR >> XSPI_SR_FLEVEL_Pos) & 0x3f)) { + if (XSPIx->SR & XSPI_SR_TEF) { + return -MP_EIO; + } + } + *dest++ = *(volatile uint8_t *)&XSPIx->DR; + } + + XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag + + return 0; +} + +static int xspi_write_888_dtr_ext(uint16_t cmd, bool addr_enabled, uint32_t addr, size_t len, const uint8_t *src) { + uint32_t dmode = len == 0 ? 0 : 4; + uint32_t admode = addr_enabled ? 4 : 0; + + // Configure and start the transfer. + // Transfer starts with IR write if no address or data, with AR write if no data, + // otherwise with DR write. + + XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 0 << XSPI_CR_FMODE_Pos; // indirect write mode + XSPIx->CCR = + 1 << XSPI_CCR_DDTR_Pos // data DTR enabled + | dmode << XSPI_CCR_DMODE_Pos // data on 8 lines, or disabled + | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address size + | 1 << XSPI_CCR_ADDTR_Pos // address DTR enabled + | admode << XSPI_CCR_ADMODE_Pos // address on 8 lines, or disabled + | 1 << XSPI_CCR_ISIZE_Pos // 16-bit instruction + | 1 << XSPI_CCR_IDTR_Pos // instruction DTR enabled + | 4 << XSPI_CCR_IMODE_Pos // instruction on 8 lines + ; + XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // 0 dummy cycles + if (len != 0) { + XSPIx->DLR = len - 1; + } + XSPIx->IR = cmd; // write opcode + if (addr_enabled) { + XSPIx->AR = addr; // address + } + + // Write out the data one byte at a time + while (len--) { + while (!(XSPIx->SR & XSPI_SR_FTF)) { + if (XSPIx->SR & XSPI_SR_TEF) { + return -MP_EIO; + } + } + *(volatile uint8_t *)&XSPIx->DR = *src++; + } + + // Wait for write to finish + while (!(XSPIx->SR & XSPI_SR_TCF)) { + if (XSPIx->SR & XSPI_SR_TEF) { + return -MP_EIO; + } + } + + XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag + + // Wait for peripheral to return to idle. + while (XSPIx->SR & XSPI_SR_BUSY) { + } + + return 0; +} + +static void xspi_memory_map_111(void) { + XSPIx->CCR = + 1 << XSPI_CCR_DMODE_Pos // data on 1 line + | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address + | 1 << XSPI_CCR_ADMODE_Pos // address on 1 line + | 1 << XSPI_CCR_IMODE_Pos // instruction on 1 line + ; + + XSPIx->TCR = 0 << XSPI_TCR_DCYC_Pos; // no dummy cycles + XSPIx->IR = 0x13; // READ4B + XSPIx->LPTR = 1024; // timeout period in number of CLK cycles + + // Enable the XSPI peripheral in memory-mapped mode. + XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 3 << XSPI_CR_FMODE_Pos; +} + +static void xspi_memory_map_888(void) { + XSPIx->CCR = + 1 << XSPI_CCR_DQSE_Pos // DQS enabled + | 1 << XSPI_CCR_DDTR_Pos // data DTR enabled + | 4 << XSPI_CCR_DMODE_Pos // data on 8 lines + | 3 << XSPI_CCR_ADSIZE_Pos // 32-bit address + | 1 << XSPI_CCR_ADDTR_Pos // address DTR enabled + | 4 << XSPI_CCR_ADMODE_Pos // address on 8 lines + | 1 << XSPI_CCR_ISIZE_Pos // 16-bit instruction + | 1 << XSPI_CCR_IDTR_Pos // instruction DTR enabled + | 4 << XSPI_CCR_IMODE_Pos // instruction on 8 lines + ; + + XSPIx->TCR = 20 << XSPI_TCR_DCYC_Pos; // 20 dummy cycles for reading (minimum, flash may insert more by holding DQS low) + XSPIx->IR = 0xee11; // octal DTR read mode (8DTRD) + XSPIx->LPTR = 1024; // timeout period in number of CLK cycles + + // Enable the XSPI peripheral in memory-mapped mode. + XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 3 << XSPI_CR_FMODE_Pos; +} + +static void xspi_memory_map_exit(void) { + // Abort any ongoing transfer if peripheral is busy. + if (XSPIx->SR & XSPI_SR_BUSY) { + XSPIx->CR |= XSPI_CR_ABORT; + while (!(XSPIx->SR & XSPI_SR_TCF)) { + } + XSPIx->FCR = XSPI_FCR_CTCF; // clear TC flag + while (XSPIx->SR & XSPI_SR_BUSY) { + } + } + XSPIx->CR = (XSPIx->CR & ~XSPI_CR_FMODE_Msk) | 0 << XSPI_CR_FMODE_Pos; // indirect write mode +} + +void xspi_switch_to_dtr(void) { + uint8_t buf[4]; + + // WREN. + xspi_write_111_ext(0x06, false, 0, 0, NULL); + + // Wait WEL=1, with small timeout. + for (unsigned int i = 0; i < 100; ++i) { + xspi_read_111_ext(0x05, false, 0, 1, buf); + if (buf[0] & 2) { + break; + } + } + + // Switch to DOPI DTR mode. + buf[0] = 2; + xspi_write_111_ext(0x72, true, 0x00000000, 1, buf); + + xspi_dtr_enabled = true; +} + +void xspi_switch_to_spi(void) { + uint8_t buf[4]; + + // WREN. + xspi_write_888_dtr_ext(0x06f9, false, 0, 0, NULL); + + // Wait WEL=1, with small timeout. + for (unsigned int i = 0; i < 100; ++i) { + xspi_read_111_ext(0x05, false, 0, 1, buf); + if (buf[0] & 2) { + break; + } + } + + // Switch to SPI mode. + buf[0] = 0; + buf[1] = 0; + xspi_write_888_dtr_ext(0x728d, true, 0x00000000, 2, buf); + + xspi_dtr_enabled = false; +} + +static int xspi_ioctl(void *self_in, uint32_t cmd, uintptr_t arg) { + xspi_flash_t *self = self_in; + switch (cmd) { + case MP_QSPI_IOCTL_INIT: + // XSPI must be manually initialise by calling `xspi_init()` at boot. + // Here, just determine if it's in SPI or DTR mode. + xspi_dtr_enabled = XSPIx->IR == 0xee11; + break; + case MP_QSPI_IOCTL_BUS_ACQUIRE: + xspi_memory_map_exit(); + break; + case MP_QSPI_IOCTL_BUS_RELEASE: + if (xspi_dtr_enabled) { + xspi_memory_map_888(); + } else { + xspi_memory_map_111(); + } + break; + case MP_QSPI_IOCTL_MEMORY_MODIFIED: { + uintptr_t *addr_len = (uintptr_t *)arg; + volatile void *addr = (volatile void *)(self->xip_base + addr_len[0]); + size_t len = addr_len[1]; + SCB_InvalidateICache_by_Addr(addr, len); + SCB_InvalidateDCache_by_Addr(addr, len); + break; + } + } + return 0; // success +} + +// These commands may be passed to this function. +#define CMD_WREN (0x06) +#define CMD_RSTEN (0x66) +#define CMD_RESET (0x99) +#define CMD_SLEEP (0xb9) +#define CMD_AWAKE (0xab) +static int xspi_write_cmd_data(void *self_in, uint8_t cmd, size_t len, uint32_t data) { + if (xspi_dtr_enabled) { + uint16_t cmd16 = 0; + if (cmd == CMD_WREN) { + cmd16 = 0x06f9; + } else if (cmd == CMD_SLEEP) { + cmd16 = 0xb946; + } else if (cmd == CMD_AWAKE) { + cmd16 = 0xab54; + } + return xspi_write_888_dtr_ext(cmd16, false, 0, len, (const uint8_t *)&data); + } + return xspi_write_111_ext(cmd, false, 0, len, (const uint8_t *)&data); +} + +// These commands may be passed to this function. +#define CMD_WRITE (0x02) +#define CMD_WRITE_32 (0x12) +#define CMD_SEC_ERASE (0x20) +#define CMD_SEC_ERASE_32 (0x21) +static int xspi_write_cmd_addr_data(void *self_in, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src) { + // Convert 24-bit address commands to 32-bit address commands. + if (cmd == CMD_WRITE) { + cmd = CMD_WRITE_32; + } else if (cmd == CMD_SEC_ERASE) { + cmd = CMD_SEC_ERASE_32; + } + if (xspi_dtr_enabled) { + uint16_t cmd16 = 0; + if (cmd == CMD_WRITE_32) { + cmd16 = 0x12ed; + } else if (cmd == CMD_SEC_ERASE_32) { + cmd16 = 0x21de; + } + return xspi_write_888_dtr_ext(cmd16, true, addr, len, src); + } + return xspi_write_111_ext(cmd, true, addr, len, src); +} + +// These commands may be passed to this function. +#define CMD_RDSR (0x05) +#define CMD_RD_DEVID (0x9f) +static int xspi_read_cmd(void *self_in, uint8_t cmd, size_t len, uint32_t *dest) { + (void)self_in; + if (xspi_dtr_enabled) { + uint16_t cmd16 = 0; + uint32_t num_dummy = 0; + if (cmd == CMD_RDSR) { + cmd16 = 0x05fa; + num_dummy = 4; + len = 2; + } else if (cmd == CMD_RD_DEVID) { + // TODO this doesn't really work, because result is in STR format. + cmd16 = 0x9f60; + num_dummy = 4; + } + return xspi_read_888_dtr_ext(cmd16, true, 0, num_dummy, len, (uint8_t *)dest); + } + return xspi_read_111_ext(cmd, false, 0, len, (uint8_t *)dest); +} + +static int xspi_direct_read(void *self_in, uint32_t addr, size_t len, uint8_t *dest) { + xspi_flash_t *self = self_in; + memcpy(dest, (const void *)(self->xip_base + addr), len); + return 0; +} + +const mp_qspi_proto_t xspi_proto = { + .ioctl = xspi_ioctl, + .write_cmd_data = xspi_write_cmd_data, + .write_cmd_addr_data = xspi_write_cmd_addr_data, + .read_cmd = xspi_read_cmd, + .read_cmd_qaddr_qdata = NULL, // unused because .direct_read is set below, and caching is disabled + .direct_read = xspi_direct_read, +}; + +#endif // defined(MICROPY_HW_XSPIFLASH_SIZE_BITS_LOG2) diff --git a/ports/stm32/xspi.h b/ports/stm32/xspi.h new file mode 100644 index 0000000000..cd21662966 --- /dev/null +++ b/ports/stm32/xspi.h @@ -0,0 +1,43 @@ +/* + * This file is part of the MicroPython project, http://micropython.org/ + * + * The MIT License (MIT) + * + * Copyright (c) 2024-2025 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. + */ +#ifndef MICROPY_INCLUDED_STM32_XSPI_H +#define MICROPY_INCLUDED_STM32_XSPI_H + +#include "drivers/bus/qspi.h" + +typedef struct _xspi_flash_t xspi_flash_t; + +extern const mp_qspi_proto_t xspi_proto; +extern const xspi_flash_t xspi_flash1; +extern const xspi_flash_t xspi_flash2; + +void xspi_init(void); +uint32_t xspi_get_xip_base(const xspi_flash_t *self); +bool xspi_is_valid_addr(const xspi_flash_t *self, uint32_t addr); +void xspi_switch_to_spi(void); +void xspi_switch_to_dtr(void); + +#endif // MICROPY_INCLUDED_STM32_XSPI_H diff --git a/ports/unix/Makefile b/ports/unix/Makefile index 88fa1af045..8bd58a2542 100644 --- a/ports/unix/Makefile +++ b/ports/unix/Makefile @@ -52,6 +52,10 @@ CFLAGS += $(INC) $(CWARN) -std=gnu99 -DUNIX $(COPT) -I$(VARIANT_DIR) $(CFLAGS_EX # This option has no effect on 64-bit builds. CFLAGS += -D_FILE_OFFSET_BITS=64 +# Force the use of 64-bits for time_t in C library functions on 32-bit platforms. +# This option has no effect on 64-bit builds. +CFLAGS += -D_TIME_BITS=64 + # Debugging/Optimization ifdef DEBUG COPT ?= -Og @@ -256,21 +260,24 @@ endif include $(TOP)/py/mkrules.mk -.PHONY: test test_full +.PHONY: test test_full_no_native test_full test: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py -test_full: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py +test_full_no_native: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py $(eval DIRNAME=ports/$(notdir $(CURDIR))) cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py -d thread - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --emit native cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) -d basics float micropython - cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) --emit native -d basics float micropython cat $(TOP)/tests/basics/0prelim.py | ./$(BUILD)/$(PROG) | grep -q 'abc' +test_full: $(BUILD)/$(PROG) $(TOP)/tests/run-tests.py test_full_no_native + $(eval DIRNAME=ports/$(notdir $(CURDIR))) + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --emit native + cd $(TOP)/tests && MICROPY_MICROPYTHON=../$(DIRNAME)/$(BUILD)/$(PROG) ./run-tests.py --via-mpy $(RUN_TESTS_MPY_CROSS_FLAGS) --emit native -d basics float micropython + test_gcov: test_full gcov -o $(BUILD)/py $(TOP)/py/*.c gcov -o $(BUILD)/extmod $(TOP)/extmod/*.c diff --git a/ports/unix/README.md b/ports/unix/README.md index b7aa6e3fef..656d4303d3 100644 --- a/ports/unix/README.md +++ b/ports/unix/README.md @@ -155,3 +155,21 @@ The default compiler optimisation level is -Os, or -Og if `DEBUG=1` is set. Setting the variable `COPT` will explicitly set the optimisation level. For example `make [other arguments] COPT=-O0 DEBUG=1` will build a binary with no optimisations, assertions enabled, and debug symbols. + +### Sanitizers + +Sanitizers are extra runtime checks supported by gcc and clang. The CI process +supports building with the "undefined behavior" (UBSan) or "address" (ASan) +sanitizers. The script `tools/ci.sh` is the source of truth about how to build +and run in these modes. + +Several classes of checks are disabled via compiler flags: + +* In the undefined behavior sanitizer, checks based on the presence of the + `non_null` attribute are disabled because the code makes technically incorrect + calls like `memset(NULL, 0, 0)`. A future C standard is likely to permit such + calls. +* In the address sanitizer, `detect_stack_use_after_return` is disabled. This + check is intended to make sure locals in a "returned from" stack frame are not + used. However, this mode interferes with various assumptions that + MicroPython's stack checking, NLR, and GC rely on. diff --git a/ports/unix/coverage.c b/ports/unix/coverage.c index 2b65b47fc5..0df6bf279a 100644 --- a/ports/unix/coverage.c +++ b/ports/unix/coverage.c @@ -184,6 +184,18 @@ static void pairheap_test(size_t nops, int *ops) { mp_printf(&mp_plat_print, "\n"); } +static mp_sched_node_t mp_coverage_sched_node; +static bool coverage_sched_function_continue; + +static void coverage_sched_function(mp_sched_node_t *node) { + (void)node; + mp_printf(&mp_plat_print, "scheduled function\n"); + if (coverage_sched_function_continue) { + // Re-scheduling node will cause it to run again next time scheduled functions are run + mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function); + } +} + // function to run extra tests for things that can't be checked by scripts static mp_obj_t extra_coverage(void) { // mp_printf (used by ports that don't have a native printf) @@ -208,6 +220,7 @@ static mp_obj_t extra_coverage(void) { mp_printf(&mp_plat_print, "%X\n", 0x80000000); // should print unsigned mp_printf(&mp_plat_print, "abc\n%"); // string ends in middle of format specifier mp_printf(&mp_plat_print, "%%\n"); // literal % character + mp_printf(&mp_plat_print, ".%-3s.\n", "a"); // left adjust } // GC @@ -463,6 +476,38 @@ static mp_obj_t extra_coverage(void) { mp_int_t value_signed; mpz_as_int_checked(&mpz, &value_signed); mp_printf(&mp_plat_print, "%d\n", (int)value_signed); + + // hash the zero mpz integer + mpz_set_from_int(&mpz, 0); + mp_printf(&mp_plat_print, "%d\n", mpz_hash(&mpz)); + + // convert the mpz zero integer to int + mp_printf(&mp_plat_print, "%d\n", mpz_as_int_checked(&mpz, &value_signed)); + mp_printf(&mp_plat_print, "%d\n", value_signed); + + // mpz_set_from_float with 0 as argument + mpz_set_from_float(&mpz, 0); + mp_printf(&mp_plat_print, "%f\n", mpz_as_float(&mpz)); + + // convert a large integer value (stored in a mpz) to mp_uint_t and to ll; + mp_obj_t obj_bigint = mp_obj_new_int_from_uint((mp_uint_t)0xdeadbeef); + mp_printf(&mp_plat_print, "%x\n", mp_obj_get_uint(obj_bigint)); + obj_bigint = mp_obj_new_int_from_ll(0xc0ffee777c0ffeell); + long long value_ll = mp_obj_get_ll(obj_bigint); + mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); + + // convert a large integer value (stored via a struct object) to uint and to ll + // `deadbeef` global is an uctypes.struct defined by extra_coverage.py + obj_bigint = mp_load_global(MP_QSTR_deadbeef); + mp_printf(&mp_plat_print, "%x\n", mp_obj_get_uint(obj_bigint)); + value_ll = mp_obj_get_ll(obj_bigint); + mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); + + // convert a smaller integer value to mp_uint_t and to ll + obj_bigint = mp_obj_new_int_from_uint(0xc0ffee); + mp_printf(&mp_plat_print, "%x\n", mp_obj_get_uint(obj_bigint)); + value_ll = mp_obj_get_ll(obj_bigint); + mp_printf(&mp_plat_print, "%x%08x\n", (uint32_t)(value_ll >> 32), (uint32_t)value_ll); } // runtime utils @@ -480,7 +525,7 @@ static mp_obj_t extra_coverage(void) { mp_call_function_2_protected(MP_OBJ_FROM_PTR(&mp_builtin_divmod_obj), mp_obj_new_str_from_cstr("abc"), mp_obj_new_str_from_cstr("abc")); // mp_obj_int_get_checked with mp_obj_int_t that has a value that is a small integer - mp_printf(&mp_plat_print, "%d\n", mp_obj_int_get_checked(mp_obj_int_new_mpz())); + mp_printf(&mp_plat_print, "%d\n", mp_obj_int_get_checked(MP_OBJ_FROM_PTR(mp_obj_int_new_mpz()))); // mp_obj_int_get_uint_checked with non-negative small-int mp_printf(&mp_plat_print, "%d\n", (int)mp_obj_int_get_uint_checked(MP_OBJ_NEW_SMALL_INT(1))); @@ -505,6 +550,22 @@ static mp_obj_t extra_coverage(void) { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); } + // mp_obj_get_uint from a non-int object (should raise exception) + if (nlr_push(&nlr) == 0) { + mp_obj_get_uint(mp_const_none); + nlr_pop(); + } else { + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + + // mp_obj_int_get_ll from a non-int object (should raise exception) + if (nlr_push(&nlr) == 0) { + mp_obj_get_ll(mp_const_none); + nlr_pop(); + } else { + mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); + } + // call mp_obj_new_exception_args (it's a part of the public C API and not used in the core) mp_obj_print_exception(&mp_plat_print, mp_obj_new_exception_args(&mp_type_ValueError, 0, NULL)); } @@ -557,12 +618,24 @@ static mp_obj_t extra_coverage(void) { fun_bc.context = &context; fun_bc.child_table = NULL; fun_bc.bytecode = (const byte *)"\x01"; // just needed for n_state + #if MICROPY_PY_SYS_SETTRACE + struct _mp_raw_code_t rc = {}; + fun_bc.rc = &rc; + #endif mp_code_state_t *code_state = m_new_obj_var(mp_code_state_t, state, mp_obj_t, 1); code_state->fun_bc = &fun_bc; code_state->ip = (const byte *)"\x00"; // just needed for an invalid opcode code_state->sp = &code_state->state[0]; code_state->exc_sp_idx = 0; code_state->old_globals = NULL; + #if MICROPY_STACKLESS + code_state->prev = NULL; + #endif + #if MICROPY_PY_SYS_SETTRACE + code_state->prev_state = NULL; + code_state->frame = NULL; + #endif + mp_vm_return_kind_t ret = mp_execute_bytecode(code_state, MP_OBJ_NULL); mp_printf(&mp_plat_print, "%d %d\n", ret, mp_obj_get_type(code_state->state[0]) == &mp_type_NotImplementedError); } @@ -621,6 +694,19 @@ static mp_obj_t extra_coverage(void) { mp_obj_print_exception(&mp_plat_print, MP_OBJ_FROM_PTR(nlr.ret_val)); } mp_handle_pending(true); + + coverage_sched_function_continue = true; + mp_sched_schedule_node(&mp_coverage_sched_node, coverage_sched_function); + for (int i = 0; i < 3; ++i) { + mp_printf(&mp_plat_print, "loop\n"); + mp_handle_pending(true); + } + // Clear this flag to prevent the function scheduling itself again + coverage_sched_function_continue = false; + // Will only run the first time through this loop, then not scheduled again + for (int i = 0; i < 3; ++i) { + mp_handle_pending(true); + } } // ringbuf @@ -794,7 +880,7 @@ static mp_obj_t extra_coverage(void) { mp_obj_streamtest_t *s2 = mp_obj_malloc(mp_obj_streamtest_t, &mp_type_stest_textio2); // return a tuple of data for testing on the Python side - mp_obj_t items[] = {(mp_obj_t)&str_no_hash_obj, (mp_obj_t)&bytes_no_hash_obj, MP_OBJ_FROM_PTR(s), MP_OBJ_FROM_PTR(s2)}; + mp_obj_t items[] = {MP_OBJ_FROM_PTR(&str_no_hash_obj), MP_OBJ_FROM_PTR(&bytes_no_hash_obj), MP_OBJ_FROM_PTR(s), MP_OBJ_FROM_PTR(s2)}; return mp_obj_new_tuple(MP_ARRAY_SIZE(items), items); } MP_DEFINE_CONST_FUN_OBJ_0(extra_coverage_obj, extra_coverage); diff --git a/ports/unix/main.c b/ports/unix/main.c index 9f51573fbf..530e20a386 100644 --- a/ports/unix/main.c +++ b/ports/unix/main.c @@ -775,7 +775,7 @@ MP_NOINLINE int main_(int argc, char **argv) { #endif #if MICROPY_PY_BLUETOOTH - void mp_bluetooth_deinit(void); + int mp_bluetooth_deinit(void); mp_bluetooth_deinit(); #endif diff --git a/ports/unix/modsocket.c b/ports/unix/modsocket.c index 6d6059ae44..2aaa21183a 100644 --- a/ports/unix/modsocket.c +++ b/ports/unix/modsocket.c @@ -701,6 +701,7 @@ static const mp_rom_map_elem_t mp_module_socket_globals_table[] = { C(MSG_DONTROUTE), C(MSG_DONTWAIT), + C(MSG_PEEK), C(SOL_SOCKET), C(SO_BROADCAST), diff --git a/ports/unix/modtime.c b/ports/unix/modtime.c index fbd94b5ecd..41b7c89df4 100644 --- a/ports/unix/modtime.c +++ b/ports/unix/modtime.c @@ -34,6 +34,7 @@ #include "py/mphal.h" #include "py/runtime.h" +#include "shared/timeutils/timeutils.h" #ifdef _WIN32 static inline int msec_sleep_tv(struct timeval *tv) { @@ -130,12 +131,7 @@ static mp_obj_t mod_time_gm_local_time(size_t n_args, const mp_obj_t *args, stru if (n_args == 0) { t = time(NULL); } else { - #if MICROPY_PY_BUILTINS_FLOAT && MICROPY_FLOAT_IMPL == MICROPY_FLOAT_IMPL_DOUBLE - mp_float_t val = mp_obj_get_float(args[0]); - t = (time_t)MICROPY_FLOAT_C_FUN(trunc)(val); - #else - t = mp_obj_get_int(args[0]); - #endif + t = (time_t)timeutils_obj_get_timestamp(args[0]); } struct tm *tm = time_func(&t); @@ -196,7 +192,7 @@ static mp_obj_t mod_time_mktime(mp_obj_t tuple) { if (ret == -1) { mp_raise_msg(&mp_type_OverflowError, MP_ERROR_TEXT("invalid mktime usage")); } - return mp_obj_new_int(ret); + return timeutils_obj_from_timestamp(ret); } MP_DEFINE_CONST_FUN_OBJ_1(mod_time_mktime_obj, mod_time_mktime); diff --git a/ports/unix/mpconfigport.h b/ports/unix/mpconfigport.h index 21ce75a351..973b5e74ce 100644 --- a/ports/unix/mpconfigport.h +++ b/ports/unix/mpconfigport.h @@ -124,6 +124,9 @@ typedef long mp_off_t; // VFS stat functions should return time values relative to 1970/1/1 #define MICROPY_EPOCH_IS_1970 (1) +// port modtime functions use time_t +#define MICROPY_TIMESTAMP_IMPL (MICROPY_TIMESTAMP_IMPL_TIME_T) + // Assume that select() call, interrupted with a signal, and erroring // with EINTR, updates remaining timeout value. #define MICROPY_SELECT_REMAINING_TIME (1) diff --git a/ports/unix/variants/coverage/mpconfigvariant.h b/ports/unix/variants/coverage/mpconfigvariant.h index 04b5b8ae1a..2f5d9683b3 100644 --- a/ports/unix/variants/coverage/mpconfigvariant.h +++ b/ports/unix/variants/coverage/mpconfigvariant.h @@ -39,11 +39,13 @@ // Enable additional features. #define MICROPY_DEBUG_PARSE_RULE_NAME (1) +#define MICROPY_PY_SYS_SETTRACE (1) #define MICROPY_TRACKED_ALLOC (1) #define MICROPY_WARNINGS_CATEGORY (1) #undef MICROPY_VFS_ROM_IOCTL #define MICROPY_VFS_ROM_IOCTL (1) #define MICROPY_PY_CRYPTOLIB_CTR (1) +#define MICROPY_SCHEDULER_STATIC_NODES (1) // Enable os.uname for attrtuple coverage test #define MICROPY_PY_OS_UNAME (1) diff --git a/ports/webassembly/main.c b/ports/webassembly/main.c index c542f0cd72..770dfbe0ca 100644 --- a/ports/webassembly/main.c +++ b/ports/webassembly/main.c @@ -233,7 +233,7 @@ void nlr_jump_fail(void *val) { } } -void NORETURN __fatal_error(const char *msg) { +void MP_NORETURN __fatal_error(const char *msg) { while (1) { ; } diff --git a/ports/webassembly/objpyproxy.js b/ports/webassembly/objpyproxy.js index 3b94f8aadc..0eafd0dec5 100644 --- a/ports/webassembly/objpyproxy.js +++ b/ports/webassembly/objpyproxy.js @@ -148,6 +148,12 @@ const py_proxy_handler = { }; }, has(target, prop) { + // avoid throwing on `Symbol() in proxy` checks + if (typeof prop !== "string") { + // returns true only on iterator because other + // symbols are not considered in the `get` trap + return prop === Symbol.iterator; + } return Module.ccall( "proxy_c_to_js_has_attr", "number", diff --git a/ports/windows/Makefile b/ports/windows/Makefile index 115d1a61ef..9eee98cdd4 100644 --- a/ports/windows/Makefile +++ b/ports/windows/Makefile @@ -46,6 +46,10 @@ LDFLAGS += -lm -lbcrypt $(LDFLAGS_EXTRA) # This option has no effect on 64-bit builds. CFLAGS += -D_FILE_OFFSET_BITS=64 +# Force the use of 64-bits for time_t in C library functions on 32-bit platforms. +# This option has no effect on 64-bit builds. +CFLAGS += -D_TIME_BITS=64 + # Debugging/Optimization ifdef DEBUG CFLAGS += -g diff --git a/ports/windows/mpconfigport.h b/ports/windows/mpconfigport.h index fabc9072d6..4e140d5edb 100644 --- a/ports/windows/mpconfigport.h +++ b/ports/windows/mpconfigport.h @@ -242,7 +242,7 @@ typedef long mp_off_t; // CL specific overrides from mpconfig -#define NORETURN __declspec(noreturn) +#define MP_NORETURN __declspec(noreturn) #define MP_WEAK #define MP_NOINLINE __declspec(noinline) #define MP_ALWAYSINLINE __forceinline @@ -266,6 +266,11 @@ typedef long mp_off_t; #endif #endif +// VC++ 2017 fixes +#if (_MSC_VER < 1920) +#define MICROPY_PY_MATH_COPYSIGN_FIX_NAN (1) +#endif + // CL specific definitions #ifndef __cplusplus diff --git a/ports/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt index b955089066..a5cc477204 100644 --- a/ports/zephyr/CMakeLists.txt +++ b/ports/zephyr/CMakeLists.txt @@ -34,6 +34,11 @@ set(MICROPY_TARGET micropython) include(${MICROPY_DIR}/py/py.cmake) include(${MICROPY_DIR}/extmod/extmod.cmake) +if (CONFIG_MICROPY_FROZEN_MODULES) + cmake_path(ABSOLUTE_PATH CONFIG_MICROPY_FROZEN_MANIFEST BASE_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}) + set(MICROPY_FROZEN_MANIFEST ${CONFIG_MICROPY_FROZEN_MANIFEST}) +endif() + set(MICROPY_SOURCE_PORT main.c help.c @@ -61,10 +66,20 @@ set(MICROPY_SOURCE_SHARED runtime/mpirq.c runtime/pyexec.c runtime/stdout_helpers.c + runtime/sys_stdio_mphal.c timeutils/timeutils.c ) list(TRANSFORM MICROPY_SOURCE_SHARED PREPEND ${MICROPY_DIR}/shared/) +set(MICROPY_SOURCE_DRIVERS + bus/softspi.c +) +list(TRANSFORM MICROPY_SOURCE_DRIVERS PREPEND ${MICROPY_DIR}/drivers/) + +set(MICROPY_QSTRDEFS_PORT + ${MICROPY_PORT_DIR}/qstrdefsport.h +) + set(MICROPY_SOURCE_LIB oofatfs/ff.c oofatfs/ffunicode.c @@ -79,6 +94,7 @@ set(MICROPY_SOURCE_QSTR ${MICROPY_SOURCE_PY} ${MICROPY_SOURCE_EXTMOD} ${MICROPY_SOURCE_SHARED} + ${MICROPY_SOURCE_DRIVERS} ${MICROPY_SOURCE_LIB} ${MICROPY_SOURCE_PORT} ) @@ -115,13 +131,15 @@ zephyr_library_compile_definitions( zephyr_library_sources(${MICROPY_SOURCE_QSTR}) zephyr_library_link_libraries(kernel) -add_dependencies(${MICROPY_TARGET} zephyr_generated_headers) - include(${MICROPY_DIR}/py/mkrules.cmake) +add_dependencies(BUILD_VERSION_HEADER zephyr_generated_headers) +add_dependencies(${MICROPY_TARGET} zephyr_generated_headers) + target_sources(app PRIVATE src/zephyr_start.c src/zephyr_getchar.c + src/usbd.c ) target_link_libraries(app PRIVATE ${MICROPY_TARGET}) diff --git a/ports/zephyr/Kconfig b/ports/zephyr/Kconfig index 227e943bcd..6db0133f4f 100644 --- a/ports/zephyr/Kconfig +++ b/ports/zephyr/Kconfig @@ -41,6 +41,22 @@ config MICROPY_VFS_LFS1 config MICROPY_VFS_LFS2 bool "LittleFs version 2 file system" +config MICROPY_FROZEN_MODULES + bool "Enable Frozen Modules" + +config MICROPY_FROZEN_MANIFEST + string "Path to Frozen Modules manifest.py" + depends on MICROPY_FROZEN_MODULES + default "boards/manifest.py" + +config MICROPY_USB_DEVICE_VID + hex "USB VID" + default 0x2fe3 + +config MICROPY_USB_DEVICE_PID + hex "USB PID" + default 0x0001 + endmenu # MicroPython Options source "Kconfig.zephyr" diff --git a/ports/zephyr/README.md b/ports/zephyr/README.md index fc18d25c0a..17c1f613de 100644 --- a/ports/zephyr/README.md +++ b/ports/zephyr/README.md @@ -95,7 +95,7 @@ qemu_cortex_m3): Networking is enabled with the default configuration, so you need to follow instructions in -https://docs.zephyrproject.org/latest/guides/networking/qemu_setup.html#networking-with-qemu +https://docs.zephyrproject.org/latest/connectivity/networking/qemu_setup.html#networking-with-qemu to setup the host side of TAP/SLIP networking. If you get an error like: could not connect serial device to character backend 'unix:/tmp/slip.sock' diff --git a/ports/zephyr/boards/bbc_microbit_v2.conf b/ports/zephyr/boards/bbc_microbit_v2.conf index 31872244ca..43742078f5 100644 --- a/ports/zephyr/boards/bbc_microbit_v2.conf +++ b/ports/zephyr/boards/bbc_microbit_v2.conf @@ -1,4 +1,3 @@ -CONFIG_CONSOLE_SUBSYS=n CONFIG_NETWORKING=n CONFIG_BT=y CONFIG_BT_DEVICE_NAME_DYNAMIC=y diff --git a/ports/zephyr/boards/beagleconnect_freedom.conf b/ports/zephyr/boards/beagleconnect_freedom.conf index 14ce9c526e..e31e09444f 100644 --- a/ports/zephyr/boards/beagleconnect_freedom.conf +++ b/ports/zephyr/boards/beagleconnect_freedom.conf @@ -1,7 +1,19 @@ # Hardware features +CONFIG_ADC=y +CONFIG_PWM=y CONFIG_I2C=y CONFIG_SPI=y # Flash drivers CONFIG_FLASH=y CONFIG_FLASH_MAP=y + +# Networking +CONFIG_BT=n +CONFIG_NET_IPV4=n +CONFIG_NET_CONFIG_NEED_IPV6=y +CONFIG_NET_CONFIG_NEED_IPV4=n +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_L2_IEEE802154=y +CONFIG_NET_DHCPV4=n +CONFIG_NET_CONFIG_IEEE802154_CHANNEL=1 diff --git a/ports/zephyr/boards/beagleplay_cc1352p7.conf b/ports/zephyr/boards/beagleplay_cc1352p7.conf new file mode 100644 index 0000000000..f63d184e38 --- /dev/null +++ b/ports/zephyr/boards/beagleplay_cc1352p7.conf @@ -0,0 +1,13 @@ +# Flash drivers +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y + +# Networking +CONFIG_BT=n +CONFIG_NET_IPV4=n +CONFIG_NET_CONFIG_NEED_IPV6=y +CONFIG_NET_CONFIG_NEED_IPV4=n +CONFIG_NET_CONFIG_MY_IPV4_ADDR="" +CONFIG_NET_L2_IEEE802154=y +CONFIG_NET_DHCPV4=n +CONFIG_NET_CONFIG_IEEE802154_CHANNEL=1 diff --git a/ports/zephyr/boards/frdm_k64f.conf b/ports/zephyr/boards/frdm_k64f.conf index c164c0c32c..71f8cc178b 100644 --- a/ports/zephyr/boards/frdm_k64f.conf +++ b/ports/zephyr/boards/frdm_k64f.conf @@ -3,6 +3,7 @@ CONFIG_NET_L2_ETHERNET=y # Hardware features CONFIG_I2C=y +CONFIG_PWM=y CONFIG_SPI=y # Sensor drivers @@ -14,3 +15,6 @@ CONFIG_FXOS8700_TEMP=y CONFIG_FLASH=y CONFIG_FLASH_MAP=y CONFIG_MPU_ALLOW_FLASH_WRITE=y + +# MicroPython config +CONFIG_MICROPY_HEAP_SIZE=114688 diff --git a/ports/zephyr/boards/manifest.py b/ports/zephyr/boards/manifest.py new file mode 100644 index 0000000000..df1169c081 --- /dev/null +++ b/ports/zephyr/boards/manifest.py @@ -0,0 +1,7 @@ +# This is an example frozen module manifest. Enable this by configuring +# the Zephyr project and enabling the frozen modules config feature. + +freeze("$(PORT_DIR)/modules") + +# Require a micropython-lib module. +require("upysh") diff --git a/ports/zephyr/boards/nrf52840dk_nrf52840.conf b/ports/zephyr/boards/nrf52840dk_nrf52840.conf index e9ec78b64f..dc2ed1c4b4 100644 --- a/ports/zephyr/boards/nrf52840dk_nrf52840.conf +++ b/ports/zephyr/boards/nrf52840dk_nrf52840.conf @@ -1,8 +1,13 @@ CONFIG_NETWORKING=n CONFIG_BT=y CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_GATT_DYNAMIC_DB=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_CENTRAL=y +CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_L2CAP_TX_MTU=252 +CONFIG_BT_BUF_ACL_RX_SIZE=256 +CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n CONFIG_MICROPY_HEAP_SIZE=98304 CONFIG_MAIN_STACK_SIZE=8192 diff --git a/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.conf b/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.conf new file mode 100644 index 0000000000..b4aed364a7 --- /dev/null +++ b/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.conf @@ -0,0 +1,2 @@ +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y diff --git a/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.overlay b/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.overlay new file mode 100644 index 0000000000..7310d1e55f --- /dev/null +++ b/ports/zephyr/boards/nrf5340dk_nrf5340_cpuapp.overlay @@ -0,0 +1,16 @@ +// Replace default internal storage partition with external flash + +/delete-node/ &storage_partition; + +&mx25r64 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@0 { + reg = <0x00000000 0x800000>; + label = "storage"; + }; + }; +}; diff --git a/ports/zephyr/boards/nrf9151dk_nrf9151.conf b/ports/zephyr/boards/nrf9151dk_nrf9151.conf new file mode 100644 index 0000000000..e89f332ba1 --- /dev/null +++ b/ports/zephyr/boards/nrf9151dk_nrf9151.conf @@ -0,0 +1,7 @@ +# Enable external flash +CONFIG_SPI=y +CONFIG_SPI_NOR=y +CONFIG_SPI_NOR_SFDP_DEVICETREE=y + +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y diff --git a/ports/zephyr/boards/nrf9151dk_nrf9151.overlay b/ports/zephyr/boards/nrf9151dk_nrf9151.overlay new file mode 100644 index 0000000000..85cab57414 --- /dev/null +++ b/ports/zephyr/boards/nrf9151dk_nrf9151.overlay @@ -0,0 +1,22 @@ +/ { + /* Configure partition manager to use gd25wb256 as the external flash */ + chosen { + nordic,pm-ext-flash = &gd25wb256; + }; +}; + +/delete-node/ &storage_partition; + +&gd25wb256 { + status = "okay"; + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + storage_partition: partition@0 { + reg = <0x00000000 0x2000000>; + label = "storage"; + }; + }; +}; diff --git a/ports/zephyr/boards/nucleo_h743zi.conf b/ports/zephyr/boards/nucleo_h743zi.conf index 942415b796..ea6a60b352 100644 --- a/ports/zephyr/boards/nucleo_h743zi.conf +++ b/ports/zephyr/boards/nucleo_h743zi.conf @@ -1,6 +1,3 @@ -# disable console subsys to get REPL working -CONFIG_CONSOLE_SUBSYS=n - # flash config CONFIG_FLASH=y CONFIG_FLASH_MAP=y diff --git a/ports/zephyr/boards/nucleo_wb55rg.conf b/ports/zephyr/boards/nucleo_wb55rg.conf index baf0f28075..adfab367c8 100644 --- a/ports/zephyr/boards/nucleo_wb55rg.conf +++ b/ports/zephyr/boards/nucleo_wb55rg.conf @@ -1,6 +1,21 @@ -CONFIG_CONSOLE_SUBSYS=n CONFIG_NETWORKING=n + +# Hardware features +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_I2C=y +CONFIG_SPI=y + +# Bluetooth CONFIG_BT=y CONFIG_BT_DEVICE_NAME_DYNAMIC=y +CONFIG_BT_GATT_DYNAMIC_DB=y CONFIG_BT_PERIPHERAL=y CONFIG_BT_CENTRAL=y +CONFIG_BT_GATT_CLIENT=y +CONFIG_BT_L2CAP_TX_MTU=252 +CONFIG_BT_BUF_ACL_RX_SIZE=256 +CONFIG_BT_GATT_ENFORCE_SUBSCRIPTION=n + +# MicroPython config +CONFIG_MICROPY_HEAP_SIZE=131072 diff --git a/ports/zephyr/boards/nucleo_wb55rg.overlay b/ports/zephyr/boards/nucleo_wb55rg.overlay new file mode 100644 index 0000000000..d9a4d3f24c --- /dev/null +++ b/ports/zephyr/boards/nucleo_wb55rg.overlay @@ -0,0 +1,23 @@ +/* + * Copyright (c) 2025 Damien P. George + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/* Delete the defined partitions and create bigger one for storage. */ +/delete-node/ &slot1_partition; +/delete-node/ &scratch_partition; +/delete-node/ &storage_partition; +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Storage slot: 424 KiB placed after slot0_partition. */ + storage_partition: partition@70000 { + label = "storage"; + reg = <0x00070000 DT_SIZE_K(424)>; + }; + }; +}; diff --git a/ports/zephyr/boards/qemu_cortex_m3.conf b/ports/zephyr/boards/qemu_cortex_m3.conf index dac0c358dc..11eebd9606 100644 --- a/ports/zephyr/boards/qemu_cortex_m3.conf +++ b/ports/zephyr/boards/qemu_cortex_m3.conf @@ -1,7 +1,6 @@ -# Interrupt-driven UART console has emulation artifacts under QEMU, -# disable it -CONFIG_CONSOLE_SUBSYS=n +# This QEMU target only has 256k ROM and 64k RAM, so disable networking +CONFIG_NETWORKING=n -# Networking drivers -# SLIP driver for QEMU -CONFIG_NET_SLIP_TAP=y +# QEMU doesn't have a watchdog, so disable it +CONFIG_WATCHDOG=n +CONFIG_WDT_DISABLE_AT_BOOT=n diff --git a/ports/zephyr/boards/qemu_x86.conf b/ports/zephyr/boards/qemu_x86.conf index dac0c358dc..1d04675df4 100644 --- a/ports/zephyr/boards/qemu_x86.conf +++ b/ports/zephyr/boards/qemu_x86.conf @@ -1,7 +1,7 @@ -# Interrupt-driven UART console has emulation artifacts under QEMU, -# disable it -CONFIG_CONSOLE_SUBSYS=n - # Networking drivers # SLIP driver for QEMU CONFIG_NET_SLIP_TAP=y + +# QEMU doesn't have a watchdog, so disable it +CONFIG_WATCHDOG=n +CONFIG_WDT_DISABLE_AT_BOOT=n diff --git a/ports/zephyr/boards/rpi_pico.conf b/ports/zephyr/boards/rpi_pico.conf new file mode 100644 index 0000000000..6b31bc9f98 --- /dev/null +++ b/ports/zephyr/boards/rpi_pico.conf @@ -0,0 +1,19 @@ +# Disable floating point hardware. +CONFIG_FPU=n + +# Configure serial console over USB CDC ACM. +CONFIG_USB_DEVICE_STACK_NEXT=y +CONFIG_USBD_CDC_ACM_CLASS=y +CONFIG_UART_LINE_CTRL=y + +# Disable networking. +CONFIG_NETWORKING=n + +# Hardware features. +CONFIG_FLASH=y +CONFIG_FLASH_MAP=y +CONFIG_I2C=y +CONFIG_SPI=y + +# MicroPython config. +CONFIG_MICROPY_HEAP_SIZE=196608 diff --git a/ports/zephyr/boards/rpi_pico.overlay b/ports/zephyr/boards/rpi_pico.overlay new file mode 100644 index 0000000000..d63ed73bdf --- /dev/null +++ b/ports/zephyr/boards/rpi_pico.overlay @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2025 Damien P. George + * + * SPDX-License-Identifier: Apache-2.0 + */ + +/ { + chosen { + /* Use USB CDC ACM as the console. */ + zephyr,console = &cdc_acm_uart0; + }; +}; + +/* Delete defined partitions and make a layout matching the rp2 port RPI_PICO configuration. */ +/delete-node/ &code_partition; +&flash0 { + partitions { + compatible = "fixed-partitions"; + #address-cells = <1>; + #size-cells = <1>; + + /* Code slot: 640 KiB - 0x100 placed after second-stage bootloader. */ + code_partition: partition@100 { + label = "code-partition"; + reg = <0x00000100 (DT_SIZE_K(640) - 0x100)>; + read-only; + }; + + /* Storage slot: 1408 KiB placed after code_partition. */ + storage_partition: partition@a0000 { + label = "storage"; + reg = <0x000a0000 DT_SIZE_K(1408)>; + }; + }; +}; + +&zephyr_udc0 { + cdc_acm_uart0: cdc_acm_uart0 { + compatible = "zephyr,cdc-acm-uart"; + }; +}; + +&i2c1 { + clock-frequency = <I2C_BITRATE_STANDARD>; + status = "okay"; + pinctrl-0 = <&i2c1_default>; + pinctrl-names = "default"; +}; diff --git a/ports/zephyr/machine_pin.c b/ports/zephyr/machine_pin.c index 2240196690..e0718588d6 100644 --- a/ports/zephyr/machine_pin.c +++ b/ports/zephyr/machine_pin.c @@ -36,6 +36,7 @@ #include "py/gc.h" #include "py/mphal.h" #include "extmod/modmachine.h" +#include "extmod/virtpin.h" #include "shared/runtime/mpirq.h" #include "modmachine.h" #include "zephyr_device.h" @@ -115,6 +116,10 @@ static mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_ar } int ret = gpio_pin_configure(self->port, self->pin, mode | pull | init); + if (ret == -ENOTSUP && mode == (GPIO_OUTPUT | GPIO_INPUT)) { + // Some targets (eg frdm_k64f) don't support GPIO_OUTPUT|GPIO_INPUT, so try again with just GPIO_OUTPUT. + ret = gpio_pin_configure(self->port, self->pin, GPIO_OUTPUT | pull | init); + } if (ret) { mp_raise_ValueError(MP_ERROR_TEXT("invalid pin")); } @@ -126,19 +131,26 @@ static mp_obj_t machine_pin_obj_init_helper(machine_pin_obj_t *self, size_t n_ar mp_obj_t mp_pin_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); - // get the wanted port - if (!mp_obj_is_type(args[0], &mp_type_tuple)) { + machine_pin_obj_t *pin; + if (mp_obj_is_type(args[0], &machine_pin_type)) { + // Already a Pin object, reuse it. + pin = MP_OBJ_TO_PTR(args[0]); + } else if (mp_obj_is_type(args[0], &mp_type_tuple)) { + // Get the wanted (port, pin) values. + mp_obj_t *items; + mp_obj_get_array_fixed_n(args[0], 2, &items); + const struct device *wanted_port = zephyr_device_find(items[0]); + int wanted_pin = mp_obj_get_int(items[1]); + + pin = m_new_obj(machine_pin_obj_t); + pin->base = machine_pin_obj_template; + pin->port = wanted_port; + pin->pin = wanted_pin; + pin->irq = NULL; + } else { + // Unknown Pin. mp_raise_ValueError(MP_ERROR_TEXT("Pin id must be tuple of (\"GPIO_x\", pin#)")); } - mp_obj_t *items; - mp_obj_get_array_fixed_n(args[0], 2, &items); - const struct device *wanted_port = zephyr_device_find(items[0]); - int wanted_pin = mp_obj_get_int(items[1]); - - machine_pin_obj_t *pin = m_new_obj(machine_pin_obj_t); - pin->base = machine_pin_obj_template; - pin->port = wanted_port; - pin->pin = wanted_pin; if (n_args > 1 || n_kw > 0) { // pin mode given, so configure this GPIO @@ -270,7 +282,8 @@ static const mp_rom_map_elem_t machine_pin_locals_dict_table[] = { // class constants { MP_ROM_QSTR(MP_QSTR_IN), MP_ROM_INT(GPIO_INPUT) }, - { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_OUTPUT) }, + { MP_ROM_QSTR(MP_QSTR_OUT), MP_ROM_INT(GPIO_OUTPUT | GPIO_INPUT) }, + { MP_ROM_QSTR(MP_QSTR_OPEN_DRAIN), MP_ROM_INT(GPIO_OUTPUT | GPIO_INPUT | GPIO_OPEN_DRAIN) }, { MP_ROM_QSTR(MP_QSTR_PULL_UP), MP_ROM_INT(GPIO_PULL_UP) }, { MP_ROM_QSTR(MP_QSTR_PULL_DOWN), MP_ROM_INT(GPIO_PULL_DOWN) }, { MP_ROM_QSTR(MP_QSTR_IRQ_RISING), MP_ROM_INT(GPIO_INT_EDGE_RISING) }, diff --git a/ports/zephyr/machine_timer.c b/ports/zephyr/machine_timer.c index 8ab2f62913..1bb743c2eb 100644 --- a/ports/zephyr/machine_timer.c +++ b/ports/zephyr/machine_timer.c @@ -77,9 +77,14 @@ static void machine_timer_print(const mp_print_t *print, mp_obj_t self_in, mp_pr } static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { - mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); - - if (mp_obj_get_int(args[0]) != -1) { + // Get timer id (only soft timer (-1) supported at the moment) + mp_int_t id = -1; + if (n_args > 0) { + id = mp_obj_get_int(args[0]); + --n_args; + ++args; + } + if (id != -1) { mp_raise_ValueError(MP_ERROR_TEXT("only virtual timers are supported")); } @@ -90,10 +95,10 @@ static mp_obj_t machine_timer_make_new(const mp_obj_type_t *type, size_t n_args, self->next = MP_STATE_PORT(machine_timer_obj_head); MP_STATE_PORT(machine_timer_obj_head) = self; - if (n_args > 1 || n_kw > 0) { + if (n_args > 0 || n_kw > 0) { mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); - machine_timer_init_helper(self, n_args - 1, args + 1, &kw_args); + machine_timer_init_helper(self, n_args, args, &kw_args); } return self; } diff --git a/ports/zephyr/machine_uart.c b/ports/zephyr/machine_uart.c index 1927335ddf..60613befb1 100644 --- a/ports/zephyr/machine_uart.c +++ b/ports/zephyr/machine_uart.c @@ -5,6 +5,7 @@ * * Copyright (c) 2016 Damien P. George * Copyright (c) 2020 Yonatan Schachter + * Copyright (c) 2025 Daniel Campora on behalf of REMOTE TECH LTD * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -32,16 +33,32 @@ #include <zephyr/drivers/uart.h> #include "py/mperrno.h" +#include "py/ringbuf.h" #include "zephyr_device.h" -// The UART class doesn't have any constants for this port. -#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS + +#define MACHINE_UART_RTS 1 +#define MACHINE_UART_CTS 2 + +// This class needs a finalizer, so we add it here +#define MICROPY_PY_MACHINE_UART_CLASS_CONSTANTS \ + { MP_ROM_QSTR(MP_QSTR_RTS), MP_ROM_INT(MACHINE_UART_RTS) }, \ + { MP_ROM_QSTR(MP_QSTR_CTS), MP_ROM_INT(MACHINE_UART_CTS) }, \ + { MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&machine_uart_deinit_obj) }, + +#define UART_RX_RING_BUF_DEF_SIZE 128 +#define UART_TX_RING_BUF_DEF_SIZE 128 + +static void uart_interrupt_handler(const struct device *dev, void *user_data); typedef struct _machine_uart_obj_t { mp_obj_base_t base; const struct device *dev; uint16_t timeout; // timeout waiting for first char (in ms) uint16_t timeout_char; // timeout waiting between chars (in ms) + ringbuf_t rx_ringbuffer; + ringbuf_t tx_ringbuffer; + bool tx_complete; } machine_uart_obj_t; static const char *_parity_name[] = {"None", "Odd", "Even", "Mark", "Space"}; @@ -60,23 +77,96 @@ static void mp_machine_uart_print(const mp_print_t *print, mp_obj_t self_in, mp_ } static void mp_machine_uart_init_helper(machine_uart_obj_t *self, size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_timeout, ARG_timeout_char }; + enum { ARG_baudrate, ARG_bits, ARG_parity, ARG_stop, ARG_txbuf, ARG_rxbuf, ARG_timeout, ARG_timeout_char, ARG_flow }; static const mp_arg_t allowed_args[] = { + { MP_QSTR_baudrate, MP_ARG_INT, {.u_int = 115200} }, + { MP_QSTR_bits, MP_ARG_INT, {.u_int = 8} }, + { MP_QSTR_parity, MP_ARG_OBJ, {.u_obj = mp_const_none} }, + { MP_QSTR_stop, MP_ARG_INT, {.u_int = 1} }, + { MP_QSTR_txbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_RX_RING_BUF_DEF_SIZE} }, + { MP_QSTR_rxbuf, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = UART_TX_RING_BUF_DEF_SIZE} }, { MP_QSTR_timeout, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, { MP_QSTR_timeout_char, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, + { MP_QSTR_flow, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} }, }; + mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)]; mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); self->timeout = args[ARG_timeout].u_int; self->timeout_char = args[ARG_timeout_char].u_int; + + uint8_t data_bits; + if (args[ARG_bits].u_int == 5) { + data_bits = UART_CFG_DATA_BITS_5; + } else if (args[ARG_bits].u_int == 6) { + data_bits = UART_CFG_DATA_BITS_6; + } else if (args[ARG_bits].u_int == 7) { + data_bits = UART_CFG_DATA_BITS_7; + } else if (args[ARG_bits].u_int == 8) { + data_bits = UART_CFG_DATA_BITS_8; + } else if (args[ARG_bits].u_int == 9) { + data_bits = UART_CFG_DATA_BITS_9; + } else { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid data bits")); + } + + uint8_t parity; + if (args[ARG_parity].u_obj == mp_const_none) { + parity = UART_CFG_PARITY_NONE; + } else if (mp_obj_get_int(args[ARG_parity].u_obj) == 0) { + parity = UART_CFG_PARITY_EVEN; + } else if (mp_obj_get_int(args[ARG_parity].u_obj) == 1) { + parity = UART_CFG_PARITY_ODD; + } else { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid parity")); + } + + uint8_t stop_bits; + if (args[ARG_stop].u_int == 1) { + stop_bits = UART_CFG_STOP_BITS_1; + } else if (args[ARG_stop].u_int == 2) { + data_bits = UART_CFG_STOP_BITS_2; + } else { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid stop bits")); + } + + uint8_t flow_ctrl; + if (args[ARG_flow].u_int == 0) { + flow_ctrl = UART_CFG_FLOW_CTRL_NONE; + } else if (args[ARG_flow].u_int == (MACHINE_UART_RTS | MACHINE_UART_CTS)) { + flow_ctrl = UART_CFG_FLOW_CTRL_RTS_CTS; + } else { + mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("invalid flow control")); + } + + const struct uart_config cfg = { + .baudrate = args[ARG_baudrate].u_int, + .parity = parity, + .stop_bits = args[ARG_stop].u_int, + .data_bits = data_bits, + .flow_ctrl = flow_ctrl + }; + + int ret = uart_configure(self->dev, &cfg); + if (ret < 0) { + mp_raise_OSError(-ret); + } + + ringbuf_alloc(&self->tx_ringbuffer, args[ARG_txbuf].u_int); + ringbuf_alloc(&self->rx_ringbuffer, args[ARG_rxbuf].u_int); + + uart_irq_callback_user_data_set(self->dev, uart_interrupt_handler, (void *)self); + uart_irq_rx_enable(self->dev); } static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) { mp_arg_check_num(n_args, n_kw, 1, MP_OBJ_FUN_ARGS_MAX, true); - machine_uart_obj_t *self = mp_obj_malloc(machine_uart_obj_t, &machine_uart_type); - self->dev = zephyr_device_find(args[0]); + const struct device *dev = zephyr_device_find(args[0]); + machine_uart_obj_t *self = mp_obj_malloc_with_finaliser(machine_uart_obj_t, &machine_uart_type); + self->dev = dev; + self->tx_complete = true; mp_map_t kw_args; mp_map_init_fixed_table(&kw_args, n_kw, args + n_args); @@ -86,37 +176,38 @@ static mp_obj_t mp_machine_uart_make_new(const mp_obj_type_t *type, size_t n_arg } static void mp_machine_uart_deinit(machine_uart_obj_t *self) { - (void)self; + uart_irq_rx_disable(self->dev); + uart_irq_tx_disable(self->dev); } static mp_int_t mp_machine_uart_any(machine_uart_obj_t *self) { - (void)self; - mp_raise_NotImplementedError(NULL); // TODO + return ringbuf_avail(&self->rx_ringbuffer); } static bool mp_machine_uart_txdone(machine_uart_obj_t *self) { - (void)self; - mp_raise_NotImplementedError(NULL); // TODO + return self->tx_complete && !ringbuf_avail(&self->tx_ringbuffer) ? true : false; } static mp_uint_t mp_machine_uart_read(mp_obj_t self_in, void *buf_in, mp_uint_t size, int *errcode) { machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); uint8_t *buffer = (uint8_t *)buf_in; - uint8_t data; mp_uint_t bytes_read = 0; size_t elapsed_ms = 0; size_t time_to_wait = self->timeout; - while ((elapsed_ms < time_to_wait) && (bytes_read < size)) { - if (!uart_poll_in(self->dev, &data)) { - buffer[bytes_read++] = data; + do { + int _rx_len = MIN(ringbuf_avail(&self->rx_ringbuffer), size - bytes_read); + if (_rx_len > 0) { + ringbuf_get_bytes(&self->rx_ringbuffer, &buffer[bytes_read], _rx_len); + bytes_read += _rx_len; elapsed_ms = 0; time_to_wait = self->timeout_char; } else { k_msleep(1); elapsed_ms++; } - } + } while ((elapsed_ms < time_to_wait) && (bytes_read < size)); + return bytes_read; } @@ -124,27 +215,86 @@ static mp_uint_t mp_machine_uart_write(mp_obj_t self_in, const void *buf_in, mp_ machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); uint8_t *buffer = (uint8_t *)buf_in; - for (mp_uint_t i = 0; i < size; i++) { + // wait for any pending transmission to complete + while (!mp_machine_uart_txdone(self)) { + MICROPY_EVENT_POLL_HOOK; + } + + int _ex_size = 0; + int _free_space = ringbuf_free(&self->tx_ringbuffer); + if (size > _free_space) { + _ex_size = size - _free_space; + } + + // do a blocking tx of what doesn't fit into the outgoing ring buffer + for (mp_uint_t i = 0; i < _ex_size; i++) { uart_poll_out(self->dev, buffer[i]); } + ringbuf_put_bytes(&self->tx_ringbuffer, &buffer[_ex_size], size - _ex_size); + self->tx_complete = false; + uart_irq_tx_enable(self->dev); + return size; } static mp_uint_t mp_machine_uart_ioctl(mp_obj_t self_in, mp_uint_t request, uintptr_t arg, int *errcode) { - mp_uint_t ret; + machine_uart_obj_t *self = MP_OBJ_TO_PTR(self_in); + mp_uint_t ret = 0; if (request == MP_STREAM_POLL) { - ret = 0; - // read is always blocking - - if (arg & MP_STREAM_POLL_WR) { + uintptr_t flags = arg; + if ((flags & MP_STREAM_POLL_RD) && (mp_machine_uart_any(self) > 0)) { + ret |= MP_STREAM_POLL_RD; + } + if ((flags & MP_STREAM_POLL_WR) && mp_machine_uart_txdone(self)) { ret |= MP_STREAM_POLL_WR; } - return ret; + } else if (request == MP_STREAM_FLUSH) { + while (!mp_machine_uart_txdone(self)) { + MICROPY_EVENT_POLL_HOOK; + } } else { *errcode = MP_EINVAL; ret = MP_STREAM_ERROR; } + return ret; } + +static void uart_interrupt_handler(const struct device *dev, void *user_data) { + machine_uart_obj_t *self = (machine_uart_obj_t *)user_data; + + while (uart_irq_update(dev) && uart_irq_is_pending(dev)) { + if (uart_irq_rx_ready(dev)) { + uint8_t _rx_buffer[32]; + size_t _free_space = MIN(ringbuf_free(&self->rx_ringbuffer), sizeof(_rx_buffer)); + + // empty the uart fifo even if we can't store bytes anymore + // otherwise we will never exit this interrupt handler + int rcv_len = uart_fifo_read(dev, _rx_buffer, (_free_space > 0) ? _free_space : 1); + if ((rcv_len <= 0) || (_free_space == 0)) { + continue; + } + + ringbuf_put_bytes(&self->rx_ringbuffer, _rx_buffer, rcv_len); + } + + int _max_uart_tx_len = uart_irq_tx_ready(dev); + if (_max_uart_tx_len > 0) { + uint8_t _tx_buffer[32]; + size_t _buffer_tx_len; + + _max_uart_tx_len = MIN(_max_uart_tx_len, sizeof(_tx_buffer)); + _buffer_tx_len = ringbuf_avail(&self->tx_ringbuffer); + if (_buffer_tx_len > 0) { + _buffer_tx_len = MIN(_max_uart_tx_len, _buffer_tx_len); + ringbuf_get_bytes(&self->tx_ringbuffer, _tx_buffer, _buffer_tx_len); + uart_fifo_fill(dev, _tx_buffer, _buffer_tx_len); + } else if (uart_irq_tx_complete(dev)) { + uart_irq_tx_disable(dev); + self->tx_complete = true; + } + } + } +} diff --git a/ports/zephyr/main.c b/ports/zephyr/main.c index 206b7f92d3..eaef34a786 100644 --- a/ports/zephyr/main.c +++ b/ports/zephyr/main.c @@ -63,6 +63,10 @@ static char heap[MICROPY_HEAP_SIZE]; +#if defined(CONFIG_USB_DEVICE_STACK_NEXT) +extern int mp_usbd_init(void); +#endif // defined(CONFIG_USB_DEVICE_STACK_NEXT) + void init_zephyr(void) { // We now rely on CONFIG_NET_APP_SETTINGS to set up bootstrap // network addresses. @@ -95,6 +99,7 @@ static void vfs_init(void) { mp_obj_t bdev = NULL; mp_obj_t mount_point; const char *mount_point_str = NULL; + qstr path_lib_qstr = MP_QSTRnull; int ret = 0; #ifdef CONFIG_DISK_DRIVER_SDMMC @@ -105,15 +110,18 @@ static void vfs_init(void) { #endif bdev = MP_OBJ_TYPE_GET_SLOT(&zephyr_disk_access_type, make_new)(&zephyr_disk_access_type, ARRAY_SIZE(args), 0, args); mount_point_str = "/sd"; + path_lib_qstr = MP_QSTR__slash_sd_slash_lib; #elif defined(CONFIG_FLASH_MAP) && FIXED_PARTITION_EXISTS(storage_partition) mp_obj_t args[] = { MP_OBJ_NEW_SMALL_INT(FIXED_PARTITION_ID(storage_partition)), MP_OBJ_NEW_SMALL_INT(4096) }; bdev = MP_OBJ_TYPE_GET_SLOT(&zephyr_flash_area_type, make_new)(&zephyr_flash_area_type, ARRAY_SIZE(args), 0, args); mount_point_str = "/flash"; + path_lib_qstr = MP_QSTR__slash_flash_slash_lib; #endif if ((bdev != NULL)) { mount_point = mp_obj_new_str_from_cstr(mount_point_str); ret = mp_vfs_mount_and_chdir_protected(bdev, mount_point); + mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(path_lib_qstr)); // TODO: if this failed, make a new file system and try to mount again } } @@ -143,12 +151,26 @@ soft_reset: usb_enable(NULL); #endif + #ifdef CONFIG_USB_DEVICE_STACK_NEXT + mp_usbd_init(); + #endif + #if MICROPY_VFS vfs_init(); #endif #if MICROPY_MODULE_FROZEN || MICROPY_VFS - pyexec_file_if_exists("main.py"); + // Execute user scripts. + int ret = pyexec_file_if_exists("boot.py"); + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + if (pyexec_mode_kind == PYEXEC_MODE_FRIENDLY_REPL && ret != 0) { + ret = pyexec_file_if_exists("main.py"); + if (ret & PYEXEC_FORCED_EXIT) { + goto soft_reset_exit; + } + } #endif for (;;) { @@ -163,7 +185,11 @@ soft_reset: } } - printf("soft reboot\n"); + #if MICROPY_MODULE_FROZEN || MICROPY_VFS +soft_reset_exit: + #endif + + mp_printf(MP_PYTHON_PRINTER, "MPY: soft reboot\n"); #if MICROPY_PY_BLUETOOTH mp_bluetooth_deinit(); @@ -211,7 +237,7 @@ mp_obj_t mp_builtin_open(size_t n_args, const mp_obj_t *args, mp_map_t *kwargs) MP_DEFINE_CONST_FUN_OBJ_KW(mp_builtin_open_obj, 1, mp_builtin_open); #endif -NORETURN void nlr_jump_fail(void *val) { +MP_NORETURN void nlr_jump_fail(void *val) { while (1) { ; } diff --git a/ports/zephyr/modbluetooth_zephyr.c b/ports/zephyr/modbluetooth_zephyr.c index 8947bdf535..c1b8ddfd8b 100644 --- a/ports/zephyr/modbluetooth_zephyr.c +++ b/ports/zephyr/modbluetooth_zephyr.c @@ -31,8 +31,12 @@ #if MICROPY_PY_BLUETOOTH +#include <zephyr/types.h> #include <zephyr/bluetooth/bluetooth.h> #include <zephyr/bluetooth/hci.h> +#include <zephyr/bluetooth/conn.h> +#include <zephyr/bluetooth/uuid.h> +#include <zephyr/bluetooth/gatt.h> #include "extmod/modbluetooth.h" #define DEBUG_printf(...) // printk("BLE: " __VA_ARGS__) @@ -44,6 +48,23 @@ #define ERRNO_BLUETOOTH_NOT_ACTIVE MP_ENODEV +#define MP_BLUETOOTH_ZEPHYR_MAX_SERVICES (8) + +/* This masks Permission bits from GATT API */ +#define GATT_PERM_MASK (BT_GATT_PERM_READ | \ + BT_GATT_PERM_READ_AUTHEN | \ + BT_GATT_PERM_READ_ENCRYPT | \ + BT_GATT_PERM_WRITE | \ + BT_GATT_PERM_WRITE_AUTHEN | \ + BT_GATT_PERM_WRITE_ENCRYPT | \ + BT_GATT_PERM_PREPARE_WRITE) + +#define GATT_PERM_ENC_READ_MASK (BT_GATT_PERM_READ_ENCRYPT | \ + BT_GATT_PERM_READ_AUTHEN) + +#define GATT_PERM_ENC_WRITE_MASK (BT_GATT_PERM_WRITE_ENCRYPT | \ + BT_GATT_PERM_WRITE_AUTHEN) + enum { MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF, MP_BLUETOOTH_ZEPHYR_BLE_STATE_ACTIVE, @@ -56,9 +77,42 @@ enum { MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_ACTIVE, }; +union uuid_u { + struct bt_uuid uuid; + struct bt_uuid_16 u16; + struct bt_uuid_32 u32; + struct bt_uuid_128 u128; +}; + +struct add_characteristic { + uint8_t properties; + uint8_t permissions; + const struct bt_uuid *uuid; +}; + +struct add_descriptor { + uint8_t permissions; + const struct bt_uuid *uuid; +}; + +typedef struct _mp_bt_zephyr_conn_t { + struct bt_conn *conn; + struct _mp_bt_zephyr_conn_t *next; +} mp_bt_zephyr_conn_t; + typedef struct _mp_bluetooth_zephyr_root_pointers_t { + // list of objects to be tracked by the gc + mp_obj_t objs_list; + // Characteristic (and descriptor) value storage. mp_gatts_db_t gatts_db; + + // Service definitions. + size_t n_services; + struct bt_gatt_service *services[MP_BLUETOOTH_ZEPHYR_MAX_SERVICES]; + + // active connections + mp_bt_zephyr_conn_t *connections; } mp_bluetooth_zephyr_root_pointers_t; static int mp_bluetooth_zephyr_ble_state; @@ -69,6 +123,98 @@ static struct k_timer mp_bluetooth_zephyr_gap_scan_timer; static struct bt_le_scan_cb mp_bluetooth_zephyr_gap_scan_cb_struct; #endif +static struct bt_data bt_ad_data[8]; +static size_t bt_ad_len = 0; +static struct bt_data bt_sd_data[8]; +static size_t bt_sd_len = 0; + +static mp_bt_zephyr_conn_t *mp_bt_zephyr_next_conn; + +static mp_bt_zephyr_conn_t *mp_bt_zephyr_find_connection(uint8_t conn_handle); +static void mp_bt_zephyr_insert_connection(mp_bt_zephyr_conn_t *connection); +static void mp_bt_zephyr_remove_connection(uint8_t conn_handle); +static void mp_bt_zephyr_connected(struct bt_conn *connected, uint8_t err); +static void mp_bt_zephyr_disconnected(struct bt_conn *disconn, uint8_t reason); +static struct bt_uuid *create_zephyr_uuid(const mp_obj_bluetooth_uuid_t *uuid); +static void gatt_db_add(const struct bt_gatt_attr *pattern, struct bt_gatt_attr *attr, size_t user_data_len); +static void add_service(const struct bt_uuid *u, struct bt_gatt_attr *attr); +static void add_characteristic(struct add_characteristic *ch, struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_value); +static void add_ccc(struct bt_gatt_attr *attr, struct bt_gatt_attr *attr_desc); +static void add_cep(const struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_desc); +static void add_descriptor(struct bt_gatt_attr *chrc, struct add_descriptor *d, struct bt_gatt_attr *attr_desc); +static void mp_bt_zephyr_gatt_indicate_done(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err); +static struct bt_gatt_attr *mp_bt_zephyr_find_attr_by_handle(uint16_t value_handle); + +static struct bt_conn_cb mp_bt_zephyr_conn_callbacks = { + .connected = mp_bt_zephyr_connected, + .disconnected = mp_bt_zephyr_disconnected, +}; + +static mp_bt_zephyr_conn_t *mp_bt_zephyr_find_connection(uint8_t conn_handle) { + struct bt_conn_info info; + for (mp_bt_zephyr_conn_t *connection = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; connection != NULL; connection = connection->next) { + if (connection->conn) { + bt_conn_get_info(connection->conn, &info); + if (info.id == conn_handle) { + return connection; + } + } + } + return NULL; +} + +static void mp_bt_zephyr_insert_connection(mp_bt_zephyr_conn_t *connection) { + connection->next = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = connection; +} + +static void mp_bt_zephyr_remove_connection(uint8_t conn_handle) { + struct bt_conn_info info; + mp_bt_zephyr_conn_t *prev = NULL; + for (mp_bt_zephyr_conn_t *connection = MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections; connection != NULL; connection = connection->next) { + if (connection->conn) { + bt_conn_get_info(connection->conn, &info); + if (info.id == conn_handle) { + // unlink this item and the gc will eventually collect it + if (prev != NULL) { + prev->next = connection->next; + } else { + // move the start pointer + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = connection->next; + } + break; + } else { + prev = connection; + } + } + } +} + +static void mp_bt_zephyr_connected(struct bt_conn *conn, uint8_t err) { + struct bt_conn_info info; + bt_conn_get_info(conn, &info); + + if (err) { + uint8_t addr[6] = {0}; + DEBUG_printf("Connection from central failed (err %u)\n", err); + mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, info.id, 0xff, addr); + } else { + DEBUG_printf("Central connected with id %d\n", info.id); + mp_bt_zephyr_next_conn->conn = bt_conn_ref(conn); + mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_CONNECT, info.id, info.le.dst->type, info.le.dst->a.val); + mp_bt_zephyr_insert_connection(mp_bt_zephyr_next_conn); + } +} + +static void mp_bt_zephyr_disconnected(struct bt_conn *conn, uint8_t reason) { + struct bt_conn_info info; + bt_conn_get_info(conn, &info); + DEBUG_printf("Central disconnected (id %d reason %u)\n", info.id, reason); + bt_conn_unref(conn); + mp_bt_zephyr_remove_connection(info.id); + mp_bluetooth_gap_on_connected_disconnected(MP_BLUETOOTH_IRQ_CENTRAL_DISCONNECT, info.id, info.le.dst->type, info.le.dst->a.val); +} + static int bt_err_to_errno(int err) { // Zephyr uses errno codes directly, but they are negative. return -err; @@ -128,6 +274,11 @@ int mp_bluetooth_init(void) { MP_STATE_PORT(bluetooth_zephyr_root_pointers) = m_new0(mp_bluetooth_zephyr_root_pointers_t, 1); mp_bluetooth_gatts_db_create(&MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db); + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = NULL; + mp_bt_zephyr_next_conn = NULL; + + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list = mp_obj_new_list(0, NULL); + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE mp_bluetooth_zephyr_gap_scan_state = MP_BLUETOOTH_ZEPHYR_GAP_SCAN_STATE_INACTIVE; k_timer_init(&mp_bluetooth_zephyr_gap_scan_timer, gap_scan_cb_timeout, NULL); @@ -137,6 +288,9 @@ int mp_bluetooth_init(void) { #endif if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF) { + + bt_conn_cb_register(&mp_bt_zephyr_conn_callbacks); + // bt_enable can only be called once. int ret = bt_enable(NULL); if (ret) { @@ -151,15 +305,22 @@ int mp_bluetooth_init(void) { return 0; } -void mp_bluetooth_deinit(void) { +int mp_bluetooth_deinit(void) { DEBUG_printf("mp_bluetooth_deinit %d\n", mp_bluetooth_zephyr_ble_state); if (mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_OFF || mp_bluetooth_zephyr_ble_state == MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED) { - return; + return 0; } mp_bluetooth_gap_advertise_stop(); + #if CONFIG_BT_GATT_DYNAMIC_DB + for (size_t i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; ++i) { + bt_gatt_service_unregister(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]); + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i] = NULL; + } + #endif + #if MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE mp_bluetooth_gap_scan_stop(); bt_le_scan_cb_unregister(&mp_bluetooth_zephyr_gap_scan_cb_struct); @@ -170,6 +331,8 @@ void mp_bluetooth_deinit(void) { mp_bluetooth_zephyr_ble_state = MP_BLUETOOTH_ZEPHYR_BLE_STATE_SUSPENDED; MP_STATE_PORT(bluetooth_zephyr_root_pointers) = NULL; + mp_bt_zephyr_next_conn = NULL; + return 0; } bool mp_bluetooth_is_active(void) { @@ -191,7 +354,7 @@ void mp_bluetooth_get_current_address(uint8_t *addr_type, uint8_t *addr) { } void mp_bluetooth_set_address_mode(uint8_t addr_mode) { - // TODO: implement + mp_raise_OSError(MP_EOPNOTSUPP); } size_t mp_bluetooth_gap_get_device_name(const uint8_t **buf) { @@ -232,15 +395,11 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons mp_bluetooth_gap_advertise_stop(); - struct bt_data bt_ad_data[8]; - size_t bt_ad_len = 0; if (adv_data) { bt_ad_len = MP_ARRAY_SIZE(bt_ad_data); mp_bluetooth_prepare_bt_data(adv_data, adv_data_len, bt_ad_data, &bt_ad_len); } - struct bt_data bt_sd_data[8]; - size_t bt_sd_len = 0; if (sr_data) { bt_sd_len = MP_ARRAY_SIZE(bt_sd_data); mp_bluetooth_prepare_bt_data(sr_data, sr_data_len, bt_sd_data, &bt_sd_len); @@ -259,6 +418,10 @@ int mp_bluetooth_gap_advertise_start(bool connectable, int32_t interval_us, cons .peer = NULL, }; + // pre-allocate a new connection structure as we cannot allocate this inside the connection callback + mp_bt_zephyr_next_conn = m_new0(mp_bt_zephyr_conn_t, 1); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(mp_bt_zephyr_next_conn)); + return bt_err_to_errno(bt_le_adv_start(¶m, bt_ad_data, bt_ad_len, bt_sd_data, bt_sd_len)); } @@ -271,6 +434,8 @@ void mp_bluetooth_gap_advertise_stop(void) { } int mp_bluetooth_gatts_register_service_begin(bool append) { + #if CONFIG_BT_GATT_DYNAMIC_DB + if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } @@ -280,25 +445,190 @@ int mp_bluetooth_gatts_register_service_begin(bool append) { return MP_EOPNOTSUPP; } + // Unregister and unref any previous service definitions. + for (size_t i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; ++i) { + bt_gatt_service_unregister(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]); + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i] = NULL; + } + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services = 0; + // Reset the gatt characteristic value db. mp_bluetooth_gatts_db_reset(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db); + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->connections = NULL; + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list = mp_obj_new_list(0, NULL); + mp_bt_zephyr_next_conn = NULL; + return 0; + + #else return MP_EOPNOTSUPP; + #endif } int mp_bluetooth_gatts_register_service_end(void) { - return MP_EOPNOTSUPP; + return 0; } int mp_bluetooth_gatts_register_service(mp_obj_bluetooth_uuid_t *service_uuid, mp_obj_bluetooth_uuid_t **characteristic_uuids, uint16_t *characteristic_flags, mp_obj_bluetooth_uuid_t **descriptor_uuids, uint16_t *descriptor_flags, uint8_t *num_descriptors, uint16_t *handles, size_t num_characteristics) { + #if CONFIG_BT_GATT_DYNAMIC_DB + if (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services >= MP_BLUETOOTH_ZEPHYR_MAX_SERVICES) { + return MP_E2BIG; + } + + // first of all allocate the entire memory for all the attributes that this service is composed of + // 1 for the service itself, 2 for each characteristic (the declaration and the value), and one for each descriptor + size_t total_descriptors = 0; + for (size_t i = 0; i < num_characteristics; ++i) { + total_descriptors += num_descriptors[i]; + // we have to add the CCC manually + if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { + total_descriptors += 1; + } + } + size_t total_attributes = 1 + (num_characteristics * 2) + total_descriptors; + + // allocate one extra so that we can know later where the final attribute is + struct bt_gatt_attr *svc_attributes = m_new(struct bt_gatt_attr, total_attributes + 1); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(svc_attributes)); + + size_t handle_index = 0; + size_t descriptor_index = 0; + size_t attr_index = 0; + // bitfield of the handles we should ignore, should be more than enough for most applications + uint64_t attrs_to_ignore = 0; + uint64_t attrs_are_chrs = 0; + uint64_t chr_has_ccc = 0; + + add_service(create_zephyr_uuid(service_uuid), &svc_attributes[attr_index]); + attr_index += 1; + + for (size_t i = 0; i < num_characteristics; ++i) { + + struct add_characteristic add_char; + add_char.uuid = create_zephyr_uuid(characteristic_uuids[i]); + add_char.permissions = 0; + add_char.properties = 0; + if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) { + add_char.permissions |= BT_GATT_PERM_READ; + add_char.properties |= BT_GATT_CHRC_READ; + } + if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY) { + add_char.properties |= BT_GATT_CHRC_NOTIFY; + } + if (characteristic_flags[i] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE) { + add_char.properties |= BT_GATT_CHRC_INDICATE; + } + if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE | MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE)) { + add_char.permissions |= BT_GATT_PERM_WRITE; + add_char.properties |= (BT_GATT_CHRC_WRITE | BT_GATT_CHRC_WRITE_WITHOUT_RESP); + } + + add_characteristic(&add_char, &svc_attributes[attr_index], &svc_attributes[attr_index + 1]); + + struct bt_gatt_attr *curr_char = &svc_attributes[attr_index]; + attrs_are_chrs |= (1 << attr_index); + if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { + chr_has_ccc |= (1 << attr_index); + } + attr_index += 1; + attrs_to_ignore |= (1 << attr_index); // ignore the value handle + attr_index += 1; + + if (num_descriptors[i] > 0) { + for (size_t j = 0; j < num_descriptors[i]; ++j) { + + struct add_descriptor add_desc; + add_desc.uuid = create_zephyr_uuid(descriptor_uuids[descriptor_index]); + add_desc.permissions = 0; + if (descriptor_flags[descriptor_index] & MP_BLUETOOTH_CHARACTERISTIC_FLAG_READ) { + add_desc.permissions |= BT_GATT_PERM_READ; + } + if (descriptor_flags[descriptor_index] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE | MP_BLUETOOTH_CHARACTERISTIC_FLAG_WRITE_NO_RESPONSE)) { + add_desc.permissions |= BT_GATT_PERM_WRITE; + } + + add_descriptor(curr_char, &add_desc, &svc_attributes[attr_index]); + attr_index += 1; + + descriptor_index++; + } + } + + // to support indications and notifications we must add the CCC descriptor manually + if (characteristic_flags[i] & (MP_BLUETOOTH_CHARACTERISTIC_FLAG_NOTIFY | MP_BLUETOOTH_CHARACTERISTIC_FLAG_INDICATE)) { + struct add_descriptor add_desc; + mp_obj_bluetooth_uuid_t ccc_uuid; + ccc_uuid.base.type = &mp_type_bluetooth_uuid; + ccc_uuid.data[0] = BT_UUID_GATT_CCC_VAL & 0xff; + ccc_uuid.data[1] = (BT_UUID_GATT_CCC_VAL >> 8) & 0xff; + ccc_uuid.type = MP_BLUETOOTH_UUID_TYPE_16; + add_desc.uuid = create_zephyr_uuid(&ccc_uuid); + add_desc.permissions = BT_GATT_PERM_READ | BT_GATT_PERM_WRITE; + + attrs_to_ignore |= (1 << attr_index); + + add_descriptor(curr_char, &add_desc, &svc_attributes[attr_index]); + attr_index += 1; + } + } + + struct bt_gatt_service *service = m_new(struct bt_gatt_service, 1); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(service)); + service->attrs = svc_attributes; + service->attr_count = attr_index; + // invalidate the last attribute uuid pointer so that we new this is the end of attributes for this service + svc_attributes[attr_index].uuid = NULL; + + // Note: advertising must be stopped for gatts registration to work + + int err = bt_gatt_service_register(service); + if (err) { + return bt_err_to_errno(err); + } + + // now that the service has been registered, we can assign the handles for the characteristics and the descriptors + // we are not interested in the handle of the service itself, so we start the loop from index 1 + for (int i = 1; i < total_attributes; i++) { + // store all the relevant handles (characteristics and descriptors defined in Python) + if (!((uint64_t)(attrs_to_ignore >> i) & (uint64_t)0x01)) { + if (svc_attributes[i].user_data == NULL) { + mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN); + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle); + svc_attributes[i].user_data = entry->data; + } else if (((uint64_t)(attrs_are_chrs >> i) & (uint64_t)0x01)) { + if (svc_attributes[i + 1].user_data == NULL) { + mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle, MP_BLUETOOTH_DEFAULT_ATTR_LEN); + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle); + svc_attributes[i + 1].user_data = entry->data; + + if (((uint64_t)(chr_has_ccc >> i) & (uint64_t)0x01)) { + // create another database entry for the ccc of this characteristic + mp_bluetooth_gatts_db_create_entry(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, svc_attributes[i].handle + 2, 1); + } + } + } + handles[handle_index++] = svc_attributes[i].handle; + } + } + + MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services++] = service; + + return 0; + + #else return MP_EOPNOTSUPP; + #endif } int mp_bluetooth_gap_disconnect(uint16_t conn_handle) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - return MP_EOPNOTSUPP; + mp_bt_zephyr_conn_t *connection = mp_bt_zephyr_find_connection(conn_handle); + if (connection) { + return bt_conn_disconnect(connection->conn, BT_HCI_ERR_REMOTE_USER_TERM_CONN); + } + return MP_ENOENT; } int mp_bluetooth_gatts_read(uint16_t value_handle, const uint8_t **value, size_t *value_len) { @@ -312,24 +642,155 @@ int mp_bluetooth_gatts_write(uint16_t value_handle, const uint8_t *value, size_t if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - if (send_update) { - return MP_EOPNOTSUPP; + + int err = mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len); + + if ((err == 0) && send_update) { + struct bt_gatt_attr *attr_val = mp_bt_zephyr_find_attr_by_handle(value_handle + 1); + mp_bluetooth_gatts_db_entry_t *ccc_entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle + 2); + + if (ccc_entry && (ccc_entry->data[0] == BT_GATT_CCC_NOTIFY)) { + err = bt_gatt_notify(NULL, attr_val, value, value_len); + } else if (ccc_entry && (ccc_entry->data[0] == BT_GATT_CCC_INDICATE)) { + struct bt_gatt_indicate_params params = { + .uuid = NULL, + .attr = attr_val, + .func = mp_bt_zephyr_gatt_indicate_done, + .destroy = NULL, + .data = value, + .len = value_len + }; + err = bt_gatt_indicate(NULL, ¶ms); + } + } + return err; +} + +static void mp_bt_zephyr_gatt_indicate_done(struct bt_conn *conn, struct bt_gatt_indicate_params *params, uint8_t err) { + struct bt_conn_info info; + bt_conn_get_info(conn, &info); + uint16_t chr_handle = params->attr->handle - 1; + mp_bluetooth_gatts_on_indicate_complete(info.id, chr_handle, err); +} + +static ssize_t mp_bt_zephyr_gatts_attr_read(struct bt_conn *conn, const struct bt_gatt_attr *attr, void *buf, uint16_t len, uint16_t offset) { + // we receive the value handle, but to look up in the gatts db we need the characteristic handle, and that is is the value handle minus 1 + uint16_t _handle = attr->handle - 1; + + DEBUG_printf("BLE attr read for handle %d\n", attr->handle); + + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); + if (!entry) { + // it could be a descriptor instead + _handle = attr->handle; + entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); + if (!entry) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_HANDLE); + } + } + + return bt_gatt_attr_read(conn, attr, buf, len, offset, entry->data, entry->data_len); +} + +static ssize_t mp_bt_zephyr_gatts_attr_write(struct bt_conn *conn, const struct bt_gatt_attr *attr, const void *buf, uint16_t len, uint16_t offset, uint8_t flags) { + struct bt_conn_info info; + bt_conn_get_info(conn, &info); + + DEBUG_printf("BLE attr write for handle %d\n", attr->handle); + + // the characteristic handle is the value handle minus 1 + uint16_t _handle = attr->handle - 1; + + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); + if (!entry) { + // it could be a descriptor instead + _handle = attr->handle; + entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, _handle); + if (!entry) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_HANDLE); + } + } + + // Don't write anything if prepare flag is set + if (flags & BT_GATT_WRITE_FLAG_PREPARE) { + return 0; + } + + if (offset > entry->data_alloc) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_OFFSET); + } + + if ((offset + len) > entry->data_alloc) { + return BT_GATT_ERR(BT_ATT_ERR_INVALID_ATTRIBUTE_LEN); + } + + if (entry->append) { + offset = entry->data_len; + } + + // copy the data into the buffer in the gatts database + memcpy(&entry->data[offset], buf, len); + entry->data_len = offset + len; + + mp_bluetooth_gatts_on_write(info.id, _handle); + + return len; +} + +static struct bt_gatt_attr *mp_bt_zephyr_find_attr_by_handle(uint16_t value_handle) { + for (int i = 0; i < MP_STATE_PORT(bluetooth_zephyr_root_pointers)->n_services; i++) { + int j = 0; + while (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j].uuid != NULL) { + if (MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j].handle == value_handle) { + return &MP_STATE_PORT(bluetooth_zephyr_root_pointers)->services[i]->attrs[j]; + } + j++; + } } - return mp_bluetooth_gatts_db_write(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, value, value_len); + return NULL; } int mp_bluetooth_gatts_notify_indicate(uint16_t conn_handle, uint16_t value_handle, int gatts_op, const uint8_t *value, size_t value_len) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - return MP_EOPNOTSUPP; + + int err = MP_ENOENT; + mp_bt_zephyr_conn_t *connection = mp_bt_zephyr_find_connection(conn_handle); + + if (connection) { + struct bt_gatt_attr *attr_val = mp_bt_zephyr_find_attr_by_handle(value_handle + 1); + + if (attr_val) { + switch (gatts_op) { + case MP_BLUETOOTH_GATTS_OP_NOTIFY: { + err = bt_gatt_notify(connection->conn, attr_val, value, value_len); + break; + } + case MP_BLUETOOTH_GATTS_OP_INDICATE: { + struct bt_gatt_indicate_params params = { + .uuid = NULL, + .attr = attr_val, + .func = mp_bt_zephyr_gatt_indicate_done, + .destroy = NULL, + .data = value, + .len = value_len + }; + err = bt_gatt_indicate(connection->conn, ¶ms); + break; + } + } + } + } + + return err; } int mp_bluetooth_gatts_set_buffer(uint16_t value_handle, size_t len, bool append) { if (!mp_bluetooth_is_active()) { return ERRNO_BLUETOOTH_NOT_ACTIVE; } - return MP_EOPNOTSUPP; + return mp_bluetooth_gatts_db_resize(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, value_handle, len, append); } int mp_bluetooth_get_preferred_mtu(void) { @@ -404,6 +865,133 @@ int mp_bluetooth_gap_peripheral_connect_cancel(void) { #endif // MICROPY_PY_BLUETOOTH_ENABLE_CENTRAL_MODE +// Note: modbluetooth UUIDs store their data in LE. +static struct bt_uuid *create_zephyr_uuid(const mp_obj_bluetooth_uuid_t *uuid) { + struct bt_uuid *result = (struct bt_uuid *)m_new(union uuid_u, 1); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(result)); + if (uuid->type == MP_BLUETOOTH_UUID_TYPE_16) { + bt_uuid_create(result, uuid->data, 2); + } else if (uuid->type == MP_BLUETOOTH_UUID_TYPE_32) { + bt_uuid_create(result, uuid->data, 4); + } else { // MP_BLUETOOTH_UUID_TYPE_128 + bt_uuid_create(result, uuid->data, 16); + } + return result; +} + +static void gatt_db_add(const struct bt_gatt_attr *pattern, struct bt_gatt_attr *attr, size_t user_data_len) { + const union uuid_u *u = CONTAINER_OF(pattern->uuid, union uuid_u, uuid); + size_t uuid_size = sizeof(u->u16); + + if (u->uuid.type == BT_UUID_TYPE_32) { + uuid_size = sizeof(u->u32); + } else if (u->uuid.type == BT_UUID_TYPE_128) { + uuid_size = sizeof(u->u128); + } + + memcpy(attr, pattern, sizeof(*attr)); + + // Store the UUID. + attr->uuid = (const struct bt_uuid *)m_new(union uuid_u, 1); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(attr->uuid)); + memcpy((void *)attr->uuid, &u->uuid, uuid_size); + + // Copy user_data to the buffer. + if (user_data_len) { + attr->user_data = m_new(uint8_t, user_data_len); + mp_obj_list_append(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->objs_list, MP_OBJ_FROM_PTR(attr->user_data)); + memcpy(attr->user_data, pattern->user_data, user_data_len); + } +} + +static void add_service(const struct bt_uuid *u, struct bt_gatt_attr *attr) { + union uuid_u *uuid = (union uuid_u *)u; + + size_t uuid_size = sizeof(uuid->u16); + + if (uuid->uuid.type == BT_UUID_TYPE_32) { + uuid_size = sizeof(uuid->u32); + } else if (uuid->uuid.type == BT_UUID_TYPE_128) { + uuid_size = sizeof(uuid->u128); + } + + gatt_db_add(&(struct bt_gatt_attr)BT_GATT_PRIMARY_SERVICE(&uuid->uuid), attr, uuid_size); +} + +static void add_characteristic(struct add_characteristic *ch, struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_value) { + struct bt_gatt_chrc *chrc_data; + + // Add Characteristic Declaration + gatt_db_add(&(struct bt_gatt_attr) + BT_GATT_ATTRIBUTE(BT_UUID_GATT_CHRC, + BT_GATT_PERM_READ, + bt_gatt_attr_read_chrc, NULL, + (&(struct bt_gatt_chrc) {})), attr_chrc, sizeof(*chrc_data)); + + // Allow prepare writes + ch->permissions |= BT_GATT_PERM_PREPARE_WRITE; + + // Add Characteristic Value + gatt_db_add(&(struct bt_gatt_attr) + BT_GATT_ATTRIBUTE(ch->uuid, + ch->permissions & GATT_PERM_MASK, + mp_bt_zephyr_gatts_attr_read, mp_bt_zephyr_gatts_attr_write, NULL), attr_value, 0); + + chrc_data = attr_chrc->user_data; + chrc_data->properties = ch->properties; + chrc_data->uuid = attr_value->uuid; +} + +static void ccc_cfg_changed(const struct bt_gatt_attr *attr, uint16_t value) { + mp_bluetooth_gatts_db_entry_t *entry = mp_bluetooth_gatts_db_lookup(MP_STATE_PORT(bluetooth_zephyr_root_pointers)->gatts_db, attr->handle); + entry->data[0] = value; +} + +static struct bt_gatt_attr ccc_definition = BT_GATT_CCC(ccc_cfg_changed, BT_GATT_PERM_READ | BT_GATT_PERM_WRITE); + +static void add_ccc(struct bt_gatt_attr *attr, struct bt_gatt_attr *attr_desc) { + struct bt_gatt_chrc *chrc = attr->user_data; + + // Check characteristic properties + if (!(chrc->properties & (BT_GATT_CHRC_NOTIFY | BT_GATT_CHRC_INDICATE))) { + mp_raise_OSError(MP_EINVAL); + } + + // Add CCC descriptor to GATT database + gatt_db_add(&ccc_definition, attr_desc, 0); +} + +static void add_cep(const struct bt_gatt_attr *attr_chrc, struct bt_gatt_attr *attr_desc) { + struct bt_gatt_chrc *chrc = attr_chrc->user_data; + struct bt_gatt_cep cep_value; + + // Extended Properties bit shall be set + if (!(chrc->properties & BT_GATT_CHRC_EXT_PROP)) { + mp_raise_OSError(MP_EINVAL); + } + + cep_value.properties = 0x0000; + + // Add CEP descriptor to GATT database + gatt_db_add(&(struct bt_gatt_attr)BT_GATT_CEP(&cep_value), attr_desc, sizeof(cep_value)); +} + +static void add_descriptor(struct bt_gatt_attr *chrc, struct add_descriptor *d, struct bt_gatt_attr *attr_desc) { + if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CEP)) { + add_cep(chrc, attr_desc); + } else if (!bt_uuid_cmp(d->uuid, BT_UUID_GATT_CCC)) { + add_ccc(chrc, attr_desc); + } else { + // Allow prepare writes + d->permissions |= BT_GATT_PERM_PREPARE_WRITE; + + gatt_db_add(&(struct bt_gatt_attr) + BT_GATT_DESCRIPTOR(d->uuid, + d->permissions & GATT_PERM_MASK, + mp_bt_zephyr_gatts_attr_read, mp_bt_zephyr_gatts_attr_write, NULL), attr_desc, 0); + } +} + MP_REGISTER_ROOT_POINTER(struct _mp_bluetooth_zephyr_root_pointers_t *bluetooth_zephyr_root_pointers); #endif // MICROPY_PY_BLUETOOTH diff --git a/ports/zephyr/modsocket.c b/ports/zephyr/modsocket.c index 1ca84edcac..d8955bffbe 100644 --- a/ports/zephyr/modsocket.c +++ b/ports/zephyr/modsocket.c @@ -32,8 +32,6 @@ #include <stdio.h> #include <zephyr/kernel.h> -// Zephyr's generated version header -#include <version.h> #include <zephyr/net/net_context.h> #include <zephyr/net/net_pkt.h> #include <zephyr/net/dns_resolve.h> diff --git a/ports/zephyr/mpconfigport.h b/ports/zephyr/mpconfigport.h index e015776a4e..62226a2ded 100644 --- a/ports/zephyr/mpconfigport.h +++ b/ports/zephyr/mpconfigport.h @@ -26,7 +26,7 @@ #include <alloca.h> // Include Zephyr's autoconf.h, which should be made first by Zephyr makefiles -#include "autoconf.h" +#include <zephyr/autoconf.h> // Included here to get basic Zephyr environment (macros, etc.) #include <zephyr/kernel.h> #include <zephyr/drivers/spi.h> @@ -36,17 +36,21 @@ #define MICROPY_HEAP_SIZE (16 * 1024) #endif +// We can't guarantee object layout of nlr code so use long jump by default. +#define MICROPY_NLR_THUMB_USE_LONG_JUMP (1) + +#define MICROPY_PERSISTENT_CODE_LOAD (1) #define MICROPY_ENABLE_SOURCE_LINE (1) #define MICROPY_STACK_CHECK (1) #define MICROPY_ENABLE_GC (1) #define MICROPY_ENABLE_FINALISER (MICROPY_VFS) #define MICROPY_HELPER_REPL (1) #define MICROPY_REPL_AUTO_INDENT (1) +#define MICROPY_ENABLE_EMERGENCY_EXCEPTION_BUF (1) #define MICROPY_KBD_EXCEPTION (1) #define MICROPY_PY_ASYNC_AWAIT (0) #define MICROPY_PY_BUILTINS_BYTES_HEX (1) #define MICROPY_PY_BUILTINS_FILTER (0) -#define MICROPY_PY_BUILTINS_MIN_MAX (0) #define MICROPY_PY_BUILTINS_PROPERTY (0) #define MICROPY_PY_BUILTINS_RANGE_ATTRS (0) #define MICROPY_PY_BUILTINS_REVERSED (0) @@ -55,16 +59,18 @@ #define MICROPY_PY_BUILTINS_HELP (1) #define MICROPY_PY_BUILTINS_HELP_TEXT zephyr_help_text #define MICROPY_PY_ARRAY (0) +#define MICROPY_PY_ARRAY_SLICE_ASSIGN (1) #define MICROPY_PY_COLLECTIONS (0) #define MICROPY_PY_CMATH (0) -#define MICROPY_PY_IO (0) #define MICROPY_PY_MICROPYTHON_MEM_INFO (1) #define MICROPY_PY_MACHINE (1) #define MICROPY_PY_MACHINE_INCLUDEFILE "ports/zephyr/modmachine.c" #define MICROPY_PY_MACHINE_I2C (1) +#define MICROPY_PY_MACHINE_SOFTI2C (1) #define MICROPY_PY_MACHINE_SPI (1) #define MICROPY_PY_MACHINE_SPI_MSB (SPI_TRANSFER_MSB) #define MICROPY_PY_MACHINE_SPI_LSB (SPI_TRANSFER_LSB) +#define MICROPY_PY_MACHINE_SOFTSPI (1) #define MICROPY_PY_MACHINE_PIN_MAKE_NEW mp_pin_make_new #define MICROPY_PY_MACHINE_UART (1) #define MICROPY_PY_MACHINE_UART_INCLUDEFILE "ports/zephyr/machine_uart.c" @@ -95,7 +101,8 @@ #define MICROPY_PY_TIME_INCLUDEFILE "ports/zephyr/modtime.c" #define MICROPY_PY_ZEPHYR (1) #define MICROPY_PY_ZSENSOR (1) -#define MICROPY_PY_SYS_MODULES (0) +#define MICROPY_PY_SYS_MAXSIZE (1) +#define MICROPY_PY_SYS_STDFILES (1) #define MICROPY_LONGINT_IMPL (MICROPY_LONGINT_IMPL_MPZ) #define MICROPY_FLOAT_IMPL (MICROPY_FLOAT_IMPL_FLOAT) #define MICROPY_PY_BUILTINS_COMPLEX (0) @@ -139,12 +146,14 @@ void mp_hal_signal_event(void); #define MICROPY_HW_MCU_NAME "unknown-cpu" #endif -typedef int mp_int_t; // must be pointer size -typedef unsigned mp_uint_t; // must be pointer size +typedef intptr_t mp_int_t; // must be pointer size +typedef uintptr_t mp_uint_t; // must be pointer size typedef long mp_off_t; #define MP_STATE_PORT MP_STATE_VM +#define MP_SSIZE_MAX (0x7fffffff) + // 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/zephyr/mpconfigport_minimal.h b/ports/zephyr/mpconfigport_minimal.h index 9aae20c72c..a0a7f97394 100644 --- a/ports/zephyr/mpconfigport_minimal.h +++ b/ports/zephyr/mpconfigport_minimal.h @@ -26,7 +26,7 @@ #include <alloca.h> // Include Zephyr's autoconf.h, which should be made first by Zephyr makefiles -#include "autoconf.h" +#include <zephyr/autoconf.h> // Included here to get basic Zephyr environment (macros, etc.) #include <zephyr/kernel.h> diff --git a/ports/zephyr/mphalport.c b/ports/zephyr/mphalport.c index 2c95032843..db536ec085 100644 --- a/ports/zephyr/mphalport.c +++ b/ports/zephyr/mphalport.c @@ -26,6 +26,7 @@ #include "py/runtime.h" #include "py/mphal.h" +#include "extmod/modmachine.h" static struct k_poll_signal wait_signal; static struct k_poll_event wait_events[2] = { @@ -75,3 +76,10 @@ void mp_hal_wait_sem(struct k_sem *sem, uint32_t timeout_ms) { } } } + +mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in) { + if (mp_obj_is_type(pin_in, &machine_pin_type)) { + return MP_OBJ_TO_PTR(pin_in); + } + mp_raise_ValueError(MP_ERROR_TEXT("invalid pin")); +} diff --git a/ports/zephyr/mphalport.h b/ports/zephyr/mphalport.h index e5414c2705..7410204621 100644 --- a/ports/zephyr/mphalport.h +++ b/ports/zephyr/mphalport.h @@ -1,4 +1,5 @@ #include <zephyr/kernel.h> +#include <zephyr/drivers/gpio.h> #include "shared/runtime/interrupt_char.h" #define MICROPY_BEGIN_ATOMIC_SECTION irq_lock @@ -35,3 +36,48 @@ static inline uint64_t mp_hal_time_ns(void) { } #define mp_hal_delay_us_fast(us) (mp_hal_delay_us(us)) + +// C-level pin HAL + +#include "modmachine.h" + +#define MP_HAL_PIN_FMT "%u" +#define mp_hal_pin_obj_t const machine_pin_obj_t * + +mp_hal_pin_obj_t mp_hal_get_pin_obj(mp_obj_t pin_in); + +static inline unsigned int mp_hal_pin_name(mp_hal_pin_obj_t pin) { + // TODO make it include the port + return pin->pin; +} + +static inline void mp_hal_pin_input(mp_hal_pin_obj_t pin) { + (void)gpio_pin_configure(pin->port, pin->pin, GPIO_INPUT); +} + +static inline void mp_hal_pin_output(mp_hal_pin_obj_t pin) { + if (gpio_pin_configure(pin->port, pin->pin, GPIO_OUTPUT | GPIO_INPUT) == -ENOTSUP) { + // If GPIO_OUTPUT|GPIO_INPUT is not supported (eg frdm_k64f) then try just GPIO_OUTPUT. + (void)gpio_pin_configure(pin->port, pin->pin, GPIO_OUTPUT); + } +} + +static inline void mp_hal_pin_open_drain(mp_hal_pin_obj_t pin) { + (void)gpio_pin_configure(pin->port, pin->pin, GPIO_OUTPUT | GPIO_INPUT | GPIO_OPEN_DRAIN); +} + +static inline int mp_hal_pin_read(mp_hal_pin_obj_t pin) { + return gpio_pin_get_raw(pin->port, pin->pin); +} + +static inline void mp_hal_pin_write(mp_hal_pin_obj_t pin, int v) { + (void)gpio_pin_set_raw(pin->port, pin->pin, v); +} + +static inline void mp_hal_pin_od_low(mp_hal_pin_obj_t pin) { + (void)gpio_pin_set_raw(pin->port, pin->pin, 0); +} + +static inline void mp_hal_pin_od_high(mp_hal_pin_obj_t pin) { + (void)gpio_pin_set_raw(pin->port, pin->pin, 1); +} diff --git a/ports/zephyr/prj.conf b/ports/zephyr/prj.conf index 0325cddd20..0939e226cf 100644 --- a/ports/zephyr/prj.conf +++ b/ports/zephyr/prj.conf @@ -6,11 +6,6 @@ CONFIG_STDOUT_CONSOLE=y CONFIG_CONSOLE_HANDLER=y CONFIG_UART_CONSOLE_DEBUG_SERVER_HOOKS=y -CONFIG_CONSOLE_SUBSYS=y -CONFIG_CONSOLE_GETCHAR=y -CONFIG_CONSOLE_GETCHAR_BUFSIZE=258 -CONFIG_CONSOLE_PUTCHAR_BUFSIZE=128 - CONFIG_NEWLIB_LIBC=y CONFIG_FPU=y CONFIG_MAIN_STACK_SIZE=4736 @@ -81,3 +76,10 @@ CONFIG_MICROPY_VFS_LFS2=y CONFIG_WATCHDOG=y CONFIG_WDT_DISABLE_AT_BOOT=y + +CONFIG_SERIAL=y +CONFIG_UART_INTERRUPT_DRIVEN=y +CONFIG_UART_LINE_CTRL=y +CONFIG_UART_USE_RUNTIME_CONFIGURE=y + +CONFIG_LEGACY_GENERATED_INCLUDE_PATH=n diff --git a/ports/zephyr/prj_minimal.conf b/ports/zephyr/prj_minimal.conf index 6c850751d7..f58c932cea 100644 --- a/ports/zephyr/prj_minimal.conf +++ b/ports/zephyr/prj_minimal.conf @@ -8,8 +8,7 @@ CONFIG_CONSOLE_SUBSYS=y CONFIG_CONSOLE_GETCHAR=y CONFIG_CONSOLE_GETCHAR_BUFSIZE=258 CONFIG_CONSOLE_PUTCHAR_BUFSIZE=32 -# TODO: Disable once https://github.com/zephyrproject-rtos/zephyr/pull/13731 is merged -#CONFIG_CONSOLE=n -#CONFIG_UART_CONSOLE=n CONFIG_MICROPY_HEAP_SIZE=16384 + +CONFIG_LEGACY_GENERATED_INCLUDE_PATH=n diff --git a/ports/zephyr/qstrdefsport.h b/ports/zephyr/qstrdefsport.h new file mode 100644 index 0000000000..66819e432c --- /dev/null +++ b/ports/zephyr/qstrdefsport.h @@ -0,0 +1,4 @@ +// qstrs specific to this port +// *FORMAT-OFF* +Q(/flash/lib) +Q(/sd/lib) diff --git a/ports/zephyr/src/usbd.c b/ports/zephyr/src/usbd.c new file mode 100644 index 0000000000..36b07a8638 --- /dev/null +++ b/ports/zephyr/src/usbd.c @@ -0,0 +1,193 @@ +/* +* This file is part of the MicroPython project, http://micropython.org/ +* +* The MIT License (MIT) +* +* Copyright (c) 2024-2025 MASSDRIVER EI (massdriver.space) +* +* 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 <stdint.h> + +#include <zephyr/device.h> +#include <zephyr/usb/usbd.h> +#include <zephyr/usb/bos.h> + +#if defined(CONFIG_USB_DEVICE_STACK_NEXT) + +#include <zephyr/logging/log.h> +LOG_MODULE_REGISTER(mp_usbd); + +#if KERNEL_VERSION_NUMBER >= ZEPHYR_VERSION(4, 1, 0) + +#define BLOCKLIST , blocklist + +/* By default, do not register the USB DFU class DFU mode instance. */ +static const char *const blocklist[] = { + "dfu_dfu", + NULL, +}; + +#else + +#define BLOCKLIST + +#endif + +USBD_DEVICE_DEFINE(mp_usbd, + DEVICE_DT_GET(DT_NODELABEL(zephyr_udc0)), + CONFIG_MICROPY_USB_DEVICE_VID, CONFIG_MICROPY_USB_DEVICE_PID); + +USBD_DESC_LANG_DEFINE(mp_lang); +USBD_DESC_MANUFACTURER_DEFINE(mp_mfr, "Zephyr Project"); +USBD_DESC_PRODUCT_DEFINE(mp_product, "Micropython on Zephyr RTOS"); +USBD_DESC_SERIAL_NUMBER_DEFINE(mp_sn); + +USBD_DESC_CONFIG_DEFINE(fs_cfg_desc, "FS Configuration"); +USBD_DESC_CONFIG_DEFINE(hs_cfg_desc, "HS Configuration"); + +/* not self-powered, no remote wakeup */ +static const uint8_t attributes = 0; + +/* Full speed configuration +* power = 250 * 2 mA = 500mA +*/ +USBD_CONFIGURATION_DEFINE(mp_fs_config, + attributes, + 250, &fs_cfg_desc); + +/* High speed configuration */ +USBD_CONFIGURATION_DEFINE(mp_hs_config, + attributes, + 250, &hs_cfg_desc); + +static void mp_fix_code_triple(struct usbd_context *uds_ctx, + const enum usbd_speed speed) { + /* Always use class code information from Interface Descriptors */ + if (IS_ENABLED(CONFIG_USBD_CDC_ACM_CLASS) || + IS_ENABLED(CONFIG_USBD_CDC_ECM_CLASS) || + IS_ENABLED(CONFIG_USBD_CDC_NCM_CLASS) || + IS_ENABLED(CONFIG_USBD_AUDIO2_CLASS)) { + /* + * Class with multiple interfaces have an Interface + * Association Descriptor available, use an appropriate triple + * to indicate it. + */ + usbd_device_set_code_triple(uds_ctx, speed, + USB_BCC_MISCELLANEOUS, 0x02, 0x01); + } else { + usbd_device_set_code_triple(uds_ctx, speed, 0, 0, 0); + } +} + +struct usbd_context *mp_usbd_init_device(usbd_msg_cb_t msg_cb) { + int err; + + err = usbd_add_descriptor(&mp_usbd, &mp_lang); + if (err) { + LOG_ERR("Failed to initialize language descriptor (%d)", err); + return NULL; + } + + err = usbd_add_descriptor(&mp_usbd, &mp_mfr); + if (err) { + LOG_ERR("Failed to initialize manufacturer descriptor (%d)", err); + return NULL; + } + + err = usbd_add_descriptor(&mp_usbd, &mp_product); + if (err) { + LOG_ERR("Failed to initialize product descriptor (%d)", err); + return NULL; + } + + err = usbd_add_descriptor(&mp_usbd, &mp_sn); + if (err) { + LOG_ERR("Failed to initialize SN descriptor (%d)", err); + return NULL; + } + + if (usbd_caps_speed(&mp_usbd) == USBD_SPEED_HS) { + err = usbd_add_configuration(&mp_usbd, USBD_SPEED_HS, + &mp_hs_config); + if (err) { + LOG_ERR("Failed to add High-Speed configuration"); + return NULL; + } + + err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_HS, 1 BLOCKLIST); + if (err) { + LOG_ERR("Failed to add register classes"); + return NULL; + } + + mp_fix_code_triple(&mp_usbd, USBD_SPEED_HS); + } + + err = usbd_add_configuration(&mp_usbd, USBD_SPEED_FS, + &mp_fs_config); + if (err) { + LOG_ERR("Failed to add Full-Speed configuration"); + return NULL; + } + + err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_FS, 1 BLOCKLIST); + if (err) { + LOG_ERR("Failed to add register classes"); + return NULL; + } + + mp_fix_code_triple(&mp_usbd, USBD_SPEED_FS); + + if (msg_cb != NULL) { + err = usbd_msg_register_cb(&mp_usbd, msg_cb); + if (err) { + LOG_ERR("Failed to register message callback"); + return NULL; + } + } + + err = usbd_init(&mp_usbd); + if (err) { + LOG_ERR("Failed to initialize device support"); + return NULL; + } + + return &mp_usbd; +} + +static struct usbd_context *mp_usbd_context; + +int mp_usbd_init(void) { + int err; + + mp_usbd_context = mp_usbd_init_device(NULL); + if (mp_usbd_context == NULL) { + return -ENODEV; + } + + err = usbd_enable(mp_usbd_context); + if (err) { + return err; + } + + return 0; +} + +#endif // defined(CONFIG_USB_DEVICE_STACK_NEXT) diff --git a/ports/zephyr/src/zephyr_getchar.c b/ports/zephyr/src/zephyr_getchar.c index 94e35e2e84..bf504a97c9 100644 --- a/ports/zephyr/src/zephyr_getchar.c +++ b/ports/zephyr/src/zephyr_getchar.c @@ -23,12 +23,10 @@ extern int mp_interrupt_char; void mp_sched_keyboard_interrupt(void); void mp_hal_signal_event(void); -void mp_hal_wait_sem(struct k_sem *sem, uint32_t timeout_ms); -static struct k_sem uart_sem; -#define UART_BUFSIZE 256 +#define UART_BUFSIZE (512) static uint8_t uart_ringbuf[UART_BUFSIZE]; -static uint8_t i_get, i_put; +static uint16_t i_get, i_put; static int console_irq_input_hook(uint8_t ch) { int i_next = (i_put + 1) & (UART_BUFSIZE - 1); @@ -44,14 +42,16 @@ static int console_irq_input_hook(uint8_t ch) { uart_ringbuf[i_put] = ch; i_put = i_next; } - // printk("%x\n", ch); - k_sem_give(&uart_sem); return 1; } +// Returns true if a char is available for reading. +int zephyr_getchar_check(void) { + return i_get != i_put; +} + int zephyr_getchar(void) { - mp_hal_wait_sem(&uart_sem, 0); - if (k_sem_take(&uart_sem, K_MSEC(0)) == 0) { + if (i_get != i_put) { unsigned int key = irq_lock(); int c = (int)uart_ringbuf[i_get++]; i_get &= UART_BUFSIZE - 1; @@ -62,7 +62,6 @@ int zephyr_getchar(void) { } void zephyr_getchar_init(void) { - k_sem_init(&uart_sem, 0, UINT_MAX); uart_console_in_debug_hook_install(console_irq_input_hook); // All NULLs because we're interested only in the callback above uart_register_input(NULL, NULL, NULL); diff --git a/ports/zephyr/src/zephyr_getchar.h b/ports/zephyr/src/zephyr_getchar.h index fee899e1b4..3f31c4317f 100644 --- a/ports/zephyr/src/zephyr_getchar.h +++ b/ports/zephyr/src/zephyr_getchar.h @@ -17,4 +17,5 @@ #include <stdint.h> void zephyr_getchar_init(void); +int zephyr_getchar_check(void); int zephyr_getchar(void); diff --git a/ports/zephyr/uart_core.c b/ports/zephyr/uart_core.c index ee525c33f7..fe8a2a51db 100644 --- a/ports/zephyr/uart_core.c +++ b/ports/zephyr/uart_core.c @@ -26,6 +26,7 @@ #include <unistd.h> #include "py/mpconfig.h" #include "py/runtime.h" +#include "py/stream.h" #include "src/zephyr_getchar.h" // Zephyr headers #include <zephyr/kernel.h> @@ -52,6 +53,24 @@ static uint8_t mp_console_txbuf[CONFIG_CONSOLE_PUTCHAR_BUFSIZE]; * Core UART functions to implement for a port */ +uintptr_t mp_hal_stdio_poll(uintptr_t poll_flags) { + uintptr_t ret = 0; + if (poll_flags & MP_STREAM_POLL_RD) { + #ifdef CONFIG_CONSOLE_SUBSYS + // It's not easy to test if tty is readable, so just unconditionally set it for now. + ret |= MP_STREAM_POLL_RD; + #else + if (zephyr_getchar_check()) { + ret |= MP_STREAM_POLL_RD; + } + #endif + } + if (poll_flags & MP_STREAM_POLL_WR) { + ret |= MP_STREAM_POLL_WR; + } + return ret; +} + // Receive single character int mp_hal_stdin_rx_chr(void) { for (;;) { diff --git a/ports/zephyr/zephyr_storage.c b/ports/zephyr/zephyr_storage.c index 484feb1130..40bcef7338 100644 --- a/ports/zephyr/zephyr_storage.c +++ b/ports/zephyr/zephyr_storage.c @@ -139,6 +139,20 @@ MP_DEFINE_CONST_OBJ_TYPE( #endif // CONFIG_DISK_ACCESS #ifdef CONFIG_FLASH_MAP + +#define FLASH_AREA_DEFINE_LABEL(part) CONCAT(MP_QSTR_ID_, DT_STRING_TOKEN(part, label)) +#define FLASH_AREA_DEFINE_NB(part) CONCAT(MP_QSTR_ID_, DT_FIXED_PARTITION_ID(part)) + +#define FLASH_AREA_DEFINE_GETNAME(part) COND_CODE_1(DT_NODE_HAS_PROP(part, label), \ + (FLASH_AREA_DEFINE_LABEL(part)), (FLASH_AREA_DEFINE_NB(part))) + +#define FLASH_AREA_DEFINE_DEFINE(part) { MP_ROM_QSTR(FLASH_AREA_DEFINE_GETNAME(part)), MP_ROM_INT(DT_FIXED_PARTITION_ID(part)) }, + +#define FLASH_AREA_DEFINE(part) COND_CODE_1(DT_NODE_HAS_STATUS_OKAY(DT_MTD_FROM_FIXED_PARTITION(part)), \ + (FLASH_AREA_DEFINE_DEFINE(part)), ()) + +#define FOREACH_PARTITION(n) DT_FOREACH_CHILD(n, FLASH_AREA_DEFINE) + const mp_obj_type_t zephyr_flash_area_type; typedef struct _zephyr_flash_area_obj_t { @@ -244,9 +258,8 @@ static const mp_rom_map_elem_t zephyr_flash_area_locals_dict_table[] = { { MP_ROM_QSTR(MP_QSTR_readblocks), MP_ROM_PTR(&zephyr_flash_area_readblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_writeblocks), MP_ROM_PTR(&zephyr_flash_area_writeblocks_obj) }, { MP_ROM_QSTR(MP_QSTR_ioctl), MP_ROM_PTR(&zephyr_flash_area_ioctl_obj) }, - #if FIXED_PARTITION_EXISTS(storage_partition) - { MP_ROM_QSTR(MP_QSTR_STORAGE), MP_ROM_INT(FIXED_PARTITION_ID(storage_partition)) }, - #endif + /* Generate list of partition IDs from Zephyr Devicetree */ + DT_FOREACH_STATUS_OKAY(fixed_partitions, FOREACH_PARTITION) }; static MP_DEFINE_CONST_DICT(zephyr_flash_area_locals_dict, zephyr_flash_area_locals_dict_table); |