summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDamien George <damien@micropython.org>2024-06-18 17:46:21 +1000
committerDamien George <damien@micropython.org>2025-07-08 16:24:27 +1000
commiteb3ea9ee13093d81053f5d07b8c96e4fc0e1383d (patch)
tree07f7bb3e3f9c6fb1f4204c531d3c18a29c3cb189
parent24fd5f72682922664c0bcf70d6e3631a6d5b8d2b (diff)
stm32: Add support for STM32N6xx MCUs.
This commit adds preliminary support for ST's new STM32N6xx MCUs. Supported features of this MCU so far are: - basic clock tree initialisation, running at 800MHz - fully working USB - XSPI in memory-mapped mode - machine.Pin - machine.UART - RTC and deepsleep support - SD card - filesystem - ROMFS - WiFi and BLE via cyw43-driver (SDIO backend) Note that the N6 does not have internal flash, and has some tricky boot sequence, so using a custom bootloader (mboot) is almost a necessity. Signed-off-by: Damien George <damien@micropython.org>
-rw-r--r--ports/stm32/Makefile54
-rw-r--r--ports/stm32/adc.c56
-rw-r--r--ports/stm32/adc.h2
-rw-r--r--ports/stm32/boardctrl.h1
-rw-r--r--ports/stm32/boards/common_n6_flash.ld57
-rw-r--r--ports/stm32/boards/common_text.ld8
-rw-r--r--ports/stm32/boards/pllvalues.py2
-rw-r--r--ports/stm32/dma.c83
-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/machine_adc.c36
-rw-r--r--ports/stm32/machine_uart.c2
-rw-r--r--ports/stm32/main.c47
-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/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.c32
-rw-r--r--ports/stm32/spibdev.c39
-rw-r--r--ports/stm32/stm32.mk8
-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.c51
-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
42 files changed, 1659 insertions, 135 deletions
diff --git a/ports/stm32/Makefile b/ports/stm32/Makefile
index eabbd64a3..affd9d2f2 100644
--- a/ports/stm32/Makefile
+++ b/ports/stm32/Makefile
@@ -123,7 +123,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 +308,7 @@ SRC_C += \
adc.c \
sdio.c \
subghz.c \
+ xspi.c \
$(wildcard $(BOARD_DIR)/*.c)
SRC_O += \
@@ -316,6 +325,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 +345,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 +361,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 +376,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 +409,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 +525,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 +591,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 +601,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 +658,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 28e254ace..f47e9eaad 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,7 +107,7 @@
#define ADC_CAL2 ((uint16_t *)(ADC_CAL_ADDRESS + 4))
#define ADC_CAL_BITS (12)
-#elif defined(STM32G0) || defined(STM32G4) || defined(STM32H5) || defined(STM32L1) || defined(STM32L4) || defined(STM32WB)
+#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)
@@ -166,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
@@ -247,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));
@@ -260,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
@@ -279,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();
@@ -352,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
@@ -384,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);
@@ -433,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
@@ -510,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;
@@ -527,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;
@@ -542,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) {
@@ -563,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);
@@ -654,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
@@ -764,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
@@ -898,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
@@ -909,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
@@ -931,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 0518cdcd9..0adc9ac2b 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 1a03925ef..cb5380c29 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/common_n6_flash.ld b/ports/stm32/boards/common_n6_flash.ld
new file mode 100644
index 000000000..a1f1fa531
--- /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 16eea43ba..d95467bab 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/pllvalues.py b/ports/stm32/boards/pllvalues.py
index d8856bfec..ae042d999 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/dma.c b/ports/stm32/dma.c
index 53c53868c..c25277074 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)
@@ -830,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};
@@ -853,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)
@@ -1185,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]); \
@@ -1204,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)
@@ -1212,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)
@@ -1424,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)
@@ -1432,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;
@@ -1463,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
@@ -1740,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 7ba04baf5..f05b22b5d 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 5b5658809..9c3c24325 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 d5abb04d6..801dcf62b 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 7668b53a4..85bcee5a9 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 a4b2957de..cc4e7f9be 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/machine_adc.c b/ports/stm32/machine_adc.c
index 77d6248d0..63cd4e089 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)
@@ -85,6 +85,9 @@
#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();
@@ -233,7 +238,7 @@ void adc_config(ADC_TypeDef *adc, uint32_t bits) {
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
}
@@ -257,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
@@ -312,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;
@@ -412,9 +428,9 @@ 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)
+ #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)
+ #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)
@@ -432,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;
@@ -440,18 +457,19 @@ 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) || defined(STM32WB)
+ #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 8444b2998..8f1faea4b 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 5e114f562..aea1953fd 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,7 +367,7 @@ 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();
@@ -376,6 +394,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 +424,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/modmachine.c b/ports/stm32/modmachine.c
index f3bdf1bc5..8123cd801 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 e01a4d4b8..9fa9bf771 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 bfaa3fb0b..41ed3d08b 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 b4b2267fa..fcd08cbd8 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 5756cb056..8713fe837 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/powerctrl.c b/ports/stm32/powerctrl.c
index e3e2fcdd4..a750e8f5b 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 05a70e52c..724ab5836 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 31dae527c..059d2a45d 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 000000000..49a8b4006
--- /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 8dadc4a88..b90d17149 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 706d6315c..b91fa3a9c 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 99d05a515..de82ceadc 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 96dd17065..19f2b65ed 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) {
@@ -470,7 +496,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 +750,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 fecd4a991..d7a75ed24 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 718fa8cf0..e2e7d955c 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,6 @@ 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
diff --git a/ports/stm32/stm32_it.c b/ports/stm32/stm32_it.c
index 4bf509bb9..3639e2f04 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 d810261fb..d26ac821e 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 accf6c390..75cb0e9c1 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 9d65b484c..4ec467d9d 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 e3f8dc1f9..9354af4a2 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) {
@@ -976,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)
@@ -1001,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);
@@ -1018,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);
@@ -1026,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);
@@ -1077,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;
@@ -1232,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
diff --git a/ports/stm32/usb.c b/ports/stm32/usb.c
index af9dd1d70..2d70dcb26 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 829037ba9..7a9e63c9f 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 2c90ce165..34f041253 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 7592aa22d..5dbc85586 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 000000000..b113110c0
--- /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 000000000..cd2166296
--- /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