diff options
author | Damien George <damien@micropython.org> | 2024-06-18 17:46:21 +1000 |
---|---|---|
committer | Damien George <damien@micropython.org> | 2025-07-08 16:24:27 +1000 |
commit | eb3ea9ee13093d81053f5d07b8c96e4fc0e1383d (patch) | |
tree | 07f7bb3e3f9c6fb1f4204c531d3c18a29c3cb189 | |
parent | 24fd5f72682922664c0bcf70d6e3631a6d5b8d2b (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>
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 |