From c23a158d5d28f8647d48e09092ceac580aec2dca Mon Sep 17 00:00:00 2001 From: Dominic Clifton Date: Fri, 3 Feb 2023 20:24:01 +0100 Subject: [PATCH] OctoSPI and Memory Mapped Flash support (#11825) * STM32H730/STM32H750 - Fix use of USE_LP_UART1 instead of USE_LPUART1. * STM32H723 - Prepare for being able to build for using non-internal-flash config storage. * STM32H723 - Prepare for using non-default strings. * STM32H723 - Disable 'use custom defaults' when using USE_EXST. * STM32H723 - Disable CUSTOM_DEFAULTS_EXTENDED when EXST is used. * OCTOSPI - Add initialisation code. * Add support for RAM_CODE. * STM32H730 - Add support for RAM_CODE via the .ram_code attribute. * OCTOSPI - Proof-of-concept for disabling/enabling memory mapped mode on a running system. NOTE: The HAL libs are compiled into a memory mapped region, and this cannot be used for OctoSPI access when memory mapped mode is disabled. * OCTOSPI - Drop HAL support after determining that it's not suitable for the memory mapped flash use-case. * OCTOSPI - Sometimes, when disabling memory map mode, the abort fails. Handle this by disabling the OSPI peripheral first and re-enabling it afterwards. * SD/FLASH - Update comments regarding possible solutions to the catch-22 issue with SD/SPI/QUADSPI/OCTOSPI pin configurations. * OCTOSPI - Use device instance directly. * OCTOSPI - Prepare W25Q flash driver for octospi support. * OCTOSPI - Add octospi flash detection. Note: The method to detect flash chips is similar to the method used for QUADSPI and as such the code was used as a base. However the initial OCTOSPI implementation doesn't support the non-memory-mapped use-case so the un-tested code is gated with `USE_OCTOSPI_EXPERIMENTAL`. The key differences are: * uses octospi api not quadspi api. * flash chip clock speeds should not be changed for memory-mapped flash chips, bootloader already set it correctly. * only certain flash chips are capable of memory mapped support. * W25Q - Ensure w25q128fv_readRegister returns 0 if the receive fails. * OCTOSPI - Implement octoSpiTransmit1LINE, octoSpiReceive1LINE and octoSpiReceive4LINES. * OCTOSPI - Specify device from init. * OCTOSPI - More fixes and work on init. Current status is that memory mapped mode is disabled and flash chip is detected, but w25q128fv_detect should not be calling w25q128fv_reset. * FLASH - Add comment regarding wasted flash space on targets that only use one bus type for the flash chip. * FLASH - Split `detect` into `identify` and `configure`. * OCTOSPI - Extract flashMemoryMappedModeEnable/Disable to separate methods. * FLASH - Reduce size of targets that don't support the use of multiple flash interfaces. * Single-flash-chip targets usually only support one type of io interface. * Without this, compiler warnings are generated in `flashSpiInit` for targets that only use flash chip drivers that support quadspi or octospi that don't even use SPI for flash. * FLASH - Use MMFLASH_CODE/DATA to conditionally move code/data to RAM. Only targets compiled to support memory mapped flash chips need the some specific code in RAM. Otherwise the code/data should be in the normal linker section. * FLASH - W25Q Prepare for memory mapped flash usage. * Wait/Delay functions must work with interrupts disabled. * Code used for reading/writing must run from RAM. * OCTOSPI - Implement remaining required methods. * OCTOSPI - Fixes for earlier code (not last commit). * FLASH - W25Q update timeout values from Datasheet Rev L. * FLASH - Prepare flash driver for use when memory mapped flash is disabled. * System - Prepare microsISR for use when memory mapped flash is disabled. * FLASH - Add support for CONFIG_IN_MEMORY_MAPPED_FLASH. * Flash - Fix incorrect gating on cli flash commands. When compiling with USE_FLASH_CHIP and without USE_FLASHFS there were compiler warnings. * MMFLASH - Fix release-mode build. * FLASH - Allow SPI pins to be reconfigured when using CONFIG_IN_MEMORY_MAPPED_FLASH. MMFLASH only works via QuadSPI/OctoSPI peripherals. * EXST - Disable the 2GB workaround which is now causing a different error. The error we get with 'remove-section' enabled is: "error in private header data: sorry, cannot handle this file". The cause of this new error in the objcopy codebase is an out of memory condition, somehow the 2GB files and this error are related but the root cause of both is still unknown. * OCTOSPI - Add support for STM32H723. * STM32H723 - Add linker scripts for EXST usage. * NucleoH723ZG - Add build script to demonstrate OCTOSPI and Memory Mapped flash support. * FLASH - WUse the size in bits to set the size of the buffer. * FLASH - Fix typo in W25N driver defines. Was using W28N instead of W25N * OCTOSPI - Fix missing semilcolon when compiling without USE_FLASH_MEMORY_MAPPED. * OCTPSPI - Fix missing call to 'memoryMappedModeInit'. * SPRacingH7RF - Add example build script to allow for testing prior to unified target / cloud-build support. --- Makefile | 5 +- make/mcu/STM32H7.mk | 22 +- make/source.mk | 1 + src/link/stm32_h723_common.ld | 187 ++++ src/link/stm32_h723_common_post.ld | 44 + src/link/stm32_h730_common.ld | 13 + src/link/stm32_ram_h723_exst.ld | 143 +++ src/link/stm32_ram_h723_exst_post.ld | 29 + src/main/cli/cli.c | 4 +- src/main/cli/settings.c | 2 +- src/main/config/config_eeprom.c | 60 +- src/main/config/config_streamer.c | 10 +- src/main/config/config_streamer.h | 2 +- src/main/drivers/bus_octospi.c | 119 +++ src/main/drivers/bus_octospi.h | 62 ++ src/main/drivers/bus_octospi_impl.h | 40 + src/main/drivers/bus_octospi_stm32h7xx.c | 853 ++++++++++++++++++ src/main/drivers/flash.c | 238 ++++- src/main/drivers/flash.h | 14 + src/main/drivers/flash_impl.h | 50 +- src/main/drivers/flash_m25p16.c | 39 +- src/main/drivers/flash_m25p16.h | 2 +- src/main/drivers/flash_w25m.c | 17 +- src/main/drivers/flash_w25m.h | 2 +- src/main/drivers/flash_w25n01g.c | 53 +- src/main/drivers/flash_w25n01g.h | 2 +- src/main/drivers/flash_w25q128fv.c | 153 +++- src/main/drivers/flash_w25q128fv.h | 2 +- src/main/drivers/system.c | 13 +- src/main/drivers/system.h | 4 + src/main/drivers/system_stm32h7xx.c | 35 + src/main/fc/init.c | 87 +- src/main/pg/flash.c | 10 +- src/main/pg/flash.h | 1 + src/main/startup/system_stm32h7xx.c | 6 + src/main/target/STM32H723/target.h | 34 +- src/main/target/STM32H723/target.mk | 2 + src/main/target/STM32H730/target.h | 2 +- src/main/target/STM32H750/target.h | 4 +- src/main/target/common_post.h | 37 +- support/scripts/build_nucleoh723zg_mmapped.sh | 108 +++ support/scripts/build_spracingh7rf.sh | 145 +++ 42 files changed, 2447 insertions(+), 209 deletions(-) create mode 100644 src/link/stm32_h723_common.ld create mode 100644 src/link/stm32_h723_common_post.ld create mode 100644 src/link/stm32_ram_h723_exst.ld create mode 100644 src/link/stm32_ram_h723_exst_post.ld create mode 100644 src/main/drivers/bus_octospi.c create mode 100644 src/main/drivers/bus_octospi.h create mode 100644 src/main/drivers/bus_octospi_impl.h create mode 100644 src/main/drivers/bus_octospi_stm32h7xx.c create mode 100644 support/scripts/build_nucleoh723zg_mmapped.sh create mode 100644 support/scripts/build_spracingh7rf.sh diff --git a/Makefile b/Makefile index 1dde7cc6efb..56132fb16cc 100644 --- a/Makefile +++ b/Makefile @@ -367,11 +367,8 @@ $(TARGET_BIN): $(TARGET_UNPATCHED_BIN) @echo "Patching MD5 hash into HASH section" "$(STDOUT)" $(V1) cat $(TARGET_UNPATCHED_BIN).md5 | awk '{printf("%08x: %s",64-16,$$2);}' | xxd -r - $(TARGET_EXST_HASH_SECTION_FILE) -# For some currently unknown reason, OBJCOPY, with only input/output files, will generate a file around 2GB for the H730 unless we remove an unused-section -# As a workaround drop the ._user_heap_stack section, which is only used during build to show errors if there's not enough space for the heap/stack. -# The issue can be seen with `readelf -S $(TARGET_EXST_ELF)' vs `readelf -S $(TARGET_ELF)` $(V1) @echo "Patching updated HASH section into $(TARGET_EXST_ELF)" "$(STDOUT)" - $(OBJCOPY) $(TARGET_ELF) $(TARGET_EXST_ELF) --remove-section ._user_heap_stack --update-section .exst_hash=$(TARGET_EXST_HASH_SECTION_FILE) + $(OBJCOPY) $(TARGET_ELF) $(TARGET_EXST_ELF) --update-section .exst_hash=$(TARGET_EXST_HASH_SECTION_FILE) $(V1) $(READELF) -S $(TARGET_EXST_ELF) $(V1) $(READELF) -l $(TARGET_EXST_ELF) diff --git a/make/mcu/STM32H7.mk b/make/mcu/STM32H7.mk index 5649818ecb6..69f5fd2cc1b 100644 --- a/make/mcu/STM32H7.mk +++ b/make/mcu/STM32H7.mk @@ -207,9 +207,22 @@ else ifeq ($(TARGET),$(filter $(TARGET),$(H723xG_TARGETS))) DEVICE_FLAGS += -DSTM32H723xx DEFAULT_LD_SCRIPT = $(LINKER_DIR)/stm32_flash_h723_1m.ld STARTUP_SRC = startup_stm32h723xx.s -MCU_FLASH_SIZE := 1024 +DEFAULT_TARGET_FLASH := 1024 DEVICE_FLAGS += -DMAX_MPU_REGIONS=16 +ifeq ($(TARGET_FLASH),) +MCU_FLASH_SIZE := $(DEFAULT_TARGET_FLASH) +endif + +ifeq ($(EXST),yes) +FIRMWARE_SIZE := 1024 +# TARGET_FLASH now becomes the amount of MEMORY-MAPPED address space that is occupied by the firmware +# and the maximum size of the data stored on the external flash device. +MCU_FLASH_SIZE := FIRMWARE_SIZE +DEFAULT_LD_SCRIPT = $(LINKER_DIR)/stm32_ram_h723_exst.ld +LD_SCRIPTS = $(LINKER_DIR)/stm32_h723_common.ld $(LINKER_DIR)/stm32_h723_common_post.ld +endif + else ifeq ($(TARGET),$(filter $(TARGET),$(H725xG_TARGETS))) DEVICE_FLAGS += -DSTM32H725xx DEFAULT_LD_SCRIPT = $(LINKER_DIR)/stm32_flash_h723_1m.ld @@ -226,7 +239,7 @@ DEVICE_FLAGS += -DMAX_MPU_REGIONS=16 ifeq ($(TARGET_FLASH),) -MCU_FLASH_SIZE := $(DEFAULT_TARGET_FLASH) +MCU_FLASH_SIZE := $(DEFAULT_TARGET_FLASH) endif ifeq ($(EXST),yes) @@ -246,7 +259,7 @@ STARTUP_SRC = startup_stm32h743xx.s DEFAULT_TARGET_FLASH := 128 ifeq ($(TARGET_FLASH),) -MCU_FLASH_SIZE := $(DEFAULT_TARGET_FLASH) +MCU_FLASH_SIZE := $(DEFAULT_TARGET_FLASH) endif ifeq ($(EXST),yes) @@ -281,7 +294,7 @@ LD_SCRIPT = $(DEFAULT_LD_SCRIPT) endif ifneq ($(FIRMWARE_SIZE),) -DEVICE_FLAGS += -DFIRMWARE_SIZE=$(FIRMWARE_SIZE) +DEVICE_FLAGS += -DFIRMWARE_SIZE=$(FIRMWARE_SIZE) endif DEVICE_FLAGS += -DHSE_VALUE=$(HSE_VALUE) -DHSE_STARTUP_TIMEOUT=1000 @@ -304,6 +317,7 @@ MCU_COMMON_SRC = \ drivers/serial_uart_hal.c \ drivers/serial_uart_stm32h7xx.c \ drivers/bus_quadspi_hal.c \ + drivers/bus_octospi_stm32h7xx.c \ drivers/bus_spi_ll.c \ drivers/dma_stm32h7xx.c \ drivers/dshot_bitbang.c \ diff --git a/make/source.mk b/make/source.mk index 1590e1b4860..92ea4a2aaeb 100644 --- a/make/source.mk +++ b/make/source.mk @@ -20,6 +20,7 @@ COMMON_SRC = \ drivers/bus_i2c_config.c \ drivers/bus_i2c_busdev.c \ drivers/bus_i2c_soft.c \ + drivers/bus_octospi.c \ drivers/bus_quadspi.c \ drivers/bus_spi.c \ drivers/bus_spi_config.c \ diff --git a/src/link/stm32_h723_common.ld b/src/link/stm32_h723_common.ld new file mode 100644 index 00000000000..d10f3d957fa --- /dev/null +++ b/src/link/stm32_h723_common.ld @@ -0,0 +1,187 @@ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* Highest address of the user mode stack */ +_estack = ORIGIN(STACKRAM) + LENGTH(STACKRAM); /* end of RAM */ + +/* Base address where the quad spi. */ +__octospi1_start = ORIGIN(OCTOSPI1); +__octospi2_start = ORIGIN(OCTOSPI2); + +/* Generate a link error if heap and stack don't fit into RAM */ +_Min_Heap_Size = 0; /* required amount of heap */ +_Min_Stack_Size = 0x800; /* required amount of stack */ + +/* Define output sections */ +SECTIONS +{ + _isr_vector_table_flash_base = LOADADDR(.isr_vector); + PROVIDE (isr_vector_table_flash_base = _isr_vector_table_flash_base); + + .isr_vector : + { + . = ALIGN(512); + PROVIDE (isr_vector_table_base = .); + KEEP(*(.isr_vector)) /* Startup code */ + . = ALIGN(4); + PROVIDE (isr_vector_table_end = .); + } >VECTAB AT> MAIN + + _ram_isr_vector_table_base = LOADADDR(.ram_isr_vector); + PROVIDE (ram_isr_vector_table_base = _ram_isr_vector_table_base); + + .ram_isr_vector (NOLOAD) : + { + . = ALIGN(512); /* Vector table offset must be multiple of 0x200 */ + PROVIDE (ram_isr_vector_table_base = .); + . += (isr_vector_table_end - isr_vector_table_base); + . = ALIGN(4); + PROVIDE (ram_isr_vector_table_end = .); + } >DTCM_RAM + + /* The program code and other data goes into MAIN */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + + KEEP (*(.init)) + KEEP (*(.fini)) + + . = ALIGN(4); + _etext = .; /* define a global symbols at end of code */ + } >MAIN + + /* Critical program code goes into ITCM RAM */ + /* Copy specific fast-executing code to ITCM RAM */ + tcm_code = LOADADDR(.tcm_code); + .tcm_code : + { + . = ALIGN(4); + tcm_code_start = .; + *(.tcm_code) + *(.tcm_code*) + . = ALIGN(4); + tcm_code_end = .; + } >ITCM_RAM AT >MAIN + + .ARM.extab : + { + *(.ARM.extab* .gnu.linkonce.armextab.*) + } >MAIN + + .ARM : + { + __exidx_start = .; + *(.ARM.exidx*) __exidx_end = .; + } >MAIN + + .pg_registry : + { + PROVIDE_HIDDEN (__pg_registry_start = .); + KEEP (*(.pg_registry)) + KEEP (*(SORT(.pg_registry.*))) + PROVIDE_HIDDEN (__pg_registry_end = .); + } >MAIN + + .pg_resetdata : + { + PROVIDE_HIDDEN (__pg_resetdata_start = .); + KEEP (*(.pg_resetdata)) + PROVIDE_HIDDEN (__pg_resetdata_end = .); + } >MAIN + + /* used by the startup to initialize data */ + _sidata = LOADADDR(.data); + + /* Initialized data sections goes into RAM, load LMA copy after code */ + .data : + { + . = ALIGN(4); + _sdata = .; /* create a global symbol at data start */ + *(.data) /* .data sections */ + *(.data*) /* .data* sections */ + + . = ALIGN(4); + _edata = .; /* define a global symbol at data end */ + } >DTCM_RAM AT >MAIN + + /* Non-critical program code goes into RAM */ + /* Copy specific slow-executing code to RAM */ + ram_code = LOADADDR(.ram_code); + .ram_code : + { + . = ALIGN(4); + ram_code_start = .; + *(.ram_code) + *(.ram_code*) + . = ALIGN(4); + ram_code_end = .; + } >RAM AT >MAIN + + /* Uninitialized data section */ + . = ALIGN(4); + .bss (NOLOAD) : + { + /* This is used by the startup in order to initialize the .bss secion */ + _sbss = .; /* define a global symbol at bss start */ + __bss_start__ = _sbss; + *(.bss) + *(SORT_BY_ALIGNMENT(.bss*)) + *(COMMON) + + . = ALIGN(4); + _ebss = .; /* define a global symbol at bss end */ + __bss_end__ = _ebss; + } >RAM + + /* Uninitialized data section */ + . = ALIGN(4); + .sram2 (NOLOAD) : + { + /* This is used by the startup in order to initialize the .sram2 secion */ + _ssram2 = .; /* define a global symbol at sram2 start */ + __sram2_start__ = _ssram2; + *(.sram2) + *(SORT_BY_ALIGNMENT(.sram2*)) + + . = ALIGN(4); + _esram2 = .; /* define a global symbol at sram2 end */ + __sram2_end__ = _esram2; + } >RAM + + /* used during startup to initialized fastram_data */ + _sfastram_idata = LOADADDR(.fastram_data); + + /* Initialized FAST_DATA section for unsuspecting developers */ + .fastram_data : + { + . = ALIGN(4); + _sfastram_data = .; /* create a global symbol at data start */ + *(.fastram_data) /* .data sections */ + *(.fastram_data*) /* .data* sections */ + + . = ALIGN(4); + _efastram_data = .; /* define a global symbol at data end */ + } >FASTRAM AT >MAIN + + . = ALIGN(4); + .fastram_bss (NOLOAD) : + { + _sfastram_bss = .; + __fastram_bss_start__ = _sfastram_bss; + *(.fastram_bss) + *(SORT_BY_ALIGNMENT(.fastram_bss*)) + + . = ALIGN(4); + _efastram_bss = .; + __fastram_bss_end__ = _efastram_bss; + } >FASTRAM +} diff --git a/src/link/stm32_h723_common_post.ld b/src/link/stm32_h723_common_post.ld new file mode 100644 index 00000000000..89b4224806f --- /dev/null +++ b/src/link/stm32_h723_common_post.ld @@ -0,0 +1,44 @@ +SECTIONS +{ + .persistent_data (NOLOAD) : + { + __persistent_data_start__ = .; + *(.persistent_data) + . = ALIGN(4); + __persistent_data_end__ = .; + } >RAM + + /* User_heap_stack section, used to check that there is enough RAM left */ + _heap_stack_end = ORIGIN(STACKRAM)+LENGTH(STACKRAM) - 8; /* 8 bytes to allow for alignment */ + _heap_stack_begin = _heap_stack_end - _Min_Stack_Size - _Min_Heap_Size; + . = _heap_stack_begin; + ._user_heap_stack : + { + . = ALIGN(4); + PROVIDE ( end = . ); + PROVIDE ( _end = . ); + . = . + _Min_Heap_Size; + . = . + _Min_Stack_Size; + . = ALIGN(4); + } >STACKRAM = 0xa5 + + /* MEMORY_bank1 section, code must be located here explicitly */ + /* Example: extern int foo(void) __attribute__ ((section (".mb1text"))); */ + .memory_b1_text : + { + *(.mb1text) /* .mb1text sections (code) */ + *(.mb1text*) /* .mb1text* sections (code) */ + *(.mb1rodata) /* read-only data (constants) */ + *(.mb1rodata*) + } >MEMORY_B1 + + /* Remove information from the standard libraries */ + /DISCARD/ : + { + libc.a ( * ) + libm.a ( * ) + libgcc.a ( * ) + } + + .ARM.attributes 0 : { *(.ARM.attributes) } +} diff --git a/src/link/stm32_h730_common.ld b/src/link/stm32_h730_common.ld index 0c99b43778a..db81e6c9e6d 100644 --- a/src/link/stm32_h730_common.ld +++ b/src/link/stm32_h730_common.ld @@ -128,6 +128,19 @@ SECTIONS _edata = .; /* define a global symbol at data end */ } >DTCM_RAM AT >MAIN + /* Non-critical program code goes into RAM */ + /* Copy specific slow-executing code to RAM */ + ram_code = LOADADDR(.ram_code); + .ram_code : + { + . = ALIGN(4); + ram_code_start = .; + *(.ram_code) + *(.ram_code*) + . = ALIGN(4); + ram_code_end = .; + } >RAM AT >MAIN + /* Uninitialized data section */ . = ALIGN(4); .bss (NOLOAD) : diff --git a/src/link/stm32_ram_h723_exst.ld b/src/link/stm32_ram_h723_exst.ld new file mode 100644 index 00000000000..c9f0bcd8741 --- /dev/null +++ b/src/link/stm32_ram_h723_exst.ld @@ -0,0 +1,143 @@ +/* +***************************************************************************** +** +** File : stm32_ram_h723_exst.ld +** +** Abstract : Linker script for STM32H723xG Device with +** 320K AXI-SRAM mapped onto AXI bus on D1 domain + (Shared AXI/I-TCM 192KB is all configured as AXI-SRAM) +** 16K SRAM1 mapped on D2 domain +** 16K SRAM2 mapped on D2 domain +** 16K SRAM mapped on D3 domain +** 64K ITCM +** 128K DTCM +** +***************************************************************************** +*/ + +/* Entry Point */ +ENTRY(Reset_Handler) + +/* +0x00000000 to 0x0000FFFF 64K ITCM +0x20000000 to 0x2001FFFF 128K DTCM, main RAM +0x24000000 to 0x2404FFFF 320K AXI SRAM, D1 domain +0x30000000 to 0x30003FFF 16K SRAM1, D2 domain +0x30004000 to 0x30007FFF 16K SRAM2, D2 domain +0x38000000 to 0x38003FFF 16K SRAM4, D3 domain, unused +0x38800000 to 0x38800FFF 4K BACKUP SRAM, Backup domain, unused + +0x08000000 to 0x0801FFFF 128K isr vector, startup code, firmware, no config! // FLASH_Sector_0 +*/ + +/* + +For H7 EXFL (External Flash) targets a binary is built that is placed on an external device. +The bootloader will enable the memory mapped mode on the CPU which allows code to run directly from +the external flash device. + +The bootloader then executes code at the CODE_RAM address. The address of CODE_RAM is fixed to 0x90010000 +and must not be changed. + +The initial CODE_RAM is sized at 1MB. + +*/ + +/* see .exst section below */ +_exst_hash_size = 64; + +/* Specify the memory areas */ +MEMORY +{ + ITCM_RAM (rwx) : ORIGIN = 0x00000000, LENGTH = 64K + DTCM_RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 128K + RAM (rwx) : ORIGIN = 0x24000000, LENGTH = 128K + 192K /* 128K AXI SRAM + 192K ITCM & AXI = 320K */ + + D2_RAM (rwx) : ORIGIN = 0x30000000, LENGTH = 32K /* SRAM1 16K + SRAM2 16K */ + D3_RAM (rwx) : ORIGIN = 0x38000000, LENGTH = 16K /* SRAM4 16K */ + + BACKUP_SRAM (rwx) : ORIGIN = 0x38800000, LENGTH = 4K + + MEMORY_B1 (rx) : ORIGIN = 0x60000000, LENGTH = 0K + + OCTOSPI2 (rx) : ORIGIN = 0x70000000, LENGTH = 256M + OCTOSPI1 (rx) : ORIGIN = 0x90000000, LENGTH = 256M + OCTOSPI1_CODE (rx): ORIGIN = ORIGIN(OCTOSPI1) + 1M, LENGTH = 1M - _exst_hash_size /* hard coded start address, as required by SPRACINGH7 boot loader, don't change! */ + EXST_HASH (rx) : ORIGIN = ORIGIN(OCTOSPI1_CODE) + LENGTH(OCTOSPI1_CODE), LENGTH = _exst_hash_size +} + +REGION_ALIAS("STACKRAM", DTCM_RAM) +REGION_ALIAS("FASTRAM", DTCM_RAM) +REGION_ALIAS("MAIN", OCTOSPI1_CODE) + +REGION_ALIAS("VECTAB", MAIN) + +INCLUDE "stm32_h723_common.ld" + +SECTIONS +{ + /* used during startup to initialized dmaram_data */ + _sdmaram_idata = LOADADDR(.dmaram_data); + + . = ALIGN(32); + .dmaram_data : + { + PROVIDE(dmaram_start = .); + _sdmaram = .; + _dmaram_start__ = _sdmaram; + _sdmaram_data = .; /* create a global symbol at data start */ + *(.dmaram_data) /* .data sections */ + *(.dmaram_data*) /* .data* sections */ + . = ALIGN(32); + _edmaram_data = .; /* define a global symbol at data end */ + } >RAM AT >MAIN + + . = ALIGN(32); + .dmaram_bss (NOLOAD) : + { + _sdmaram_bss = .; + __dmaram_bss_start__ = _sdmaram_bss; + *(.dmaram_bss) + *(SORT_BY_ALIGNMENT(.dmaram_bss*)) + . = ALIGN(32); + _edmaram_bss = .; + __dmaram_bss_end__ = _edmaram_bss; + } >RAM + + . = ALIGN(32); + .DMA_RAM (NOLOAD) : + { + KEEP(*(.DMA_RAM)) + PROVIDE(dmaram_end = .); + _edmaram = .; + _dmaram_end__ = _edmaram; + } >RAM + + .DMA_RW_D2 (NOLOAD) : + { + . = ALIGN(32); + PROVIDE(dmarw_start = .); + _sdmarw = .; + _dmarw_start__ = _sdmarw; + KEEP(*(.DMA_RW)) + PROVIDE(dmarw_end = .); + _edmarw = .; + _dmarw_end__ = _edmarw; + } >D2_RAM + + .DMA_RW_AXI (NOLOAD) : + { + . = ALIGN(32); + PROVIDE(dmarwaxi_start = .); + _sdmarwaxi = .; + _dmarwaxi_start__ = _sdmarwaxi; + KEEP(*(.DMA_RW_AXI)) + PROVIDE(dmarwaxi_end = .); + _edmarwaxi = .; + _dmarwaxi_end__ = _edmarwaxi; + } >RAM +} + +INCLUDE "stm32_h723_common_post.ld" +INCLUDE "stm32_ram_h723_exst_post.ld" + diff --git a/src/link/stm32_ram_h723_exst_post.ld b/src/link/stm32_ram_h723_exst_post.ld new file mode 100644 index 00000000000..97ed6c28f3a --- /dev/null +++ b/src/link/stm32_ram_h723_exst_post.ld @@ -0,0 +1,29 @@ +SECTIONS +{ + /* Create space for a hash. Currently an MD5 has is used, which is 16 */ + /* bytes long. however the last 64 bytes are RESERVED for hash related */ + .exst_hash : + { + /* 64 bytes is the size of an MD5 hashing block size. */ + . = ORIGIN(EXST_HASH); + + BYTE(0x00); /* block format */ + BYTE(0x00); /* Checksum method, 0x00 = MD5 hash */ + BYTE(0x00); /* Reserved */ + BYTE(0x00); /* Reserved */ + + /* Fill the last 60 bytes with data, including an empty hash aligned */ + + /* to the last 16 bytes. */ + FILL(0x00000000); /* Reserved */ + + . = ORIGIN(EXST_HASH) + LENGTH(EXST_HASH) - 16; + __md5_hash_address__ = .; + LONG(0x00000000); + LONG(0x00000000); + LONG(0x00000000); + LONG(0x00000000); + . = ORIGIN(EXST_HASH) + LENGTH(EXST_HASH); + __firmware_end__ = .; + } >EXST_HASH +} diff --git a/src/main/cli/cli.c b/src/main/cli/cli.c index 95208d7c59d..6031adb4370 100644 --- a/src/main/cli/cli.c +++ b/src/main/cli/cli.c @@ -2582,8 +2582,8 @@ static void cliFlashRead(const char *cmdName, char *cmdline) cliPrintLinefeed(); } } -#endif -#endif +#endif // USE_FLASH_TOOLS +#endif // USE_FLASHFS #ifdef USE_VTX_CONTROL static void printVtx(dumpFlags_t dumpMask, const vtxConfig_t *vtxConfig, const vtxConfig_t *vtxConfigDefault, const char *headingStr) diff --git a/src/main/cli/settings.c b/src/main/cli/settings.c index f406524b72a..d46030f0251 100644 --- a/src/main/cli/settings.c +++ b/src/main/cli/settings.c @@ -1592,7 +1592,7 @@ const clivalue_t valueTable[] = { { "usb_msc_pin_pullup", VAR_UINT8 | HARDWARE_VALUE | MODE_LOOKUP, .config.lookup = { TABLE_OFF_ON }, PG_USB_CONFIG, offsetof(usbDev_t, mscButtonUsePullup) }, #endif // PG_FLASH_CONFIG -#ifdef USE_FLASH_CHIP +#ifdef USE_FLASH_SPI { "flash_spi_bus", VAR_UINT8 | HARDWARE_VALUE, .config.minmaxUnsigned = { 0, SPIDEV_COUNT }, PG_FLASH_CONFIG, offsetof(flashConfig_t, spiDevice) }, #endif // RCDEVICE diff --git a/src/main/config/config_eeprom.c b/src/main/config/config_eeprom.c index d9adb982ea8..1e166cab7b7 100644 --- a/src/main/config/config_eeprom.c +++ b/src/main/config/config_eeprom.c @@ -83,8 +83,8 @@ typedef struct { uint32_t word; } PG_PACKED packingTest_t; -#if defined(CONFIG_IN_EXTERNAL_FLASH) -bool loadEEPROMFromExternalFlash(void) +#if defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH) +MMFLASH_CODE bool loadEEPROMFromExternalFlash(void) { const flashPartition_t *flashPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_CONFIG); const flashGeometry_t *flashGeometry = flashGetGeometry(); @@ -95,7 +95,9 @@ bool loadEEPROMFromExternalFlash(void) int bytesRead = 0; bool success = false; - +#ifdef CONFIG_IN_MEMORY_MAPPED_FLASH + flashMemoryMappedModeDisable(); +#endif do { bytesRead = flashReadBytes(flashStartAddress + totalBytesRead, &eepromData[totalBytesRead], EEPROM_SIZE - totalBytesRead); if (bytesRead > 0) { @@ -103,9 +105,55 @@ bool loadEEPROMFromExternalFlash(void) success = (totalBytesRead == EEPROM_SIZE); } } while (!success && bytesRead > 0); +#ifdef CONFIG_IN_MEMORY_MAPPED_FLASH + flashMemoryMappedModeEnable(); +#endif return success; } + +#ifdef CONFIG_IN_MEMORY_MAPPED_FLASH +MMFLASH_CODE_NOINLINE void saveEEPROMToMemoryMappedFlash(void) +{ + const flashPartition_t *flashPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_CONFIG); + const flashGeometry_t *flashGeometry = flashGetGeometry(); + + uint32_t flashSectorSize = flashGeometry->sectorSize; + uint32_t flashPageSize = flashGeometry->pageSize; + + uint32_t flashStartAddress = flashPartition->startSector * flashGeometry->sectorSize; + + uint32_t bytesRemaining = EEPROM_SIZE; + uint32_t offset = 0; + + flashMemoryMappedModeDisable(); + + do { + uint32_t flashAddress = flashStartAddress + offset; + + uint32_t bytesToWrite = bytesRemaining; + if (bytesToWrite > flashPageSize) { + bytesToWrite = flashPageSize; + } + + bool onSectorBoundary = flashAddress % flashSectorSize == 0; + if (onSectorBoundary) { + flashEraseSector(flashAddress); + } + + flashPageProgram(flashAddress, (uint8_t *)&eepromData[offset], bytesToWrite, NULL); + + bytesRemaining -= bytesToWrite; + offset += bytesToWrite; + } while (bytesRemaining > 0); + + flashWaitForReady(); + + flashMemoryMappedModeEnable(); +} +#endif + + #elif defined(CONFIG_IN_SDCARD) enum { @@ -255,7 +303,7 @@ void initEEPROM(void) #if defined(CONFIG_IN_FILE) loadEEPROMFromFile(); -#elif defined(CONFIG_IN_EXTERNAL_FLASH) +#elif defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH) bool eepromLoaded = loadEEPROMFromExternalFlash(); if (!eepromLoaded) { // Flash read failed - just die now @@ -336,7 +384,7 @@ uint16_t getEEPROMConfigSize(void) size_t getEEPROMStorageSize(void) { -#if defined(CONFIG_IN_EXTERNAL_FLASH) +#if defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH) const flashPartition_t *flashPartition = flashPartitionFindByType(FLASH_PARTITION_TYPE_CONFIG); return FLASH_PARTITION_SECTOR_COUNT(flashPartition) * flashGetGeometry()->sectorSize; @@ -464,7 +512,7 @@ void writeConfigToEEPROM(void) if (writeSettingsToEEPROM()) { success = true; -#ifdef CONFIG_IN_EXTERNAL_FLASH +#if defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH) // copy it back from flash to the in-memory buffer. success = loadEEPROMFromExternalFlash(); #endif diff --git a/src/main/config/config_streamer.c b/src/main/config/config_streamer.c index b66c1ba793a..0d4ef55faf8 100644 --- a/src/main/config/config_streamer.c +++ b/src/main/config/config_streamer.c @@ -36,7 +36,7 @@ uint8_t eepromData[EEPROM_SIZE]; #endif -#if (defined(STM32H750xx) || defined(STM32H730xx)) && !(defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_RAM) || defined(CONFIG_IN_SDCARD)) +#if (defined(STM32H750xx) || defined(STM32H730xx)) && !(defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH) || defined(CONFIG_IN_RAM) || defined(CONFIG_IN_SDCARD)) #error "The configured MCU only has one flash page which contains the bootloader, no spare flash pages available, use external storage for persistent config or ram for target testing" #endif // @todo this is not strictly correct for F4/F7, where sector sizes are variable @@ -400,7 +400,7 @@ static int write_word(config_streamer_t *c, config_streamer_buffer_align_type_t flashPageProgramContinue(buffers, bufferSizes, 1); -#elif defined(CONFIG_IN_RAM) || defined(CONFIG_IN_SDCARD) +#elif defined(CONFIG_IN_RAM) || defined(CONFIG_IN_SDCARD) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH) if (c->address == (uintptr_t)&eepromData[0]) { memset(eepromData, 0, sizeof(eepromData)); } @@ -541,11 +541,13 @@ int config_streamer_finish(config_streamer_t *c) { if (c->unlocked) { #if defined(CONFIG_IN_SDCARD) - bool saveEEPROMToSDCard(void); // XXX forward declaration to avoid circular dependency between config_streamer / config_eeprom + bool saveEEPROMToSDCard(void); // forward declaration to avoid circular dependency between config_streamer / config_eeprom saveEEPROMToSDCard(); - // TODO overwrite the data in the file on the SD card. #elif defined(CONFIG_IN_EXTERNAL_FLASH) flashFlush(); +#elif defined(CONFIG_IN_MEMORY_MAPPED_FLASH) + void saveEEPROMToMemoryMappedFlash(void); // forward declaration to avoid circular dependency between config_streamer / config_eeprom + saveEEPROMToMemoryMappedFlash(); #elif defined(CONFIG_IN_RAM) // NOP #elif defined(CONFIG_IN_FILE) diff --git a/src/main/config/config_streamer.h b/src/main/config/config_streamer.h index 20b3861a2a9..98680089980 100644 --- a/src/main/config/config_streamer.h +++ b/src/main/config/config_streamer.h @@ -26,7 +26,7 @@ // Streams data out to the EEPROM, padding to the write size as // needed, and updating the checksum as it goes. -#ifdef CONFIG_IN_EXTERNAL_FLASH +#if defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH) #define CONFIG_STREAMER_BUFFER_SIZE 8 // Must not be greater than the smallest flash page size of all compiled-in flash devices. typedef uint32_t config_streamer_buffer_align_type_t; #elif defined(STM32H743xx) || defined(STM32H750xx) || defined(STM32H723xx) || defined(STM32H725xx) diff --git a/src/main/drivers/bus_octospi.c b/src/main/drivers/bus_octospi.c new file mode 100644 index 00000000000..d51c59397a0 --- /dev/null +++ b/src/main/drivers/bus_octospi.c @@ -0,0 +1,119 @@ +/* + * This file is part of Cleanflight and Betaflight. + * + * Cleanflight and Betaflight are free software. You can redistribute + * this software and/or modify this software under the terms of the + * GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Cleanflight and Betaflight are distributed in the hope that they + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software. + * + * If not, see . + * + * Author: Dominic Clifton + */ + +/* + * OctoSPI support. + * + * Some STM32H7 MCUs support 2 OCTOSPI peripherals, each with up to 2 flash chips, using 2/4/8 IO lines each. Small MCU packages only have one instance. + * Additionally there is an OCTOSPIM peripheral which maps OCTOSPI1/2 peripherals to IO pins and can perform arbitration. + * + * Initial implementation is focused on supporting memory-mapped flash chips connected to an OCTOSPI peripheral that is already initialised by a bootloader. + * + * As such the following things are NOT supported: + * * Configuration of IO pins. + * * Clock configuration. + * * User-configuration. + * * OCTOSPIM configuration. + * * OCTOSPI2. + * + * Should the firmware need to know about the pins used by OCTOSPI then code can be written to determine this from the registers of the OCTOSPIM and OCTOSPI1/2 peripherals. + * + * Implementation notes: + * It's not possible to use the HAL libraries without modifying them and maintaining the internal state of the HAL structures. + * The HAL libraries were not designed to support the use-case of a bootloader configuring the flash in memory mapped mode and then + * having a second piece of software (this firmware) also use the flash. Furthermore many HAL methods were not designed to run with + * interrupts disabled which is necessary as other ISRs in this firmware will be running from external flash and must be disabled. + * See HAL_OSPI_Abort, OSPI_WaitFlagStateUntilTimeout, etc. + * All code that is executed when memory mapped mode is disabled needs to run from RAM, this would also involve modification of the HAL + * libraries. + */ + +#include +#include +#include + +#include "platform.h" + +#ifdef USE_OCTOSPI + +#include "bus_octospi.h" +#include "bus_octospi_impl.h" + +octoSpiDevice_t octoSpiDevice[OCTOSPIDEV_COUNT] = { 0 }; + +MMFLASH_CODE_NOINLINE OCTOSPIDevice octoSpiDeviceByInstance(OCTOSPI_TypeDef *instance) +{ +#ifdef USE_OCTOSPI_DEVICE_1 + if (instance == OCTOSPI1) { + return OCTOSPIDEV_1; + } +#endif + + return OCTOSPIINVALID; +} + +OCTOSPI_TypeDef *octoSpiInstanceByDevice(OCTOSPIDevice device) +{ + if (device == OCTOSPIINVALID || device >= OCTOSPIDEV_COUNT) { + return NULL; + } + + return octoSpiDevice[device].dev; +} + +const octoSpiHardware_t octoSpiHardware[] = { +#if defined(STM32H730xx) || defined(STM32H723xx) + { + .device = OCTOSPIDEV_1, + .reg = OCTOSPI1, + } +#else +#error MCU not supported. +#endif +}; + +bool octoSpiInit(OCTOSPIDevice device) +{ + for (size_t hwindex = 0; hwindex < ARRAYLEN(octoSpiHardware); hwindex++) { + const octoSpiHardware_t *hw = &octoSpiHardware[hwindex]; + + OCTOSPIDevice device = hw->device; + octoSpiDevice_t *pDev = &octoSpiDevice[device]; + + pDev->dev = hw->reg; + } + + switch (device) { + case OCTOSPIINVALID: + return false; + case OCTOSPIDEV_1: +#ifdef USE_OCTOSPI_DEVICE_1 + octoSpiInitDevice(OCTOSPIDEV_1); + return true; +#else + break; +#endif + } + return false; +} + +#endif diff --git a/src/main/drivers/bus_octospi.h b/src/main/drivers/bus_octospi.h new file mode 100644 index 00000000000..14178bd7934 --- /dev/null +++ b/src/main/drivers/bus_octospi.h @@ -0,0 +1,62 @@ +/* + * This file is part of Cleanflight and Betaflight. + * + * Cleanflight and Betaflight are free software. You can redistribute + * this software and/or modify this software under the terms of the + * GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Cleanflight and Betaflight are distributed in the hope that they + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software. + * + * If not, see . + * + * Author: Dominic Clifton + */ + +#pragma once + +#ifdef USE_OCTOSPI + +typedef enum OCTOSPIDevice { + OCTOSPIINVALID = -1, + OCTOSPIDEV_1 = 0, +} OCTOSPIDevice; + +#define OCTOSPIDEV_COUNT 1 + +// Macros to convert between configuration ids and device ids. +#define OCTOSPI_CFG_TO_DEV(x) ((x) - 1) +#define OCTOSPI_DEV_TO_CFG(x) ((x) + 1) + +#if !defined(STM32H7) +#error OctoSPI unsupported on this MCU +#endif + +OCTOSPIDevice octoSpiDeviceByInstance(OCTOSPI_TypeDef *instance); +OCTOSPI_TypeDef *octoSpiInstanceByDevice(OCTOSPIDevice device); + + +bool octoSpiInit(OCTOSPIDevice device); +bool octoSpiReceive1LINE(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint8_t *in, int length); +bool octoSpiReceive4LINES(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint8_t *in, int length); +bool octoSpiTransmit1LINE(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, const uint8_t *out, int length); + +bool octoSpiReceiveWithAddress1LINE(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, uint8_t *in, int length); +bool octoSpiReceiveWithAddress4LINES(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, uint8_t *in, int length); +bool octoSpiTransmitWithAddress1LINE(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, const uint8_t *out, int length); +bool octoSpiTransmitWithAddress4LINES(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, const uint8_t *out, int length); + +bool octoSpiInstructionWithAddress1LINE(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize); + + +void octoSpiDisableMemoryMappedMode(OCTOSPI_TypeDef *instance); +void octoSpiEnableMemoryMappedMode(OCTOSPI_TypeDef *instance); + +#endif diff --git a/src/main/drivers/bus_octospi_impl.h b/src/main/drivers/bus_octospi_impl.h new file mode 100644 index 00000000000..3a229892383 --- /dev/null +++ b/src/main/drivers/bus_octospi_impl.h @@ -0,0 +1,40 @@ +/* + * This file is part of Cleanflight and Betaflight. + * + * Cleanflight and Betaflight are free software. You can redistribute + * this software and/or modify this software under the terms of the + * GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Cleanflight and Betaflight are distributed in the hope that they + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software. + * + * If not, see . + * + * Author: Dominic Clifton + */ + +#pragma once + +#ifdef USE_OCTOSPI + +typedef struct octoSpiHardware_s { + OCTOSPIDevice device; + OCTOSPI_TypeDef *reg; +} octoSpiHardware_t; + +typedef struct OCTOSPIDevice_s { + OCTOSPI_TypeDef *dev; +} octoSpiDevice_t; + +extern octoSpiDevice_t octoSpiDevice[OCTOSPIDEV_COUNT]; + +void octoSpiInitDevice(OCTOSPIDevice device); + +#endif diff --git a/src/main/drivers/bus_octospi_stm32h7xx.c b/src/main/drivers/bus_octospi_stm32h7xx.c new file mode 100644 index 00000000000..f45aaddb441 --- /dev/null +++ b/src/main/drivers/bus_octospi_stm32h7xx.c @@ -0,0 +1,853 @@ +/* + * This file is part of Cleanflight and Betaflight. + * + * Cleanflight and Betaflight are free software. You can redistribute + * this software and/or modify this software under the terms of the + * GNU General Public License as published by the Free Software + * Foundation, either version 3 of the License, or (at your option) + * any later version. + * + * Cleanflight and Betaflight are distributed in the hope that they + * will be useful, but WITHOUT ANY WARRANTY; without even the implied + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software. + * + * If not, see . + * + * Author: Dominic Clifton + */ + +/* + * + */ +#include +#include +#include + +#include "platform.h" + +#ifdef USE_OCTOSPI + +#include "drivers/system.h" + +#include "drivers/bus_octospi.h" +#include "drivers/bus_octospi_impl.h" + +#if !(defined(STM32H730xx) || defined(STM32H723xx)) +#error MCU not supported. +#endif + +#define OCTOSPI_INTERFACE_COUNT 1 + +#define OSPI_FUNCTIONAL_MODE_INDIRECT_WRITE ((uint32_t)0x00000000) +#define OSPI_FUNCTIONAL_MODE_INDIRECT_READ ((uint32_t)OCTOSPI_CR_FMODE_0) +#define OSPI_FUNCTIONAL_MODE_AUTO_POLLING ((uint32_t)OCTOSPI_CR_FMODE_1) +#define OSPI_FUNCTIONAL_MODE_MEMORY_MAPPED ((uint32_t)OCTOSPI_CR_FMODE) + +#define OSPI_DHQC_DISABLE ((uint32_t)0x00000000U) +#define OSPI_DHQC_ENABLE ((uint32_t)OCTOSPI_TCR_DHQC) + +#define OSPI_OPTYPE_COMMON_CFG ((uint32_t)0x00000000U) + +#define OSPI_OPTYPE_READ_CFG ((uint32_t)0x00000001U) +#define OSPI_OPTYPE_WRITE_CFG ((uint32_t)0x00000002U) +#define OSPI_OPTYPE_WRAP_CFG ((uint32_t)0x00000003U) + +#define OSPI_INSTRUCTION_NONE ((uint32_t)0x00000000U) +#define OSPI_INSTRUCTION_1_LINE ((uint32_t)OCTOSPI_CCR_IMODE_0) +#define OSPI_INSTRUCTION_2_LINES ((uint32_t)OCTOSPI_CCR_IMODE_1) +#define OSPI_INSTRUCTION_4_LINES ((uint32_t)(OCTOSPI_CCR_IMODE_0 | OCTOSPI_CCR_IMODE_1)) +#define OSPI_INSTRUCTION_8_LINES ((uint32_t)OCTOSPI_CCR_IMODE_2) + +#define OSPI_INSTRUCTION_8_BITS ((uint32_t)0x00000000U) +#define OSPI_INSTRUCTION_16_BITS ((uint32_t)OCTOSPI_CCR_ISIZE_0) +#define OSPI_INSTRUCTION_24_BITS ((uint32_t)OCTOSPI_CCR_ISIZE_1) +#define OSPI_INSTRUCTION_32_BITS ((uint32_t)OCTOSPI_CCR_ISIZE) + +#define OSPI_INSTRUCTION_DTR_DISABLE ((uint32_t)0x00000000U) +#define OSPI_INSTRUCTION_DTR_ENABLE ((uint32_t)OCTOSPI_CCR_IDTR) + +#define OSPI_ADDRESS_NONE ((uint32_t)0x00000000U) /*!< No address */ +#define OSPI_ADDRESS_1_LINE ((uint32_t)OCTOSPI_CCR_ADMODE_0) /*!< Address on a single line */ +#define OSPI_ADDRESS_2_LINES ((uint32_t)OCTOSPI_CCR_ADMODE_1) /*!< Address on two lines */ +#define OSPI_ADDRESS_4_LINES ((uint32_t)(OCTOSPI_CCR_ADMODE_0 | OCTOSPI_CCR_ADMODE_1)) /*!< Address on four lines */ +#define OSPI_ADDRESS_8_LINES ((uint32_t)OCTOSPI_CCR_ADMODE_2) /*!< Address on eight lines */ + +#define OSPI_ADDRESS_8_BITS ((uint32_t)0x00000000U) /*!< 8-bit address */ +#define OSPI_ADDRESS_16_BITS ((uint32_t)OCTOSPI_CCR_ADSIZE_0) /*!< 16-bit address */ +#define OSPI_ADDRESS_24_BITS ((uint32_t)OCTOSPI_CCR_ADSIZE_1) /*!< 24-bit address */ +#define OSPI_ADDRESS_32_BITS ((uint32_t)OCTOSPI_CCR_ADSIZE) /*!< 32-bit address */ + +#define OSPI_ADDRESS_DTR_DISABLE ((uint32_t)0x00000000U) /*!< DTR mode disabled for address phase */ +#define OSPI_ADDRESS_DTR_ENABLE ((uint32_t)OCTOSPI_CCR_ADDTR) + +#define OSPI_DATA_NONE ((uint32_t)0x00000000U) +#define OSPI_DATA_1_LINE ((uint32_t)OCTOSPI_CCR_DMODE_0) +#define OSPI_DATA_2_LINES ((uint32_t)OCTOSPI_CCR_DMODE_1) +#define OSPI_DATA_4_LINES ((uint32_t)(OCTOSPI_CCR_DMODE_0 | OCTOSPI_CCR_DMODE_1)) +#define OSPI_DATA_8_LINES ((uint32_t)OCTOSPI_CCR_DMODE_2) + +#define OSPI_DATA_DTR_DISABLE ((uint32_t)0x00000000U) +#define OSPI_DATA_DTR_ENABLE ((uint32_t)OCTOSPI_CCR_DDTR) + +#define OSPI_ALTERNATE_BYTES_NONE ((uint32_t)0x00000000U) +#define OSPI_ALTERNATE_BYTES_1_LINE ((uint32_t)OCTOSPI_CCR_ABMODE_0) +#define OSPI_ALTERNATE_BYTES_2_LINES ((uint32_t)OCTOSPI_CCR_ABMODE_1) +#define OSPI_ALTERNATE_BYTES_4_LINES ((uint32_t)(OCTOSPI_CCR_ABMODE_0 | OCTOSPI_CCR_ABMODE_1)) +#define OSPI_ALTERNATE_BYTES_8_LINES ((uint32_t)OCTOSPI_CCR_ABMODE_2) + +#define OSPI_ALTERNATE_BYTES_8_BITS ((uint32_t)0x00000000U) +#define OSPI_ALTERNATE_BYTES_16_BITS ((uint32_t)OCTOSPI_CCR_ABSIZE_0) +#define OSPI_ALTERNATE_BYTES_24_BITS ((uint32_t)OCTOSPI_CCR_ABSIZE_1) +#define OSPI_ALTERNATE_BYTES_32_BITS ((uint32_t)OCTOSPI_CCR_ABSIZE) + +#define OSPI_ALTERNATE_BYTES_DTR_DISABLE ((uint32_t)0x00000000U) +#define OSPI_ALTERNATE_BYTES_DTR_ENABLE ((uint32_t)OCTOSPI_CCR_ABDTR) + +#define OSPI_DQS_DISABLE ((uint32_t)0x00000000U) +#define OSPI_DQS_ENABLE ((uint32_t)OCTOSPI_CCR_DQSE) + +#define OSPI_SIOO_INST_EVERY_CMD ((uint32_t)0x00000000U) +#define OSPI_SIOO_INST_ONLY_FIRST_CMD ((uint32_t)OCTOSPI_CCR_SIOO) + +MMFLASH_CODE_NOINLINE static void Error_Handler(void) { + while (1) { + NOOP; + } +} + + +#define __OSPI_GET_FLAG(__INSTANCE__, __FLAG__) ((READ_BIT((__INSTANCE__)->SR, (__FLAG__)) != 0U) ? SET : RESET) +#define __OSPI_CLEAR_FLAG(__INSTANCE__, __FLAG__) WRITE_REG((__INSTANCE__)->FCR, (__FLAG__)) +#define __OSPI_ENABLE(__INSTANCE__) SET_BIT((__INSTANCE__)->CR, OCTOSPI_CR_EN) +#define __OSPI_DISABLE(__INSTANCE__) CLEAR_BIT((__INSTANCE__)->CR, OCTOSPI_CR_EN) +#define __OSPI_IS_ENABLED(__INSTANCE__) (READ_BIT((__INSTANCE__)->CR, OCTOSPI_CR_EN) != 0U) + +MMFLASH_CODE_NOINLINE static void octoSpiAbort(OCTOSPI_TypeDef *instance) +{ + SET_BIT(instance->CR, OCTOSPI_CR_ABORT); +} + +MMFLASH_CODE_NOINLINE static void octoSpiWaitStatusFlags(OCTOSPI_TypeDef *instance, uint32_t mask, FlagStatus flagStatus) +{ + uint32_t regval; + + switch (flagStatus) { + case SET: + while (!((regval = READ_REG(instance->SR)) & mask)) + {} + break; + case RESET: + while (((regval = READ_REG(instance->SR)) & mask)) + {} + break; + } +} + + +typedef struct { + uint32_t OperationType; + + uint32_t Instruction; + uint32_t InstructionMode; + uint32_t InstructionSize; + uint32_t InstructionDtrMode; + + uint32_t Address; + uint32_t AddressMode; + uint32_t AddressSize; + uint32_t AddressDtrMode; + + uint32_t AlternateBytes; + uint32_t AlternateBytesMode; + uint32_t AlternateBytesSize; + uint32_t AlternateBytesDtrMode; + + uint32_t DummyCycles; // 0-31 + + uint32_t DataMode; + uint32_t DataDtrMode; + uint32_t NbData; + + uint32_t DQSMode; + uint32_t SIOOMode; +} OSPI_Command_t; + +// TODO rename cmd to command +MMFLASH_CODE_NOINLINE static ErrorStatus octoSpiConfigureCommand(OCTOSPI_TypeDef *instance, OSPI_Command_t *cmd) +{ + ErrorStatus status = SUCCESS; + + MODIFY_REG(instance->CR, OCTOSPI_CR_FMODE, 0U); + + instance->CCR = (cmd->DQSMode | cmd->SIOOMode); + + if (cmd->AlternateBytesMode != OSPI_ALTERNATE_BYTES_NONE) + { + instance->ABR = cmd->AlternateBytes; + + MODIFY_REG( + instance->ABR, + (OCTOSPI_CCR_ABMODE | OCTOSPI_CCR_ABDTR | OCTOSPI_CCR_ABSIZE), + (cmd->AlternateBytesMode | cmd->AlternateBytesDtrMode | cmd->AlternateBytesSize) + ); + } + + MODIFY_REG(instance->TCR, OCTOSPI_TCR_DCYC, cmd->DummyCycles); + + if (cmd->DataMode != OSPI_DATA_NONE) + { + if (cmd->OperationType == OSPI_OPTYPE_COMMON_CFG) + { + instance->DLR = (cmd->NbData - 1U); + } + } + + + if (cmd->InstructionMode != OSPI_INSTRUCTION_NONE) + { + if (cmd->AddressMode != OSPI_ADDRESS_NONE) + { + if (cmd->DataMode != OSPI_DATA_NONE) + { + // instruction, address and data + + MODIFY_REG(instance->CCR, (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + OCTOSPI_CCR_ADMODE | OCTOSPI_CCR_ADDTR | OCTOSPI_CCR_ADSIZE | + OCTOSPI_CCR_DMODE | OCTOSPI_CCR_DDTR), + (cmd->InstructionMode | cmd->InstructionDtrMode | cmd->InstructionSize | + cmd->AddressMode | cmd->AddressDtrMode | cmd->AddressSize | + cmd->DataMode | cmd->DataDtrMode)); + } + else + { + // instruction and address + + MODIFY_REG(instance->CCR, (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + OCTOSPI_CCR_ADMODE | OCTOSPI_CCR_ADDTR | OCTOSPI_CCR_ADSIZE), + (cmd->InstructionMode | cmd->InstructionDtrMode | cmd->InstructionSize | + cmd->AddressMode | cmd->AddressDtrMode | cmd->AddressSize)); + + // DHQC bit is linked with DDTR + if (((instance->TCR & OCTOSPI_TCR_DHQC_Msk) == OSPI_DHQC_ENABLE) && + (cmd->InstructionDtrMode == OSPI_INSTRUCTION_DTR_ENABLE)) + { + MODIFY_REG(instance->CCR, OCTOSPI_CCR_DDTR, OSPI_DATA_DTR_ENABLE); + } + } + + instance->IR = cmd->Instruction; + + instance->AR = cmd->Address; + } + else + { + if (cmd->DataMode != OSPI_DATA_NONE) + { + // instruction and data + + MODIFY_REG(instance->CCR, (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE | + OCTOSPI_CCR_DMODE | OCTOSPI_CCR_DDTR), + (cmd->InstructionMode | cmd->InstructionDtrMode | cmd->InstructionSize | + cmd->DataMode | cmd->DataDtrMode)); + } + else + { + // instruction only + + MODIFY_REG(instance->CCR, (OCTOSPI_CCR_IMODE | OCTOSPI_CCR_IDTR | OCTOSPI_CCR_ISIZE), + (cmd->InstructionMode | cmd->InstructionDtrMode | cmd->InstructionSize)); + + // DHQC bit is linked with DDTR + if (((instance->TCR & OCTOSPI_TCR_DHQC_Msk) == OSPI_DHQC_ENABLE) && + (cmd->InstructionDtrMode == OSPI_INSTRUCTION_DTR_ENABLE)) + { + MODIFY_REG(instance->CCR, OCTOSPI_CCR_DDTR, OSPI_DATA_DTR_ENABLE); + } + } + + instance->IR = cmd->Instruction; + + } + } + else + { + if (cmd->AddressMode != OSPI_ADDRESS_NONE) + { + if (cmd->DataMode != OSPI_DATA_NONE) + { + // address and data + + MODIFY_REG(instance->CCR, (OCTOSPI_CCR_ADMODE | OCTOSPI_CCR_ADDTR | OCTOSPI_CCR_ADSIZE | + OCTOSPI_CCR_DMODE | OCTOSPI_CCR_DDTR), + (cmd->AddressMode | cmd->AddressDtrMode | cmd->AddressSize | + cmd->DataMode | cmd->DataDtrMode)); + } + else + { + // address only + + MODIFY_REG(instance->CCR, (OCTOSPI_CCR_ADMODE | OCTOSPI_CCR_ADDTR | OCTOSPI_CCR_ADSIZE), + (cmd->AddressMode | cmd->AddressDtrMode | cmd->AddressSize)); + } + + instance->AR = cmd->Address; + } + else + { + // no instruction, no address + status = ERROR; + } + } + + return status; +} + +MMFLASH_CODE_NOINLINE ErrorStatus octoSpiCommand(OCTOSPI_TypeDef *instance, OSPI_Command_t *cmd) +{ + octoSpiWaitStatusFlags(instance, OCTOSPI_SR_BUSY, RESET); + + ErrorStatus status = octoSpiConfigureCommand(instance, cmd); + if (status == SUCCESS) { + if (cmd->DataMode == OSPI_DATA_NONE) + { + // transfer is already started, wait now. + octoSpiWaitStatusFlags(instance, OCTOSPI_SR_TCF, SET); + __OSPI_CLEAR_FLAG(instance, OCTOSPI_SR_TCF); + } + } + + return status; +} + +/* + * Transmit + * + * Call optoSpiCommand first to configure the transaction stages. + */ +MMFLASH_CODE_NOINLINE ErrorStatus octoSpiTransmit(OCTOSPI_TypeDef *instance, uint8_t *data) +{ + if (data == NULL) { + return ERROR; + } + + + __IO uint32_t XferCount = READ_REG(instance->DLR) + 1U; + uint8_t *pBuffPtr = data; + + MODIFY_REG(instance->CR, OCTOSPI_CR_FMODE, OSPI_FUNCTIONAL_MODE_INDIRECT_WRITE); + + __IO uint32_t *data_reg = &instance->DR; + do + { + octoSpiWaitStatusFlags(instance, OCTOSPI_SR_FTF, SET); + + *((__IO uint8_t *)data_reg) = *pBuffPtr; + pBuffPtr++; + XferCount--; + } while (XferCount > 0U); + + octoSpiWaitStatusFlags(instance, OCTOSPI_SR_TCF, SET); + __OSPI_CLEAR_FLAG(instance, OCTOSPI_SR_TCF); + + return SUCCESS; +} + +/* + * Receive + * + * Call optoSpiCommand first to configure the transaction stages. + */ +MMFLASH_CODE_NOINLINE ErrorStatus octoSpiReceive(OCTOSPI_TypeDef *instance, uint8_t *data) +{ + if (data == NULL) { + return ERROR; + } + + __IO uint32_t XferCount = READ_REG(instance->DLR) + 1U; + uint8_t *pBuffPtr = data; + + MODIFY_REG(instance->CR, OCTOSPI_CR_FMODE, OSPI_FUNCTIONAL_MODE_INDIRECT_READ); + + uint32_t addr_reg = instance->AR; + uint32_t ir_reg = instance->IR; + + // Trigger the transfer by re-writing the address or instruction register + if (READ_BIT(instance->CCR, OCTOSPI_CCR_ADMODE) != OSPI_ADDRESS_NONE) + { + WRITE_REG(instance->AR, addr_reg); + } + else + { + WRITE_REG(instance->IR, ir_reg); + } + + __IO uint32_t *data_reg = &instance->DR; + + do + { + octoSpiWaitStatusFlags(instance, OCTOSPI_SR_FTF | OCTOSPI_SR_TCF, SET); + + *pBuffPtr = *((__IO uint8_t *)data_reg); + pBuffPtr++; + XferCount--; + } while(XferCount > 0U); + + octoSpiWaitStatusFlags(instance, OCTOSPI_SR_TCF, SET); + __OSPI_CLEAR_FLAG(instance, OCTOSPI_SR_TCF); + + return SUCCESS; +} + +typedef struct +{ + // CR register contains the all-important FMODE bits. + uint32_t CR; + + // flash chip specific configuration set by the bootloader + uint32_t CCR; + uint32_t TCR; + uint32_t IR; + uint32_t ABR; + // address register - no meaning. + // data length register no meaning. + +} octoSpiMemoryMappedModeConfigurationRegisterBackup_t; + +octoSpiMemoryMappedModeConfigurationRegisterBackup_t ospiMMMCRBackups[OCTOSPI_INTERFACE_COUNT]; + +void octoSpiBackupMemoryMappedModeConfiguration(OCTOSPI_TypeDef *instance) +{ + OCTOSPIDevice device = octoSpiDeviceByInstance(instance); + if (device == OCTOSPIINVALID) { + return; + } + + octoSpiMemoryMappedModeConfigurationRegisterBackup_t *ospiMMMCRBackup = &ospiMMMCRBackups[device]; + + // backup all the registers used by memory mapped mode that: + // a) the bootloader configured. + // b) that other code in this implementation may have modified when memory mapped mode is disabled. + + ospiMMMCRBackup->CR = instance->CR; + ospiMMMCRBackup->IR = instance->IR; + ospiMMMCRBackup->CCR = instance->CCR; + ospiMMMCRBackup->TCR = instance->TCR; + ospiMMMCRBackup->ABR = instance->ABR; +} + +MMFLASH_CODE_NOINLINE void octoSpiRestoreMemoryMappedModeConfiguration(OCTOSPI_TypeDef *instance) +{ + OCTOSPIDevice device = octoSpiDeviceByInstance(instance); + if (device == OCTOSPIINVALID) { + return; + } + + octoSpiMemoryMappedModeConfigurationRegisterBackup_t *ospiMMMCRBackup = &ospiMMMCRBackups[device]; + + octoSpiWaitStatusFlags(instance, OCTOSPI_SR_BUSY, RESET); + + instance->ABR = ospiMMMCRBackup->ABR; + instance->TCR = ospiMMMCRBackup->TCR; + + instance->DLR = 0; // "no meaning" in MM mode. + + instance->CCR = ospiMMMCRBackup->CCR; + + instance->IR = ospiMMMCRBackup->IR; + instance->AR = 0; // "no meaning" in MM mode. + + octoSpiAbort(instance); + octoSpiWaitStatusFlags(instance, OCTOSPI_SR_BUSY, RESET); + + instance->CR = ospiMMMCRBackup->CR; +} + +/* + * Disable memory mapped mode. + * + * @See octoSpiEnableMemoryMappedMode + * @See MMFLASH_CODE_NOINLINE + * + * Once this is called any code or data in the memory mapped region cannot be accessed. + * Thus, this function itself must be in RAM, and the caller's code and data should all be in RAM + * and this requirement continues until octoSpiEnableMemoryMappedMode is called. + * This applies to ISR code that runs from the memory mapped region, so likely the caller should + * also disable IRQs before calling this. + */ +MMFLASH_CODE_NOINLINE void octoSpiDisableMemoryMappedMode(OCTOSPI_TypeDef *instance) +{ + if (READ_BIT(OCTOSPI1->CR, OCTOSPI_CR_FMODE) != OCTOSPI_CR_FMODE) { + failureMode(FAILURE_DEVELOPER); // likely not booted with memory mapped mode enabled, or mismatched calls to enable/disable memory map mode. + } + + octoSpiAbort(instance); + if (__OSPI_GET_FLAG(instance, OCTOSPI_SR_BUSY) == SET) { + + __OSPI_DISABLE(instance); + octoSpiAbort(instance); + } + octoSpiWaitStatusFlags(instance, OCTOSPI_SR_BUSY, RESET); + + uint32_t fmode = 0x0; // b00 = indirect write, see OCTOSPI->CR->FMODE + MODIFY_REG(instance->CR, OCTOSPI_CR_FMODE, fmode); + + uint32_t regval = READ_REG(instance->CR); + if ((regval & OCTOSPI_CR_FMODE) != fmode) { + Error_Handler(); + } + + if (!__OSPI_IS_ENABLED(instance)) { + __OSPI_ENABLE(instance); + } +} + +/* + * Enable memory mapped mode. + * + * @See octoSpiDisableMemoryMappedMode + * @See MMFLASH_CODE_NOINLINE + */ + +MMFLASH_CODE_NOINLINE void octoSpiEnableMemoryMappedMode(OCTOSPI_TypeDef *instance) +{ + octoSpiAbort(instance); + octoSpiWaitStatusFlags(instance, OCTOSPI_SR_BUSY, RESET); + + octoSpiRestoreMemoryMappedModeConfiguration(instance); +} + +MMFLASH_CODE_NOINLINE void octoSpiTestEnableDisableMemoryMappedMode(octoSpiDevice_t *octoSpi) +{ + OCTOSPI_TypeDef *instance = octoSpi->dev; + + __disable_irq(); + octoSpiDisableMemoryMappedMode(instance); + octoSpiEnableMemoryMappedMode(instance); + __enable_irq(); +} + +MMFLASH_DATA static const uint32_t octoSpi_addressSizeMap[] = { + OSPI_ADDRESS_8_BITS, + OSPI_ADDRESS_16_BITS, + OSPI_ADDRESS_24_BITS, + OSPI_ADDRESS_32_BITS +}; + +MMFLASH_CODE static uint32_t octoSpi_addressSizeFromValue(uint8_t addressSize) +{ + return octoSpi_addressSizeMap[((addressSize + 1) / 8) - 1]; // rounds to nearest OSPI_ADDRESS_* value that will hold the address. +} + + +MMFLASH_CODE_NOINLINE bool octoSpiTransmit1LINE(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, const uint8_t *out, int length) +{ + OSPI_Command_t cmd; // Can't initialise to zero as compiler optimization will use memset() which is not in RAM. + + cmd.OperationType = OSPI_OPTYPE_COMMON_CFG; + + cmd.Instruction = instruction; + cmd.InstructionDtrMode = OSPI_INSTRUCTION_DTR_DISABLE; + cmd.InstructionMode = OSPI_INSTRUCTION_1_LINE; + cmd.InstructionSize = OSPI_INSTRUCTION_8_BITS; + + cmd.AddressDtrMode = OSPI_ADDRESS_DTR_DISABLE; + cmd.AddressMode = OSPI_ADDRESS_NONE; + cmd.AddressSize = OSPI_ADDRESS_32_BITS; + + cmd.DummyCycles = dummyCycles; + + cmd.AlternateBytesMode = OSPI_ALTERNATE_BYTES_NONE; + + cmd.DataDtrMode = OSPI_DATA_DTR_DISABLE; + cmd.DataMode = OSPI_DATA_NONE; + cmd.NbData = length; + + cmd.DQSMode = OSPI_DQS_DISABLE; + cmd.SIOOMode = OSPI_SIOO_INST_EVERY_CMD; + + if (out) { + cmd.DataMode = OSPI_DATA_1_LINE; + } + + ErrorStatus status = octoSpiCommand(instance, &cmd); + + if (status == SUCCESS && out && length > 0) { + status = octoSpiTransmit(instance, (uint8_t *)out); + } + return status == SUCCESS; +} + +MMFLASH_CODE_NOINLINE bool octoSpiReceive1LINE(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint8_t *in, int length) +{ + OSPI_Command_t cmd; // Can't initialise to zero as compiler optimization will use memset() which is not in RAM. + + cmd.OperationType = OSPI_OPTYPE_COMMON_CFG; + + cmd.Instruction = instruction; + cmd.InstructionDtrMode = OSPI_INSTRUCTION_DTR_DISABLE; + cmd.InstructionMode = OSPI_INSTRUCTION_1_LINE; + cmd.InstructionSize = OSPI_INSTRUCTION_8_BITS; + + cmd.AddressDtrMode = OSPI_ADDRESS_DTR_DISABLE; + cmd.AddressMode = OSPI_ADDRESS_NONE; + cmd.AddressSize = OSPI_ADDRESS_32_BITS; + + cmd.AlternateBytesMode = OSPI_ALTERNATE_BYTES_NONE; + + cmd.DummyCycles = dummyCycles; + + cmd.DataDtrMode = OSPI_DATA_DTR_DISABLE; + cmd.DataMode = OSPI_DATA_1_LINE; + cmd.NbData = length; + + cmd.DQSMode = OSPI_DQS_DISABLE; + cmd.SIOOMode = OSPI_SIOO_INST_EVERY_CMD; + + ErrorStatus status = octoSpiCommand(instance, &cmd); + + if (status == SUCCESS) { + status = octoSpiReceive(instance, in); + } + + return status == SUCCESS; +} + +MMFLASH_CODE_NOINLINE bool octoSpiReceive4LINES(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint8_t *in, int length) +{ + OSPI_Command_t cmd; // Can't initialise to zero as compiler optimization will use memset() which is not in RAM. + + cmd.OperationType = OSPI_OPTYPE_COMMON_CFG; + + cmd.Instruction = instruction; + cmd.InstructionDtrMode = OSPI_INSTRUCTION_DTR_DISABLE; + cmd.InstructionMode = OSPI_INSTRUCTION_4_LINES; + cmd.InstructionSize = OSPI_INSTRUCTION_8_BITS; + + cmd.AddressDtrMode = OSPI_ADDRESS_DTR_DISABLE; + cmd.AddressMode = OSPI_ADDRESS_NONE; + cmd.AddressSize = OSPI_ADDRESS_32_BITS; + + cmd.AlternateBytesMode = OSPI_ALTERNATE_BYTES_NONE; + + cmd.DummyCycles = dummyCycles; + + cmd.DataDtrMode = OSPI_DATA_DTR_DISABLE; + cmd.DataMode = OSPI_DATA_4_LINES; + cmd.NbData = length; + + cmd.DQSMode = OSPI_DQS_DISABLE; + cmd.SIOOMode = OSPI_SIOO_INST_EVERY_CMD; + + ErrorStatus status = octoSpiCommand(instance, &cmd); + + if (status == SUCCESS) { + status = octoSpiReceive(instance, in); + } + + return status == SUCCESS; +} + +MMFLASH_CODE_NOINLINE bool octoSpiReceiveWithAddress1LINE(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, uint8_t *in, int length) +{ + OSPI_Command_t cmd; // Can't initialise to zero as compiler optimization will use memset() which is not in RAM. + + cmd.OperationType = OSPI_OPTYPE_COMMON_CFG; + + cmd.Instruction = instruction; + cmd.InstructionDtrMode = OSPI_INSTRUCTION_DTR_DISABLE; + cmd.InstructionMode = OSPI_INSTRUCTION_1_LINE; + cmd.InstructionSize = OSPI_INSTRUCTION_8_BITS; + + cmd.Address = address; + cmd.AddressDtrMode = OSPI_ADDRESS_DTR_DISABLE; + cmd.AddressMode = OSPI_ADDRESS_1_LINE; + cmd.AddressSize = octoSpi_addressSizeFromValue(addressSize); + + cmd.AlternateBytesMode = OSPI_ALTERNATE_BYTES_NONE; + + cmd.DummyCycles = dummyCycles; + + cmd.DataDtrMode = OSPI_DATA_DTR_DISABLE; + cmd.DataMode = OSPI_DATA_1_LINE; + cmd.NbData = length; + + cmd.SIOOMode = OSPI_SIOO_INST_EVERY_CMD; + cmd.DQSMode = OSPI_DQS_DISABLE; + + ErrorStatus status = octoSpiCommand(instance, &cmd); + + if (status == SUCCESS) { + status = octoSpiReceive(instance, in); + } + + return status == SUCCESS; +} + +MMFLASH_CODE_NOINLINE bool octoSpiReceiveWithAddress4LINES(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, uint8_t *in, int length) +{ + OSPI_Command_t cmd; // Can't initialise to zero as compiler optimization will use memset() which is not in RAM. + + cmd.OperationType = OSPI_OPTYPE_COMMON_CFG; + + cmd.Instruction = instruction; + cmd.InstructionDtrMode = OSPI_INSTRUCTION_DTR_DISABLE; + cmd.InstructionMode = OSPI_INSTRUCTION_1_LINE; + cmd.InstructionSize = OSPI_INSTRUCTION_8_BITS; + + cmd.Address = address; + cmd.AddressDtrMode = OSPI_ADDRESS_DTR_DISABLE; + cmd.AddressMode = OSPI_ADDRESS_1_LINE; + cmd.AddressSize = octoSpi_addressSizeFromValue(addressSize); + + cmd.AlternateBytesMode = OSPI_ALTERNATE_BYTES_NONE; + + cmd.DummyCycles = dummyCycles; + + cmd.DataDtrMode = OSPI_DATA_DTR_DISABLE; + cmd.DataMode = OSPI_DATA_4_LINES; + cmd.NbData = length; + + cmd.DQSMode = OSPI_DQS_DISABLE; + cmd.SIOOMode = OSPI_SIOO_INST_EVERY_CMD; + + ErrorStatus status = octoSpiCommand(instance, &cmd); + + if (status == SUCCESS) { + status = octoSpiReceive(instance, in); + } + + return status == SUCCESS; +} + +MMFLASH_CODE_NOINLINE bool octoSpiTransmitWithAddress1LINE(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, const uint8_t *out, int length) +{ + OSPI_Command_t cmd; // Can't initialise to zero as compiler optimization will use memset() which is not in RAM. + + cmd.OperationType = OSPI_OPTYPE_COMMON_CFG; + + cmd.Instruction = instruction; + cmd.InstructionDtrMode = OSPI_INSTRUCTION_DTR_DISABLE; + cmd.InstructionMode = OSPI_INSTRUCTION_1_LINE; + cmd.InstructionSize = OSPI_INSTRUCTION_8_BITS; + + cmd.Address = address; + cmd.AddressDtrMode = OSPI_ADDRESS_DTR_DISABLE; + cmd.AddressMode = OSPI_ADDRESS_1_LINE; + cmd.AddressSize = octoSpi_addressSizeFromValue(addressSize); + + cmd.AlternateBytesMode = OSPI_ALTERNATE_BYTES_NONE; + + cmd.DummyCycles = dummyCycles; + + cmd.DataDtrMode = OSPI_DATA_DTR_DISABLE; + cmd.DataMode = OSPI_DATA_1_LINE; + cmd.NbData = length; + + cmd.DQSMode = OSPI_DQS_DISABLE; + cmd.SIOOMode = OSPI_SIOO_INST_EVERY_CMD; + + ErrorStatus status = octoSpiCommand(instance, &cmd); + + if (status == SUCCESS) { + status = octoSpiTransmit(instance, (uint8_t *)out); + } + + return status == SUCCESS; +} + +MMFLASH_CODE_NOINLINE bool octoSpiTransmitWithAddress4LINES(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize, const uint8_t *out, int length) +{ + OSPI_Command_t cmd; // Can't initialise to zero as compiler optimization will use memset() which is not in RAM. + + cmd.OperationType = OSPI_OPTYPE_COMMON_CFG; + + cmd.Instruction = instruction; + cmd.InstructionDtrMode = OSPI_INSTRUCTION_DTR_DISABLE; + cmd.InstructionMode = OSPI_INSTRUCTION_1_LINE; + cmd.InstructionSize = OSPI_INSTRUCTION_8_BITS; + + cmd.Address = address; + cmd.AddressDtrMode = OSPI_ADDRESS_DTR_DISABLE; + cmd.AddressMode = OSPI_ADDRESS_1_LINE; + cmd.AddressSize = octoSpi_addressSizeFromValue(addressSize); + + cmd.AlternateBytesMode = OSPI_ALTERNATE_BYTES_NONE; + + cmd.DummyCycles = dummyCycles; + + cmd.DataDtrMode = OSPI_DATA_DTR_DISABLE; + cmd.DataMode = OSPI_DATA_4_LINES; + cmd.NbData = length; + + cmd.DQSMode = OSPI_DQS_DISABLE; + cmd.SIOOMode = OSPI_SIOO_INST_EVERY_CMD; + + ErrorStatus status = octoSpiCommand(instance, &cmd); + + if (status == SUCCESS) { + status = octoSpiTransmit(instance, (uint8_t *)out); + } + + + return status == SUCCESS; +} + +MMFLASH_CODE_NOINLINE bool octoSpiInstructionWithAddress1LINE(OCTOSPI_TypeDef *instance, uint8_t instruction, uint8_t dummyCycles, uint32_t address, uint8_t addressSize) +{ + OSPI_Command_t cmd; // Can't initialise to zero as compiler optimization will use memset() which is not in RAM. + + cmd.OperationType = OSPI_OPTYPE_COMMON_CFG; + + cmd.Instruction = instruction; + cmd.InstructionDtrMode = OSPI_INSTRUCTION_DTR_DISABLE; + cmd.InstructionMode = OSPI_INSTRUCTION_1_LINE; + cmd.InstructionSize = OSPI_INSTRUCTION_8_BITS; + + cmd.Address = address; + cmd.AddressDtrMode = OSPI_ADDRESS_DTR_DISABLE; + cmd.AddressMode = OSPI_ADDRESS_1_LINE; + cmd.AddressSize = octoSpi_addressSizeFromValue(addressSize); + + cmd.AlternateBytesMode = OSPI_ALTERNATE_BYTES_NONE; + + cmd.DummyCycles = dummyCycles; + + cmd.DataDtrMode = OSPI_DATA_DTR_DISABLE; + cmd.DataMode = OSPI_DATA_NONE; + cmd.NbData = 0; + + cmd.DQSMode = OSPI_DQS_DISABLE; + cmd.SIOOMode = OSPI_SIOO_INST_EVERY_CMD; + + ErrorStatus status = octoSpiCommand(instance, &cmd); + + return status == SUCCESS; +} + + +void octoSpiInitDevice(OCTOSPIDevice device) +{ + octoSpiDevice_t *octoSpi = &(octoSpiDevice[device]); + +#if defined(STM32H730xx) || defined(STM32H723xx) + if (isMemoryMappedModeEnabledOnBoot()) { + // Bootloader has already configured the IO, clocks and peripherals. + octoSpiBackupMemoryMappedModeConfiguration(octoSpi->dev); + + octoSpiTestEnableDisableMemoryMappedMode(octoSpi); + } else { + failureMode(FAILURE_DEVELOPER); // trying to use this implementation when memory mapped mode is not already enabled by a bootloader + + // Here is where we would configure the OCTOSPI1/2 and OCTOSPIM peripherals for the non-memory-mapped use case. + } +#else +#error MCU not supported. +#endif + +} + +#endif diff --git a/src/main/drivers/flash.c b/src/main/drivers/flash.c index ee8b55a3d77..39be0e9d838 100644 --- a/src/main/drivers/flash.c +++ b/src/main/drivers/flash.c @@ -36,8 +36,12 @@ #include "flash_w25m.h" #include "drivers/bus_spi.h" #include "drivers/bus_quadspi.h" +#include "drivers/bus_octospi.h" #include "drivers/io.h" #include "drivers/time.h" +#include "drivers/system.h" + +#ifdef USE_FLASH_SPI // 20 MHz max SPI frequency #define FLASH_MAX_SPI_CLK_HZ 20000000 @@ -47,13 +51,143 @@ static extDevice_t devInstance; static extDevice_t *dev; +#endif + static flashDevice_t flashDevice; static flashPartitionTable_t flashPartitionTable; static int flashPartitions = 0; #define FLASH_INSTRUCTION_RDID 0x9F -#ifdef USE_QUADSPI +#ifdef USE_FLASH_MEMORY_MAPPED +MMFLASH_CODE_NOINLINE void flashMemoryMappedModeDisable(void) +{ + __disable_irq(); +#ifdef USE_FLASH_OCTOSPI + octoSpiDisableMemoryMappedMode(flashDevice.io.handle.octoSpi); +#else +#error Invalid configuration - Not implemented +#endif +} + +MMFLASH_CODE_NOINLINE void flashMemoryMappedModeEnable(void) +{ +#ifdef USE_FLASH_OCTOSPI + octoSpiEnableMemoryMappedMode(flashDevice.io.handle.octoSpi); + __enable_irq(); +#else +#error Invalid configuration - Not implemented +#endif +} +#endif + +#ifdef USE_FLASH_OCTOSPI +MMFLASH_CODE_NOINLINE static bool flashOctoSpiInit(const flashConfig_t *flashConfig) +{ + bool detected = false; + + enum { + TRY_1LINE = 0, TRY_4LINE, BAIL + } phase = TRY_1LINE; + +#ifdef USE_FLASH_MEMORY_MAPPED + bool memoryMappedModeEnabledOnBoot = isMemoryMappedModeEnabledOnBoot(); +#else + bool memoryMappedModeEnabledOnBoot = false; +#endif + +#ifndef USE_OCTOSPI_EXPERIMENTAL + if (!memoryMappedModeEnabledOnBoot) { + return false; // Not supported yet, enable USE_OCTOSPI_EXPERIMENTAL and test/update implementation as required. + } +#endif + + OCTOSPI_TypeDef *instance = octoSpiInstanceByDevice(OCTOSPI_CFG_TO_DEV(flashConfig->octoSpiDevice)); + + flashDevice.io.handle.octoSpi = instance; + flashDevice.io.mode = FLASHIO_OCTOSPI; + + if (memoryMappedModeEnabledOnBoot) { + flashMemoryMappedModeDisable(); + } + + do { +#ifdef USE_OCTOSPI_EXPERIMENTAL + if (!memoryMappedMode) { + octoSpiSetDivisor(instance, OCTOSPI_CLOCK_INITIALISATION); + } +#endif + // for the memory-mapped use-case, we rely on the bootloader to have already selected the correct speed for the flash chip. + + // 3 bytes for what we need, but some IC's need 8 dummy cycles after the instruction, so read 4 and make two attempts to + // assemble the chip id from the response. + uint8_t readIdResponse[4]; + + bool status = false; + switch (phase) { + case TRY_1LINE: + status = octoSpiReceive1LINE(instance, FLASH_INSTRUCTION_RDID, 0, readIdResponse, 4); + break; + case TRY_4LINE: + status = octoSpiReceive4LINES(instance, FLASH_INSTRUCTION_RDID, 2, readIdResponse, 3); + break; + default: + break; + } + + if (!status) { + phase++; + continue; + } + +#ifdef USE_OCTOSPI_EXPERIMENTAL + if (!memoryMappedModeEnabledOnBoot) { + octoSpiSetDivisor(instance, OCTOSPI_CLOCK_ULTRAFAST); + } +#endif + + for (uint8_t offset = 0; offset <= 1 && !detected; offset++) { + + uint32_t jedecID = (readIdResponse[offset + 0] << 16) | (readIdResponse[offset + 1] << 8) | (readIdResponse[offset + 2]); + + if (offset == 0) { +#if defined(USE_FLASH_W25Q128FV) + if (!detected && w25q128fv_identify(&flashDevice, jedecID)) { + detected = true; + } +#endif + } + + if (offset == 1) { +#ifdef USE_OCTOSPI_EXPERIMENTAL + if (!memoryMappedModeEnabledOnBoot) { + // These flash chips DO NOT support memory mapped mode; suitable flash read commands must be available. +#if defined(USE_FLASH_W25N01G) + if (!detected && w25n01g_identify(&flashDevice, jedecID)) { + detected = true; + } +#endif +#if defined(USE_FLASH_W25M02G) + if (!detected && w25m_identify(&flashDevice, jedecID)) { + detected = true; + } +#endif + } +#endif + } + } + phase++; + } while (phase != BAIL && !detected); + + if (memoryMappedModeEnabledOnBoot) { + flashMemoryMappedModeEnable(); + } + return detected; + +} +#endif // USE_FLASH_OCTOSPI + +#ifdef USE_FLASH_QUADSPI static bool flashQuadSpiInit(const flashConfig_t *flashConfig) { bool detected = false; @@ -63,6 +197,9 @@ static bool flashQuadSpiInit(const flashConfig_t *flashConfig) QUADSPI_TypeDef *hqspi = quadSpiInstanceByDevice(QUADSPI_CFG_TO_DEV(flashConfig->quadSpiDevice)); + flashDevice.io.handle.quadSpi = hqspi; + flashDevice.io.mode = FLASHIO_QUADSPI; + do { quadSpiSetDivisor(hqspi, QUADSPI_CLOCK_INITIALISATION); @@ -87,19 +224,16 @@ static bool flashQuadSpiInit(const flashConfig_t *flashConfig) continue; } - flashDevice.io.handle.quadSpi = hqspi; - flashDevice.io.mode = FLASHIO_QUADSPI; - quadSpiSetDivisor(hqspi, QUADSPI_CLOCK_ULTRAFAST); for (uint8_t offset = 0; offset <= 1 && !detected; offset++) { - uint32_t chipID = (readIdResponse[offset + 0] << 16) | (readIdResponse[offset + 1] << 8) | (readIdResponse[offset + 2]); + uint32_t jedecID = (readIdResponse[offset + 0] << 16) | (readIdResponse[offset + 1] << 8) | (readIdResponse[offset + 2]); if (offset == 0) { -#ifdef USE_FLASH_W25Q128FV - if (!detected && w25q128fv_detect(&flashDevice, chipID)) { +#if defined(USE_FLASH_W25Q128FV) + if (!detected && w25q128fv_identify(&flashDevice, jedecID)) { detected = true; } #endif @@ -112,20 +246,20 @@ static bool flashQuadSpiInit(const flashConfig_t *flashConfig) } if (offset == 1) { -#ifdef USE_FLASH_W25N01G - if (!detected && w25n01g_detect(&flashDevice, chipID)) { +#if defined(USE_FLASH_W25N01G) + if (!detected && w25n01g_identify(&flashDevice, jedecID)) { detected = true; } #endif #if defined(USE_FLASH_W25M02G) - if (!detected && w25m_detect(&flashDevice, chipID)) { + if (!detected && w25m_identify(&flashDevice, jedecID)) { detected = true; } #endif } if (detected) { - flashDevice.geometry.jedecId = chipID; + flashDevice.geometry.jedecId = jedecID; } } phase++; @@ -133,15 +267,9 @@ static bool flashQuadSpiInit(const flashConfig_t *flashConfig) return detected; } -#endif // USE_QUADSPI - -#ifdef USE_SPI - -void flashPreInit(const flashConfig_t *flashConfig) -{ - spiPreinitRegister(flashConfig->csTag, IOCFG_IPU, 1); -} +#endif // USE_FLASH_QUADSPI +#ifdef USE_FLASH_SPI static bool flashSpiInit(const flashConfig_t *flashConfig) { bool detected = false; @@ -186,39 +314,39 @@ static bool flashSpiInit(const flashConfig_t *flashConfig) spiReadRegBuf(dev, FLASH_INSTRUCTION_RDID, readIdResponse, sizeof(readIdResponse)); // Manufacturer, memory type, and capacity - uint32_t chipID = (readIdResponse[0] << 16) | (readIdResponse[1] << 8) | (readIdResponse[2]); + uint32_t jedecID = (readIdResponse[0] << 16) | (readIdResponse[1] << 8) | (readIdResponse[2]); #ifdef USE_FLASH_M25P16 - if (m25p16_detect(&flashDevice, chipID)) { + if (m25p16_identify(&flashDevice, jedecID)) { detected = true; } #endif #if defined(USE_FLASH_W25M512) || defined(USE_FLASH_W25M) - if (!detected && w25m_detect(&flashDevice, chipID)) { + if (!detected && w25m_identify(&flashDevice, jedecID)) { detected = true; } #endif if (!detected) { // Newer chips - chipID = (readIdResponse[1] << 16) | (readIdResponse[2] << 8) | (readIdResponse[3]); + jedecID = (readIdResponse[1] << 16) | (readIdResponse[2] << 8) | (readIdResponse[3]); } #ifdef USE_FLASH_W25N01G - if (!detected && w25n01g_detect(&flashDevice, chipID)) { + if (!detected && w25n01g_identify(&flashDevice, jedecID)) { detected = true; } #endif #ifdef USE_FLASH_W25M02G - if (!detected && w25m_detect(&flashDevice, chipID)) { + if (!detected && w25m_identify(&flashDevice, jedecID)) { detected = true; } #endif if (detected) { - flashDevice.geometry.jedecId = chipID; + flashDevice.geometry.jedecId = jedecID; return detected; } @@ -226,39 +354,65 @@ static bool flashSpiInit(const flashConfig_t *flashConfig) return false; } -#endif // USE_SPI +#endif // USE_FLASH_SPI + +void flashPreInit(const flashConfig_t *flashConfig) +{ + spiPreinitRegister(flashConfig->csTag, IOCFG_IPU, 1); +} bool flashDeviceInit(const flashConfig_t *flashConfig) { -#ifdef USE_SPI + bool haveFlash = false; + +#ifdef USE_FLASH_SPI bool useSpi = (SPI_CFG_TO_DEV(flashConfig->spiDevice) != SPIINVALID); if (useSpi) { - return flashSpiInit(flashConfig); + haveFlash = flashSpiInit(flashConfig); } #endif -#ifdef USE_QUADSPI +#ifdef USE_FLASH_QUADSPI bool useQuadSpi = (QUADSPI_CFG_TO_DEV(flashConfig->quadSpiDevice) != QUADSPIINVALID); if (useQuadSpi) { - return flashQuadSpiInit(flashConfig); + haveFlash = flashQuadSpiInit(flashConfig); } #endif - return false; +#ifdef USE_FLASH_OCTOSPI + bool useOctoSpi = (OCTOSPI_CFG_TO_DEV(flashConfig->octoSpiDevice) != OCTOSPIINVALID); + if (useOctoSpi) { + haveFlash = flashOctoSpiInit(flashConfig); + } +#endif + + if (haveFlash && flashDevice.vTable->configure) { + uint32_t configurationFlags = 0; + +#ifdef USE_FLASH_MEMORY_MAPPED + if (isMemoryMappedModeEnabledOnBoot()) { + configurationFlags |= FLASH_CF_SYSTEM_IS_MEMORY_MAPPED; + } +#endif + + flashDevice.vTable->configure(&flashDevice, configurationFlags); + } + + return haveFlash; } -bool flashIsReady(void) +MMFLASH_CODE bool flashIsReady(void) { return flashDevice.vTable->isReady(&flashDevice); } -bool flashWaitForReady(void) +MMFLASH_CODE bool flashWaitForReady(void) { return flashDevice.vTable->waitForReady(&flashDevice); } -void flashEraseSector(uint32_t address) +MMFLASH_CODE void flashEraseSector(uint32_t address) { flashDevice.callback = NULL; flashDevice.vTable->eraseSector(&flashDevice, address); @@ -273,12 +427,12 @@ void flashEraseCompletely(void) /* The callback, if provided, will receive the totoal number of bytes transfered * by each call to flashPageProgramContinue() once the transfer completes. */ -void flashPageProgramBegin(uint32_t address, void (*callback)(uint32_t length)) +MMFLASH_CODE void flashPageProgramBegin(uint32_t address, void (*callback)(uint32_t length)) { flashDevice.vTable->pageProgramBegin(&flashDevice, address, callback); } -uint32_t flashPageProgramContinue(const uint8_t **buffers, uint32_t *bufferSizes, uint32_t bufferCount) +MMFLASH_CODE uint32_t flashPageProgramContinue(const uint8_t **buffers, uint32_t *bufferSizes, uint32_t bufferCount) { uint32_t maxBytesToWrite = flashDevice.geometry.pageSize - (flashDevice.currentWriteAddress % flashDevice.geometry.pageSize); @@ -299,23 +453,23 @@ uint32_t flashPageProgramContinue(const uint8_t **buffers, uint32_t *bufferSizes return flashDevice.vTable->pageProgramContinue(&flashDevice, buffers, bufferSizes, bufferCount); } -void flashPageProgramFinish(void) +MMFLASH_CODE void flashPageProgramFinish(void) { flashDevice.vTable->pageProgramFinish(&flashDevice); } -void flashPageProgram(uint32_t address, const uint8_t *data, uint32_t length, void (*callback)(uint32_t length)) +MMFLASH_CODE void flashPageProgram(uint32_t address, const uint8_t *data, uint32_t length, void (*callback)(uint32_t length)) { flashDevice.vTable->pageProgram(&flashDevice, address, data, length, callback); } -int flashReadBytes(uint32_t address, uint8_t *buffer, uint32_t length) +MMFLASH_CODE int flashReadBytes(uint32_t address, uint8_t *buffer, uint32_t length) { flashDevice.callback = NULL; return flashDevice.vTable->readBytes(&flashDevice, address, buffer, length); } -void flashFlush(void) +MMFLASH_CODE void flashFlush(void) { if (flashDevice.vTable->flush) { flashDevice.vTable->flush(&flashDevice); @@ -381,7 +535,7 @@ static void flashConfigurePartitions(void) startSector = 0; #endif -#if defined(CONFIG_IN_EXTERNAL_FLASH) +#if defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH) const uint32_t configSize = EEPROM_SIZE; flashSector_t configSectors = (configSize / flashGeometry->sectorSize); diff --git a/src/main/drivers/flash.h b/src/main/drivers/flash.h index 426f8479fd6..da75304bb6e 100644 --- a/src/main/drivers/flash.h +++ b/src/main/drivers/flash.h @@ -48,6 +48,16 @@ typedef struct flashGeometry_s { uint32_t jedecId; } flashGeometry_t; + +typedef enum { + /* + * When set it indicates the system was booted in memory mapped mode, flash chip is already configured by + * the bootloader and does not need re-configuration. + * When un-set the system was booted normally and the flash chip needs configuration before use. + */ + FLASH_CF_SYSTEM_IS_MEMORY_MAPPED = (1 << 0), +} flashConfigurationFlags_e; + void flashPreInit(const flashConfig_t *flashConfig); bool flashInit(const flashConfig_t *flashConfig); @@ -63,6 +73,10 @@ int flashReadBytes(uint32_t address, uint8_t *buffer, uint32_t length); void flashFlush(void); const flashGeometry_t *flashGetGeometry(void); +void flashMemoryMappedModeDisable(void); +void flashMemoryMappedModeEnable(void); + + // // flash partitioning api // diff --git a/src/main/drivers/flash_impl.h b/src/main/drivers/flash_impl.h index afa0fdd1a6d..6bd88bd8dbb 100644 --- a/src/main/drivers/flash_impl.h +++ b/src/main/drivers/flash_impl.h @@ -22,6 +22,30 @@ * Author: jflyper */ +/* + * Each flash chip should: + * + * * expose a public `identify` method. + * - return true if the driver supports the passed JEDEC ID and false otherwise. + * - configure the `geometry` member of the flashDevice_t or set all `geometry` members to 0 if driver doesn't support the JEDEC ID. + * - configure the `vTable` member, with an appropriate API. + * - configure remaining flashDevice_t members, as appropriate. + * * not reconfigure the bus or flash chip when in memory mapped mode. + * - the firmware is free to do whatever it wants when memory mapped mode is disabled + * - when memory mapped mode is restored, e.g. after saving config to external flash, it should be in + * the same state that firmware found it in before the firmware disabled memory mapped mode. + * + * When memory mapped mode is disabled the following applies to all flash chip drivers uses in a memory mapped system: + * - do not call any methods or use data from the flash chip. i.e. memory mapped code/data is INACCESSIBLE. + * i.e. when saving the config, *ALL* the code to erase a block and write data should be in RAM, + * this includes any `delay` methods. + * - not require the use of use any ISRs - interrupts are disabled during flash access when memory mapped mode is disabled. + * + * When compiling a driver for use in a memory mapped flash system the following applies: + * - the vTable must live in RAM so it can be used when memory mapped mode is disabled. + * - other constant data structures that usually live in flash memory must be stored in RAM. + * - methods used to erase sectors, write data and read data much live in RAM. + */ #pragma once #include "drivers/bus.h" @@ -32,20 +56,30 @@ struct flashVTable_s; typedef enum { FLASHIO_NONE = 0, FLASHIO_SPI, - FLASHIO_QUADSPI + FLASHIO_QUADSPI, + FLASHIO_OCTOSPI, } flashDeviceIoMode_e; typedef struct flashDeviceIO_s { union { + #ifdef USE_FLASH_SPI extDevice_t *dev; // Device interface dependent handle (spi/i2c) - #ifdef USE_QUADSPI + #endif + #ifdef USE_FLASH_QUADSPI QUADSPI_TypeDef *quadSpi; #endif + #ifdef USE_FLASH_OCTOSPI + OCTOSPI_TypeDef *octoSpi; + #endif } handle; flashDeviceIoMode_e mode; } flashDeviceIO_t; typedef struct flashDevice_s { + // + // members to be configured by the flash chip implementation + // + const struct flashVTable_s *vTable; flashGeometry_t geometry; uint32_t currentWriteAddress; @@ -55,21 +89,33 @@ typedef struct flashDevice_s { // when it is definitely ready already. bool couldBeBusy; uint32_t timeoutAt; + + // + // members configured by the flash detection system, read-only from the flash chip implementation's perspective. + // + flashDeviceIO_t io; void (*callback)(uint32_t arg); uint32_t callbackArg; } flashDevice_t; typedef struct flashVTable_s { + void (*configure)(flashDevice_t *fdevice, uint32_t configurationFlags); + bool (*isReady)(flashDevice_t *fdevice); bool (*waitForReady)(flashDevice_t *fdevice); + void (*eraseSector)(flashDevice_t *fdevice, uint32_t address); void (*eraseCompletely)(flashDevice_t *fdevice); + void (*pageProgramBegin)(flashDevice_t *fdevice, uint32_t address, void (*callback)(uint32_t length)); uint32_t (*pageProgramContinue)(flashDevice_t *fdevice, uint8_t const **buffers, uint32_t *bufferSizes, uint32_t bufferCount); void (*pageProgramFinish)(flashDevice_t *fdevice); void (*pageProgram)(flashDevice_t *fdevice, uint32_t address, const uint8_t *data, uint32_t length, void (*callback)(uint32_t length)); + void (*flush)(flashDevice_t *fdevice); + int (*readBytes)(flashDevice_t *fdevice, uint32_t address, uint8_t *buffer, uint32_t length); + const flashGeometry_t *(*getGeometry)(flashDevice_t *fdevice); } flashVTable_t; diff --git a/src/main/drivers/flash_m25p16.c b/src/main/drivers/flash_m25p16.c index dfa10252f1f..c4a24e1bda1 100644 --- a/src/main/drivers/flash_m25p16.c +++ b/src/main/drivers/flash_m25p16.c @@ -195,13 +195,13 @@ static bool m25p16_waitForReady(flashDevice_t *fdevice) * * Returns true if we get valid ident, false if something bad happened like there is no M25P16. */ -bool m25p16_detect(flashDevice_t *fdevice, uint32_t chipID) +bool m25p16_identify(flashDevice_t *fdevice, uint32_t jedecID) { flashGeometry_t *geometry = &fdevice->geometry; uint8_t index; for (index = 0; m25p16FlashConfig[index].jedecID; index++) { - if (m25p16FlashConfig[index].jedecID == chipID) { + if (m25p16FlashConfig[index].jedecID == jedecID) { maxClkSPIHz = m25p16FlashConfig[index].maxClkSPIMHz * 1000000; maxReadClkSPIHz = m25p16FlashConfig[index].maxReadClkSPIMHz * 1000000; geometry->sectors = m25p16FlashConfig[index].sectors; @@ -224,11 +224,32 @@ bool m25p16_detect(flashDevice_t *fdevice, uint32_t chipID) geometry->sectorSize = geometry->pagesPerSector * geometry->pageSize; geometry->totalSize = geometry->sectorSize * geometry->sectors; + fdevice->couldBeBusy = true; // Just for luck we'll assume the chip could be busy even though it isn't specced to be + fdevice->couldBeBusy = true; // Just for luck we'll assume the chip could be busy even though it isn't specced to be + + if (fdevice->io.mode == FLASHIO_SPI) { + fdevice->vTable = &m25p16_vTable; + } +#ifdef USE_QUADSPI + else if (fdevice->io.mode == FLASHIO_QUADSPI) { + fdevice->vTable = &m25p16Qspi_vTable; + } +#endif + return true; +} + +void m25p16_configure(flashDevice_t *fdevice, uint32_t configurationFlags) +{ + if (configurationFlags & FLASH_CF_SYSTEM_IS_MEMORY_MAPPED) { + return; + } + if (fdevice->io.mode == FLASHIO_SPI) { // Adjust the SPI bus clock frequency spiSetClkDivisor(fdevice->io.handle.dev, spiCalculateDivider(maxReadClkSPIHz)); } + flashGeometry_t *geometry = &fdevice->geometry; if (geometry->totalSize > 16 * 1024 * 1024) { fdevice->isLargeFlash = true; @@ -244,20 +265,9 @@ bool m25p16_detect(flashDevice_t *fdevice, uint32_t chipID) } #endif } - - fdevice->couldBeBusy = true; // Just for luck we'll assume the chip could be busy even though it isn't specced to be - - if (fdevice->io.mode == FLASHIO_SPI) { - fdevice->vTable = &m25p16_vTable; - } -#ifdef USE_QUADSPI - else if (fdevice->io.mode == FLASHIO_QUADSPI) { - fdevice->vTable = &m25p16Qspi_vTable; - } -#endif - return true; } + static void m25p16_setCommandAddress(uint8_t *buf, uint32_t address, bool useLongAddress) { if (useLongAddress) { @@ -612,6 +622,7 @@ static const flashGeometry_t* m25p16_getGeometry(flashDevice_t *fdevice) } const flashVTable_t m25p16_vTable = { + .configure = m25p16_configure, .isReady = m25p16_isReady, .waitForReady = m25p16_waitForReady, .eraseSector = m25p16_eraseSector, diff --git a/src/main/drivers/flash_m25p16.h b/src/main/drivers/flash_m25p16.h index fa606a49475..2e0d186d02f 100644 --- a/src/main/drivers/flash_m25p16.h +++ b/src/main/drivers/flash_m25p16.h @@ -22,4 +22,4 @@ #include "flash_impl.h" -bool m25p16_detect(flashDevice_t *fdevice, uint32_t chipID); +bool m25p16_identify(flashDevice_t *fdevice, uint32_t jedecID); diff --git a/src/main/drivers/flash_w25m.c b/src/main/drivers/flash_w25m.c index 9600e54175f..34d19ea21a3 100644 --- a/src/main/drivers/flash_w25m.c +++ b/src/main/drivers/flash_w25m.c @@ -114,10 +114,10 @@ static bool w25m_waitForReady(flashDevice_t *fdevice) return true; } -bool w25m_detect(flashDevice_t *fdevice, uint32_t chipID) +bool w25m_identify(flashDevice_t *fdevice, uint32_t jedecID) { - switch (chipID) { + switch (jedecID) { #ifdef USE_FLASH_W25M512 case JEDEC_ID_WINBOND_W25M512: // W25Q256 x 2 @@ -127,7 +127,7 @@ bool w25m_detect(flashDevice_t *fdevice, uint32_t chipID) w25m_dieSelect(fdevice->io.handle.dev, die); dieDevice[die].io.handle.dev = fdevice->io.handle.dev; dieDevice[die].io.mode = fdevice->io.mode; - m25p16_detect(&dieDevice[die], JEDEC_ID_WINBOND_W25Q256); + m25p16_identify(&dieDevice[die], JEDEC_ID_WINBOND_W25Q256); } fdevice->geometry.flashType = FLASH_TYPE_NOR; @@ -143,7 +143,7 @@ bool w25m_detect(flashDevice_t *fdevice, uint32_t chipID) w25m_dieSelect(fdevice->io.handle.dev, die); dieDevice[die].io.handle.dev = fdevice->io.handle.dev; dieDevice[die].io.mode = fdevice->io.mode; - w25n01g_detect(&dieDevice[die], JEDEC_ID_WINBOND_W25N01GV); + w25n01g_identify(&dieDevice[die], JEDEC_ID_WINBOND_W25N01GV); } fdevice->geometry.flashType = FLASH_TYPE_NAND; @@ -170,6 +170,14 @@ bool w25m_detect(flashDevice_t *fdevice, uint32_t chipID) return true; } +void w25m_configure(flashDevice_t *fdevice, uint32_t configurationFlags) +{ + for (int dieNumber = 0 ; dieNumber < dieCount ; dieNumber++) { + w25m_dieSelect(fdevice->io.handle.dev, dieNumber); + dieDevice[dieNumber].vTable->configure(&dieDevice[dieNumber], configurationFlags); + } +} + void w25m_eraseSector(flashDevice_t *fdevice, uint32_t address) { int dieNumber = address / dieSize; @@ -255,6 +263,7 @@ const flashGeometry_t* w25m_getGeometry(flashDevice_t *fdevice) } static const flashVTable_t w25m_vTable = { + .configure = w25m_configure, .isReady = w25m_isReady, .waitForReady = w25m_waitForReady, .eraseSector = w25m_eraseSector, diff --git a/src/main/drivers/flash_w25m.h b/src/main/drivers/flash_w25m.h index f778a07eb61..5d35df4a4bf 100644 --- a/src/main/drivers/flash_w25m.h +++ b/src/main/drivers/flash_w25m.h @@ -22,4 +22,4 @@ #include "flash_impl.h" -bool w25m_detect(flashDevice_t *fdevice, uint32_t chipID); +bool w25m_identify(flashDevice_t *fdevice, uint32_t jedecID); diff --git a/src/main/drivers/flash_w25n01g.c b/src/main/drivers/flash_w25n01g.c index b69f8f18451..87c3f604194 100644 --- a/src/main/drivers/flash_w25n01g.c +++ b/src/main/drivers/flash_w25n01g.c @@ -124,9 +124,9 @@ #define W25N01G_TIMEOUT_RESET_MS 500 // tRSTmax = 500ms // Sizes (in bits) -#define W28N01G_STATUS_REGISTER_SIZE 8 -#define W28N01G_STATUS_PAGE_ADDRESS_SIZE 16 -#define W28N01G_STATUS_COLUMN_ADDRESS_SIZE 16 +#define W25N01G_STATUS_REGISTER_SIZE 8 +#define W25N01G_STATUS_PAGE_ADDRESS_SIZE 16 +#define W25N01G_STATUS_COLUMN_ADDRESS_SIZE 16 typedef struct bblut_s { uint16_t pba; @@ -188,7 +188,7 @@ static void w25n01g_performCommandWithPageAddress(flashDeviceIO_t *io, uint8_t c else if (io->mode == FLASHIO_QUADSPI) { QUADSPI_TypeDef *quadSpi = io->handle.quadSpi; - quadSpiInstructionWithAddress1LINE(quadSpi, command, 0, pageAddress & 0xffff, W28N01G_STATUS_PAGE_ADDRESS_SIZE + 8); + quadSpiInstructionWithAddress1LINE(quadSpi, command, 0, pageAddress & 0xffff, W25N01G_STATUS_PAGE_ADDRESS_SIZE + 8); } #endif } @@ -221,8 +221,8 @@ static uint8_t w25n01g_readRegister(flashDeviceIO_t *io, uint8_t reg) QUADSPI_TypeDef *quadSpi = io->handle.quadSpi; - uint8_t in[1]; - quadSpiReceiveWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_READ_STATUS_REG, 0, reg, W28N01G_STATUS_REGISTER_SIZE, in, sizeof(in)); + uint8_t in[W25N01G_STATUS_REGISTER_SIZE / 8]; + quadSpiReceiveWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_READ_STATUS_REG, 0, reg, W25N01G_STATUS_REGISTER_SIZE, in, sizeof(in)); return in[0]; } @@ -253,7 +253,7 @@ static void w25n01g_writeRegister(flashDeviceIO_t *io, uint8_t reg, uint8_t data else if (io->mode == FLASHIO_QUADSPI) { QUADSPI_TypeDef *quadSpi = io->handle.quadSpi; - quadSpiTransmitWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_WRITE_STATUS_REG, 0, reg, W28N01G_STATUS_REGISTER_SIZE, &data, 1); + quadSpiTransmitWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_WRITE_STATUS_REG, 0, reg, W25N01G_STATUS_REGISTER_SIZE, &data, 1); } #endif } @@ -311,18 +311,11 @@ static void w25n01g_writeEnable(flashDevice_t *fdevice) fdevice->couldBeBusy = true; } -/** - * Read chip identification and geometry information (into global `geometry`). - * - * Returns true if we get valid ident, false if something bad happened like there is no M25P16. - */ const flashVTable_t w25n01g_vTable; -static void w25n01g_deviceInit(flashDevice_t *flashdev); - -bool w25n01g_detect(flashDevice_t *fdevice, uint32_t chipID) +bool w25n01g_identify(flashDevice_t *fdevice, uint32_t jedecID) { - switch (chipID) { + switch (jedecID) { case JEDEC_ID_WINBOND_W25N01GV: fdevice->geometry.sectors = 1024; // Blocks fdevice->geometry.pagesPerSector = 64; // Pages/Blocks @@ -348,6 +341,19 @@ bool w25n01g_detect(flashDevice_t *fdevice, uint32_t chipID) W25N01G_BB_MANAGEMENT_START_BLOCK + W25N01G_BB_MANAGEMENT_BLOCKS - 1); fdevice->couldBeBusy = true; // Just for luck we'll assume the chip could be busy even though it isn't specced to be + fdevice->vTable = &w25n01g_vTable; + + return true; +} + +static void w25n01g_deviceInit(flashDevice_t *flashdev); + + +void w25n01g_configure(flashDevice_t *fdevice, uint32_t configurationFlags) +{ + if (configurationFlags & FLASH_CF_SYSTEM_IS_MEMORY_MAPPED) { + return; + } w25n01g_deviceReset(fdevice); @@ -366,10 +372,6 @@ bool w25n01g_detect(flashDevice_t *fdevice, uint32_t chipID) // If it ever run out, the device becomes unusable. w25n01g_deviceInit(fdevice); - - fdevice->vTable = &w25n01g_vTable; - - return true; } /** @@ -422,7 +424,7 @@ static void w25n01g_programDataLoad(flashDevice_t *fdevice, uint16_t columnAddre else if (fdevice->io.mode == FLASHIO_QUADSPI) { QUADSPI_TypeDef *quadSpi = fdevice->io.handle.quadSpi; - quadSpiTransmitWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_PROGRAM_DATA_LOAD, 0, columnAddress, W28N01G_STATUS_COLUMN_ADDRESS_SIZE, data, length); + quadSpiTransmitWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_PROGRAM_DATA_LOAD, 0, columnAddress, W25N01G_STATUS_COLUMN_ADDRESS_SIZE, data, length); } #endif @@ -453,7 +455,7 @@ static void w25n01g_randomProgramDataLoad(flashDevice_t *fdevice, uint16_t colum else if (fdevice->io.mode == FLASHIO_QUADSPI) { QUADSPI_TypeDef *quadSpi = fdevice->io.handle.quadSpi; - quadSpiTransmitWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_RANDOM_PROGRAM_DATA_LOAD, 0, columnAddress, W28N01G_STATUS_COLUMN_ADDRESS_SIZE, data, length); + quadSpiTransmitWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_RANDOM_PROGRAM_DATA_LOAD, 0, columnAddress, W25N01G_STATUS_COLUMN_ADDRESS_SIZE, data, length); } #endif @@ -696,8 +698,8 @@ int w25n01g_readBytes(flashDevice_t *fdevice, uint32_t address, uint8_t *buffer, else if (fdevice->io.mode == FLASHIO_QUADSPI) { QUADSPI_TypeDef *quadSpi = fdevice->io.handle.quadSpi; - //quadSpiReceiveWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_READ_DATA, 8, column, W28N01G_STATUS_COLUMN_ADDRESS_SIZE, buffer, length); - quadSpiReceiveWithAddress4LINES(quadSpi, W25N01G_INSTRUCTION_FAST_READ_QUAD_OUTPUT, 8, column, W28N01G_STATUS_COLUMN_ADDRESS_SIZE, buffer, length); + //quadSpiReceiveWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_READ_DATA, 8, column, W25N01G_STATUS_COLUMN_ADDRESS_SIZE, buffer, length); + quadSpiReceiveWithAddress4LINES(quadSpi, W25N01G_INSTRUCTION_FAST_READ_QUAD_OUTPUT, 8, column, W25N01G_STATUS_COLUMN_ADDRESS_SIZE, buffer, length); } #endif @@ -765,7 +767,7 @@ int w25n01g_readExtensionBytes(flashDevice_t *fdevice, uint32_t address, uint8_t else if (fdevice->io.mode == FLASHIO_QUADSPI) { QUADSPI_TypeDef *quadSpi = fdevice->io.handle.quadSpi; - quadSpiReceiveWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_READ_DATA, 8, column, W28N01G_STATUS_COLUMN_ADDRESS_SIZE, buffer, length); + quadSpiReceiveWithAddress1LINE(quadSpi, W25N01G_INSTRUCTION_READ_DATA, 8, column, W25N01G_STATUS_COLUMN_ADDRESS_SIZE, buffer, length); } #endif @@ -785,6 +787,7 @@ const flashGeometry_t* w25n01g_getGeometry(flashDevice_t *fdevice) } const flashVTable_t w25n01g_vTable = { + .configure = w25n01g_configure, .isReady = w25n01g_isReady, .waitForReady = w25n01g_waitForReady, .eraseSector = w25n01g_eraseSector, diff --git a/src/main/drivers/flash_w25n01g.h b/src/main/drivers/flash_w25n01g.h index 219a3fa4660..ef80c7b6e32 100644 --- a/src/main/drivers/flash_w25n01g.h +++ b/src/main/drivers/flash_w25n01g.h @@ -25,4 +25,4 @@ // JEDEC ID #define JEDEC_ID_WINBOND_W25N01GV 0xEFAA21 -bool w25n01g_detect(flashDevice_t *fdevice, uint32_t chipID); +bool w25n01g_identify(flashDevice_t *fdevice, uint32_t jedecID); diff --git a/src/main/drivers/flash_w25q128fv.c b/src/main/drivers/flash_w25q128fv.c index d005f8e8eeb..e8fb03bf134 100644 --- a/src/main/drivers/flash_w25q128fv.c +++ b/src/main/drivers/flash_w25q128fv.c @@ -25,7 +25,7 @@ #include "platform.h" -#if defined(USE_FLASH_W25Q128FV) && defined(USE_QUADSPI) +#if defined(USE_FLASH_W25Q128FV) && (defined(USE_QUADSPI) || defined(USE_OCTOSPI)) #define USE_FLASH_WRITES_USING_4LINES #define USE_FLASH_READS_USING_4LINES @@ -33,15 +33,19 @@ #include "build/debug.h" #include "common/utils.h" +#include "drivers/time.h" #include "drivers/flash.h" #include "drivers/flash_impl.h" #include "drivers/flash_w25q128fv.h" #include "drivers/bus_quadspi.h" +#include "drivers/bus_octospi.h" // JEDEC ID -#define JEDEC_ID_WINBOND_W25Q128FV_SPI 0xEF4018 -#define JEDEC_ID_WINBOND_W25Q128FV_QUADSPI 0xEF6018 -#define JEDEC_ID_WINBOND_W25Q128JV_QUADSPI 0xEF7018 +#define JEDEC_ID_WINBOND_W25Q128FV_SPI 0xEF4018 +#define JEDEC_ID_WINBOND_W25Q128FV_QUADSPI 0xEF6018 +#define JEDEC_ID_WINBOND_W25Q128JV_QUADSPI 0xEF7018 +#define JEDEC_ID_WINBOND_W25Q16JV_SPI 0xEF4015 +#define JEDEC_ID_WINBOND_W25Q16JV_DTR_SPI 0xEF7015 // Device size parameters #define W25Q128FV_PAGE_SIZE 2048 @@ -90,14 +94,14 @@ //#define W25Q128FV_INSTRUCTION_WRITE_DISABLE 0x04 //#define W25Q128FV_INSTRUCTION_PAGE_PROGRAM 0x02 -// Timings (2ms minimum to avoid 1 tick advance in consecutive calls to HAL_GetTick). -#define W25Q128FV_TIMEOUT_PAGE_READ_MS 4 -#define W25Q128FV_TIMEOUT_RESET_MS 2 // tRST = 30us +// Values from W25Q128FV Datasheet Rev L. +#define W25Q128FV_TIMEOUT_PAGE_READ_MS 1 // No minimum specified in datasheet +#define W25Q128FV_TIMEOUT_RESET_MS 1 // tRST = 30us #define W25Q128FV_TIMEOUT_BLOCK_ERASE_64KB_MS 2000 // tBE2max = 2000ms, tBE2typ = 150ms #define W25Q128FV_TIMEOUT_CHIP_ERASE_MS (200 * 1000) // tCEmax 200s, tCEtyp = 40s -#define W25Q128FV_TIMEOUT_PAGE_PROGRAM_MS 2 // tPPmax = 700us, tPPtyp = 250us -#define W25Q128FV_TIMEOUT_WRITE_ENABLE_MS 2 +#define W25Q128FV_TIMEOUT_PAGE_PROGRAM_MS 3 // tPPmax = 3ms, tPPtyp = 0.7ms +#define W25Q128FV_TIMEOUT_WRITE_ENABLE_MS 1 typedef enum { @@ -115,45 +119,71 @@ w25q128fvState_t w25q128fvState = { 0 }; static bool w25q128fv_waitForReady(flashDevice_t *fdevice); static void w25q128fv_waitForTimeout(flashDevice_t *fdevice); -static void w25q128fv_setTimeout(flashDevice_t *fdevice, uint32_t timeoutMillis) +MMFLASH_CODE static void w25q128fv_setTimeout(flashDevice_t *fdevice, timeMs_t timeoutMillis) { - uint32_t now = HAL_GetTick(); - fdevice->timeoutAt = now + timeoutMillis; + timeMs_t nowMs = microsISR() / 1000; + fdevice->timeoutAt = nowMs + timeoutMillis; } -static void w25q128fv_performOneByteCommand(flashDeviceIO_t *io, uint8_t command) +MMFLASH_CODE static void w25q128fv_performOneByteCommand(flashDeviceIO_t *io, uint8_t command) { +#if defined(USE_QUADSPI) QUADSPI_TypeDef *quadSpi = io->handle.quadSpi; quadSpiTransmit1LINE(quadSpi, command, 0, NULL, 0); +#elif defined(USE_OCTOSPI) + OCTOSPI_TypeDef *octoSpi = io->handle.octoSpi; + octoSpiTransmit1LINE(octoSpi, command, 0, NULL, 0); +#endif + } -static void w25q128fv_performCommandWithAddress(flashDeviceIO_t *io, uint8_t command, uint32_t address) +MMFLASH_CODE static void w25q128fv_performCommandWithAddress(flashDeviceIO_t *io, uint8_t command, uint32_t address) { +#if defined(USE_QUADSPI) QUADSPI_TypeDef *quadSpi = io->handle.quadSpi; quadSpiInstructionWithAddress1LINE(quadSpi, command, 0, address & 0xffffff, W25Q128FV_ADDRESS_BITS); +#elif defined(USE_OCTOSPI) + OCTOSPI_TypeDef *octoSpi = io->handle.octoSpi; + + octoSpiInstructionWithAddress1LINE(octoSpi, command, 0, address & 0xffffff, W25Q128FV_ADDRESS_BITS); +#endif } -static void w25q128fv_writeEnable(flashDevice_t *fdevice) +MMFLASH_CODE static void w25q128fv_writeEnable(flashDevice_t *fdevice) { w25q128fv_performOneByteCommand(&fdevice->io, W25Q128FV_INSTRUCTION_WRITE_ENABLE); } -static uint8_t w25q128fv_readRegister(flashDeviceIO_t *io, uint8_t command) +MMFLASH_CODE static uint8_t w25q128fv_readRegister(flashDeviceIO_t *io, uint8_t command) { + uint8_t in[W25Q128FV_STATUS_REGISTER_BITS / 8] = { 0 }; +#if defined(USE_QUADSPI) QUADSPI_TypeDef *quadSpi = io->handle.quadSpi; - uint8_t in[1]; quadSpiReceive1LINE(quadSpi, command, 0, in, W25Q128FV_STATUS_REGISTER_BITS / 8); +#elif defined(USE_OCTOSPI) + OCTOSPI_TypeDef *octoSpi = io->handle.octoSpi; + + octoSpiReceive1LINE(octoSpi, command, 0, in, W25Q128FV_STATUS_REGISTER_BITS / 8); +#endif + return in[0]; } static void w25q128fv_writeRegister(flashDeviceIO_t *io, uint8_t command, uint8_t data) { +#if defined(USE_QUADSPI) QUADSPI_TypeDef *quadSpi = io->handle.quadSpi; - quadSpiTransmit1LINE(quadSpi, command, 0, &data, W25Q128FV_STATUS_REGISTER_BITS / 8); + quadSpiTransmit1LINE(quadSpi, command, 0, &data, W25Q128FV_STATUS_REGISTER_BITS / 8); +#elif defined(USE_OCTOSPI) + OCTOSPI_TypeDef *octoSpi = io->handle.octoSpi; + + octoSpiTransmit1LINE(octoSpi, command, 0, &data, W25Q128FV_STATUS_REGISTER_BITS / 8); +#endif + } static void w25q128fv_deviceReset(flashDevice_t *fdevice) @@ -203,7 +233,7 @@ static void w25q128fv_deviceReset(flashDevice_t *fdevice) #endif } -bool w25q128fv_isReady(flashDevice_t *fdevice) +MMFLASH_CODE bool w25q128fv_isReady(flashDevice_t *fdevice) { uint8_t status = w25q128fv_readRegister(&fdevice->io, W25Q128FV_INSTRUCTION_READ_STATUS1_REG); @@ -212,7 +242,7 @@ bool w25q128fv_isReady(flashDevice_t *fdevice) return !busy; } -static bool w25q128fv_isWritable(flashDevice_t *fdevice) +MMFLASH_CODE static bool w25q128fv_isWritable(flashDevice_t *fdevice) { uint8_t status = w25q128fv_readRegister(&fdevice->io, W25Q128FV_INSTRUCTION_READ_STATUS1_REG); @@ -221,23 +251,23 @@ static bool w25q128fv_isWritable(flashDevice_t *fdevice) return writable; } -bool w25q128fv_hasTimedOut(flashDevice_t *fdevice) +MMFLASH_CODE bool w25q128fv_hasTimedOut(flashDevice_t *fdevice) { - uint32_t now = HAL_GetTick(); - if (cmp32(now, fdevice->timeoutAt) >= 0) { + uint32_t nowMs = microsISR() / 1000; + if (cmp32(nowMs, fdevice->timeoutAt) >= 0) { return true; } return false; } -void w25q128fv_waitForTimeout(flashDevice_t *fdevice) +MMFLASH_CODE void w25q128fv_waitForTimeout(flashDevice_t *fdevice) { while (!w25q128fv_hasTimedOut(fdevice)) { } fdevice->timeoutAt = 0; } -bool w25q128fv_waitForReady(flashDevice_t *fdevice) +MMFLASH_CODE bool w25q128fv_waitForReady(flashDevice_t *fdevice) { bool ready = true; while (!w25q128fv_isReady(fdevice)) { @@ -255,15 +285,24 @@ const flashVTable_t w25q128fv_vTable; static void w25q128fv_deviceInit(flashDevice_t *flashdev); -bool w25q128fv_detect(flashDevice_t *fdevice, uint32_t chipID) +MMFLASH_CODE_NOINLINE bool w25q128fv_identify(flashDevice_t *fdevice, uint32_t jedecID) { - switch (chipID) { + switch (jedecID) { case JEDEC_ID_WINBOND_W25Q128FV_SPI: case JEDEC_ID_WINBOND_W25Q128FV_QUADSPI: case JEDEC_ID_WINBOND_W25Q128JV_QUADSPI: fdevice->geometry.sectors = 256; fdevice->geometry.pagesPerSector = 256; fdevice->geometry.pageSize = 256; + // = 16777216 128MBit 16MB + break; + + case JEDEC_ID_WINBOND_W25Q16JV_DTR_SPI: + case JEDEC_ID_WINBOND_W25Q16JV_SPI: + fdevice->geometry.sectors = 32; + fdevice->geometry.pagesPerSector = 256; + fdevice->geometry.pageSize = 256; + // = 2097152 16MBit 2MB break; default: @@ -276,7 +315,9 @@ bool w25q128fv_detect(flashDevice_t *fdevice, uint32_t chipID) } // use the chip id to determine the initial interface mode on cold-boot. - switch (chipID) { + switch (jedecID) { + case JEDEC_ID_WINBOND_W25Q16JV_SPI: + case JEDEC_ID_WINBOND_W25Q16JV_DTR_SPI: case JEDEC_ID_WINBOND_W25Q128FV_SPI: w25q128fvState.initialMode = INITIAL_MODE_SPI; break; @@ -294,18 +335,24 @@ bool w25q128fv_detect(flashDevice_t *fdevice, uint32_t chipID) fdevice->geometry.sectorSize = fdevice->geometry.pagesPerSector * fdevice->geometry.pageSize; fdevice->geometry.totalSize = fdevice->geometry.sectorSize * fdevice->geometry.sectors; - w25q128fv_deviceReset(fdevice); - - w25q128fv_deviceInit(fdevice); - fdevice->vTable = &w25q128fv_vTable; return true; } -static void w25q128fv_eraseSector(flashDevice_t *fdevice, uint32_t address) +void w25q128fv_configure(flashDevice_t *fdevice, uint32_t configurationFlags) { + if (configurationFlags & FLASH_CF_SYSTEM_IS_MEMORY_MAPPED) { + return; + } + w25q128fv_deviceReset(fdevice); + + w25q128fv_deviceInit(fdevice); +} + +MMFLASH_CODE static void w25q128fv_eraseSector(flashDevice_t *fdevice, uint32_t address) +{ w25q128fv_waitForReady(fdevice); w25q128fv_writeEnable(fdevice); @@ -326,17 +373,26 @@ static void w25q128fv_eraseCompletely(flashDevice_t *fdevice) w25q128fv_setTimeout(fdevice, W25Q128FV_TIMEOUT_CHIP_ERASE_MS); } - -static void w25q128fv_loadProgramData(flashDevice_t *fdevice, const uint8_t *data, int length) +MMFLASH_CODE static void w25q128fv_loadProgramData(flashDevice_t *fdevice, const uint8_t *data, int length) { w25q128fv_waitForReady(fdevice); +#if defined(USE_QUADSPI) QUADSPI_TypeDef *quadSpi = fdevice->io.handle.quadSpi; #ifdef USE_FLASH_WRITES_USING_4LINES quadSpiTransmitWithAddress4LINES(quadSpi, W25Q128FV_INSTRUCTION_QUAD_PAGE_PROGRAM, 0, w25q128fvState.currentWriteAddress, W25Q128FV_ADDRESS_BITS, data, length); #else quadSpiTransmitWithAddress1LINE(quadSpi, W25Q128FV_INSTRUCTION_PAGE_PROGRAM, 0, w25q128fvState.currentWriteAddress, W25Q128FV_ADDRESS_BITS, data, length); +#endif +#elif defined(USE_OCTOSPI) + OCTOSPI_TypeDef *octoSpi = fdevice->io.handle.octoSpi; + +#ifdef USE_FLASH_WRITES_USING_4LINES + octoSpiTransmitWithAddress4LINES(octoSpi, W25Q128FV_INSTRUCTION_QUAD_PAGE_PROGRAM, 0, w25q128fvState.currentWriteAddress, W25Q128FV_ADDRESS_BITS, data, length); +#else + octoSpiTransmitWithAddress1LINE(octoSpi, W25Q128FV_INSTRUCTION_PAGE_PROGRAM, 0, w25q128fvState.currentWriteAddress, W25Q128FV_ADDRESS_BITS, data, length); +#endif #endif w25q128fv_setTimeout(fdevice, W25Q128FV_TIMEOUT_PAGE_PROGRAM_MS); @@ -344,13 +400,13 @@ static void w25q128fv_loadProgramData(flashDevice_t *fdevice, const uint8_t *dat w25q128fvState.currentWriteAddress += length; } -static void w25q128fv_pageProgramBegin(flashDevice_t *fdevice, uint32_t address, void (*callback)(uint32_t length)) +MMFLASH_CODE static void w25q128fv_pageProgramBegin(flashDevice_t *fdevice, uint32_t address, void (*callback)(uint32_t length)) { fdevice->callback = callback; w25q128fvState.currentWriteAddress = address; } -static uint32_t w25q128fv_pageProgramContinue(flashDevice_t *fdevice, uint8_t const **buffers, uint32_t *bufferSizes, uint32_t bufferCount) +MMFLASH_CODE static uint32_t w25q128fv_pageProgramContinue(flashDevice_t *fdevice, uint8_t const **buffers, uint32_t *bufferSizes, uint32_t bufferCount) { for (uint32_t i = 0; i < bufferCount; i++) { w25q128fv_waitForReady(fdevice); @@ -374,35 +430,45 @@ static uint32_t w25q128fv_pageProgramContinue(flashDevice_t *fdevice, uint8_t co return fdevice->callbackArg; } -static void w25q128fv_pageProgramFinish(flashDevice_t *fdevice) +MMFLASH_CODE static void w25q128fv_pageProgramFinish(flashDevice_t *fdevice) { UNUSED(fdevice); } -static void w25q128fv_pageProgram(flashDevice_t *fdevice, uint32_t address, const uint8_t *data, uint32_t length, void (*callback)(uint32_t length)) +MMFLASH_CODE static void w25q128fv_pageProgram(flashDevice_t *fdevice, uint32_t address, const uint8_t *data, uint32_t length, void (*callback)(uint32_t length)) { w25q128fv_pageProgramBegin(fdevice, address, callback); w25q128fv_pageProgramContinue(fdevice, &data, &length, 1); w25q128fv_pageProgramFinish(fdevice); } -void w25q128fv_flush(flashDevice_t *fdevice) +MMFLASH_CODE void w25q128fv_flush(flashDevice_t *fdevice) { UNUSED(fdevice); } -static int w25q128fv_readBytes(flashDevice_t *fdevice, uint32_t address, uint8_t *buffer, uint32_t length) +MMFLASH_CODE static int w25q128fv_readBytes(flashDevice_t *fdevice, uint32_t address, uint8_t *buffer, uint32_t length) { if (!w25q128fv_waitForReady(fdevice)) { return 0; } +#if defined(USE_QUADSPI) QUADSPI_TypeDef *quadSpi = fdevice->io.handle.quadSpi; #ifdef USE_FLASH_READS_USING_4LINES bool status = quadSpiReceiveWithAddress4LINES(quadSpi, W25Q128FV_INSTRUCTION_FAST_READ_QUAD_OUTPUT, 8, address, W25Q128FV_ADDRESS_BITS, buffer, length); #else bool status = quadSpiReceiveWithAddress1LINE(quadSpi, W25Q128FV_INSTRUCTION_FAST_READ, 8, address, W25Q128FV_ADDRESS_BITS, buffer, length); #endif +#elif defined(USE_OCTOSPI) + OCTOSPI_TypeDef *octoSpi = fdevice->io.handle.octoSpi; +#ifdef USE_FLASH_READS_USING_4LINES + bool status = octoSpiReceiveWithAddress4LINES(octoSpi, W25Q128FV_INSTRUCTION_FAST_READ_QUAD_OUTPUT, 8, address, W25Q128FV_ADDRESS_BITS, buffer, length); +#else + bool status = octoSpiReceiveWithAddress1LINE(octoSpi, W25Q128FV_INSTRUCTION_FAST_READ, 8, address, W25Q128FV_ADDRESS_BITS, buffer, length); +#endif +#endif + w25q128fv_setTimeout(fdevice, W25Q128FV_TIMEOUT_PAGE_READ_MS); if (!status) { @@ -412,14 +478,13 @@ static int w25q128fv_readBytes(flashDevice_t *fdevice, uint32_t address, uint8_t return length; } - - const flashGeometry_t* w25q128fv_getGeometry(flashDevice_t *fdevice) { return &fdevice->geometry; } -const flashVTable_t w25q128fv_vTable = { +MMFLASH_DATA const flashVTable_t w25q128fv_vTable = { + .configure = w25q128fv_configure, .isReady = w25q128fv_isReady, .waitForReady = w25q128fv_waitForReady, .eraseSector = w25q128fv_eraseSector, diff --git a/src/main/drivers/flash_w25q128fv.h b/src/main/drivers/flash_w25q128fv.h index d28de244faf..4a82c0d9984 100644 --- a/src/main/drivers/flash_w25q128fv.h +++ b/src/main/drivers/flash_w25q128fv.h @@ -3,5 +3,5 @@ #ifdef USE_FLASH_W25Q128FV -bool w25q128fv_detect(flashDevice_t *fdevice, uint32_t chipID); +bool w25q128fv_identify(flashDevice_t *fdevice, uint32_t jedecID); #endif diff --git a/src/main/drivers/system.c b/src/main/drivers/system.c index b17522d848a..ec16015e1ee 100644 --- a/src/main/drivers/system.c +++ b/src/main/drivers/system.c @@ -103,7 +103,7 @@ void SysTick_Handler(void) // Return system uptime in microseconds (rollover in 70minutes) -uint32_t microsISR(void) +MMFLASH_CODE_NOINLINE uint32_t microsISR(void) { register uint32_t ms, pending, cycle_cnt; @@ -262,7 +262,7 @@ void failureMode(failureMode_e mode) void initialiseMemorySections(void) { #ifdef USE_ITCM_RAM - /* Load functions into ITCM RAM */ + /* Load fast-functions into ITCM RAM */ extern uint8_t tcm_code_start; extern uint8_t tcm_code_end; extern uint8_t tcm_code; @@ -284,6 +284,15 @@ void initialiseMemorySections(void) extern uint8_t _sfastram_idata; memcpy(&_sfastram_data, &_sfastram_idata, (size_t) (&_efastram_data - &_sfastram_data)); #endif + +#ifdef USE_RAM_CODE + /* Load slow-functions into ITCM RAM */ + extern uint8_t ram_code_start; + extern uint8_t ram_code_end; + extern uint8_t ram_code; + memcpy(&ram_code_start, &ram_code, (size_t) (&ram_code_end - &ram_code_start)); +#endif + } #ifdef STM32H7 diff --git a/src/main/drivers/system.h b/src/main/drivers/system.h index 27609283840..21b1719c67b 100644 --- a/src/main/drivers/system.h +++ b/src/main/drivers/system.h @@ -73,6 +73,10 @@ uint32_t getCycleCounter(void); void systemProcessResetReason(void); #endif +// memory +void memoryMappedModeInit(void); +bool isMemoryMappedModeEnabledOnBoot(void); + void initialiseMemorySections(void); #ifdef STM32H7 void initialiseD2MemorySections(void); diff --git a/src/main/drivers/system_stm32h7xx.c b/src/main/drivers/system_stm32h7xx.c index 49f96f831be..8f4f8ce79b9 100644 --- a/src/main/drivers/system_stm32h7xx.c +++ b/src/main/drivers/system_stm32h7xx.c @@ -62,6 +62,41 @@ bool isMPUSoftReset(void) return false; } +#if defined(USE_FLASH_MEMORY_MAPPED) + +/* + * Memory mapped targets use a bootloader which enables memory mapped mode before running the firmware directly from external flash. + * Code running from external flash, i.e. most of the firmware, must not disable peripherals or reconfigure pins used by the CPU to access the flash chip. + * Refer to reference manuals and linker scripts for addresses of memory mapped regions. + * STM32H830 - RM0468 "Table 6. Memory map and default device memory area attributes" + * + * If the config is also stored on the same flash chip that code is running from then VERY special care must be taken when detecting the flash chip + * and when writing an updated config back to the flash. + */ + +static bool memoryMappedModeEnabledOnBoot = false; + +bool isMemoryMappedModeEnabledOnBoot(void) +{ + return memoryMappedModeEnabledOnBoot; +} + +void memoryMappedModeInit(void) +{ +#if defined(STM32H730xx) || defined(STM32H723xx) + // Smaller MCU packages have ONE OCTOSPI interface which supports memory mapped mode. + memoryMappedModeEnabledOnBoot = READ_BIT(OCTOSPI1->CR, OCTOSPI_CR_FMODE) == OCTOSPI_CR_FMODE; +#else +#error No Memory Mapped implementation on current MCU. +#endif +} +#else +bool isMemoryMappedModeEnabledOnBoot(void) +{ + return false; +} +#endif + void systemInit(void) { // Configure NVIC preempt/priority groups diff --git a/src/main/fc/init.c b/src/main/fc/init.c index 3ab56faf261..f6aa8f59b1d 100644 --- a/src/main/fc/init.c +++ b/src/main/fc/init.c @@ -47,6 +47,7 @@ #include "drivers/adc.h" #include "drivers/bus.h" #include "drivers/bus_i2c.h" +#include "drivers/bus_octospi.h" #include "drivers/bus_quadspi.h" #include "drivers/bus_spi.h" #include "drivers/buttons.h" @@ -195,8 +196,7 @@ void busSwitchInit(void) } #endif - -static void configureSPIAndQuadSPI(void) +static void configureSPIBusses(void) { #ifdef USE_SPI spiPinConfigure(spiPinConfig(0)); @@ -226,7 +226,10 @@ static void configureSPIAndQuadSPI(void) spiInit(SPIDEV_6); #endif #endif // USE_SPI +} +static void configureQuadSPIBusses(void) +{ #ifdef USE_QUADSPI quadSpiPinConfigure(quadSpiConfig(0)); @@ -236,6 +239,15 @@ static void configureSPIAndQuadSPI(void) #endif // USE_QUAD_SPI } +static void configureOctoSPIBusses(void) +{ +#ifdef USE_OCTOSPI +#ifdef USE_OCTOSPI_DEVICE_1 + octoSpiInit(OCTOSPIDEV_1); +#endif +#endif +} + #ifdef USE_SDCARD static void sdCardAndFSInit(void) { @@ -291,9 +303,10 @@ void init(void) #endif enum { - FLASH_INIT_ATTEMPTED = (1 << 0), - SD_INIT_ATTEMPTED = (1 << 1), - SPI_AND_QSPI_INIT_ATTEMPTED = (1 << 2), + FLASH_INIT_ATTEMPTED = (1 << 0), + SD_INIT_ATTEMPTED = (1 << 1), + SPI_BUSSES_INIT_ATTEMPTED = (1 << 2), + QUAD_OCTO_SPI_BUSSES_INIT_ATTEMPTED = (1 << 3), }; uint8_t initFlags = 0; @@ -303,19 +316,17 @@ void init(void) // Config in sdcard presents an issue with pin configuration since the pin and sdcard configs for the // sdcard are in the config which is on the sdcard which we can't read yet! // - // FIXME We need to add configuration somewhere, e.g. bootloader image or reserved flash area, that can be read by the firmware. - // it's currently possible for the firmware resource allocation to be wrong after the config is loaded if the user changes the settings. - // This would cause undefined behaviour once the config is loaded. so for now, users must NOT change sdio/spi configs needed for - // the system to boot and/or to save the config. + // FIXME For now, users must NOT change flash/pin configs needed for the system to boot and/or to save the config. + // One possible solution is to lock the pins for the flash chip so they cannot be modified post-boot. // - // note that target specific SDCARD/SDIO/SPI/QUADSPI configs are + // note that target specific SDCARD/SDIO/SPI/QUADSPI/OCTOSPI configs are // also not supported in USE_TARGET_CONFIG/targetConfigure() when using CONFIG_IN_SDCARD. // // // IMPORTANT: all default flash and pin configurations must be valid for the target after pgResetAll() is called. - // Target designers must ensure other devices connected the same SPI/QUADSPI interface as the flash chip do not - // cause communication issues with the flash chip. e.g. use external pullups on SPI/QUADSPI CS lines. + // Target designers must ensure other devices connected the same SPI/QUADSPI/OCTOSPI interface as the flash chip do not + // cause communication issues with the flash chip. e.g. use external pullups on SPI/QUADSPI/OCTOSPI CS lines. // #ifdef TARGET_BUS_INIT @@ -325,8 +336,8 @@ void init(void) pgResetAll(); #ifdef USE_SDCARD_SPI - configureSPIAndQuadSPI(); - initFlags |= SPI_AND_QSPI_INIT_ATTEMPTED; + configureSPIBusses(); + initFlags |= SPI_BUSSES_INIT_ATTEMPTED; #endif sdCardAndFSInit(); @@ -346,37 +357,42 @@ void init(void) #endif // CONFIG_IN_SDCARD -#ifdef CONFIG_IN_EXTERNAL_FLASH +#if defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH) // // Config on external flash presents an issue with pin configuration since the pin and flash configs for the // external flash are in the config which is on a chip which we can't read yet! // - // FIXME We need to add configuration somewhere, e.g. bootloader image or reserved flash area, that can be read by the firmware. - // it's currently possible for the firmware resource allocation to be wrong after the config is loaded if the user changes the settings. - // This would cause undefined behaviour once the config is loaded. so for now, users must NOT change flash/pin configs needed for - // the system to boot and/or to save the config. + // FIXME For now, users must NOT change flash/pin configs needed for the system to boot and/or to save the config. + // One possible solution is to lock the pins for the flash chip so they cannot be modified post-boot. // - // note that target specific FLASH/SPI/QUADSPI configs are - // also not supported in USE_TARGET_CONFIG/targetConfigure() when using CONFIG_IN_EXTERNAL_FLASH. + // note that target specific FLASH/SPI/QUADSPI/OCTOSPI configs are + // also not supported in USE_TARGET_CONFIG/targetConfigure() when using CONFIG_IN_EXTERNAL_FLASH/CONFIG_IN_MEMORY_MAPPED_FLASH. // // // IMPORTANT: all default flash and pin configurations must be valid for the target after pgResetAll() is called. - // Target designers must ensure other devices connected the same SPI/QUADSPI interface as the flash chip do not - // cause communication issues with the flash chip. e.g. use external pullups on SPI/QUADSPI CS lines. + // Target designers must ensure other devices connected the same SPI/QUADSPI/OCTOSPI interface as the flash chip do not + // cause communication issues with the flash chip. e.g. use external pullups on SPI/QUADSPI/OCTOSPI CS lines. // pgResetAll(); #ifdef TARGET_BUS_INIT -#error "CONFIG_IN_EXTERNAL_FLASH and TARGET_BUS_INIT are mutually exclusive" +#error "CONFIG_IN_EXTERNAL_FLASH/CONFIG_IN_MEMORY_MAPPED_FLASH and TARGET_BUS_INIT are mutually exclusive" #endif - configureSPIAndQuadSPI(); - initFlags |= SPI_AND_QSPI_INIT_ATTEMPTED; +#if defined(CONFIG_IN_EXTERNAL_FLASH) + configureSPIBusses(); + initFlags |= SPI_BUSSES_INIT_ATTEMPTED; +#endif +#if defined(CONFIG_IN_MEMORY_MAPPED_FLASH) || defined(CONFIG_IN_EXTERNAL_FLASH) + configureQuadSPIBusses(); + configureOctoSPIBusses(); + initFlags |= QUAD_OCTO_SPI_BUSSES_INIT_ATTEMPTED; +#endif #ifndef USE_FLASH_CHIP -#error "CONFIG_IN_EXTERNAL_FLASH requires USE_FLASH_CHIP to be defined." +#error "CONFIG_IN_EXTERNAL_FLASH/CONFIG_IN_MEMORY_MAPPED_FLASH requires USE_FLASH_CHIP to be defined." #endif bool haveFlash = flashInit(flashConfig()); @@ -386,7 +402,8 @@ void init(void) } initFlags |= FLASH_INIT_ATTEMPTED; -#endif // CONFIG_IN_EXTERNAL_FLASH +#endif // CONFIG_IN_EXTERNAL_FLASH || CONFIG_IN_MEMORY_MAPPED_FLASH + initEEPROM(); @@ -588,10 +605,16 @@ void init(void) #else - // Depending on compilation options SPI/QSPI initialisation may already be done. - if (!(initFlags & SPI_AND_QSPI_INIT_ATTEMPTED)) { - configureSPIAndQuadSPI(); - initFlags |= SPI_AND_QSPI_INIT_ATTEMPTED; + // Depending on compilation options SPI/QSPI/OSPI initialisation may already be done. + if (!(initFlags & SPI_BUSSES_INIT_ATTEMPTED)) { + configureSPIBusses(); + initFlags |= SPI_BUSSES_INIT_ATTEMPTED; + } + + if (!(initFlags & QUAD_OCTO_SPI_BUSSES_INIT_ATTEMPTED)) { + configureQuadSPIBusses(); + configureOctoSPIBusses(); + initFlags |= QUAD_OCTO_SPI_BUSSES_INIT_ATTEMPTED; } #if defined(USE_SDCARD_SDIO) && !defined(CONFIG_IN_SDCARD) && defined(STM32H7) diff --git a/src/main/pg/flash.c b/src/main/pg/flash.c index 1b3b8b4297b..e28b5707447 100644 --- a/src/main/pg/flash.c +++ b/src/main/pg/flash.c @@ -27,6 +27,7 @@ #include "drivers/bus_spi.h" #include "drivers/bus_quadspi.h" +#include "drivers/bus_octospi.h" #include "drivers/io.h" #include "pg/pg.h" @@ -42,12 +43,17 @@ PG_REGISTER_WITH_RESET_FN(flashConfig_t, flashConfig, PG_FLASH_CONFIG, 0); void pgResetFn_flashConfig(flashConfig_t *flashConfig) { + // CS pin can be used by all IO interfaces, not just SPI. flashConfig->csTag = IO_TAG(FLASH_CS_PIN); -#if defined(USE_SPI) && defined(FLASH_SPI_INSTANCE) + +#if defined(USE_FLASH_SPI) && defined(FLASH_SPI_INSTANCE) flashConfig->spiDevice = SPI_DEV_TO_CFG(spiDeviceByInstance(FLASH_SPI_INSTANCE)); #endif -#if defined(USE_QUADSPI) && defined(FLASH_QUADSPI_INSTANCE) +#if defined(USE_FLASH_QUADSPI) && defined(FLASH_QUADSPI_INSTANCE) flashConfig->quadSpiDevice = QUADSPI_DEV_TO_CFG(quadSpiDeviceByInstance(FLASH_QUADSPI_INSTANCE)); #endif +#if defined(USE_FLASH_OCTOSPI) && defined(FLASH_OCTOSPI_INSTANCE) + flashConfig->octoSpiDevice = OCTOSPI_DEV_TO_CFG(octoSpiDeviceByInstance(FLASH_OCTOSPI_INSTANCE)); +#endif } #endif diff --git a/src/main/pg/flash.h b/src/main/pg/flash.h index 20fc618b5a5..96b0bb76439 100644 --- a/src/main/pg/flash.h +++ b/src/main/pg/flash.h @@ -30,6 +30,7 @@ typedef struct flashConfig_s { ioTag_t csTag; uint8_t spiDevice; uint8_t quadSpiDevice; + uint8_t octoSpiDevice; } flashConfig_t; PG_DECLARE(flashConfig_t, flashConfig); diff --git a/src/main/startup/system_stm32h7xx.c b/src/main/startup/system_stm32h7xx.c index 890ed7ee578..c22fc2f19b2 100644 --- a/src/main/startup/system_stm32h7xx.c +++ b/src/main/startup/system_stm32h7xx.c @@ -735,6 +735,12 @@ void SystemInit (void) systemProcessResetReason(); #endif +#if defined(USE_FLASH_MEMORY_MAPPED) + memoryMappedModeInit(); + + // !IMPORTANT! Do NOT reset peripherals, clocks and GPIO pins used by the MCU to access the memory-mapped flash!!! +#endif + // FPU settings #if (__FPU_PRESENT == 1) && (__FPU_USED == 1) SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); // Set CP10 and CP11 Full Access diff --git a/src/main/target/STM32H723/target.h b/src/main/target/STM32H723/target.h index a60a97fd65d..3283a66969f 100644 --- a/src/main/target/STM32H723/target.h +++ b/src/main/target/STM32H723/target.h @@ -20,15 +20,35 @@ #pragma once +#ifndef TARGET_BOARD_IDENTIFIER #define TARGET_BOARD_IDENTIFIER "SH72" +#endif +#ifndef USBD_PRODUCT_STRING #define USBD_PRODUCT_STRING "Betaflight STM32H723" +#endif +#if !defined(USE_I2C) +#define USE_I2C #define USE_I2C_DEVICE_1 #define USE_I2C_DEVICE_2 #define USE_I2C_DEVICE_3 #define USE_I2C_DEVICE_4 #define USE_I2C_DEVICE_5 +#define I2C_FULL_RECONFIGURABILITY +#endif + +// Provide a default so that this target builds on the build server. +#if !defined(USE_SPI) +#define USE_SPI +#define USE_SPI_DEVICE_1 +#define USE_SPI_DEVICE_2 +#define USE_SPI_DEVICE_3 +#define USE_SPI_DEVICE_4 +#define USE_SPI_DEVICE_5 +#define USE_SPI_DEVICE_6 +#define SPI_FULL_RECONFIGURABILITY +#endif #define USE_UART1 #define USE_UART2 @@ -43,13 +63,6 @@ #define SERIAL_PORT_COUNT (UNIFIED_SERIAL_PORT_COUNT + 10) -#define USE_SPI_DEVICE_1 -#define USE_SPI_DEVICE_2 -#define USE_SPI_DEVICE_3 -#define USE_SPI_DEVICE_4 -#define USE_SPI_DEVICE_5 -#define USE_SPI_DEVICE_6 - #define TARGET_IO_PORTA 0xffff #define TARGET_IO_PORTB 0xffff #define TARGET_IO_PORTC 0xffff @@ -58,9 +71,6 @@ #define TARGET_IO_PORTF 0xffff #define TARGET_IO_PORTG 0xffff -#define USE_I2C -#define I2C_FULL_RECONFIGURABILITY - #define USE_BEEPER #ifdef USE_SDCARD @@ -68,8 +78,6 @@ #define USE_SDCARD_SDIO #endif -#define USE_SPI -#define SPI_FULL_RECONFIGURABILITY #define USE_VCP @@ -84,4 +92,6 @@ #define USE_ADC +#if !defined(USE_EXST) #define USE_CUSTOM_DEFAULTS +#endif diff --git a/src/main/target/STM32H723/target.mk b/src/main/target/STM32H723/target.mk index 0930806cbaf..5fb733e16d6 100644 --- a/src/main/target/STM32H723/target.mk +++ b/src/main/target/STM32H723/target.mk @@ -19,7 +19,9 @@ RX_SRC = \ H723xG_TARGETS += $(TARGET) +ifneq ($(EXST),yes) CUSTOM_DEFAULTS_EXTENDED = yes +endif FEATURES += VCP SDCARD_SPI SDCARD_SDIO ONBOARDFLASH diff --git a/src/main/target/STM32H730/target.h b/src/main/target/STM32H730/target.h index ca84647bb8f..23a1a74b0c6 100644 --- a/src/main/target/STM32H730/target.h +++ b/src/main/target/STM32H730/target.h @@ -74,7 +74,7 @@ #define USE_UART8 #define USE_UART9 #define USE_UART10 -#define USE_LP_UART1 +#define USE_LPUART1 #define SERIAL_PORT_COUNT (UNIFIED_SERIAL_PORT_COUNT + 11) diff --git a/src/main/target/STM32H750/target.h b/src/main/target/STM32H750/target.h index b8137f79cd3..2b40d8a37a9 100644 --- a/src/main/target/STM32H750/target.h +++ b/src/main/target/STM32H750/target.h @@ -84,9 +84,9 @@ #define USE_UART6 #define USE_UART7 #define USE_UART8 -#define USE_LP_UART1 +#define USE_LPUART1 -#define SERIAL_PORT_COUNT (UNIFIED_SERIAL_PORT_COUNT + 8) +#define SERIAL_PORT_COUNT (UNIFIED_SERIAL_PORT_COUNT + 9) #define TARGET_IO_PORTA 0xffff #define TARGET_IO_PORTB 0xffff diff --git a/src/main/target/common_post.h b/src/main/target/common_post.h index 61049fccc2c..7ead61095c6 100644 --- a/src/main/target/common_post.h +++ b/src/main/target/common_post.h @@ -254,6 +254,18 @@ #define USE_FLASH_CHIP #endif +#if defined(USE_SPI) && (defined(USE_FLASH_M25P16) || defined(USE_FLASH_W25M512) || defined(USE_FLASH_W25N01G) || defined(USE_FLASH_W25M02G)) +#define USE_FLASH_SPI +#endif + +#if defined(USE_QUADSPI) && (defined(USE_FLASH_W25Q128FV) || defined(USE_FLASH_W25N01G)) +#define USE_FLASH_QUADSPI +#endif + +#if defined(USE_OCTOSPI) && (defined(USE_FLASH_W25Q128FV)) +#define USE_FLASH_OCTOSPI +#endif + #ifndef USE_FLASH_CHIP #undef USE_FLASHFS #endif @@ -426,7 +438,7 @@ #undef USE_ESCSERIAL #endif -#if defined(CONFIG_IN_RAM) || defined(CONFIG_IN_FILE) || defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_SDCARD) +#if defined(CONFIG_IN_RAM) || defined(CONFIG_IN_FILE) || defined(CONFIG_IN_EXTERNAL_FLASH) || defined(CONFIG_IN_SDCARD) || defined(CONFIG_IN_MEMORY_MAPPED_FLASH) #ifndef EEPROM_SIZE #define EEPROM_SIZE 4096 #endif @@ -445,6 +457,29 @@ extern uint8_t __config_end; #define USE_FLASH_BOOT_LOADER #endif +#if defined(USE_FLASH_MEMORY_MAPPED) +#if !defined(USE_RAM_CODE) +#define USE_RAM_CODE +#endif + +#define MMFLASH_CODE RAM_CODE +#define MMFLASH_CODE_NOINLINE RAM_CODE NOINLINE + +#define MMFLASH_DATA FAST_DATA +#define MMFLASH_DATA_ZERO_INIT FAST_DATA_ZERO_INIT +#else +#define MMFLASH_CODE +#define MMFLASH_CODE_NOINLINE +#define MMFLASH_DATA +#define MMFLASH_DATA_ZERO_INIT +#endif + +#ifdef USE_RAM_CODE +// RAM_CODE for methods that need to be in RAM, but don't need to be in the fastest type of memory. +// Note: if code is marked as RAM_CODE it *MUST* be in RAM, there is no alternative unlike functions marked with FAST_CODE/CCM_CODE +#define RAM_CODE __attribute__((section(".ram_code"))) +#endif + #if !defined(USE_RPM_FILTER) #undef USE_DYN_IDLE #endif diff --git a/support/scripts/build_nucleoh723zg_mmapped.sh b/support/scripts/build_nucleoh723zg_mmapped.sh new file mode 100644 index 00000000000..35cc3088b7b --- /dev/null +++ b/support/scripts/build_nucleoh723zg_mmapped.sh @@ -0,0 +1,108 @@ +#!/bin/sh +set -x + +make DEBUG=INFO EXST=yes EXST_ADJUST_VMA=0x90100000 TARGET=STM32H723 EXTRA_FLAGS="\ +-D'TARGET_BOARD_IDENTIFIER=\"H723\"' \ +-D'USBD_PRODUCT_STRING=\"Nucleo-H723-MMAPPED\"' \ +\ +-D'EEPROM_SIZE=8192' \ +\ +-DUSE_BUTTONS \ +-D'BUTTON_A_PIN=PC13' \ +-DBUTTON_A_PIN_INVERTED \ +-D'BUTTON_B_PIN=PC13' \ +-DBUTTON_B_PIN_INVERTED \ +\ +-DUSE_OCTOSPI \ +-DUSE_OCTOSPI_DEVICE_1 \ +-D'OCTOSPIM_P1_SCK_PIN=PB2' \ +-D'OCTOSPIM_P1_CS_PIN=PB6' \ +-D'OCTOSPIM_P1_IO0_PIN=PD11' \ +-D'OCTOSPIM_P1_IO1_PIN=PD12' \ +-D'OCTOSPIM_P1_IO2_PIN=PE2' \ +-D'OCTOSPIM_P1_IO3_PIN=PD13' \ +-D'OCTOSPIM_P1_IO4_PIN=NONE' \ +-D'OCTOSPIM_P1_IO5_PIN=NONE' \ +-D'OCTOSPIM_P1_IO6_PIN=NONE' \ +-D'OCTOSPIM_P1_IO7_PIN=NONE' \ +\ +-D'OCTOSPIM_P1_MODE=OCTOSPIM_P1_MODE_IO03_ONLY' \ +-D'OCTOSPIM_P1_CS_FLAGS=(OCTOSPIM_P1_CS_HARDWARE)' \ +\ +-DUSE_SPI \ +\ +-DUSE_SPI_DEVICE_1 \ +-D'SPI1_SCK_PIN=PB3' \ +-D'SPI1_MISO_PIN=PB4' \ +-D'SPI1_MOSI_PIN=PB5' \ +\ +-DUSE_SPI_DEVICE_2 \ +-D'SPI2_SCK_PIN=NONE' \ +-D'SPI2_MISO_PIN=NONE' \ +-D'SPI2_MOSI_PIN=NONE' \ +\ +-DUSE_SPI_DEVICE_3 \ +-D'SPI3_SCK_PIN=PC10' \ +-D'SPI3_MISO_PIN=PC11' \ +-D'SPI3_MOSI_PIN=PC12' \ +\ +-DUSE_SPI_DEVICE_4 \ +-D'SPI4_SCK_PIN=NONE' \ +-D'SPI4_MISO_PIN=NONE' \ +-D'SPI4_MOSI_PIN=NONE' \ +\ +-DUSE_SPI_DEVICE_5 \ +-D'SPI5_SCK_PIN=NONE' \ +-D'SPI5_MISO_PIN=NONE' \ +-D'SPI5_MOSI_PIN=NONE' \ +\ +-DUSE_SPI_DEVICE_6 \ +-D'SPI6_SCK_PIN=NONE' \ +-D'SPI6_MISO_PIN=NONE' \ +-D'SPI6_MOSI_PIN=NONE' \ +\ +-DUSE_FLASH_TOOLS \ +-DUSE_FLASH_W25Q128FV \ +-DUSE_FLASH_MEMORY_MAPPED \ +-DCONFIG_IN_MEMORY_MAPPED_FLASH \ +-D'FLASH_SPI_INSTANCE=NULL' \ +-D'FLASH_CS_PINS=NONE' \ +\ +-DUSE_SDCARD \ +-D'SDCARD_DETECT_PIN=NONE' \ +-D'SDIO_DEVICE=SDIODEV_2' \ +-D'SDIO_USE_4BIT=false' \ +-D'SDIO_CK_PIN=PD6' \ +-D'SDIO_CMD_PIN=PD7' \ +-D'SDIO_D0_PIN=PB14' \ +-D'SDIO_D1_PIN=NONE' \ +-D'SDIO_D2_PIN=NONE' \ +-D'SDIO_D3_PIN=NONE' \ +\ +-D'TARGET_IO_PORTA=0xffff' \ +-D'TARGET_IO_PORTB=0xffff' \ +-D'TARGET_IO_PORTC=0xffff' \ +-D'TARGET_IO_PORTD=0xffff' \ +-D'TARGET_IO_PORTE=0xffff' \ +-D'TARGET_IO_PORTF=0xffff' \ +-D'TARGET_IO_PORTG=0xffff' \ +\ +-DUSE_USB_ID \ +\ +-DUSE_I2C \ +-DUSE_I2C_DEVICE_1 \ +-D'I2C1_SCL=PB8' \ +-D'I2C1_SDA=PB9' \ +-D'I2C_DEVICE=(I2CDEV_1)' \ +\ +-DUSE_ACC \ +-DUSE_ACC_SPI_MPU6500 \ +-DUSE_GYRO \ +-DUSE_GYRO_SPI_MPU6500 \ +\ +-D'ADC1_DMA_OPT=8' \ +-D'ADC2_DMA_OPT=9' \ +-D'ADC3_DMA_OPT=10' \ +\ +-DENABLE_BLACKBOX_LOGGING_ON_SDCARD_BY_DEFAULT \ +" \ No newline at end of file diff --git a/support/scripts/build_spracingh7rf.sh b/support/scripts/build_spracingh7rf.sh new file mode 100644 index 00000000000..9f51863e542 --- /dev/null +++ b/support/scripts/build_spracingh7rf.sh @@ -0,0 +1,145 @@ +#!/bin/sh +set -x + +make DEBUG=INFO TARGET=STM32H730 EXTRA_FLAGS="\ +-D'TARGET_BOARD_IDENTIFIER=\"SP7R\"' \ +-D'USBD_PRODUCT_STRING=\"SPRacingH7RF\"' \ +\ +-D'EEPROM_SIZE=8192' \ +-DUSE_SPRACING_PERSISTENT_RTC_WORKAROUND \ +\ +-DUSE_BUTTONS \ +-D'BUTTON_A_PIN=PC14' \ +-DBUTTON_A_PIN_INVERTED \ +-D'BUTTON_B_PIN=PC14' \ +-DBUTTON_B_PIN_INVERTED \ +\ +-DUSE_OCTOSPI \ +-DUSE_OCTOSPI_DEVICE_1 \ +-D'OCTOSPIM_P1_SCK_PIN=PB2' \ +-D'OCTOSPIM_P1_CS_PIN=PB10' \ +-D'OCTOSPIM_P1_IO0_PIN=PD11' \ +-D'OCTOSPIM_P1_IO1_PIN=PD12' \ +-D'OCTOSPIM_P1_IO2_PIN=PE2' \ +-D'OCTOSPIM_P1_IO3_PIN=PD13' \ +-D'OCTOSPIM_P1_IO4_PIN=NONE' \ +-D'OCTOSPIM_P1_IO5_PIN=NONE' \ +-D'OCTOSPIM_P1_IO6_PIN=NONE' \ +-D'OCTOSPIM_P1_IO7_PIN=NONE' \ +-D'OCTOSPIM_P1_MODE=OCTOSPIM_P1_MODE_IO03_ONLY' \ +-D'OCTOSPIM_P1_CS_FLAGS=(OCTOSPIM_P1_CS_HARDWARE)' \ +\ +-DUSE_SPI \ +\ +-DUSE_SPI_DEVICE_2 \ +-D'SPI2_SCK_PIN=PD3' \ +-D'SPI2_MISO_PIN=PB14' \ +-D'SPI2_MOSI_PIN=PB15' \ +-D'SPI2_NSS_PIN=PB12' \ +\ +-DUSE_SPI_DEVICE_6 \ +-D'SPI6_SCK_PIN=PB3' \ +-D'SPI6_MISO_PIN=PB4' \ +-D'SPI6_MOSI_PIN=PB5' \ +-D'SPI6_NSS_PIN=PA15' \ +\ +-D'SX1280_BUSY_PIN=PC7' \ +-D'SX1280_DIO1_PIN=PC6' \ +-D'SX1280_DIO2_PIN=PD4' \ +-D'SX1280_DIO3_PIN=NONE' \ +-D'SX1280_NRESET_PIN=PD10' \ +-DUSE_RX_SPI \ +-DUSE_RX_EXPRESSLRS \ +-DUSE_RX_SX1280 \ +-D'RX_SPI_INSTANCE=SPI2' \ +-D'RX_NSS_PIN=SPI2_NSS_PIN' \ +-D'RX_SPI_EXTI_PIN=SX1280_DIO1_PIN' \ +-D'RX_EXPRESSLRS_SPI_RESET_PIN=SX1280_NRESET_PIN' \ +-D'RX_EXPRESSLRS_SPI_BUSY_PIN=SX1280_BUSY_PIN' \ +-D'RX_EXPRESSLRS_TIMER_INSTANCE=TIM6' \ +-D'DEFAULT_RX_FEATURE=FEATURE_RX_SPI' \ +-D'RX_SPI_DEFAULT_PROTOCOL=RX_SPI_EXPRESSLRS' \ +\ +-D'VTX_ENABLE_PIN=PC15' \ +-D'PINIO1_PIN=VTX_ENABLE_PIN' \ +\ +-DUSE_FLASH_MEMORY_MAPPED \ +-DUSE_FLASH_W25Q128FV \ +-D'FLASH_OCTOSPI_INSTANCE=OCTOSPI1' \ +-DCONFIG_IN_MEMORY_MAPPED_FLASH \ +-DUSE_FIRMWARE_PARTITION \ +\ +-D'SDCARD_DETECT_PIN=PC13' \ +-DSDCARD_DETECT_INVERTED \ +-D'SDIO_DEVICE=SDIODEV_1' \ +-D'SDIO_USE_4BIT=true' \ +-D'SDIO_CK_PIN=PC12' \ +-D'SDIO_CMD_PIN=PD2' \ +-D'SDIO_D0_PIN=PC8' \ +-D'SDIO_D1_PIN=PC9' \ +-D'SDIO_D2_PIN=PC10' \ +-D'SDIO_D3_PIN=PC11' \ +\ +-D'TARGET_IO_PORTA=0xffff' \ +-D'TARGET_IO_PORTB=(0xffff & ~(BIT(2)|BIT(6)))' \ +-D'TARGET_IO_PORTC=0xffff' \ +-D'TARGET_IO_PORTD=(0xffff & ~(BIT(11)|BIT(12)|BIT(13)))' \ +-D'TARGET_IO_PORTE=(0xffff & ~(BIT(2)|BIT(7)|BIT(8)|BIT(9)|BIT(10)))' \ +-D'TARGET_IO_PORTF=0xffff' \ +-D'TARGET_IO_PORTG=0xffff' \ +-D'TARGET_IO_PORTH=0xffff' \ +\ +-DUSE_I2C \ +-DUSE_I2C_DEVICE_1 \ +-D'I2C1_SCL=PB8' \ +-D'I2C1_SDA=PB9' \ +\ +-DUSE_I2C_DEVICE_2 \ +-D'I2C2_SCL=PB10' \ +-D'I2C2_SDA=PB11' \ +-D'MAG_I2C_INSTANCE=(I2CDEV_1)' \ +-D'BARO_I2C_INSTANCE=(I2CDEV_2)' \ +\ +-DUSE_ACC \ +-DUSE_GYRO \ +\ +-DUSE_MPU_DATA_READY_SIGNAL \ +-DENSURE_MPU_DATA_READY_IS_LOW \ +\ +-D'ADC3_DMA_OPT=10' \ +-D'ADC_INSTANCE=ADC3' \ +-D'CURRENT_METER_2_ADC_PIN=PC0' \ +-D'CURRENT_METER_2_ADC_INSTANCE=ADC3' \ +-D'CURRENT_METER_1_ADC_PIN=PC1' \ +-D'CURRENT_METER_1_ADC_INSTANCE=ADC3' \ +-D'EXTERNAL1_ADC_PIN=PC2' \ +-D'EXTERNAL1_ADC_INSTANCE=ADC3' \ +-D'VIDEO_IN_ADC_PIN=PC5' \ +-D'VIDEO_OUT_ADC_PIN=PC4' \ +-D'VBAT_ADC_PIN=PC3' \ +-D'VBAT_ADC_INSTANCE=ADC3' \ +-D'RSSI_ADC_PIN=CURRENT_METER_2_ADC_PIN' \ +-D'RSSI_ADC_INSTANCE=CURRENT_METER_2_ADC_INSTANCE' \ +-D'CURRENT_METER_ADC_PIN=CURRENT_METER_1_ADC_PIN' \ +-D'CURRENT_METER_ADC_INSTANCE=CURRENT_METER_1_ADC_INSTANCE' \ +-D'DEFAULT_VOLTAGE_METER_SOURCE=VOLTAGE_METER_ADC' \ +-D'DEFAULT_CURRENT_METER_SOURCE=CURRENT_METER_ADC' \ +\ +-DENABLE_BLACKBOX_LOGGING_ON_SDCARD_BY_DEFAULT \ +\ +-DUSE_SDCARD \ +-DUSE_ACC_SPI_ICM42605 \ +-DUSE_ACC_SPI_ICM42688P \ +-DUSE_GYRO_SPI_ICM42605 \ +-DUSE_GYRO_SPI_ICM42688P \ +-DUSE_FLASH_W25Q128FV \ +" + +# Settings that are currently defined in target/common_pre.h for non-cloud builds that probably shouldn't be. +# There are here to illustrate that they SHOULD be included in THIS target when they are removed by default. + +#-DUSE_MAG \ +#-DUSE_MAG_HMC5883 \ +#-DUSE_MAG_QMC5883 \ +#-DUSE_BARO \ +#-DUSE_BARO_BMP388 \