summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--drivers/bus/qspi.h1
-rw-r--r--drivers/memory/spiflash.c8
-rw-r--r--lib/libm_dbl/rint.c2
m---------lib/stm32lib0
-rw-r--r--ports/stm32/Makefile58
-rw-r--r--ports/stm32/adc.c93
-rw-r--r--ports/stm32/adc.h2
-rw-r--r--ports/stm32/boardctrl.h1
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/bdev.c42
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/board.c122
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/board.md17
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.h103
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/mpconfigboard.mk26
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/partition_stm32n657xx.h5
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/pins.csv62
-rw-r--r--ports/stm32/boards/NUCLEO_N657X0/stm32n6xx_hal_conf.h18
-rw-r--r--ports/stm32/boards/OPENMV_N6/bdev.c41
-rw-r--r--ports/stm32/boards/OPENMV_N6/board.c131
-rw-r--r--ports/stm32/boards/OPENMV_N6/board.ld39
-rw-r--r--ports/stm32/boards/OPENMV_N6/manifest.py3
-rw-r--r--ports/stm32/boards/OPENMV_N6/mpconfigboard.h167
-rw-r--r--ports/stm32/boards/OPENMV_N6/mpconfigboard.mk30
-rw-r--r--ports/stm32/boards/OPENMV_N6/partition_stm32n657xx.h5
-rw-r--r--ports/stm32/boards/OPENMV_N6/pins.csv142
-rw-r--r--ports/stm32/boards/OPENMV_N6/stm32n6xx_hal_conf.h18
-rw-r--r--ports/stm32/boards/common_n6_flash.ld57
-rw-r--r--ports/stm32/boards/common_text.ld8
-rwxr-xr-xports/stm32/boards/make-pins.py2
-rw-r--r--ports/stm32/boards/pllvalues.py2
-rw-r--r--ports/stm32/boards/stm32n657_af.csv42
-rw-r--r--ports/stm32/boards/stm32n657x0.ld34
-rw-r--r--ports/stm32/boards/stm32n6xx_hal_conf_base.h215
-rw-r--r--ports/stm32/dma.c91
-rw-r--r--ports/stm32/dma.h13
-rw-r--r--ports/stm32/extint.c39
-rw-r--r--ports/stm32/extint.h4
-rw-r--r--ports/stm32/flash.c4
-rw-r--r--ports/stm32/i2cslave.h10
-rw-r--r--ports/stm32/irq.h5
-rw-r--r--ports/stm32/lwip_inc/lwipopts.h9
-rw-r--r--ports/stm32/machine_adc.c68
-rw-r--r--ports/stm32/machine_uart.c2
-rw-r--r--ports/stm32/main.c51
-rwxr-xr-xports/stm32/mboot/Makefile43
-rw-r--r--ports/stm32/mboot/adc.c2
-rw-r--r--ports/stm32/mboot/main.c37
-rw-r--r--ports/stm32/mboot/mphalport.h17
-rw-r--r--ports/stm32/mboot/stm32_memory_n6.ld18
-rw-r--r--ports/stm32/mboot/stm32_sections.ld8
-rw-r--r--ports/stm32/modmachine.c20
-rw-r--r--ports/stm32/mpconfigboard_common.h31
-rw-r--r--ports/stm32/mpconfigport.h2
-rw-r--r--ports/stm32/mphalport.c2
-rw-r--r--ports/stm32/mpu.h20
-rw-r--r--ports/stm32/pin_defs_stm32.h4
-rw-r--r--ports/stm32/powerctrl.c80
-rw-r--r--ports/stm32/powerctrl.h6
-rw-r--r--ports/stm32/powerctrlboot.c126
-rw-r--r--ports/stm32/resethandler_iram.s82
-rw-r--r--ports/stm32/rtc.c55
-rw-r--r--ports/stm32/sdcard.c8
-rw-r--r--ports/stm32/sdio.c30
-rw-r--r--ports/stm32/spi.c37
-rw-r--r--ports/stm32/spibdev.c39
-rw-r--r--ports/stm32/stm32.mk22
-rw-r--r--ports/stm32/stm32_it.c27
-rw-r--r--ports/stm32/storage.c15
-rw-r--r--ports/stm32/storage.h4
-rw-r--r--ports/stm32/timer.c32
-rw-r--r--ports/stm32/uart.c66
-rw-r--r--ports/stm32/uart.h4
-rw-r--r--ports/stm32/usb.c2
-rw-r--r--ports/stm32/usbd_conf.c35
-rw-r--r--ports/stm32/usbdev/class/inc/usbd_cdc_msc_hid.h6
-rw-r--r--ports/stm32/vfs_rom_ioctl.c21
-rw-r--r--ports/stm32/xspi.c599
-rw-r--r--ports/stm32/xspi.h43
-rw-r--r--ports/unix/coverage.c4
-rw-r--r--ports/zephyr/CMakeLists.txt11
-rw-r--r--ports/zephyr/README.md2
-rw-r--r--ports/zephyr/boards/bbc_microbit_v2.conf1
-rw-r--r--ports/zephyr/boards/frdm_k64f.conf4
-rw-r--r--ports/zephyr/boards/nucleo_h743zi.conf3
-rw-r--r--ports/zephyr/boards/nucleo_wb55rg.conf17
-rw-r--r--ports/zephyr/boards/nucleo_wb55rg.overlay23
-rw-r--r--ports/zephyr/boards/qemu_cortex_m3.conf11
-rw-r--r--ports/zephyr/boards/qemu_x86.conf8
-rw-r--r--ports/zephyr/boards/rpi_pico.conf19
-rw-r--r--ports/zephyr/boards/rpi_pico.overlay48
-rw-r--r--ports/zephyr/machine_pin.c37
-rw-r--r--ports/zephyr/machine_timer.c15
-rw-r--r--ports/zephyr/machine_uart.c194
-rw-r--r--ports/zephyr/main.c22
-rw-r--r--ports/zephyr/modsocket.c2
-rw-r--r--ports/zephyr/mpconfigport.h17
-rw-r--r--ports/zephyr/mpconfigport_minimal.h2
-rw-r--r--ports/zephyr/mphalport.c8
-rw-r--r--ports/zephyr/mphalport.h46
-rw-r--r--ports/zephyr/prj.conf12
-rw-r--r--ports/zephyr/prj_minimal.conf5
-rw-r--r--ports/zephyr/qstrdefsport.h4
-rw-r--r--ports/zephyr/src/usbd.c14
-rw-r--r--ports/zephyr/src/zephyr_getchar.c17
-rw-r--r--ports/zephyr/src/zephyr_getchar.h1
-rw-r--r--ports/zephyr/uart_core.c19
-rw-r--r--tests/extmod/machine_uart_tx.py5
-rw-r--r--tests/extmod/select_poll_eintr.py16
-rw-r--r--tests/extmod_hardware/machine_uart_irq_rx.py11
-rw-r--r--tests/extmod_hardware/machine_uart_irq_rxidle.py40
-rw-r--r--tests/extmod_hardware/machine_uart_irq_rxidle.py.exp24
-rw-r--r--tests/ports/stm32/adc.py5
-rw-r--r--tests/ports/stm32/adcall.py16
-rw-r--r--tests/ports/stm32/extint.py3
-rw-r--r--tests/ports/stm32/i2c.py7
-rw-r--r--tests/ports/stm32/i2c_accel.py9
-rw-r--r--tests/ports/stm32/i2c_error.py3
-rw-r--r--tests/ports/stm32/irq.py5
-rw-r--r--tests/ports/stm32/modstm.py4
-rw-r--r--tests/ports/stm32/pin.py14
-rw-r--r--tests/ports/stm32/pyb1.py4
-rw-r--r--tests/ports/stm32/rtc.py14
-rw-r--r--tests/ports/stm32/servo.py6
-rw-r--r--tests/ports/stm32/timer.py11
-rw-r--r--tests/ports/stm32/timer_callback.py26
-rw-r--r--tests/ports/stm32/uart.py6
-rw-r--r--tools/mpremote/mpremote/mp_errno.py53
-rw-r--r--tools/mpremote/mpremote/transport.py13
127 files changed, 3868 insertions, 328 deletions
diff --git a/drivers/bus/qspi.h b/drivers/bus/qspi.h
index 32b2890e3f..05d9dd473c 100644
--- a/drivers/bus/qspi.h
+++ b/drivers/bus/qspi.h
@@ -46,6 +46,7 @@ typedef struct _mp_qspi_proto_t {
int (*write_cmd_addr_data)(void *self, uint8_t cmd, uint32_t addr, size_t len, const uint8_t *src);
int (*read_cmd)(void *self, uint8_t cmd, size_t len, uint32_t *dest);
int (*read_cmd_qaddr_qdata)(void *self, uint8_t cmd, uint32_t addr, uint8_t num_dummy, size_t len, uint8_t *dest);
+ int (*direct_read)(void *self, uint32_t addr, size_t len, uint8_t *dest); // can be NULL if direct read not supported
} mp_qspi_proto_t;
typedef struct _mp_soft_qspi_obj_t {
diff --git a/drivers/memory/spiflash.c b/drivers/memory/spiflash.c
index 1ae0bbbc67..7cd1d18a3f 100644
--- a/drivers/memory/spiflash.c
+++ b/drivers/memory/spiflash.c
@@ -197,6 +197,10 @@ void mp_spiflash_init(mp_spiflash_t *self) {
} else {
uint8_t num_dummy = MICROPY_HW_SPIFLASH_QREAD_NUM_DUMMY(self);
self->config->bus.u_qspi.proto->ioctl(self->config->bus.u_qspi.data, MP_QSPI_IOCTL_INIT, num_dummy);
+ if (self->config->bus.u_qspi.proto->direct_read != NULL) {
+ // A bus with a custom read function should not have any further initialisation done.
+ return;
+ }
}
mp_spiflash_acquire_bus(self);
@@ -318,6 +322,10 @@ int mp_spiflash_read(mp_spiflash_t *self, uint32_t addr, size_t len, uint8_t *de
if (len == 0) {
return 0;
}
+ const mp_spiflash_config_t *c = self->config;
+ if (c->bus_kind == MP_SPIFLASH_BUS_QSPI && c->bus.u_qspi.proto->direct_read != NULL) {
+ return c->bus.u_qspi.proto->direct_read(c->bus.u_qspi.data, addr, len, dest);
+ }
mp_spiflash_acquire_bus(self);
int ret = mp_spiflash_read_data(self, addr, len, dest);
mp_spiflash_release_bus(self);
diff --git a/lib/libm_dbl/rint.c b/lib/libm_dbl/rint.c
index fbba390e7d..b85dec8f24 100644
--- a/lib/libm_dbl/rint.c
+++ b/lib/libm_dbl/rint.c
@@ -2,7 +2,7 @@
#include <math.h>
#include <stdint.h>
-#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1
+#if FLT_EVAL_METHOD==0 || FLT_EVAL_METHOD==1 || FLT_EVAL_METHOD==16
#define EPS DBL_EPSILON
#elif FLT_EVAL_METHOD==2
#define EPS LDBL_EPSILON
diff --git a/lib/stm32lib b/lib/stm32lib
-Subproject 928df866e4d287ebc3c60726151513ebee60912
+Subproject 8b2bb4ef44fbfab5075ed9d09e83ddc3754b925
diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile
index eabbd64a3b..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 \
)
@@ -496,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
@@ -556,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
@@ -566,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.
@@ -620,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.h b/ports/stm32/boardctrl.h
index 1a03925ef4..cb5380c298 100644
--- a/ports/stm32/boardctrl.h
+++ b/ports/stm32/boardctrl.h
@@ -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/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/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/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/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/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 c3211ea4f4..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);
}
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 5e114f562f..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,
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 ff44dac630..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
@@ -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;
}
@@ -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/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 f3bdf1bc50..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;
@@ -323,6 +323,19 @@ MP_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/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 bfaa3fb0ba..41ed3d08b7 100644
--- a/ports/stm32/mpconfigport.h
+++ b/ports/stm32/mpconfigport.h
@@ -81,7 +81,7 @@
#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
diff --git a/ports/stm32/mphalport.c b/ports/stm32/mphalport.c
index b4b2267fa0..fcd08cbd84 100644
--- a/ports/stm32/mphalport.c
+++ b/ports/stm32/mphalport.c
@@ -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/mpu.h b/ports/stm32/mpu.h
index 5756cb0560..8713fe8370 100644
--- a/ports/stm32/mpu.h
+++ b/ports/stm32/mpu.h
@@ -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 e3e2fcdd44..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() \
@@ -136,6 +136,12 @@ MP_NORETURN static __attribute__((naked)) void branch_to_bootloader(uint32_t r0,
}
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);
}
+#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 @@ MP_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 @@ MP_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 05a70e52c6..724ab58366 100644
--- a/ports/stm32/powerctrl.h
+++ b/ports/stm32/powerctrl.h
@@ -34,6 +34,12 @@ 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
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/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 55fa622142..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
}
@@ -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;
@@ -972,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)
@@ -997,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);
@@ -1014,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);
@@ -1022,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);
@@ -1073,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;
@@ -1228,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
@@ -1274,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
@@ -1282,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/coverage.c b/ports/unix/coverage.c
index 33e4208d92..cdab17cacf 100644
--- a/ports/unix/coverage.c
+++ b/ports/unix/coverage.c
@@ -505,7 +505,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)));
@@ -844,7 +844,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/zephyr/CMakeLists.txt b/ports/zephyr/CMakeLists.txt
index debf2bd2c1..a5cc477204 100644
--- a/ports/zephyr/CMakeLists.txt
+++ b/ports/zephyr/CMakeLists.txt
@@ -66,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
@@ -84,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}
)
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/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/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 45af7b0c72..eaef34a786 100644
--- a/ports/zephyr/main.c
+++ b/ports/zephyr/main.c
@@ -99,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
@@ -109,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
}
}
@@ -156,7 +160,17 @@ soft_reset:
#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 (;;) {
@@ -171,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();
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 3a6e934862..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)
@@ -145,6 +152,8 @@ 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
index 2444706cbe..36b07a8638 100644
--- a/ports/zephyr/src/usbd.c
+++ b/ports/zephyr/src/usbd.c
@@ -34,12 +34,22 @@
#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);
@@ -121,7 +131,7 @@ struct usbd_context *mp_usbd_init_device(usbd_msg_cb_t msg_cb) {
return NULL;
}
- err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_HS, 1, blocklist);
+ err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_HS, 1 BLOCKLIST);
if (err) {
LOG_ERR("Failed to add register classes");
return NULL;
@@ -137,7 +147,7 @@ struct usbd_context *mp_usbd_init_device(usbd_msg_cb_t msg_cb) {
return NULL;
}
- err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_FS, 1, blocklist);
+ err = usbd_register_all_classes(&mp_usbd, USBD_SPEED_FS, 1 BLOCKLIST);
if (err) {
LOG_ERR("Failed to add register classes");
return NULL;
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/tests/extmod/machine_uart_tx.py b/tests/extmod/machine_uart_tx.py
index f0cc912da6..85bf7e9fb8 100644
--- a/tests/extmod/machine_uart_tx.py
+++ b/tests/extmod/machine_uart_tx.py
@@ -28,7 +28,10 @@ elif "mimxrt" in sys.platform:
initial_delay_ms = 20 # UART sends idle frame after init, so wait for that
bit_margin = 1
elif "pyboard" in sys.platform:
- uart_id = 4
+ if "STM32WB" in sys.implementation._machine:
+ uart_id = "LP1"
+ else:
+ uart_id = 4
pins = {}
initial_delay_ms = 50 # UART sends idle frame after init, so wait for that
bit_margin = 1 # first start-bit must wait to sync with the UART clock
diff --git a/tests/extmod/select_poll_eintr.py b/tests/extmod/select_poll_eintr.py
index e1cbc2aaf5..d9e9b31909 100644
--- a/tests/extmod/select_poll_eintr.py
+++ b/tests/extmod/select_poll_eintr.py
@@ -10,6 +10,18 @@ except (ImportError, AttributeError):
print("SKIP")
raise SystemExit
+# Use a new UDP socket for tests, which should be writable but not readable.
+s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
+localhost_addr_info = socket.getaddrinfo("127.0.0.1", 8000)
+try:
+ s.bind(localhost_addr_info[0][-1])
+except OSError:
+ # Target can't bind to localhost.
+ # Most likely it doesn't have a NIC and the test cannot be run.
+ s.close()
+ print("SKIP")
+ raise SystemExit
+
def thread_main():
lock.acquire()
@@ -26,10 +38,6 @@ lock = _thread.allocate_lock()
lock.acquire()
_thread.start_new_thread(thread_main, ())
-# Use a new UDP socket for tests, which should be writable but not readable.
-s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
-s.bind(socket.getaddrinfo("127.0.0.1", 8000)[0][-1])
-
# Create the poller object.
poller = select.poll()
poller.register(s, select.POLLIN)
diff --git a/tests/extmod_hardware/machine_uart_irq_rx.py b/tests/extmod_hardware/machine_uart_irq_rx.py
index ecc95e62ae..3602c260e3 100644
--- a/tests/extmod_hardware/machine_uart_irq_rx.py
+++ b/tests/extmod_hardware/machine_uart_irq_rx.py
@@ -24,9 +24,14 @@ elif "esp32" in sys.platform:
tx_pin = 4
rx_pin = 5
elif "pyboard" in sys.platform:
- uart_id = 4
- tx_pin = None # PA0
- rx_pin = None # PA1
+ if "STM32WB" in sys.implementation._machine:
+ # LPUART(1) is on PA2/PA3
+ uart_id = "LP1"
+ else:
+ # UART(4) is on PA0/PA1
+ uart_id = 4
+ tx_pin = None
+ rx_pin = None
elif "samd" in sys.platform and "ItsyBitsy M0" in sys.implementation._machine:
uart_id = 0
tx_pin = "D1"
diff --git a/tests/extmod_hardware/machine_uart_irq_rxidle.py b/tests/extmod_hardware/machine_uart_irq_rxidle.py
index af2412c75e..3c743c9e0c 100644
--- a/tests/extmod_hardware/machine_uart_irq_rxidle.py
+++ b/tests/extmod_hardware/machine_uart_irq_rxidle.py
@@ -13,6 +13,9 @@ except (ImportError, AttributeError):
import time, sys
+# Target tuning options.
+tune_wait_initial_rxidle = False
+
# Configure pins based on the target.
if "alif" in sys.platform:
uart_id = 1
@@ -26,9 +29,15 @@ elif "mimxrt" in sys.platform:
uart_id = 1
tx_pin = None
elif "pyboard" in sys.platform:
- uart_id = 4
- tx_pin = None # PA0
- rx_pin = None # PA1
+ tune_wait_initial_rxidle = True
+ if "STM32WB" in sys.implementation._machine:
+ # LPUART(1) is on PA2/PA3
+ uart_id = "LP1"
+ else:
+ # UART(4) is on PA0/PA1
+ uart_id = 4
+ tx_pin = None
+ rx_pin = None
elif "renesas-ra" in sys.platform:
uart_id = 9
tx_pin = None # P602 @ RA6M2
@@ -55,20 +64,31 @@ def irq(u):
print("IRQ_RXIDLE:", bool(u.irq().flags() & u.IRQ_RXIDLE), "data:", u.read())
-text = "12345678"
+text = ("12345678", "abcdefgh")
# Test that the IRQ is called for each set of byte received.
for bits_per_s in (2400, 9600, 115200):
+ print("========")
+ print("bits_per_s:", bits_per_s)
+
if tx_pin is None:
uart = UART(uart_id, bits_per_s)
else:
uart = UART(uart_id, bits_per_s, tx=tx_pin, rx=rx_pin)
+ # Ignore a possible initial RXIDLE condition after creating UART.
+ if tune_wait_initial_rxidle:
+ uart.irq(lambda _: None, uart.IRQ_RXIDLE)
+ time.sleep_ms(10)
+
+ # Configure desired IRQ.
uart.irq(irq, uart.IRQ_RXIDLE)
- print("write", bits_per_s)
- uart.write(text)
- uart.flush()
- print("ready")
- time.sleep_ms(100)
- print("done")
+ for i in range(2):
+ # Write data and wait for IRQ.
+ print("write")
+ uart.write(text[i])
+ uart.flush()
+ print("ready")
+ time.sleep_ms(100)
+ print("done")
diff --git a/tests/extmod_hardware/machine_uart_irq_rxidle.py.exp b/tests/extmod_hardware/machine_uart_irq_rxidle.py.exp
index ce1890a06a..f3c7497e4c 100644
--- a/tests/extmod_hardware/machine_uart_irq_rxidle.py.exp
+++ b/tests/extmod_hardware/machine_uart_irq_rxidle.py.exp
@@ -1,12 +1,30 @@
-write 2400
+========
+bits_per_s: 2400
+write
ready
IRQ_RXIDLE: True data: b'12345678'
done
-write 9600
+write
+ready
+IRQ_RXIDLE: True data: b'abcdefgh'
+done
+========
+bits_per_s: 9600
+write
ready
IRQ_RXIDLE: True data: b'12345678'
done
-write 115200
+write
+ready
+IRQ_RXIDLE: True data: b'abcdefgh'
+done
+========
+bits_per_s: 115200
+write
ready
IRQ_RXIDLE: True data: b'12345678'
done
+write
+ready
+IRQ_RXIDLE: True data: b'abcdefgh'
+done
diff --git a/tests/ports/stm32/adc.py b/tests/ports/stm32/adc.py
index 875d31d732..299a5af9c6 100644
--- a/tests/ports/stm32/adc.py
+++ b/tests/ports/stm32/adc.py
@@ -1,5 +1,10 @@
+import sys
from pyb import ADC, Timer
+if "STM32WB" in sys.implementation._machine:
+ print("SKIP")
+ raise SystemExit
+
adct = ADC(16) # Temperature 930 -> 20C
print(str(adct)[:19])
adcv = ADC(17) # Voltage 1500 -> 3.3V
diff --git a/tests/ports/stm32/adcall.py b/tests/ports/stm32/adcall.py
index cfe179a97b..18896c40cb 100644
--- a/tests/ports/stm32/adcall.py
+++ b/tests/ports/stm32/adcall.py
@@ -1,5 +1,13 @@
+import sys
from pyb import Pin, ADCAll
+if "STM32WB" in sys.implementation._machine:
+ pa0_adc_channel = 5
+ skip_temp_test = True # temperature fails on WB55
+else:
+ pa0_adc_channel = 0
+ skip_temp_test = False
+
pins = [Pin.cpu.A0, Pin.cpu.A1, Pin.cpu.A2, Pin.cpu.A3]
# set pins to IN mode, init ADCAll, then check pins are ANALOG
@@ -12,7 +20,7 @@ for p in pins:
# set pins to IN mode, init ADCAll with mask, then check some pins are ANALOG
for p in pins:
p.init(p.IN)
-adc = ADCAll(12, 0x70003)
+adc = ADCAll(12, 0x70000 | 3 << pa0_adc_channel)
for p in pins:
print(p)
@@ -25,7 +33,11 @@ for c in range(19):
print(type(adc.read_channel(c)))
# call special reading functions
-print(0 < adc.read_core_temp() < 100)
+print(skip_temp_test or 0 < adc.read_core_temp() < 100)
print(0 < adc.read_core_vbat() < 4)
print(0 < adc.read_core_vref() < 2)
print(0 < adc.read_vref() < 4)
+
+if sys.implementation._build == "NUCLEO_WB55":
+ # Restore button pin settings.
+ Pin("SW", Pin.IN, Pin.PULL_UP)
diff --git a/tests/ports/stm32/extint.py b/tests/ports/stm32/extint.py
index 5510600020..d3161f7cc7 100644
--- a/tests/ports/stm32/extint.py
+++ b/tests/ports/stm32/extint.py
@@ -1,7 +1,8 @@
import pyb
# test basic functionality
-ext = pyb.ExtInt("X5", pyb.ExtInt.IRQ_RISING, pyb.Pin.PULL_DOWN, lambda l: print("line:", l))
+pin = pyb.Pin.cpu.A4
+ext = pyb.ExtInt(pin, pyb.ExtInt.IRQ_RISING, pyb.Pin.PULL_DOWN, lambda l: print("line:", l))
ext.disable()
ext.enable()
print(ext.line())
diff --git a/tests/ports/stm32/i2c.py b/tests/ports/stm32/i2c.py
index c968843273..7e7fd25040 100644
--- a/tests/ports/stm32/i2c.py
+++ b/tests/ports/stm32/i2c.py
@@ -1,5 +1,8 @@
-import pyb
-from pyb import I2C
+try:
+ from pyb import I2C
+except ImportError:
+ print("SKIP")
+ raise SystemExit
# test we can correctly create by id
for bus in (-1, 0, 1):
diff --git a/tests/ports/stm32/i2c_accel.py b/tests/ports/stm32/i2c_accel.py
index 8b87d406d0..11ff1392ba 100644
--- a/tests/ports/stm32/i2c_accel.py
+++ b/tests/ports/stm32/i2c_accel.py
@@ -1,15 +1,14 @@
# use accelerometer to test i2c bus
-import pyb
-from pyb import I2C
-
-if not hasattr(pyb, "Accel"):
+try:
+ from pyb import Accel, I2C
+except ImportError:
print("SKIP")
raise SystemExit
accel_addr = 76
-pyb.Accel() # this will init the MMA for us
+Accel() # this will init the MMA for us
i2c = I2C(1, I2C.CONTROLLER, baudrate=400000)
diff --git a/tests/ports/stm32/i2c_error.py b/tests/ports/stm32/i2c_error.py
index 1228962f5f..de6e1ca6fe 100644
--- a/tests/ports/stm32/i2c_error.py
+++ b/tests/ports/stm32/i2c_error.py
@@ -1,12 +1,13 @@
# test I2C errors, with polling (disabled irqs) and DMA
import pyb
-from pyb import I2C
if not hasattr(pyb, "Accel"):
print("SKIP")
raise SystemExit
+from pyb import I2C
+
# init accelerometer
pyb.Accel()
diff --git a/tests/ports/stm32/irq.py b/tests/ports/stm32/irq.py
index 04e70a7b79..fd8742d3ea 100644
--- a/tests/ports/stm32/irq.py
+++ b/tests/ports/stm32/irq.py
@@ -1,3 +1,4 @@
+import time
import pyb
@@ -8,7 +9,7 @@ def test_irq():
pyb.enable_irq() # by default should enable IRQ
# check that interrupts are enabled by waiting for ticks
- pyb.delay(10)
+ time.sleep_ms(10)
# check nested disable/enable
i1 = pyb.disable_irq()
@@ -18,7 +19,7 @@ def test_irq():
pyb.enable_irq(i1)
# check that interrupts are enabled by waiting for ticks
- pyb.delay(10)
+ time.sleep_ms(10)
test_irq()
diff --git a/tests/ports/stm32/modstm.py b/tests/ports/stm32/modstm.py
index f1e147c052..1459ee2a9e 100644
--- a/tests/ports/stm32/modstm.py
+++ b/tests/ports/stm32/modstm.py
@@ -1,13 +1,13 @@
# test stm module
import stm
-import pyb
+import time
# test storing a full 32-bit number
# turn on then off the A15(=yellow) LED
BSRR = 0x18
stm.mem32[stm.GPIOA + BSRR] = 0x00008000
-pyb.delay(100)
+time.sleep_ms(100)
print(hex(stm.mem32[stm.GPIOA + stm.GPIO_ODR] & 0x00008000))
stm.mem32[stm.GPIOA + BSRR] = 0x80000000
print(hex(stm.mem32[stm.GPIOA + stm.GPIO_ODR] & 0x00008000))
diff --git a/tests/ports/stm32/pin.py b/tests/ports/stm32/pin.py
index 3d2bef97e3..cbc78e68ab 100644
--- a/tests/ports/stm32/pin.py
+++ b/tests/ports/stm32/pin.py
@@ -1,14 +1,20 @@
+import sys
from pyb import Pin
-p = Pin("X8", Pin.IN)
+if "PYB" in sys.implementation._machine:
+ test_pin = "X8"
+else:
+ test_pin = Pin.cpu.A7
+
+p = Pin(test_pin, Pin.IN)
print(p)
print(p.name())
print(p.pin())
print(p.port())
-p = Pin("X8", Pin.IN, Pin.PULL_UP)
-p = Pin("X8", Pin.IN, pull=Pin.PULL_UP)
-p = Pin("X8", mode=Pin.IN, pull=Pin.PULL_UP)
+p = Pin(test_pin, Pin.IN, Pin.PULL_UP)
+p = Pin(test_pin, Pin.IN, pull=Pin.PULL_UP)
+p = Pin(test_pin, mode=Pin.IN, pull=Pin.PULL_UP)
print(p)
print(p.value())
diff --git a/tests/ports/stm32/pyb1.py b/tests/ports/stm32/pyb1.py
index e9626ecf4e..5627946dbc 100644
--- a/tests/ports/stm32/pyb1.py
+++ b/tests/ports/stm32/pyb1.py
@@ -2,6 +2,10 @@
import pyb
+if not hasattr(pyb, "delay"):
+ print("SKIP")
+ raise SystemExit
+
# test delay
pyb.delay(-1)
diff --git a/tests/ports/stm32/rtc.py b/tests/ports/stm32/rtc.py
index 013b2f3314..03ed93adc2 100644
--- a/tests/ports/stm32/rtc.py
+++ b/tests/ports/stm32/rtc.py
@@ -1,13 +1,15 @@
-import pyb, stm
+import time, stm
from pyb import RTC
+prediv_a = stm.mem32[stm.RTC + stm.RTC_PRER] >> 16
+
rtc = RTC()
rtc.init()
print(rtc)
# make sure that 1 second passes correctly
rtc.datetime((2014, 1, 1, 1, 0, 0, 0, 0))
-pyb.delay(1002)
+time.sleep_ms(1002)
print(rtc.datetime()[:7])
@@ -38,8 +40,12 @@ cal_tmp = rtc.calibration()
def set_and_print_calib(cal):
- rtc.calibration(cal)
- print(rtc.calibration())
+ if cal > 0 and prediv_a < 3:
+ # can't set positive calibration if prediv_a<3, so just make test pass
+ print(cal)
+ else:
+ rtc.calibration(cal)
+ print(rtc.calibration())
set_and_print_calib(512)
diff --git a/tests/ports/stm32/servo.py b/tests/ports/stm32/servo.py
index d15cafe483..0784f64d33 100644
--- a/tests/ports/stm32/servo.py
+++ b/tests/ports/stm32/servo.py
@@ -1,4 +1,8 @@
-from pyb import Servo
+try:
+ from pyb import Servo
+except ImportError:
+ print("SKIP")
+ raise SystemExit
servo = Servo(1)
print(servo)
diff --git a/tests/ports/stm32/timer.py b/tests/ports/stm32/timer.py
index 251a06c081..add8c29937 100644
--- a/tests/ports/stm32/timer.py
+++ b/tests/ports/stm32/timer.py
@@ -1,10 +1,15 @@
# check basic functionality of the timer class
-import pyb
+import sys
from pyb import Timer
-tim = Timer(4)
-tim = Timer(4, prescaler=100, period=200)
+if "STM32WB" in sys.implementation._machine:
+ tim_id = 16
+else:
+ tim_id = 4
+
+tim = Timer(tim_id)
+tim = Timer(tim_id, prescaler=100, period=200)
print(tim.prescaler())
print(tim.period())
tim.prescaler(300)
diff --git a/tests/ports/stm32/timer_callback.py b/tests/ports/stm32/timer_callback.py
index 5f170ccde1..4add88ec6a 100644
--- a/tests/ports/stm32/timer_callback.py
+++ b/tests/ports/stm32/timer_callback.py
@@ -1,8 +1,14 @@
# check callback feature of the timer class
-import pyb
+import sys
+import time
from pyb import Timer
+if "STM32WB" in sys.implementation._machine:
+ tim_extra_id = 16
+else:
+ tim_extra_id = 4
+
# callback function that disables the callback when called
def cb1(t):
@@ -29,27 +35,27 @@ def cb3(x):
# create a timer with a callback, using callback(None) to stop
tim = Timer(1, freq=100, callback=cb1)
-pyb.delay(5)
+time.sleep_ms(5)
print("before cb1")
-pyb.delay(15)
+time.sleep_ms(15)
# create a timer with a callback, using deinit to stop
tim = Timer(2, freq=100, callback=cb2)
-pyb.delay(5)
+time.sleep_ms(5)
print("before cb2")
-pyb.delay(15)
+time.sleep_ms(15)
# create a timer, then set the freq, then set the callback
-tim = Timer(4)
+tim = Timer(tim_extra_id)
tim.init(freq=100)
tim.callback(cb1)
-pyb.delay(5)
+time.sleep_ms(5)
print("before cb1")
-pyb.delay(15)
+time.sleep_ms(15)
# test callback with a closure
tim.init(freq=100)
tim.callback(cb3(3))
-pyb.delay(5)
+time.sleep_ms(5)
print("before cb4")
-pyb.delay(15)
+time.sleep_ms(15)
diff --git a/tests/ports/stm32/uart.py b/tests/ports/stm32/uart.py
index 53b0ea6ade..28eb2261b7 100644
--- a/tests/ports/stm32/uart.py
+++ b/tests/ports/stm32/uart.py
@@ -1,5 +1,11 @@
+import sys
from pyb import UART
+if "STM32WB" in sys.implementation._machine:
+ # UART(1) is usually connected to the REPL on these MCUs.
+ print("SKIP")
+ raise SystemExit
+
# test we can correctly create by id
for bus in (-1, 0, 1, 2, 5, 6):
try:
diff --git a/tools/mpremote/mpremote/mp_errno.py b/tools/mpremote/mpremote/mp_errno.py
new file mode 100644
index 0000000000..37cb1e0cb9
--- /dev/null
+++ b/tools/mpremote/mpremote/mp_errno.py
@@ -0,0 +1,53 @@
+import errno
+
+# This table maps numeric values defined by `py/mperrno.h` to host errno code.
+MP_ERRNO_TABLE = {
+ 1: errno.EPERM,
+ 2: errno.ENOENT,
+ 3: errno.ESRCH,
+ 4: errno.EINTR,
+ 5: errno.EIO,
+ 6: errno.ENXIO,
+ 7: errno.E2BIG,
+ 8: errno.ENOEXEC,
+ 9: errno.EBADF,
+ 10: errno.ECHILD,
+ 11: errno.EAGAIN,
+ 12: errno.ENOMEM,
+ 13: errno.EACCES,
+ 14: errno.EFAULT,
+ 15: errno.ENOTBLK,
+ 16: errno.EBUSY,
+ 17: errno.EEXIST,
+ 18: errno.EXDEV,
+ 19: errno.ENODEV,
+ 20: errno.ENOTDIR,
+ 21: errno.EISDIR,
+ 22: errno.EINVAL,
+ 23: errno.ENFILE,
+ 24: errno.EMFILE,
+ 25: errno.ENOTTY,
+ 26: errno.ETXTBSY,
+ 27: errno.EFBIG,
+ 28: errno.ENOSPC,
+ 29: errno.ESPIPE,
+ 30: errno.EROFS,
+ 31: errno.EMLINK,
+ 32: errno.EPIPE,
+ 33: errno.EDOM,
+ 34: errno.ERANGE,
+ 95: errno.EOPNOTSUPP,
+ 97: errno.EAFNOSUPPORT,
+ 98: errno.EADDRINUSE,
+ 103: errno.ECONNABORTED,
+ 104: errno.ECONNRESET,
+ 105: errno.ENOBUFS,
+ 106: errno.EISCONN,
+ 107: errno.ENOTCONN,
+ 110: errno.ETIMEDOUT,
+ 111: errno.ECONNREFUSED,
+ 113: errno.EHOSTUNREACH,
+ 114: errno.EALREADY,
+ 115: errno.EINPROGRESS,
+ 125: errno.ECANCELED,
+}
diff --git a/tools/mpremote/mpremote/transport.py b/tools/mpremote/mpremote/transport.py
index 1b70f9b2ed..d7568b281b 100644
--- a/tools/mpremote/mpremote/transport.py
+++ b/tools/mpremote/mpremote/transport.py
@@ -24,8 +24,9 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
-import ast, errno, hashlib, os, sys
+import ast, errno, hashlib, os, re, sys
from collections import namedtuple
+from .mp_errno import MP_ERRNO_TABLE
def stdout_write_bytes(b):
@@ -62,6 +63,16 @@ def _convert_filesystem_error(e, info):
]:
if estr in e.error_output:
return OSError(code, info)
+
+ # Some targets don't render OSError with the name of the errno, so in these
+ # cases support an explicit mapping of errnos to known numeric codes.
+ error_lines = e.error_output.splitlines()
+ match = re.match(r"OSError: (\d+)$", error_lines[-1])
+ if match:
+ value = int(match.group(1), 10)
+ if value in MP_ERRNO_TABLE:
+ return OSError(MP_ERRNO_TABLE[value], info)
+
return e