diff --git a/boards/stm32f429i-disc1/Kconfig b/boards/stm32f429i-disc1/Kconfig index 33303137a9e9f..ca1945d6f3378 100644 --- a/boards/stm32f429i-disc1/Kconfig +++ b/boards/stm32f429i-disc1/Kconfig @@ -15,6 +15,9 @@ config BOARD_STM32F429I_DISC1 # Put defined MCU peripherals here (in alphabetical order) select HAS_PERIPH_DMA + select HAS_PERIPH_FMC + select HAS_PERIPH_FMC_SDRAM + select HAS_PERIPH_FMC_16BIT select HAS_PERIPH_I2C select HAS_PERIPH_SPI select HAS_PERIPH_TIMER diff --git a/boards/stm32f429i-disc1/Makefile.dep b/boards/stm32f429i-disc1/Makefile.dep index 18b88c76114ea..e9b97fc20022c 100644 --- a/boards/stm32f429i-disc1/Makefile.dep +++ b/boards/stm32f429i-disc1/Makefile.dep @@ -2,6 +2,10 @@ ifneq (,$(filter periph_usbdev,$(USEMODULE))) USEMODULE += periph_usbdev_hs endif +ifneq (,$(filter periph_fmc,$(USEMODULE))) + FEATURES_REQUIRED += periph_fmc_16bit +endif + ifneq (,$(filter saul_default,$(USEMODULE))) USEMODULE += saul_gpio USEMODULE += l3gxxxx diff --git a/boards/stm32f429i-disc1/Makefile.features b/boards/stm32f429i-disc1/Makefile.features index 3ddc733cbfa24..001a9f0aeb555 100644 --- a/boards/stm32f429i-disc1/Makefile.features +++ b/boards/stm32f429i-disc1/Makefile.features @@ -3,6 +3,9 @@ CPU_MODEL = stm32f429zi # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_dma +FEATURES_PROVIDED += periph_fmc +FEATURES_PROVIDED += periph_fmc_16bit +FEATURES_PROVIDED += periph_fmc_sdram FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_spi FEATURES_PROVIDED += periph_timer diff --git a/boards/stm32f429i-disc1/Makefile.include b/boards/stm32f429i-disc1/Makefile.include index b932c4f45748a..83545bdb4f5a9 100644 --- a/boards/stm32f429i-disc1/Makefile.include +++ b/boards/stm32f429i-disc1/Makefile.include @@ -8,3 +8,6 @@ OPENOCD_DEBUG_ADAPTER ?= stlink # openocd programmer is supported PROGRAMMERS_SUPPORTED += openocd + +FMC_RAM_ADDR=0xd0000000 +FMC_RAM_LEN=8192K diff --git a/boards/stm32f429i-disc1/include/periph_conf.h b/boards/stm32f429i-disc1/include/periph_conf.h index 24cb9581e76aa..2857f3d058e77 100644 --- a/boards/stm32f429i-disc1/include/periph_conf.h +++ b/boards/stm32f429i-disc1/include/periph_conf.h @@ -132,6 +132,111 @@ static const i2c_conf_t i2c_config[] = { #define I2C_NUMOF ARRAY_SIZE(i2c_config) /** @} */ +/** + * @name FMC configuration + * @{ + */ +/** + * @brief FMC controller configuration + */ +static const fmc_conf_t fmc_config = { + .bus = AHB3, + .rcc_mask = RCC_AHB3ENR_FMCEN, +#if MODULE_PERIPH_FMC_SDRAM + .ba0_pin = { .pin = GPIO_PIN(PORT_G, 4), .af = GPIO_AF12, }, /* FMC_BA0 signal */ + .ba1_pin = { .pin = GPIO_PIN(PORT_G, 5), .af = GPIO_AF12, }, /* FMC_BA1 signal */ + .sdclk_pin = { .pin = GPIO_PIN(PORT_G, 8), .af = GPIO_AF12, }, /* FMC_SDCLK signal */ + .sdnwe_pin = { .pin = GPIO_PIN(PORT_C, 0), .af = GPIO_AF12, }, /* FMC_SDNWE signal */ + .sdnras_pin = { .pin = GPIO_PIN(PORT_F, 11), .af = GPIO_AF12, }, /* FMC_SDNRAS signal */ + .sdncas_pin = { .pin = GPIO_PIN(PORT_G, 15), .af = GPIO_AF12, }, /* FMC_SDNCAS signal */ + .sdcke1_pin = { .pin = GPIO_PIN(PORT_B, 5), .af = GPIO_AF12, }, /* FMC_SDCKE1 signal */ + .sdne1_pin = { .pin = GPIO_PIN(PORT_B, 6), .af = GPIO_AF12, }, /* FMC_SDNE1 signal */ + .addr = { + { .pin = GPIO_PIN(PORT_F, 0), .af = GPIO_AF12, }, /* FMC_A0 signal */ + { .pin = GPIO_PIN(PORT_F, 1), .af = GPIO_AF12, }, /* FMC_A1 signal */ + { .pin = GPIO_PIN(PORT_F, 2), .af = GPIO_AF12, }, /* FMC_A2 signal */ + { .pin = GPIO_PIN(PORT_F, 3), .af = GPIO_AF12, }, /* FMC_A3 signal */ + { .pin = GPIO_PIN(PORT_F, 4), .af = GPIO_AF12, }, /* FMC_A4 signal */ + { .pin = GPIO_PIN(PORT_F, 5), .af = GPIO_AF12, }, /* FMC_A5 signal */ + { .pin = GPIO_PIN(PORT_F, 12), .af = GPIO_AF12, }, /* FMC_A6 signal */ + { .pin = GPIO_PIN(PORT_F, 13), .af = GPIO_AF12, }, /* FMC_A7 signal */ + { .pin = GPIO_PIN(PORT_F, 14), .af = GPIO_AF12, }, /* FMC_A8 signal */ + { .pin = GPIO_PIN(PORT_F, 15), .af = GPIO_AF12, }, /* FMC_A9 signal */ + { .pin = GPIO_PIN(PORT_G, 0), .af = GPIO_AF12, }, /* FMC_A10 signal */ + { .pin = GPIO_PIN(PORT_G, 1), .af = GPIO_AF12, }, /* FMC_A11 signal */ + }, +#endif + .data = { + { .pin = GPIO_PIN(PORT_D, 14), .af = GPIO_AF12, }, /* FMC_D0 signal */ + { .pin = GPIO_PIN(PORT_D, 15), .af = GPIO_AF12, }, /* FMC_D1 signal */ + { .pin = GPIO_PIN(PORT_D, 0), .af = GPIO_AF12, }, /* FMC_D2 signal */ + { .pin = GPIO_PIN(PORT_D, 1), .af = GPIO_AF12, }, /* FMC_D3 signal */ + { .pin = GPIO_PIN(PORT_E, 7), .af = GPIO_AF12, }, /* FMC_D4 signal */ + { .pin = GPIO_PIN(PORT_E, 8), .af = GPIO_AF12, }, /* FMC_D5 signal */ + { .pin = GPIO_PIN(PORT_E, 9), .af = GPIO_AF12, }, /* FMC_D6 signal */ + { .pin = GPIO_PIN(PORT_E, 10), .af = GPIO_AF12, }, /* FMC_D7 signal */ +#if MODULE_PERIPH_FMC_16BIT + { .pin = GPIO_PIN(PORT_E, 11), .af = GPIO_AF12, }, /* FMC_D8 signal */ + { .pin = GPIO_PIN(PORT_E, 12), .af = GPIO_AF12, }, /* FMC_D9 signal */ + { .pin = GPIO_PIN(PORT_E, 13), .af = GPIO_AF12, }, /* FMC_D10 signal */ + { .pin = GPIO_PIN(PORT_E, 14), .af = GPIO_AF12, }, /* FMC_D11 signal */ + { .pin = GPIO_PIN(PORT_E, 15), .af = GPIO_AF12, }, /* FMC_D12 signal */ + { .pin = GPIO_PIN(PORT_D, 8), .af = GPIO_AF12, }, /* FMC_D13 signal */ + { .pin = GPIO_PIN(PORT_D, 9), .af = GPIO_AF12, }, /* FMC_D14 signal */ + { .pin = GPIO_PIN(PORT_D, 10), .af = GPIO_AF12, }, /* FMC_D15 signal */ +#endif + }, + .nbl0_pin = { .pin = GPIO_PIN(PORT_E, 0), .af = GPIO_AF12, }, /* FMC_NBL0 signal (LB) */ + .nbl1_pin = { .pin = GPIO_PIN(PORT_E, 1), .af = GPIO_AF12, }, /* FMC_NBL1 signal (UB) */ +}; + +/** + * @brief FMC Bank configuration + * + * The board has a SDRAM IS42S16400J-7TL with 64 MBit on-board. + * It is organized in 4 banks of 1M x 16 bits each and connected to bank 6 + * at address 0xd0000000. + */ +static const fmc_bank_conf_t fmc_bank_config[] = { + /* bank 6 is used for SDRAM */ + { + .bank = FMC_BANK_6, + .mem_type = FMC_SDRAM, + .data_width = FMC_BUS_WIDTH_16BIT, + .address = 0xd0000000, /* Bank 6 is mapped to 0xd0000000 */ + .size = MiB(8), /* Size in Mbyte, 4M x 16 bit */ + .sdram = { + .clk_period = 2, /* SDCLK = 2 x HCLK */ + .row_bits = 12, /* A11..A0 used for row address */ + .col_bits = 8, /* A8..A0 used for column address */ + .cas_latency = 3, /* CAS latency is 3 clock cycles */ + .read_delay = 0, /* No read delay after CAS */ + .burst_read = false, /* Burst read mode disabled */ + .burst_write = false, /* Burst write mode disabled */ + .burst_len = FMC_BURST_LENGTH_1, /* Burst length is 1 if enabled */ + .burst_interleaved = false, /* Burst mode interleaved */ + .write_protect = false, /* No write protection */ + .four_banks = true, /* SDRAM has four internal banks */ + .timing = { /* SDRAM Timing parameters */ + .row_to_col_delay = 2, /* Row to column delay (2 clock cycles) */ + .row_precharge = 2, /* Row precharge delay (2 clock cycles) */ + .recovery_delay = 2, /* Recovery delay is (2 clock cycles) */ + .row_cylce = 7, /* Row cycle delay is (7 clock cycles) */ + .self_refresh = 4, /* Self refresh time is (4 clock cycles) */ + .exit_self_refresh = 7, /* Exit self-refresh delay (7 clock cycles) */ + .load_mode_register = 2, /* Load Mode Register to Activate delay */ + .refresh_period = 64, /* Refresh period in ms */ + }, + }, + }, +}; + +/** + * @brief Number of configured FMC banks + */ +#define FMC_BANK_NUMOF ARRAY_SIZE(fmc_bank_config) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/boards/stm32f723e-disco/Kconfig b/boards/stm32f723e-disco/Kconfig index fa9b2e71c6e2d..4989348a8bc5b 100644 --- a/boards/stm32f723e-disco/Kconfig +++ b/boards/stm32f723e-disco/Kconfig @@ -14,6 +14,9 @@ config BOARD_STM32F723E_DISCO select CPU_MODEL_STM32F723IE # Put defined MCU peripherals here (in alphabetical order) + select HAS_PERIPH_FMC + select HAS_PERIPH_FMC_16BIT + select HAS_PERIPH_FMC_NOR_SRAM select HAS_PERIPH_I2C select HAS_PERIPH_RTC select HAS_PERIPH_RTT diff --git a/boards/stm32f723e-disco/Makefile.dep b/boards/stm32f723e-disco/Makefile.dep index 6334b41c7e129..d90328e2326c0 100644 --- a/boards/stm32f723e-disco/Makefile.dep +++ b/boards/stm32f723e-disco/Makefile.dep @@ -10,6 +10,10 @@ ifneq (,$(filter touch_dev,$(USEMODULE))) USEMODULE += ft5x06 endif +ifneq (,$(filter periph_fmc,$(USEMODULE))) + FEATURES_REQUIRED += periph_fmc_16bit +endif + ifneq (,$(filter periph_spi,$(USEMODULE))) # The LED pin is also used for SPI DISABLE_MODULE += periph_init_led0 diff --git a/boards/stm32f723e-disco/Makefile.features b/boards/stm32f723e-disco/Makefile.features index 7ee69ea0c1a2a..9eece2e4632ae 100644 --- a/boards/stm32f723e-disco/Makefile.features +++ b/boards/stm32f723e-disco/Makefile.features @@ -3,6 +3,9 @@ CPU = stm32 CPU_MODEL = stm32f723ie # Put defined MCU peripherals here (in alphabetical order) +FEATURES_PROVIDED += periph_fmc +FEATURES_PROVIDED += periph_fmc_16bit +FEATURES_PROVIDED += periph_fmc_nor_sram FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_rtc FEATURES_PROVIDED += periph_rtt diff --git a/boards/stm32f723e-disco/Makefile.include b/boards/stm32f723e-disco/Makefile.include index 2d4c3e4ebc1be..1fb2585feb1f8 100644 --- a/boards/stm32f723e-disco/Makefile.include +++ b/boards/stm32f723e-disco/Makefile.include @@ -13,3 +13,8 @@ PROGRAMMERS_SUPPORTED += openocd # The board can become un-flashable after some execution or after being plugged, # use connect_assert_srst to always be able to flash or reset the board. OPENOCD_RESET_USE_CONNECT_ASSERT_SRST ?= 1 + +# Since only 18 of the 19 address lines are connected, only 512 kByte of the +# 1 MByte PSRAM can be used. +FMC_RAM_ADDR=0x60000000 +FMC_RAM_LEN=512K diff --git a/boards/stm32f723e-disco/include/periph_conf.h b/boards/stm32f723e-disco/include/periph_conf.h index 5081f8b73243f..4a1ccf349321e 100644 --- a/boards/stm32f723e-disco/include/periph_conf.h +++ b/boards/stm32f723e-disco/include/periph_conf.h @@ -232,6 +232,107 @@ static const spi_conf_t spi_config[] = { #define SPI_NUMOF ARRAY_SIZE(spi_config) /** @} */ +/** + * @name FMC configuration + * @{ + */ + +/** + * @brief FMC controller configuration + */ +static const fmc_conf_t fmc_config = { + .bus = AHB3, + .rcc_mask = RCC_AHB3ENR_FMCEN, +#if MODULE_PERIPH_FMC_NOR_SRAM + .ne1_pin = { .pin = GPIO_PIN(PORT_D, 7), .af = GPIO_AF12, }, /* PSRAM_NE1 signal, subbank 1 */ + .ne2_pin = { .pin = GPIO_PIN(PORT_G, 9), .af = GPIO_AF12, }, /* LCD_NE signal, subbank 2 */ + .noe_pin = { .pin = GPIO_PIN(PORT_D, 4), .af = GPIO_AF12, }, /* LCD_PSRAM_NOE */ + .nwe_pin = { .pin = GPIO_PIN(PORT_D, 5), .af = GPIO_AF12, }, /* LCD_PSRAM_NWE signal */ + .addr = { + { .pin = GPIO_PIN(PORT_F, 0), .af = GPIO_AF12, }, /* PSRAM_A0 / LCD_RS signal */ + { .pin = GPIO_PIN(PORT_F, 1), .af = GPIO_AF12, }, /* PSRAM_A1 signal */ + { .pin = GPIO_PIN(PORT_F, 2), .af = GPIO_AF12, }, /* PSRAM_A2 signal */ + { .pin = GPIO_PIN(PORT_F, 3), .af = GPIO_AF12, }, /* PSRAM_A3 signal */ + { .pin = GPIO_PIN(PORT_F, 4), .af = GPIO_AF12, }, /* PSRAM_A4 signal */ + { .pin = GPIO_PIN(PORT_F, 5), .af = GPIO_AF12, }, /* PSRAM_A5 signal */ + { .pin = GPIO_PIN(PORT_F, 12), .af = GPIO_AF12, }, /* PSRAM_A6 signal */ + { .pin = GPIO_PIN(PORT_F, 13), .af = GPIO_AF12, }, /* PSRAM_A7 signal */ + { .pin = GPIO_PIN(PORT_F, 14), .af = GPIO_AF12, }, /* PSRAM_A8 signal */ + { .pin = GPIO_PIN(PORT_F, 15), .af = GPIO_AF12, }, /* PSRAM_A9 signal */ + { .pin = GPIO_PIN(PORT_G, 0), .af = GPIO_AF12, }, /* PSRAM_A10 signal */ + { .pin = GPIO_PIN(PORT_G, 1), .af = GPIO_AF12, }, /* PSRAM_A11 signal */ + { .pin = GPIO_PIN(PORT_G, 2), .af = GPIO_AF12, }, /* PSRAM_A12 signal */ + { .pin = GPIO_PIN(PORT_G, 3), .af = GPIO_AF12, }, /* PSRAM_A13 signal */ + { .pin = GPIO_PIN(PORT_G, 4), .af = GPIO_AF12, }, /* PSRAM_A14 signal */ + { .pin = GPIO_PIN(PORT_G, 5), .af = GPIO_AF12, }, /* PSRAM_A15 signal */ + { .pin = GPIO_PIN(PORT_D, 11), .af = GPIO_AF12, }, /* PSRAM_A16 signal */ + { .pin = GPIO_PIN(PORT_D, 12), .af = GPIO_AF12, }, /* PSRAM_A17 signal */ + }, +#endif + .data = { + { .pin = GPIO_PIN(PORT_D, 14), .af = GPIO_AF12, }, /* LCD_PSRAM_D0 signal */ + { .pin = GPIO_PIN(PORT_D, 15), .af = GPIO_AF12, }, /* LCD_PSRAM_D1 signal */ + { .pin = GPIO_PIN(PORT_D, 0), .af = GPIO_AF12, }, /* LCD_PSRAM_D2 signal */ + { .pin = GPIO_PIN(PORT_D, 1), .af = GPIO_AF12, }, /* LCD_PSRAM_D3 signal */ + { .pin = GPIO_PIN(PORT_E, 7), .af = GPIO_AF12, }, /* LCD_PSRAM_D4 signal */ + { .pin = GPIO_PIN(PORT_E, 8), .af = GPIO_AF12, }, /* LCD_PSRAM_D5 signal */ + { .pin = GPIO_PIN(PORT_E, 9), .af = GPIO_AF12, }, /* LCD_PSRAM_D6 signal */ + { .pin = GPIO_PIN(PORT_E, 10), .af = GPIO_AF12, }, /* LCD_PSRAM_D7 signal */ +#if MODULE_PERIPH_FMC_16BIT + { .pin = GPIO_PIN(PORT_E, 11), .af = GPIO_AF12, }, /* LCD_PSRAM_D8 signal */ + { .pin = GPIO_PIN(PORT_E, 12), .af = GPIO_AF12, }, /* LCD_PSRAM_D9 signal */ + { .pin = GPIO_PIN(PORT_E, 13), .af = GPIO_AF12, }, /* LCD_PSRAM_D10 signal */ + { .pin = GPIO_PIN(PORT_E, 14), .af = GPIO_AF12, }, /* LCD_PSRAM_D11 signal */ + { .pin = GPIO_PIN(PORT_E, 15), .af = GPIO_AF12, }, /* LCD_PSRAM_D12 signal */ + { .pin = GPIO_PIN(PORT_D, 8), .af = GPIO_AF12, }, /* LCD_PSRAM_D13 signal */ + { .pin = GPIO_PIN(PORT_D, 9), .af = GPIO_AF12, }, /* LCD_PSRAM_D14 signal */ + { .pin = GPIO_PIN(PORT_D, 10), .af = GPIO_AF12, }, /* LCD_PSRAM_D15 signal */ +#endif + }, + .nbl0_pin = { .pin = GPIO_PIN(PORT_E, 0), .af = GPIO_AF12, }, /* PSRAM_NBL0 signal (LB) */ + .nbl1_pin = { .pin = GPIO_PIN(PORT_E, 1), .af = GPIO_AF12, }, /* PSRAM_NBL1 signal (UB) */ +}; + +/** + * @brief FMC Bank configuration + * + * The board has a PSRAM IS66WV51216EBLL-55BLI with MBit on-board. + * It is organized in 512K x 16 bits and connected to bank 1, subbank 1 + * at address 0x60000000. + * + * @note A18 of the PSRAM is not used. Therefore, only 256K x 16 bits + * (512 kByte) of the 1 MByte PSRAM can be used. + * + * The LCD display of the board is connected to bank 1, subbank2 + * at address 0x64000000. + */ +static const fmc_bank_conf_t fmc_bank_config[] = { + /* bank 1, subbank 1 is used for PSRAM with asynchronuous + * access in Mode 1, i.e. write timings are not used */ + { + .bank = FMC_BANK_1, + .mem_type = FMC_SRAM, + .data_width = FMC_BUS_WIDTH_16BIT, + .address = 0x60000000, /* Bank 1, subbank 1 is mapped to 0x60000000 */ + .size = KiB(512), /* Size in byte, 256K x 16 bit */ + .nor_sram = { + .sub_bank = 1, + .ext_mode = false, /* Mode 1 used, no separate w_timing */ + /* timings for IS66WV51216EBLL-55BLI + @216 MHz AHB clock */ + .r_timing = { .addr_setup = 13, /* t_AA = max 60 ns (13 HCLKs a 4.63 ns) */ + .data_setup = 6, /* t_SD = min 25 ns (6 HCLKs a 4.63 ns) */ + .bus_turnaround = 3, }, /* 3 HCLKs a 4.63 ns */ + }, + }, +}; + +/** + * @brief Number of configured FMC banks + */ +#define FMC_BANK_NUMOF ARRAY_SIZE(fmc_bank_config) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/boards/stm32f746g-disco/Kconfig b/boards/stm32f746g-disco/Kconfig index 94adb26dbc216..0b40bb655152d 100644 --- a/boards/stm32f746g-disco/Kconfig +++ b/boards/stm32f746g-disco/Kconfig @@ -15,6 +15,9 @@ config BOARD_STM32F746G_DISCO # Put defined MCU peripherals here (in alphabetical order) select HAS_PERIPH_DMA select HAS_PERIPH_ETH + select HAS_PERIPH_FMC + select HAS_PERIPH_FMC_SDRAM + select HAS_PERIPH_FMC_16BIT select HAS_PERIPH_I2C select HAS_PERIPH_LTDC select HAS_PERIPH_RTC diff --git a/boards/stm32f746g-disco/Makefile.dep b/boards/stm32f746g-disco/Makefile.dep index a3fab7ee7d994..fe69ca75f91da 100644 --- a/boards/stm32f746g-disco/Makefile.dep +++ b/boards/stm32f746g-disco/Makefile.dep @@ -13,3 +13,7 @@ endif ifneq (,$(filter touch_dev,$(USEMODULE))) USEMODULE += ft5x06 endif + +ifneq (,$(filter periph_fmc,$(USEMODULE))) + FEATURES_REQUIRED += periph_fmc_16bit +endif diff --git a/boards/stm32f746g-disco/Makefile.include b/boards/stm32f746g-disco/Makefile.include index 8cd0470e6ac17..75ad7ead50a40 100644 --- a/boards/stm32f746g-disco/Makefile.include +++ b/boards/stm32f746g-disco/Makefile.include @@ -13,3 +13,6 @@ PROGRAMMERS_SUPPORTED += openocd # The board can become un-flashable after some execution, # use connect_assert_srst to always be able to flash or reset the board. OPENOCD_RESET_USE_CONNECT_ASSERT_SRST ?= 1 + +FMC_RAM_ADDR=0xc0000000 +FMC_RAM_LEN=8192K diff --git a/boards/stm32f746g-disco/features-shared.mk b/boards/stm32f746g-disco/features-shared.mk index 7678cd7dca847..27ce5d56deab0 100644 --- a/boards/stm32f746g-disco/features-shared.mk +++ b/boards/stm32f746g-disco/features-shared.mk @@ -1,6 +1,9 @@ # Put defined MCU peripherals here (in alphabetical order) FEATURES_PROVIDED += periph_dma FEATURES_PROVIDED += periph_eth +FEATURES_PROVIDED += periph_fmc +FEATURES_PROVIDED += periph_fmc_16bit +FEATURES_PROVIDED += periph_fmc_sdram FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_ltdc FEATURES_PROVIDED += periph_rtc diff --git a/boards/stm32f746g-disco/include/periph_conf.h b/boards/stm32f746g-disco/include/periph_conf.h index 9ad0453e91f65..3a4aa377d6ef8 100644 --- a/boards/stm32f746g-disco/include/periph_conf.h +++ b/boards/stm32f746g-disco/include/periph_conf.h @@ -321,6 +321,115 @@ static const dwc2_usb_otg_fshs_config_t dwc2_usb_otg_fshs_config[] = { /** @} */ #endif /* defined(MODULE_PERIPH_USBDEV_HS_ULPI) || DOXYGEN */ +/** + * @name FMC configuration + * @{ + */ +/** + * @brief FMC controller configuration + */ +static const fmc_conf_t fmc_config = { + .bus = AHB3, + .rcc_mask = RCC_AHB3ENR_FMCEN, +#if MODULE_PERIPH_FMC_SDRAM + .ba0_pin = { .pin = GPIO_PIN(PORT_G, 4), .af = GPIO_AF12, }, /* FMC_BA0 signal */ + .ba1_pin = { .pin = GPIO_PIN(PORT_G, 5), .af = GPIO_AF12, }, /* FMC_BA1 signal */ + .sdclk_pin = { .pin = GPIO_PIN(PORT_G, 8), .af = GPIO_AF12, }, /* FMC_SDCLK signal */ + .sdnwe_pin = { .pin = GPIO_PIN(PORT_H, 5), .af = GPIO_AF12, }, /* FMC_SDNWE signal */ + .sdnras_pin = { .pin = GPIO_PIN(PORT_F, 11), .af = GPIO_AF12, }, /* FMC_SDNRAS signal */ + .sdncas_pin = { .pin = GPIO_PIN(PORT_G, 15), .af = GPIO_AF12, }, /* FMC_SDNCAS signal */ + .sdcke0_pin = { .pin = GPIO_PIN(PORT_C, 3), .af = GPIO_AF12, }, /* FMC_SDCKE0 signal */ + .sdne0_pin = { .pin = GPIO_PIN(PORT_H, 3), .af = GPIO_AF12, }, /* FMC_SDNE0 signal */ + .addr = { + { .pin = GPIO_PIN(PORT_F, 0), .af = GPIO_AF12, }, /* FMC_A0 signal */ + { .pin = GPIO_PIN(PORT_F, 1), .af = GPIO_AF12, }, /* FMC_A1 signal */ + { .pin = GPIO_PIN(PORT_F, 2), .af = GPIO_AF12, }, /* FMC_A2 signal */ + { .pin = GPIO_PIN(PORT_F, 3), .af = GPIO_AF12, }, /* FMC_A3 signal */ + { .pin = GPIO_PIN(PORT_F, 4), .af = GPIO_AF12, }, /* FMC_A4 signal */ + { .pin = GPIO_PIN(PORT_F, 5), .af = GPIO_AF12, }, /* FMC_A5 signal */ + { .pin = GPIO_PIN(PORT_F, 12), .af = GPIO_AF12, }, /* FMC_A6 signal */ + { .pin = GPIO_PIN(PORT_F, 13), .af = GPIO_AF12, }, /* FMC_A7 signal */ + { .pin = GPIO_PIN(PORT_F, 14), .af = GPIO_AF12, }, /* FMC_A8 signal */ + { .pin = GPIO_PIN(PORT_F, 15), .af = GPIO_AF12, }, /* FMC_A9 signal */ + { .pin = GPIO_PIN(PORT_G, 0), .af = GPIO_AF12, }, /* FMC_A10 signal */ + { .pin = GPIO_PIN(PORT_G, 1), .af = GPIO_AF12, }, /* FMC_A11 signal */ + }, +#endif + .data = { + { .pin = GPIO_PIN(PORT_D, 14), .af = GPIO_AF12, }, /* FMC_D0 signal */ + { .pin = GPIO_PIN(PORT_D, 15), .af = GPIO_AF12, }, /* FMC_D1 signal */ + { .pin = GPIO_PIN(PORT_D, 0), .af = GPIO_AF12, }, /* FMC_D2 signal */ + { .pin = GPIO_PIN(PORT_D, 1), .af = GPIO_AF12, }, /* FMC_D3 signal */ + { .pin = GPIO_PIN(PORT_E, 7), .af = GPIO_AF12, }, /* FMC_D4 signal */ + { .pin = GPIO_PIN(PORT_E, 8), .af = GPIO_AF12, }, /* FMC_D5 signal */ + { .pin = GPIO_PIN(PORT_E, 9), .af = GPIO_AF12, }, /* FMC_D6 signal */ + { .pin = GPIO_PIN(PORT_E, 10), .af = GPIO_AF12, }, /* FMC_D7 signal */ +#if MODULE_PERIPH_FMC_16BIT + { .pin = GPIO_PIN(PORT_E, 11), .af = GPIO_AF12, }, /* FMC_D8 signal */ + { .pin = GPIO_PIN(PORT_E, 12), .af = GPIO_AF12, }, /* FMC_D9 signal */ + { .pin = GPIO_PIN(PORT_E, 13), .af = GPIO_AF12, }, /* FMC_D10 signal */ + { .pin = GPIO_PIN(PORT_E, 14), .af = GPIO_AF12, }, /* FMC_D11 signal */ + { .pin = GPIO_PIN(PORT_E, 15), .af = GPIO_AF12, }, /* FMC_D12 signal */ + { .pin = GPIO_PIN(PORT_D, 8), .af = GPIO_AF12, }, /* FMC_D13 signal */ + { .pin = GPIO_PIN(PORT_D, 9), .af = GPIO_AF12, }, /* FMC_D14 signal */ + { .pin = GPIO_PIN(PORT_D, 10), .af = GPIO_AF12, }, /* FMC_D15 signal */ +#endif + }, + .nbl0_pin = { .pin = GPIO_PIN(PORT_E, 0), .af = GPIO_AF12, }, /* FMC_NBL0 signal (LB) */ + .nbl1_pin = { .pin = GPIO_PIN(PORT_E, 1), .af = GPIO_AF12, }, /* FMC_NBL1 signal (UB) */ +}; + +/** + * @brief FMC Bank configuration + * + * The board has a SDRAM IS42S32400F-6BL with 128 MBit on-board. + * It is organized in 4 banks of 1M x 32 bits each and connected to bank 5 + * at address 0xc0000000. + * + * @note Since only D0 to D15 are connected and D16 to D31 are unused, 4 banks + * with only 1M x 16 bits and thus half the capacity (8 MByte) can be + * used. + */ +static const fmc_bank_conf_t fmc_bank_config[] = { + /* bank 5 is used for SDRAM */ + { + .bank = FMC_BANK_5, + .mem_type = FMC_SDRAM, + .data_width = FMC_BUS_WIDTH_16BIT, + .address = 0xc0000000, /* Bank 6 is mapped to 0xd0000000 */ + .size = MiB(8), /* Size in MByte, 4M x 16 Bit */ + .sdram = { + .clk_period = 2, /* SDCLK = 2 x HCLK */ + .row_bits = 12, /* A11..A0 used for row address */ + .col_bits = 8, /* A7..A0 used for column address */ + .cas_latency = 2, /* CAS latency is 2 clock cycles */ + .read_delay = 0, /* No read delay after CAS */ + .burst_read = true, /* Burst read mode enabled */ + .burst_write = false, /* Burst write mode disabled */ + .burst_len = FMC_BURST_LENGTH_1, /* Burst length is 1 */ + .burst_interleaved = false, /* Burst mode interleaved */ + .write_protect = false, /* No write protection */ + .four_banks = true, /* SDRAM has four internal banks */ + .timing = { /* SDRAM Timing parameters */ + .row_to_col_delay = 2, /* Row to column delay (2 clock cycles) */ + .row_precharge = 2, /* Row precharge delay (2 clock cycles) */ + .recovery_delay = 2, /* Recovery delay is (2 clock cycles) */ + .row_cylce = 7, /* Row cycle delay is (7 clock cycles) */ + .self_refresh = 4, /* Self refresh time is (4 clock cycles) */ + .exit_self_refresh = 7, /* Exit self-refresh delay (7 clock cycles) */ + .load_mode_register = 2, /* Load Mode Register to Activate delay */ + .refresh_period = 16, /* Refresh period in ms */ + }, + }, + }, +}; + +/** + * @brief Number of configured FMC banks + */ +#define FMC_BANK_NUMOF ARRAY_SIZE(fmc_bank_config) +/** @} */ + #ifdef __cplusplus } #endif diff --git a/boards/stm32f7508-dk/Kconfig b/boards/stm32f7508-dk/Kconfig index 7c0ee5a55fa5b..775528664a7df 100644 --- a/boards/stm32f7508-dk/Kconfig +++ b/boards/stm32f7508-dk/Kconfig @@ -15,6 +15,9 @@ config BOARD_STM32F7508_DK # Put defined MCU peripherals here (in alphabetical order) select HAS_PERIPH_DMA select HAS_PERIPH_ETH + select HAS_PERIPH_FMC + select HAS_PERIPH_FMC_SDRAM + select HAS_PERIPH_FMC_16BIT select HAS_PERIPH_I2C select HAS_PERIPH_LTDC select HAS_PERIPH_RTC diff --git a/boards/stm32l496g-disco/Kconfig b/boards/stm32l496g-disco/Kconfig index 44f34f387bb90..b041984ac964f 100644 --- a/boards/stm32l496g-disco/Kconfig +++ b/boards/stm32l496g-disco/Kconfig @@ -17,6 +17,9 @@ config BOARD_STM32L496G_DISCO select HAS_PERIPH_ADC select HAS_PERIPH_DAC select HAS_PERIPH_DMA + select HAS_PERIPH_FMC + select HAS_PERIPH_FMC_NOR_SRAM + select HAS_PERIPH_FMC_16BIT select HAS_PERIPH_I2C select HAS_PERIPH_LPUART select HAS_PERIPH_RTC diff --git a/boards/stm32l496g-disco/Makefile.dep b/boards/stm32l496g-disco/Makefile.dep index 27f8d0c45ecd5..b53a09193aa45 100644 --- a/boards/stm32l496g-disco/Makefile.dep +++ b/boards/stm32l496g-disco/Makefile.dep @@ -10,6 +10,10 @@ ifneq (,$(filter touch_dev,$(USEMODULE))) USEMODULE += ft5x06 endif +ifneq (,$(filter periph_fmc,$(USEMODULE))) + FEATURES_REQUIRED += periph_fmc_16bit +endif + ifneq (,$(filter periph_uart,$(USEMODULE))) USEMODULE += periph_lpuart ifeq (,$(filter periph_spi_stmod_plus,$(USEMODULE))) diff --git a/boards/stm32l496g-disco/Makefile.features b/boards/stm32l496g-disco/Makefile.features index 45a92f47d7073..c284ab673f17c 100644 --- a/boards/stm32l496g-disco/Makefile.features +++ b/boards/stm32l496g-disco/Makefile.features @@ -5,6 +5,9 @@ CPU_MODEL = stm32l496ag FEATURES_PROVIDED += periph_adc FEATURES_PROVIDED += periph_dac FEATURES_PROVIDED += periph_dma +FEATURES_PROVIDED += periph_fmc +FEATURES_PROVIDED += periph_fmc_16bit +FEATURES_PROVIDED += periph_fmc_nor_sram FEATURES_PROVIDED += periph_i2c FEATURES_PROVIDED += periph_lpuart FEATURES_PROVIDED += periph_rtc diff --git a/boards/stm32l496g-disco/Makefile.include b/boards/stm32l496g-disco/Makefile.include index 039078d9865e0..b0f06c87076bf 100644 --- a/boards/stm32l496g-disco/Makefile.include +++ b/boards/stm32l496g-disco/Makefile.include @@ -16,3 +16,6 @@ ifneq (,$(filter usbus_dfu tinyusb_dfu,$(USEMODULE))) else RIOTBOOT_LEN ?= 0x2000 endif + +FMC_RAM_ADDR=0x64000000 +FMC_RAM_LEN=1024K diff --git a/boards/stm32l496g-disco/doc.txt b/boards/stm32l496g-disco/doc.txt index 4b833122c64b3..1449687af614c 100644 --- a/boards/stm32l496g-disco/doc.txt +++ b/boards/stm32l496g-disco/doc.txt @@ -36,7 +36,7 @@ The main features of this board are: | Capacitive Touch Screen | - | FT3267 used as driver IC (not supported yet) | | Stereo microphones | - | | | SAI audio codec | - | | -| External PSRAM | - | Connected to FMC peripheral (not supported yet) | +| External PSRAM | x | Connected to FMC peripheral | | External Quad-SPI Flash | - | QSPI peripheral is not yet supported | | SD Card Interface | - | | diff --git a/boards/stm32l496g-disco/include/periph_conf.h b/boards/stm32l496g-disco/include/periph_conf.h index d6fd662908c4b..7fb21039b9bf0 100644 --- a/boards/stm32l496g-disco/include/periph_conf.h +++ b/boards/stm32l496g-disco/include/periph_conf.h @@ -156,15 +156,111 @@ static const dac_conf_t dac_config[] = { { GPIO_PIN(PORT_A, 5), .chan = 1 }, /* Arduino D13, conflicts with SPI_DEV(0) */ #endif }; -/** @}*/ /** * @brief Number of DACs - * @{ */ #define DAC_NUMOF ARRAY_SIZE(dac_config) /** @} */ +/** + * @name FMC configuration + * @{ + */ + +/** + * @brief FMC controller configuration + */ +static const fmc_conf_t fmc_config = { + .bus = AHB3, + .rcc_mask = RCC_AHB3ENR_FMCEN, +#if MODULE_PERIPH_FMC_NOR_SRAM + .ne1_pin = { .pin = GPIO_PIN(PORT_D, 7), .af = GPIO_AF12, }, /* LCD_NE signal, subbank 1 */ + .ne2_pin = { .pin = GPIO_PIN(PORT_G, 9), .af = GPIO_AF12, }, /* PSRAM_NE signal, subbank 2 */ + .noe_pin = { .pin = GPIO_PIN(PORT_D, 4), .af = GPIO_AF12, }, /* PSRAM/LCD_OE signal (OE) */ + .nwe_pin = { .pin = GPIO_PIN(PORT_D, 5), .af = GPIO_AF12, }, /* PSRAM/LCD_WE signal (WE) */ + .addr = { + { .pin = GPIO_PIN(PORT_F, 0), .af = GPIO_AF12, }, /* PSRAM_A0 signal */ + { .pin = GPIO_PIN(PORT_F, 1), .af = GPIO_AF12, }, /* PSRAM_A1 signal */ + { .pin = GPIO_PIN(PORT_F, 2), .af = GPIO_AF12, }, /* PSRAM_A2 signal */ + { .pin = GPIO_PIN(PORT_F, 3), .af = GPIO_AF12, }, /* PSRAM_A3 signal */ + { .pin = GPIO_PIN(PORT_F, 4), .af = GPIO_AF12, }, /* PSRAM_A4 signal */ + { .pin = GPIO_PIN(PORT_F, 5), .af = GPIO_AF12, }, /* PSRAM_A5 signal */ + { .pin = GPIO_PIN(PORT_F, 12), .af = GPIO_AF12, }, /* PSRAM_A6 signal */ + { .pin = GPIO_PIN(PORT_F, 13), .af = GPIO_AF12, }, /* PSRAM_A7 signal */ + { .pin = GPIO_PIN(PORT_F, 14), .af = GPIO_AF12, }, /* PSRAM_A8 signal */ + { .pin = GPIO_PIN(PORT_F, 15), .af = GPIO_AF12, }, /* PSRAM_A9 signal */ + { .pin = GPIO_PIN(PORT_G, 0), .af = GPIO_AF12, }, /* PSRAM_A10 signal */ + { .pin = GPIO_PIN(PORT_G, 1), .af = GPIO_AF12, }, /* PSRAM_A11 signal */ + { .pin = GPIO_PIN(PORT_G, 2), .af = GPIO_AF12, }, /* PSRAM_A12 signal */ + { .pin = GPIO_PIN(PORT_G, 3), .af = GPIO_AF12, }, /* PSRAM_A13 signal */ + { .pin = GPIO_PIN(PORT_G, 4), .af = GPIO_AF12, }, /* PSRAM_A14 signal */ + { .pin = GPIO_PIN(PORT_G, 5), .af = GPIO_AF12, }, /* PSRAM_A15 signal */ + { .pin = GPIO_PIN(PORT_D, 11), .af = GPIO_AF12, }, /* PSRAM_A16 signal */ + { .pin = GPIO_PIN(PORT_D, 12), .af = GPIO_AF12, }, /* PSRAM_A17 signal */ + { .pin = GPIO_PIN(PORT_D, 13), .af = GPIO_AF12, }, /* PSRAM_A18 / LCD_RS signal */ + }, +#endif + .data = { + { .pin = GPIO_PIN(PORT_D, 14), .af = GPIO_AF12, }, /* PSRAM_D0 / LCD_D0 signal */ + { .pin = GPIO_PIN(PORT_D, 15), .af = GPIO_AF12, }, /* PSRAM_D1 / LCD_D1 signal */ + { .pin = GPIO_PIN(PORT_D, 0), .af = GPIO_AF12, }, /* PSRAM_D2 / LCD_D2 signal */ + { .pin = GPIO_PIN(PORT_D, 1), .af = GPIO_AF12, }, /* PSRAM_D3 / LCD_D3 signal */ + { .pin = GPIO_PIN(PORT_E, 7), .af = GPIO_AF12, }, /* PSRAM_D4 / LCD_D4 signal */ + { .pin = GPIO_PIN(PORT_E, 8), .af = GPIO_AF12, }, /* PSRAM_D5 / LCD_D5 signal */ + { .pin = GPIO_PIN(PORT_E, 9), .af = GPIO_AF12, }, /* PSRAM_D6 / LCD_D6 signal */ + { .pin = GPIO_PIN(PORT_E, 10), .af = GPIO_AF12, }, /* PSRAM_D7 / LCD_D7 signal */ +#if MODULE_PERIPH_FMC_16BIT + { .pin = GPIO_PIN(PORT_E, 11), .af = GPIO_AF12, }, /* PSRAM_D8 / LCD_D8 signal */ + { .pin = GPIO_PIN(PORT_E, 12), .af = GPIO_AF12, }, /* PSRAM_D9 / LCD_D9 signal */ + { .pin = GPIO_PIN(PORT_E, 13), .af = GPIO_AF12, }, /* PSRAM_D10 / LCD_D10 signal */ + { .pin = GPIO_PIN(PORT_E, 14), .af = GPIO_AF12, }, /* PSRAM_D11 / LCD_D11 signal */ + { .pin = GPIO_PIN(PORT_E, 15), .af = GPIO_AF12, }, /* PSRAM_D12 / LCD_D12 signal */ + { .pin = GPIO_PIN(PORT_D, 8), .af = GPIO_AF12, }, /* PSRAM_D13 / LCD_D13 signal */ + { .pin = GPIO_PIN(PORT_D, 9), .af = GPIO_AF12, }, /* PSRAM_D14 / LCD_D14 signal */ + { .pin = GPIO_PIN(PORT_D, 10), .af = GPIO_AF12, }, /* PSRAM_D15 / LCD_D15 signal */ +#endif + }, + .nbl0_pin = { .pin = GPIO_PIN(PORT_E, 0), .af = GPIO_AF12, }, /* PSRAM_NBL0 signal (LB) */ + .nbl1_pin = { .pin = GPIO_PIN(PORT_E, 1), .af = GPIO_AF12, }, /* PSRAM_NBL1 signal (UB) */ +}; + +/** + * @brief FMC Bank configuration + * + * The board has a PSRAM IS66WV51216EBLL-70BLI with 8 MBit on-board. + * It is organized in 512K x 16 bits and connected to bank 1, subbank 2 + * at address 0x64000000. + * + * The LCD display of the board is connected to bank 1, subbank1 + * at address 0x60000000. + */ +static const fmc_bank_conf_t fmc_bank_config[] = { + /* bank 1, subbank 2 is used for PSRAM with asynchronuous + * access in Mode 1, i.e. write timings are not used */ + { + .bank = FMC_BANK_1, + .mem_type = FMC_SRAM, + .data_width = FMC_BUS_WIDTH_16BIT, + .address = 0x64000000, /* Bank 1, subbank 2 is mapped to 0x64000000 */ + .size = MiB(1), /* Size in Mbyte, 512K x 16 bit */ + .nor_sram = { + .sub_bank = 2, + .ext_mode = false, /* Mode 1 used, no separate w_timing */ + /* timings for IS66WV51216EBLL-70BLI */ + .r_timing = { .addr_setup = 6, /* t_AA = 70 ns (6 HCLKs a 12.5 ns) */ + .data_setup = 2, /* t_SD = 30 ns (3 HCLKs a 12.5 ns) */ + .bus_turnaround = 1, }, /* 1 HCLK a 12.5 ns */ + }, + }, +}; + +/** + * @brief Number of configured FMC banks + */ +#define FMC_BANK_NUMOF ARRAY_SIZE(fmc_bank_config) +/** @} */ + /** * @name I2C configuration * diff --git a/cpu/stm32/Kconfig b/cpu/stm32/Kconfig index 07a920dc459af..1641e79bc54e6 100644 --- a/cpu/stm32/Kconfig +++ b/cpu/stm32/Kconfig @@ -74,6 +74,8 @@ config RDP2 bool "RDP2" endchoice +rsource "periph/Kconfig.fmc" + if TEST_KCONFIG rsource "periph/Kconfig" diff --git a/cpu/stm32/Makefile.dep b/cpu/stm32/Makefile.dep index 4bc8896a49508..2b85221dc165c 100644 --- a/cpu/stm32/Makefile.dep +++ b/cpu/stm32/Makefile.dep @@ -79,4 +79,8 @@ ifneq (,$(filter periph_vbat,$(USEMODULE))) FEATURES_REQUIRED += periph_adc endif +ifneq (,$(filter periph_fmc_%,$(USEMODULE))) + FEATURES_REQUIRED += periph_fmc +endif + include $(RIOTCPU)/cortexm_common/Makefile.dep diff --git a/cpu/stm32/Makefile.include b/cpu/stm32/Makefile.include index 5afe9f77c5a26..962e53e241962 100644 --- a/cpu/stm32/Makefile.include +++ b/cpu/stm32/Makefile.include @@ -57,7 +57,6 @@ info-stm32: @$(COLOR_ECHO) "\tROM size:\t$(ROM_LEN) ($(FLASHSIZE) Bytes)" @$(COLOR_ECHO) "\tRAM size:\t$(RAM_LEN_K)KiB" - ifneq (,$(CCMRAM_LEN)) LINKFLAGS += $(LINKFLAGPREFIX)--defsym=_ccmram_length=$(CCMRAM_LEN) endif @@ -65,6 +64,17 @@ ifneq (,$(SRAM4_LEN)) LINKFLAGS += $(LINKFLAGPREFIX)--defsym=_sram4_length=$(SRAM4_LEN) endif +# if the board uses a FMC RAM, all 4 heaps have to be used even if +# some of them are not available +ifneq (,$(filter periph_fmc_sdram periph_fmc_nor_sram,$(USEMODULE))) + ifneq (,$(FMC_RAM_LEN)) + LINKFLAGS += $(LINKFLAGPREFIX)--defsym=_fmc_ram_addr=$(FMC_RAM_ADDR) + LINKFLAGS += $(LINKFLAGPREFIX)--defsym=_fmc_ram_len=$(FMC_RAM_LEN) + CFLAGS += -DCPU_HAS_FMC_RAM=1 + CFLAGS += -DNUM_HEAPS=4 + endif +endif + VECTORS_O ?= $(BINDIR)/stm32_vectors/$(CPU_LINE).o VECTORS_FILE = $(RIOTCPU)/stm32/vectors/$(CPU_LINE).c BUILDDEPS += $(VECTORS_FILE) diff --git a/cpu/stm32/include/periph/cpu_fmc.h b/cpu/stm32/include/periph/cpu_fmc.h new file mode 100644 index 0000000000000..528c6d8d0f87a --- /dev/null +++ b/cpu/stm32/include/periph/cpu_fmc.h @@ -0,0 +1,370 @@ +/* + * Copyright (C) 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @defgroup cpu_stm32_periph_fmc STM32 FMC/FSMC peripheral driver + * @ingroup cpu_stm32 + * + * @{ + * + * The `periph_fmc` module implements a driver for STM32 FMC/FSMC peripherals. + * It supports + * - NOR Flashes, + * - PSRAMs/SRAMs, + * - SDRAMs, and + * - Display Controllers with MCU 8-/16-bit parallel interface. + * + * NAND Flashes are not yet supported. + * + * To use the FMC/FSMC, the `periph_fmc` module has to be enabled. + * To keep required data structures and resulting code as small as possible, + * a couple of pseudomodules are defined that have to be used in addition to + * the `periph_fmc` module to enable supported features. These are: + * + * | Module | Feature | + * |:-----------------------|:----------------------------------------| + * | `periph_fmc_nor_sram` | enable NOR Flash and PSRAM/SRAM support | + * | `periph_fmc_sdram` | enable SDRAM support | + * | `periph_fmc_16bit` | enable 16-bit support | + * | `periph_fmc_32bit` | enable 32-bit support | + * \n + * + * The board has then to define + * - the corresponding features according to the connected external device, + * - the peripheral configuration of the FMC/FSMC of type @ref fmc_conf_t, + * - the configuration of the FMC banks which describe the connected external + * devices. + * + * As examples for such configurations, see @ref boards_stm32l496g-disco + * (FMC with Display and SRAM) or @ref boards_stm32f429i-disc1 (FMC with SDRAM). + * + * To use the RAM connected to the FMC as heap, the board has also to define + * @ref FMC_RAM_ADDR and @ref FMC_RAM_LEN. + * + * @file + * @brief Specific FMC definitions for the STM32 + * + * @author Gunar Schorcht + */ + +#ifndef PERIPH_CPU_FMC_H +#define PERIPH_CPU_FMC_H + +#include + +#include "cpu.h" +#include "periph/cpu_gpio.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Number of data pins used + * + * The number of configured data pins depends on the memory with the maximum + * data bus width. The maximum data bus width used has to be specified by the + * pseudomodules `periph_fmc_16bit` and `periph_fmc_32bit`. 8 bit data bus + * width is the default. + */ +#if MODULE_PERIPH_FMC_32BIT || DOXYGEN +#define FMC_DATA_PIN_NUMOF (32) +#elif MODULE_PERIPH_FMC_16BIT +#define FMC_DATA_PIN_NUMOF (16) +#else +#define FMC_DATA_PIN_NUMOF (8) +#endif + +/** + * @brief Number of address pins used + * + * The number of configured address pins depends on the memory types used. + * NORs, PSRAMs and SRAMs (module `periph_fmc_nor_sram`) use up to 26 address + * signals A0...A25 if address/data multiplexing is not used. SDRAMs (module + * `periph_fmc_sdram`) use only up to 13 address signals A0...A12. + * NANDs (module `periph_fmc_sdram`) don't need separate address signals. + * The type of used memories is specified by the corresponding pseudomodules. + */ +#if MODULE_PERIPH_FMC_NOR_SRAM || DOXYGEN +#define FMC_ADDR_PIN_NUMOF (26) +#elif MODULE_PERIPH_FMC_SDRAM +#define FMC_ADDR_PIN_NUMOF (13) +#else +#define FMC_ADDR_PIN_NUMOF (0) +#endif + +#if DOXYGEN +/** + * @brief Start address of the heap for the FMC RAM + * + * This variable has to be defined in `Makefile.include` of the board + * definition to use the a RAM connected to the FMC as heap. It has to + * correspond to the @ref fmc_bank_conf_t::address in FMC bank configuration. + * + * @note `FMC_RAM_ADDR` is not a macro and cannot be used in C code. + * It is just defined for documentation purpose. + */ +#define FMC_RAM_ADDR 0x60000000 + +/** + * @brief Length of the heap for the FMC RAM + * + * This variable has to be defined in `Makefile.include` of the board + * definition to use the a RAM connected to the FMC as heap. It has to + * correspond to the @ref fmc_bank_conf_t::size in FMC bank configuration. + * Since it is used in linker script, it has to be defined in kByte with + * suffix `K`, e.g. `1024K`. + * + * @note `FMC_RAM_SIZE` is not a macro and cannot be used in C code. + * It is just defined for documentation purpose. + */ +#define FMC_RAM_LEN 1024K +#endif + +/** + * @name Type declarations for NOR Flash, PSRAM and SRAM + * @{ + */ +/** + * @brief Memory access modes for NOR/PSRAM/SRAM in extended mode + */ +typedef enum { + FMC_MODE_A = 0, /**< Access mode A */ + FMC_MODE_B = 1, /**< Access mode B */ + FMC_MODE_C = 2, /**< Access mode C */ + FMC_MODE_D = 3, /**< Access mode D */ +} fmc_access_mode_t; + +/** + * @brief Timing configuration for NOR/PSRAM/SRAM + * + * Used timing configuration parameters depend on used access mode. Please refer + * the reference manual of the respective MCU for details. + */ +typedef struct { + fmc_access_mode_t mode; /**< Access Mode used (only used if + @ref fmc_nor_sram_bank_conf_t::ext_mode is true) */ + uint8_t clk_div; /**< Clock divide ratio, FMC_CLK = HCLK / (DIV + 1) */ + uint8_t addr_setup; /**< Address setup time [0..15], default 15 */ + uint8_t addr_hold; /**< Address hold time [0..15], default 15 */ + uint8_t data_setup; /**< Data setup time [0..15], default 15 */ + uint8_t data_latency; /**< Data latency for synchronous access [0..15], + default 15 (only used in read timing) */ + uint8_t bus_turnaround; /**< Bus turnaround phase duration [0..15], default 15 */ +} fmc_nor_sram_timing_t; + +/** + * @brief Bank configuration structure for NOR/PSRAM/SRAM + */ +typedef struct { + uint8_t sub_bank; /**< Bank1 has 4 subbanks 1..4 */ + bool mux_enable; /**< Multiplexed address/data signals used + (only valid for PSRAMs and NORs */ + bool wait_enable; /**< Wait signal used for synchronous + access */ + bool ext_mode; /**< Extended mode used (separate read and + write timings) */ + fmc_nor_sram_timing_t r_timing; /**< Read timings (also used for write if + @ref fmc_nor_sram_bank_conf_t::ext_mode is false) */ + fmc_nor_sram_timing_t w_timing; /**< Write timings (only used if + @ref fmc_nor_sram_bank_conf_t::ext_mode is true) */ +} fmc_nor_sram_bank_conf_t; +/** @} */ + +/** + * @name Type declarations for SDRAMs + * @{ + */ +/** + * @brief SDRAM Burst Length as an exponent of a power of two + */ +typedef enum { + FMC_BURST_LENGTH_1 = 0, /* Burst length is 1 */ + FMC_BURST_LENGTH_2 = 1, /* Burst length is 2 */ + FMC_BURST_LENGTH_4 = 2, /* Burst length is 4 */ + FMC_BURST_LENGTH_8 = 3, /* Burst length is 8 */ + FMC_BURST_LENGTH_16 = 4, /* Burst length is 16 */ + FMC_BURST_LENGTH_32 = 5, /* Burst length is 32 */ + FMC_BURST_LENGTH_64 = 6, /* Burst length is 64 */ + FMC_BURST_LENGTH_FULL = 7, /* Burst length is full page */ +} fmc_bust_length_t; + +/** + * @brief Timing configuration for SDRAM + */ +typedef struct { + uint8_t row_to_col_delay; /**< Row to column delay in SDCLK clock + cycles [1..16], delay between Activate + and Read/Write command */ + uint8_t row_precharge; /**< Row precharge delay in SDCLK clock + cycles [1..15], delay between Precharge + and another command */ + uint8_t recovery_delay; /**< Recovery delay in SDCLK clock + cycles [1..15], delay between Write and + Precharge command */ + uint8_t row_cylce; /**< Row cycle delay in SDCLK clock + cycles [1..15], delay between Refresh and + Activate command */ + uint8_t self_refresh; /**< Self refresh time in SDCLK clock + cycles [1..15] */ + uint8_t exit_self_refresh; /**< Exit self-refresh delay in SDCLK clock + cycles [1..15], delay between Self-Refresh + and Activate command */ + uint8_t load_mode_register; /**< Load Mode Register to Activate delay in + SDCLK clock cycles [1..15], delay between + Load Mode Register and Activate command */ + uint8_t refresh_period; /**< Refresh period in milliseconds */ +} fmc_sdram_timing_t; + +/** + * @brief Bank configuration structure for SDRAM + */ +typedef struct { + uint8_t clk_period; /**< CLK period [0,2,3] (0 - disabled, n * HCLK cycles) */ + uint8_t row_bits; /**< Number row address bits [11..13] */ + uint8_t col_bits; /**< Number column address bits [8..11]*/ + uint8_t cas_latency; /**< CAS latency in SDCLK clock cycles [1..3] */ + uint8_t read_delay; /**< Delay for reading data after CAS latency in HCLKs [0..2] */ + bool four_banks; /**< SDRAM has four internal banks */ + bool write_protect; /**< Write protection enabled */ + bool burst_read; /**< Burst read mode enabled */ + bool burst_write; /**< Burst write mode enabled */ + bool burst_interleaved; /**< Burst mode interleaved, otherwise sequential */ + fmc_bust_length_t burst_len; /**< Burst length as an exponent of a power of two */ + fmc_sdram_timing_t timing; /**< SDRAM Timing configuration */ +} fmc_sdram_bank_conf_t; +/** @} */ + +/** + * @name Type declarations that are common for all memories + * @{ + */ +/** + * @brief FMC GPIO configuration type + */ +typedef struct { + gpio_t pin; /**< GPIO pin */ + gpio_af_t af; /**< Alternate function */ +} fmc_gpio_t; + +/** + * @brief FMC peripheral configuration + * + * The GPIOs are defined depending on used memory type according to the + * FMC pin definition in Table 12 of section 4 in the + * [Datasheet for STM32F765xx, STM32F767xx, STM32F768Ax, STM32F769xx] + * (https://www.st.com/resource/en/datasheet/stm32f767zi.pdf). + * Which memory types are used is defined by the pseudomodules + * `periph_fmc_nor_sram`, `periph_fmc_nand` and `periph_fmc_sdram` + * + * @note For easier handling the configuration structure does not take + * multiplexed address/data bits into account. + */ +typedef struct { + uint8_t bus; /**< AHB/APB bus */ + uint32_t rcc_mask; /**< Bit in clock enable register */ + fmc_gpio_t data[FMC_DATA_PIN_NUMOF]; /**< Data pins D0 ... */ +#if FMC_ADDR_PIN_NUMOF || DOXYGEN + fmc_gpio_t addr[FMC_ADDR_PIN_NUMOF]; /**< Address pins A0 ... if any */ +#endif + /* signals used by all kind of memories */ + fmc_gpio_t nbl0_pin; /**< NBL0 pin */ + fmc_gpio_t nbl1_pin; /**< NBL1 pin */ + fmc_gpio_t nbl2_pin; /**< NBL2 pin */ + fmc_gpio_t nbl3_pin; /**< NBL3 pin */ + fmc_gpio_t nwait_pin; /**< NWAIT pin */ +#if MODULE_PERIPH_FMC_NOR_SRAM + /* NORs, PSRAMs, and SRAMs use CLK, NOE, NWE, NE, and NADV signals **/ + fmc_gpio_t clk_pin; /**< CLK pin */ + fmc_gpio_t noe_pin; /**< NOE pin (output enable / read enable) */ + fmc_gpio_t nwe_pin; /**< NWE pin (write enable) */ + fmc_gpio_t ne1_pin; /**< NE1 pin (subbank 1 enable) */ + fmc_gpio_t ne2_pin; /**< NE2 pin (subbank 2 enable) */ + fmc_gpio_t ne3_pin; /**< NE3 pin (subbank 3 enable) */ + fmc_gpio_t ne4_pin; /**< NE4 pin (subbank 4 enable) */ + fmc_gpio_t nadv_pin; /**< NADV pin (address valid) */ +#endif /* MODULE_PERIPH_FMC_NORSRAM */ +#if MODULE_PERIPH_FMC_SDRAM + /* SDRAMs use BAx, CLK, RAS, CAS, WE, ... signals **/ + fmc_gpio_t ba0_pin; /**< BA0 pin */ + fmc_gpio_t ba1_pin; /**< BA1 pin */ + fmc_gpio_t sdclk_pin; /**< SDCLK pin */ + fmc_gpio_t sdnwe_pin; /**< SDWE pin */ + fmc_gpio_t sdnras_pin; /**< SDNRAS pin */ + fmc_gpio_t sdncas_pin; /**< SDNCAS pin */ + fmc_gpio_t sdcke0_pin; /**< SDCKE0 pin (SDRAM bank 5 clock enable) */ + fmc_gpio_t sdcke1_pin; /**< SDCKE1 pin (SDRAM bank 6 clock enable) */ + fmc_gpio_t sdne0_pin; /**< SDNE0 pin (SDRAM bank 5 enable) */ + fmc_gpio_t sdne1_pin; /**< SDNE1 pin (SDRAM bank 6 enable) */ +#endif /* MODULE_PERIPH_FMC_SDRAM */ +} fmc_conf_t; + +/** + * @brief Memory banks + */ +typedef enum { + FMC_BANK_1 = 1, /**< Bank 1 is always available and used for NOR, PSRAM, SRAM */ +#if defined(FMC_Bank2_3_R_BASE) + FMC_BANK_2 = 2, /**< Bank2 is used for NAND if available */ +#endif +#if defined(FMC_Bank2_3_R_BASE) || defined(FMC_Bank3_R_BASE) + FMC_BANK_3 = 3, /**< Bank3 is used for NAND if available */ +#endif +#if defined(FMC_Bank4_R_BASE) + FMC_BANK_4 = 4, /**< Bank4 is used for PC Card if available */ +#endif +#if defined(FMC_Bank5_6_R_BASE) + FMC_BANK_5 = 5, /**< Bank5 is used for SDRAM if available */ + FMC_BANK_6 = 6, /**< Bank6 is used for SDRAM if available */ +#endif +} fmc_bank_t; + +/** + * @brief Memory types supported by the FMC controller + */ +typedef enum { + FMC_SRAM = 0, /**< SRAM */ + FMC_PSRAM = 1, /**< PSRAM */ + FMC_NOR = 2, /**< NOR Flash */ + FMC_NAND = 3, /**< NAND Flash */ + FMC_SDRAM = 4, /**< SDRAM Controller used */ +} fmc_mem_type_t; + +/** + * @brief Memory data bus widths + */ +typedef enum { + FMC_BUS_WIDTH_8BIT = 0, /**< 8 bit data bus width */ + FMC_BUS_WIDTH_16BIT = 1, /**< 16 bit data bus width */ + FMC_BUS_WIDTH_32BIT = 2, /**< 32 bit data bus width */ +} fmc_bus_width_t; + +/** + * @brief Bank configuration structure + */ +typedef struct { + fmc_bank_t bank; /**< Bank1 .. Bank4/Bank6 (maximum) */ + fmc_mem_type_t mem_type; /**< Type of memory */ + fmc_bus_width_t data_width; /**< Data bus width */ + uint32_t address; /**< Address of the memory bank */ + uint32_t size; /**< Size in bytes of the memory bank */ + union { + fmc_nor_sram_bank_conf_t nor_sram; /* Configuration in case of NOR/PSRAM/SRAM */ + fmc_sdram_bank_conf_t sdram; /* Configuration in case of SDRAM */ + }; +} fmc_bank_conf_t; + +typedef uint8_t fmc_bank_id_t; /**< FMC bank identifier */ +/** @} */ + +#ifdef __cplusplus +} +#endif + +#endif /* PERIPH_CPU_FMC_H */ +/** @} */ diff --git a/cpu/stm32/include/periph_cpu.h b/cpu/stm32/include/periph_cpu.h index c8b27306ff3ae..95a9bd4036bfc 100644 --- a/cpu/stm32/include/periph_cpu.h +++ b/cpu/stm32/include/periph_cpu.h @@ -62,6 +62,7 @@ #include "periph/cpu_common.h" #include "periph/cpu_dma.h" #include "periph/cpu_eth.h" +#include "periph/cpu_fmc.h" #include "periph/cpu_gpio.h" #include "periph/cpu_gpio_ll.h" #include "periph/cpu_i2c.h" diff --git a/cpu/stm32/ldscripts/stm32.ld b/cpu/stm32/ldscripts/stm32.ld index e1434277435e1..23234ee77ab2c 100644 --- a/cpu/stm32/ldscripts/stm32.ld +++ b/cpu/stm32/ldscripts/stm32.ld @@ -19,12 +19,18 @@ */ ccmram_length = DEFINED( _ccmram_len ) ? _ccmram_len : 0x0 ; + +sram4_addr = DEFINED( _sram4_length ) ? 0x28000000 : 0x0 ; sram4_length = DEFINED( _sram4_length ) ? _sram4_length : 0x0 ; +fmc_ram_addr = DEFINED( _fmc_ram_addr ) ? _fmc_ram_addr : 0x0 ; +fmc_ram_len = DEFINED( _fmc_ram_len ) ? _fmc_ram_len : 0x0 ; + MEMORY { ccmram : ORIGIN = 0x10000000, LENGTH = ccmram_length - sram4 : ORIGIN = 0x28000000, LENGTH = sram4_length + sram4 : ORIGIN = sram4_addr, LENGTH = sram4_length + fmcram : ORIGIN = fmc_ram_addr, LENGTH = fmc_ram_len } SECTIONS @@ -34,6 +40,12 @@ SECTIONS _sheap2 = ORIGIN(sram4); _eheap2 = ORIGIN(sram4) + LENGTH(sram4); } > sram4 + + .heap4 ALIGN(4) (NOLOAD) : + { + _sheap3 = ORIGIN(fmcram); + _eheap3 = ORIGIN(fmcram) + LENGTH(fmcram); + } > fmcram } INCLUDE cortexm.ld diff --git a/cpu/stm32/periph/Kconfig.fmc b/cpu/stm32/periph/Kconfig.fmc new file mode 100644 index 0000000000000..e9304a96228f8 --- /dev/null +++ b/cpu/stm32/periph/Kconfig.fmc @@ -0,0 +1,97 @@ +# Copyright (c) 2023 Gunar Schorcht +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. +# + +menuconfig MODULE_PERIPH_FMC + bool "FMC/FSMC peripheral driver" + depends on TEST_KCONFIG + depends on HAS_PERIPH_FMC + help + STM32 FMC/FSMC controller + +if MODULE_PERIPH_FMC + +config MODULE_PERIPH_FMC_NOR_SRAM + bool "NOR/PSRAM/SRAM support" + depends on HAS_PERIPH_FMC_NOR_SRAM + help + Enables NOR Flash, PSRAM and SRAM support of the STM32 FMC/FSMC + driver. + +config MODULE_PERIPH_FMC_SDRAM + bool "SDRAM support" + depends on HAS_PERIPH_FMC_SDRAM + help + Enables SDRAM support of the STM32 FMC/FSMC driver. + +config MODULE_PERIPH_FMC_16BIT + bool + default y if HAS_PERIPH_FMC_16BIT + help + Enables 16-bit data bus support of the STM32 FMC/FSMC driver. + +config MODULE_PERIPH_FMC_32BIT + bool + default y if HAS_PERIPH_FMC_32BIT + help + Enables 32-bit data bus support of the STM32 FMC/FSMC driver. + +config MODULE_PERIPH_INIT_FMC + bool "Auto initialize STM32 FMC/FMSC peripheral" + default y if MODULE_PERIPH_INIT + +config MODULE_PERIPH_INIT_FMC_NOR_SRAM + bool "Auto initialize NOR/PSRAM/SRAM support" + default y if MODULE_PERIPH_INIT + depends on MODULE_PERIPH_FMC_NOR_SRAM + +config MODULE_PERIPH_INIT_FMC_SDRAM + bool "Auto initialize SDRAM support" + default y if MODULE_PERIPH_INIT + depends on MODULE_PERIPH_FMC_SDRAM + +config MODULE_PERIPH_INIT_FMC_16BIT + bool "Auto initialize 16-bit data bus" + default y if MODULE_PERIPH_INIT + depends on MODULE_PERIPH_FMC_16BIT + +config MODULE_PERIPH_INIT_FMC_32BIT + bool "Auto initialize 32-bit data bus" + default y if MODULE_PERIPH_INIT + depends on MODULE_PERIPH_FMC_32BIT + +endif + +config HAS_PERIPH_FMC + bool + help + Indicates that a STM32 FMC/FSMC peripheral is present. + +config HAS_PERIPH_FMC_NOR_SRAM + bool + select HAS_PERIPH_FMC + help + Indicates that a NOR Flash, PSRAM or SRAM connected to the + STM32 FMC/FSMC peripheral is present. + +config HAS_PERIPH_FMC_SDRAM + bool + select HAS_PERIPH_FMC + help + Indicates that a SDRAM connected to the STM32 FMC/FSMC peripheral + is present. + +config HAS_PERIPH_FMC_16BIT + bool + select HAS_PERIPH_FMC + help + Indicates that the STM32 FMC/FSMC peripheral uses a 16-bit data bus. + +config HAS_PERIPH_FMC_32BIT + bool + select HAS_PERIPH_FMC + help + Indicates that the STM32 FMC/FSMC peripheral uses a 32-bit data bus. diff --git a/cpu/stm32/periph/fmc.c b/cpu/stm32/periph/fmc.c new file mode 100644 index 0000000000000..181e40a181893 --- /dev/null +++ b/cpu/stm32/periph/fmc.c @@ -0,0 +1,575 @@ +/* + * Copyright (C) 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @ingroup cpu_stm32 + * @ingroup drivers_periph_fmc + * @{ + * + * @file + * @brief FMC peripheral driver implementation + * + * @author Gunar Schorcht + * @} + */ + +#include + +#include "log.h" +#include "periph/gpio.h" +#include "pm_layered.h" + +#define ENABLE_DEBUG 0 +#include "debug.h" + +#ifndef FMC_BASE +#define FMC_BASE (0x60000000UL) +#endif + +#if !defined(FMC_Bank1_R_BASE) && defined(FSMC_Bank1_R_BASE) +/* FSMC Symbols are defined, mapping is required */ + +#define FMC_Bank1_R_BASE FSMC_Bank1_R_BASE /* (FMC_R_BASE + 0x0000UL) */ +#define FMC_Bank1_TypeDef FSMC_Bank1_TypeDef + +#define FMC_Bank1E_R_BASE FSMC_Bank1E_R_BASE /* (FMC_R_BASE + 0x0104UL) */ +#define FMC_Bank1E_TypeDef FSMC_Bank1E_TypeDef + +#if defined(FSMC_Bank2_3_R_BASE) +#define FMC_Bank2_3_R_BASE FSMC_Bank2_3_R_BASE /* (FMC_R_BASE + 0x0060UL) */ +#define FMC_Bank2_3_TypeDef FSMC_Bank2_3_TypeDef +#endif + +#if defined(FSMC_Bank4_R_BASE) +#define FMC_Bank4_R_BASE FSMC_Bank4_R_BASE /* (FMC_R_BASE + 0x00A0UL) */ +#define FMC_Bank4_TypeDef FSMC_Bank4_TypeDef +#endif + +#endif /* !defined(FMC_Bank1_R_BASE) && defined(FSMC_Bank1_R_BASE) */ + +#if !defined(FMC_Bank1) && defined(FMC_Bank1_R_BASE) +#define FMC_Bank1 ((FMC_Bank1_TypeDef *) FMC_Bank1_R_BASE) +#endif +#if !defined(FMC_Bank1E) && defined(FMC_Bank1E_R_BASE) +#define FMC_Bank1E ((FMC_Bank1E_TypeDef *) FMC_Bank1E_R_BASE) +#endif +#if !defined(FMC_Bank2_3) && defined(FMC_Bank2_3_R_BASE) +#define FMC_Bank2_3 ((FMC_Bank3_TypeDef *) FMC_Bank2_3_R_BASE) +#endif +#if !defined(FMC_Bank3) && defined(FMC_Bank3_R_BASE) +#define FMC_Bank3 ((FMC_Bank3_TypeDef *) FMC_Bank3_R_BASE) +#endif +#if !defined(FMC_Bank4) && defined(FMC_Bank4_R_BASE) +#define FMC_Bank4 ((FMC_Bank4_TypeDef *) FMC_Bank3_R_BASE) +#endif +#if !defined(FMC_Bank5_6) && defined(FMC_Bank5_6_R_BASE) +#define FMC_Bank5_6 ((FMC_Bank5_6_TypeDef *) FMC_Bank5_6_R_BASE) +#endif + +#if defined(FMC_BCR1_MBKEN) +/* if CMSIS header define FMC_BCR1_* macros instead of FMC_BCRx_*, + * mapping is needed */ +#define FMC_BCRx_MBKEN FMC_BCR1_MBKEN +#define FMC_BCRx_MBKEN_Pos FMC_BCR1_MBKEN_Pos +#define FMC_BCRx_MUXEN FMC_BCR1_MUXEN +#define FMC_BCRx_MUXEN_Pos FMC_BCR1_MUXEN_Pos +#define FMC_BCRx_MTYP_Msk FMC_BCR1_MTYP_Msk +#define FMC_BCRx_MTYP_Pos FMC_BCR1_MTYP_Pos +#define FMC_BCRx_MWID_Msk FMC_BCR1_MWID_Msk +#define FMC_BCRx_MWID_Pos FMC_BCR1_MWID_Pos +#define FMC_BCRx_FACCEN FMC_BCR1_FACCEN +#define FMC_BCRx_FACCEN_Pos FMC_BCR1_FACCEN_Pos +#define FMC_BCRx_WAITEN FMC_BCR1_WAITEN +#define FMC_BCRx_WAITEN_Pos FMC_BCR1_WAITEN_Pos +#define FMC_BCRx_EXTMOD FMC_BCR1_EXTMOD +#define FMC_BCRx_EXTMOD_Pos FMC_BCR1_EXTMOD_Pos + +#define FMC_BTRx_ADDSET_Pos FMC_BTR1_ADDSET_Pos +#define FMC_BTRx_ADDHLD_Pos FMC_BTR1_ADDHLD_Pos +#define FMC_BTRx_DATAST_Pos FMC_BTR1_DATAST_Pos +#define FMC_BTRx_DATLAT_Pos FMC_BTR1_DATLAT_Pos +#define FMC_BTRx_BUSTURN_Pos FMC_BTR1_BUSTURN_Pos +#define FMC_BTRx_CLKDIV_Pos FMC_BTR1_CLKDIV_Pos +#endif /* defined(FMC_BCR1_MBKEN) */ + +static void _fmc_init_gpio_common(void) +{ + for (unsigned i = 0; i < FMC_DATA_PIN_NUMOF; i++) { + assert(gpio_is_valid(fmc_config.data[i].pin)); + gpio_init(fmc_config.data[i].pin, GPIO_OUT); + gpio_init_af(fmc_config.data[i].pin, fmc_config.data[i].af); + } + +#if FMC_ADDR_PIN_NUMOF + for (unsigned i = 0; i < FMC_ADDR_PIN_NUMOF; i++) { + if (gpio_is_valid(fmc_config.addr[i].pin)) { + gpio_init(fmc_config.addr[i].pin, GPIO_OUT); + gpio_init_af(fmc_config.addr[i].pin, fmc_config.addr[i].af); + } + } +#endif /* FMC_ADDR_PIN_NUMOF */ + + if (gpio_is_valid(fmc_config.nbl0_pin.pin)) { + gpio_init(fmc_config.nbl0_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.nbl0_pin.pin, fmc_config.nbl0_pin.af); + } + if (gpio_is_valid(fmc_config.nbl1_pin.pin)) { + gpio_init(fmc_config.nbl1_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.nbl1_pin.pin, fmc_config.nbl1_pin.af); + } + if (gpio_is_valid(fmc_config.nbl2_pin.pin)) { + gpio_init(fmc_config.nbl2_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.nbl2_pin.pin, fmc_config.nbl2_pin.af); + } + if (gpio_is_valid(fmc_config.nbl3_pin.pin)) { + gpio_init(fmc_config.nbl3_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.nbl3_pin.pin, fmc_config.nbl3_pin.af); + } + if (gpio_is_valid(fmc_config.nwait_pin.pin)) { + gpio_init(fmc_config.nwait_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.nwait_pin.pin, fmc_config.nwait_pin.af); + } +} + +#if MODULE_PERIPH_FMC_NOR_SRAM +static void _fmc_init_gpio_nor_sram(uint8_t sub_bank, fmc_mem_type_t type) +{ + /* at least either NOE or NWE has to be valid */ + assert(gpio_is_valid(fmc_config.noe_pin.pin) || + gpio_is_valid(fmc_config.nwe_pin.pin)); + + if (gpio_is_valid(fmc_config.clk_pin.pin)) { + gpio_init(fmc_config.clk_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.clk_pin.pin, fmc_config.clk_pin.af); + } + + if (gpio_is_valid(fmc_config.noe_pin.pin)) { + gpio_init(fmc_config.noe_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.noe_pin.pin, fmc_config.noe_pin.af); + } + + if (gpio_is_valid(fmc_config.nwe_pin.pin)) { + gpio_init(fmc_config.nwe_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.nwe_pin.pin, fmc_config.nwe_pin.af); + } + + if (type == FMC_NOR) { + /* NADV has to be valid for NOR Flash memories */ + assert(gpio_is_valid(fmc_config.nadv_pin.pin)); + gpio_init(fmc_config.nadv_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.nadv_pin.pin, fmc_config.nadv_pin.af); + } + + /* the corresponding NE pin has to be valid */ + switch (sub_bank) { + case 1: + assert(gpio_is_valid(fmc_config.ne1_pin.pin)); + gpio_init(fmc_config.ne1_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.ne1_pin.pin, fmc_config.ne1_pin.af); + break; + case 2: + assert(gpio_is_valid(fmc_config.ne2_pin.pin)); + gpio_init(fmc_config.ne2_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.ne2_pin.pin, fmc_config.ne2_pin.af); + break; + case 3: + assert(gpio_is_valid(fmc_config.ne3_pin.pin)); + gpio_init(fmc_config.ne3_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.ne3_pin.pin, fmc_config.ne3_pin.af); + break; + case 4: + assert(gpio_is_valid(fmc_config.ne4_pin.pin)); + gpio_init(fmc_config.ne4_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.ne4_pin.pin, fmc_config.ne4_pin.af); + break; + } +} + +static void _fmc_init_nor_sram_bank(const fmc_bank_conf_t *bank_conf) +{ + DEBUG("[%s] subbank %u\n", __func__, bank_conf->nor_sram.sub_bank); + + const fmc_nor_sram_bank_conf_t *conf = &bank_conf->nor_sram; + assert((bank_conf->nor_sram.sub_bank) >= 1 && + (bank_conf->nor_sram.sub_bank) <= 4); + + _fmc_init_gpio_nor_sram(conf->sub_bank, bank_conf->mem_type); + + uint32_t *bcr1 = (uint32_t *)(FMC_Bank1_R_BASE); + uint32_t *bcr = (uint32_t *)(FMC_Bank1_R_BASE + ((conf->sub_bank - 1) * 8)); + uint32_t *btr = (uint32_t *)(FMC_Bank1_R_BASE + ((conf->sub_bank - 1) * 8) + 0x04); + uint32_t *bwtr = (uint32_t *)(FMC_Bank1E_R_BASE + ((conf->sub_bank - 1) * 8)); + + /* disable Memory Bank 1 */ + *bcr &= ~FMC_BCRx_MBKEN; + + /* set read timing */ + *btr = conf->r_timing.mode + | (conf->r_timing.addr_setup << FMC_BTRx_ADDSET_Pos) + | (conf->r_timing.addr_hold << FMC_BTRx_ADDHLD_Pos) + | (conf->r_timing.data_setup << FMC_BTRx_DATAST_Pos) + | (conf->r_timing.data_latency << FMC_BTRx_DATLAT_Pos) + | (conf->r_timing.bus_turnaround << FMC_BTRx_BUSTURN_Pos) + | (conf->r_timing.clk_div << FMC_BTRx_CLKDIV_Pos); + + if (conf->mux_enable) { + /* enable Extended mode if set and set write timings */ + *bcr |= FMC_BCRx_EXTMOD; + *bwtr = conf->w_timing.mode + | (conf->w_timing.addr_setup << FMC_BTRx_ADDSET_Pos) + | (conf->w_timing.addr_hold << FMC_BTRx_ADDHLD_Pos) + | (conf->w_timing.data_setup << FMC_BTRx_DATAST_Pos) + | (conf->w_timing.bus_turnaround << FMC_BTRx_BUSTURN_Pos); + } + else { + *bcr &= ~FMC_BCRx_EXTMOD; + } + +#ifdef FMC_BCR1_WFDIS + /* disable Write FIFO for all subbanks */ + *bcr1 |= FMC_BCR1_WFDIS; +#else + (void)bcr1; +#endif + + /* set memory Type */ + *bcr &= ~FMC_BCRx_MTYP_Msk; + *bcr |= bank_conf->mem_type << FMC_BCRx_MTYP_Pos; + + /* set Memory Type to 16 bit (reset default) */ + *bcr &= ~FMC_BCRx_MWID_Msk; + *bcr |= bank_conf->data_width << FMC_BCRx_MWID_Pos; + + /* clear Write Wait, Flash Access and Address/Data Multiplexing */ + *bcr &= ~(FMC_BCRx_WAITEN | FMC_BCRx_FACCEN | FMC_BCRx_MUXEN); + + /* enable WAIT signal if set */ + *bcr |= (conf->wait_enable) << FMC_BCRx_WAITEN_Pos; + + /* enable NOR Flash memory access in case of NOR Flash */ + *bcr |= (bank_conf->mem_type == FMC_NOR) << FMC_BCRx_FACCEN_Pos; + + /* enable MUX in case of PSRAM or NOR if set */ + *bcr |= (((bank_conf->mem_type == FMC_PSRAM) || + (bank_conf->mem_type == FMC_NOR)) && + conf->mux_enable) << FMC_BCRx_MUXEN_Pos; + + /* leave all other values at reset default 0x000030d2 */ + /* enable Memory Bank 1 */ + *bcr |= FMC_BCRx_MBKEN; /* Memory Bank Enable = enabled */ + + DEBUG("[%s] FMC BCRx=%08lx BTRx=%08lx BWTR=%08lx\n", + __func__, *bcr, *btr, *bwtr); +} +#endif /* MODULE_PERIPH_FMC_NOR_SRAM */ + +#if MODULE_PERIPH_FMC_SDRAM +/** + * @brief SDRAM Command Mode Register definition + */ +typedef union { + struct __attribute__((__packed__)) { + uint32_t command:3; /**< Command mode */ + uint32_t target:2; /**< Command target */ + uint32_t auto_refresh:4; /**< Number of auto-refresh cycles */ + uint32_t mode_reg:13; /**< Mode register */ + uint32_t reserved:10; /**< reserved bits */ + }; + uint32_t value; +} fmc_sdram_sdcmr_t; + +/** + * @brief SDRAM Mode Register definition + */ +typedef union __attribute__((__packed__)) { + struct { + uint16_t burst_len:3; /**< Burst length (0..6: 2^burst_len, 7: full page) */ + uint16_t burst_type:1; /**< Burst type (0: sequential, 1: interleaved) */ + uint16_t cas_latency:3; /**< CAS latency (1, 2, 3, other values are reserved) */ + uint16_t op_mode:2; /**< Operating mode (must be 0) */ + uint16_t write_burst:1; /**< Write burst mode (0: use read burst length, 1: no bursts) */ + uint16_t unused:6; /**< unused */ + }; + uint16_t value; +} fmc_sdram_modereg_t; + +/** + * @brief SDRAM Command Mode + */ +typedef enum { + FMC_CMD_NORMAL_MODE = 0, /**< Normal Mode */ + FMC_CMD_CLK_ENABLE = 1, /**< Clock Configuration Enable */ + FMC_CMD_PALL = 2, /**< All Bank Precharge command */ + FMC_CMD_AUTO_REFRESH = 3, /**< Auto-refresh command */ + FMC_CMD_LOAD_MODE = 4, /**< Load Mode Register */ + FMC_CMD_SELF_REFRESH = 5, /**< Self-refresh command */ + FMC_CMD_POWER_DOWN = 6, /**< Power-down command */ + FMC_CMD_RESERVED = 7, /**< reserved */ +} fmc_sdram_cmd_t; + +static void _fmc_init_gpio_sdram(fmc_bank_t bank) +{ + /* CLK, RAS, CAS, BA0, BA1 and WE must be valid */ + assert(gpio_is_valid(fmc_config.sdclk_pin.pin) && + gpio_is_valid(fmc_config.sdnras_pin.pin) && + gpio_is_valid(fmc_config.sdncas_pin.pin) && + gpio_is_valid(fmc_config.sdnwe_pin.pin) && + gpio_is_valid(fmc_config.ba0_pin.pin) && + gpio_is_valid(fmc_config.ba1_pin.pin)); + + gpio_init(fmc_config.ba0_pin.pin, GPIO_OUT); + gpio_init(fmc_config.ba1_pin.pin, GPIO_OUT); + gpio_init(fmc_config.sdclk_pin.pin, GPIO_OUT); + gpio_init(fmc_config.sdnras_pin.pin, GPIO_OUT); + gpio_init(fmc_config.sdncas_pin.pin, GPIO_OUT); + gpio_init(fmc_config.sdnwe_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.ba0_pin.pin, fmc_config.ba0_pin.af); + gpio_init_af(fmc_config.ba1_pin.pin, fmc_config.ba1_pin.af); + gpio_init_af(fmc_config.sdclk_pin.pin, fmc_config.sdclk_pin.af); + gpio_init_af(fmc_config.sdnras_pin.pin, fmc_config.sdnras_pin.af); + gpio_init_af(fmc_config.sdncas_pin.pin, fmc_config.sdncas_pin.af); + gpio_init_af(fmc_config.sdnwe_pin.pin, fmc_config.sdnwe_pin.af); + + /* corresponding NE pin and CKE pin must be valid */ + if (bank == FMC_BANK_5) { + assert(gpio_is_valid(fmc_config.sdne0_pin.pin)); + assert(gpio_is_valid(fmc_config.sdcke0_pin.pin)); + gpio_init(fmc_config.sdne0_pin.pin, GPIO_OUT); + gpio_init(fmc_config.sdcke0_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.sdne0_pin.pin, fmc_config.sdne0_pin.af); + gpio_init_af(fmc_config.sdcke0_pin.pin, fmc_config.sdcke0_pin.af); + } + else { + assert(gpio_is_valid(fmc_config.sdne1_pin.pin)); + assert(gpio_is_valid(fmc_config.sdcke1_pin.pin)); + gpio_init(fmc_config.sdne1_pin.pin, GPIO_OUT); + gpio_init(fmc_config.sdcke1_pin.pin, GPIO_OUT); + gpio_init_af(fmc_config.sdne1_pin.pin, fmc_config.sdne1_pin.af); + gpio_init_af(fmc_config.sdcke1_pin.pin, fmc_config.sdcke1_pin.af); + } +} + +static void _fmc_init_sdram_bank(const fmc_bank_conf_t *bank_conf) +{ + _fmc_init_gpio_sdram(bank_conf->bank); + + const fmc_sdram_bank_conf_t *conf = &bank_conf->sdram; + + uint8_t bank = (bank_conf->bank == FMC_BANK_5) ? 0 : 1; + uint32_t sdcr; + + /* following settings are always configured in SDCR1 independent on the + * bank and are don't care in SDCR2 */ + sdcr = FMC_Bank5_6->SDCR[0]; + + /* set read delay */ + assert(conf->read_delay <= 2); + sdcr &= ~FMC_SDCR1_RPIPE_Msk; + sdcr |= conf->read_delay << FMC_SDCR1_RPIPE_Pos; + + /* clear and set RBURST if enabled */ + sdcr &= ~FMC_SDCR1_RBURST; + sdcr |= conf->burst_read << FMC_SDCR1_RBURST_Pos; + + /* set clock period */ + assert(conf->clk_period <= 3); + sdcr &= ~FMC_SDCR1_SDCLK_Msk; + sdcr |= conf->clk_period << FMC_SDCR1_SDCLK_Pos; + + /* write back register SDCR1 */ + FMC_Bank5_6->SDCR[0] = sdcr; + + /* other settings are done in SDCRx for bannk x */ + sdcr = FMC_Bank5_6->SDCR[bank]; + + /* set number of row and column bits and the data bus width */ + assert((conf->row_bits >= 11) && (conf->row_bits <= 13)); + assert((conf->col_bits >= 8) && (conf->col_bits <= 11)); + sdcr &= ~(FMC_SDCR1_NR_Msk | FMC_SDCR1_NC_Msk | FMC_SDCR1_MWID_Msk); + sdcr |= (conf->row_bits - 11) << FMC_SDCR1_NR_Pos; + sdcr |= (conf->col_bits - 8) << FMC_SDCR1_NC_Pos; + sdcr |= bank_conf->data_width << FMC_SDCR1_MWID_Pos; + + /* set CAS latency */ + assert((conf->cas_latency >= 1) && (conf->cas_latency <= 3)); + sdcr &= ~FMC_SDCR1_CAS_Msk; + sdcr |= conf->cas_latency << FMC_SDCR1_CAS_Pos; + + /* clear and set NB and WP */ + sdcr &= ~(FMC_SDCR1_NB | FMC_SDCR1_WP); + sdcr |= conf->four_banks << FMC_SDCR1_NB_Pos; + sdcr |= conf->write_protect << FMC_SDCR1_WP_Pos; + + /* write back register SDCRx */ + FMC_Bank5_6->SDCR[bank] = sdcr; + + uint32_t sdtr = 0; + + assert((conf->timing.row_to_col_delay - 1) <= 15); + assert((conf->timing.row_precharge - 1) <= 15); + assert((conf->timing.recovery_delay - 1) <= 15); + assert((conf->timing.row_cylce - 1) <= 15); + assert((conf->timing.self_refresh - 1) <= 15); + assert((conf->timing.exit_self_refresh - 1) <= 15); + assert((conf->timing.load_mode_register - 1) <= 15); + + /* following settings are always configured in SDTR1 independent on the + * bank and are don't care in SDTR2 */ + sdtr = FMC_Bank5_6->SDTR[0]; + + sdtr &= ~FMC_SDTR1_TRP; + sdtr |= (conf->timing.row_precharge - 1) << FMC_SDTR1_TRP_Pos; + + sdtr &= ~FMC_SDTR1_TRC; + sdtr |= (conf->timing.row_cylce - 1) << FMC_SDTR1_TRC_Pos; + + /* write back register SDCRx */ + FMC_Bank5_6->SDTR[0] = sdtr; + + /* other settings are done in SDTRx for bannk x */ + sdtr = FMC_Bank5_6->SDTR[0]; + + sdtr &= ~FMC_SDTR1_TRCD; + sdtr |= (conf->timing.row_to_col_delay - 1) << FMC_SDTR1_TRCD_Pos; + + sdtr &= ~FMC_SDTR1_TWR; + sdtr |= (conf->timing.recovery_delay - 1) << FMC_SDTR1_TWR_Pos; + + sdtr &= ~FMC_SDTR1_TRAS; + sdtr |= (conf->timing.self_refresh - 1) << FMC_SDTR1_TRAS_Pos; + + sdtr &= ~FMC_SDTR1_TXSR; + sdtr |= (conf->timing.exit_self_refresh - 1) << FMC_SDTR1_TXSR_Pos; + + sdtr &= ~FMC_SDTR1_TMRD; + sdtr |= (conf->timing.load_mode_register - 1) << FMC_SDTR1_TMRD_Pos; + + /* write register SDTRx */ + FMC_Bank5_6->SDTR[bank] = sdtr; + + DEBUG("[%s] DMC SDCR%d=%08lx SDTR%d=%08lx SDCMR=%08lx SDRTR=%08lx SDSR=%08lx\n", + __func__, + bank, FMC_Bank5_6->SDCR[bank], + bank, FMC_Bank5_6->SDTR[bank], + FMC_Bank5_6->SDCMR, FMC_Bank5_6->SDRTR, FMC_Bank5_6->SDSR); + + /* Initialization sequence according to the reference manual */ + + fmc_sdram_sdcmr_t sdcmr = { .target = (bank) ? 0b01 : 0b10, + .auto_refresh = 1, + .mode_reg = 0 }; + + /* enable clock (SDCKE HIGH) */ + sdcmr.command = FMC_CMD_CLK_ENABLE; + FMC_Bank5_6->SDCMR = sdcmr.value; + + /* wait at least 100 us */ + uint32_t count = 0xffffff; + while (count--) {} + + /* issue Precharge All command */ + sdcmr.command = FMC_CMD_PALL; + FMC_Bank5_6->SDCMR = sdcmr.value; + + /* issue the typical number of Auto-refresh commands */ + sdcmr.command = FMC_CMD_AUTO_REFRESH; + sdcmr.auto_refresh = 8; + FMC_Bank5_6->SDCMR = sdcmr.value; + + /* load mode register */ + fmc_sdram_modereg_t modreg = { + .burst_len = 0, // conf->burst_len, + .burst_type = conf->burst_interleaved, + .cas_latency = conf->cas_latency, + .op_mode = 0, + .write_burst = (conf->burst_write) ? 0 : 1, + }; + sdcmr.command = FMC_CMD_LOAD_MODE; + sdcmr.auto_refresh = 1; + sdcmr.mode_reg = modreg.value; + FMC_Bank5_6->SDCMR = sdcmr.value; + + /* set refresh timer register */ + uint32_t cycles; + cycles = CLOCK_AHB / KHZ(1) / conf->clk_period; /* SDCLK cycles per ms */ + cycles = cycles * conf->timing.refresh_period; /* SDCLK cycles per refresh period */ + cycles = cycles / (1 << conf->row_bits); /* SDCLK cycles per row */ + cycles = ((cycles - 20) > 41) ? cycles - 20 : 41; /* cycles - margin > min 41 */ + FMC_Bank5_6->SDRTR &= FMC_SDRTR_COUNT_Msk; + FMC_Bank5_6->SDRTR |= cycles << FMC_SDRTR_COUNT_Pos; +} +#endif + +void fmc_init_bank(fmc_bank_id_t bank) +{ + DEBUG("[%s] bank id %u\n", __func__, bank); + + assert(bank < FMC_BANK_NUMOF); + + const fmc_bank_conf_t *conf = &fmc_bank_config[bank]; + + /* ensure that configured bank is valid */ + switch (conf->bank) { + case 1: break; +#if defined(FMC_Bank2_3_R_BASE) + case 2: break; +#endif +#if defined(FMC_Bank2_3_R_BASE) || defined(FMC_Bank3_R_BASE) + case 3: break; +#endif +#if defined(FMC_Bank4_R_BASE) + case 4: break; +#endif +#if defined(FMC_Bank5_6_R_BASE) + case 5: + case 6: break; +#endif + default: assert(false); + } + + if (conf->bank == FMC_BANK_1) { +#if MODULE_PERIPH_FMC_NOR_SRAM + /* bank 1 has to be NOR, PSRAM or SRAM */ + assert((conf->mem_type == FMC_SRAM) || + (conf->mem_type == FMC_PSRAM) || + (conf->mem_type == FMC_NOR)); + _fmc_init_nor_sram_bank(conf); +#else + LOG_WARNING("NOR/PSRAM/SRAM configured but not enabled by feature periph_fmc_sdram\n"); +#endif + } + +#if defined(FMC_Bank5_6_R_BASE) + else if ((conf->bank == FMC_BANK_5) || (conf->bank == FMC_BANK_6)) { +#if MODULE_PERIPH_FMC_SDRAM + _fmc_init_sdram_bank(conf); +#else + LOG_WARNING("SDRAM configured but not enabled by feature periph_fmc_nor_sram\n"); +#endif + } +#endif + + else { + LOG_ERROR("[fmc] Bank %u not supported\n", conf->bank); + assert(false); + } +} + +#include "periph_cpu.h" + +void fmc_init(void) +{ + DEBUG("[%s]\n", __func__); + + periph_clk_en(fmc_config.bus, fmc_config.rcc_mask); + + _fmc_init_gpio_common(); + for (unsigned i = 0; i < FMC_BANK_NUMOF; i++) { + fmc_init_bank(i); + } +} diff --git a/dist/tools/codespell/ignored_words.txt b/dist/tools/codespell/ignored_words.txt index f738fa0a96870..d0acc35204493 100644 --- a/dist/tools/codespell/ignored_words.txt +++ b/dist/tools/codespell/ignored_words.txt @@ -156,3 +156,9 @@ didi # loath => loathe (both correct, one is an adjective, the other a verb) loath + +# NOE (Negative Output Enable) ==> NOT, NO, NODE, NOTE, KNOW, NOW +noe + +# NWE (Negative Write Enable) ==> NEW +nwe diff --git a/drivers/periph_common/init.c b/drivers/periph_common/init.c index c8b4ea57b6d0d..a92a4cf2e4a04 100644 --- a/drivers/periph_common/init.c +++ b/drivers/periph_common/init.c @@ -117,6 +117,11 @@ void periph_init(void) ptp_init(); #endif +#if defined(MODULE_PERIPH_INIT_FMC) + extern void fmc_init(void); + fmc_init(); +#endif + #if defined(MODULE_PERIPH_INIT_VBAT) vbat_init(); #endif diff --git a/tests/periph/fmc/Kconfig b/tests/periph/fmc/Kconfig new file mode 100644 index 0000000000000..a0134acf7f559 --- /dev/null +++ b/tests/periph/fmc/Kconfig @@ -0,0 +1,6 @@ +config APPLICATION + bool + default y + imply MODULE_PERIPH_FMC_NOR_SRAM + imply MODULE_PERIPH_FMC_SDRAM + depends on TEST_KCONFIG diff --git a/tests/periph/fmc/Makefile b/tests/periph/fmc/Makefile new file mode 100644 index 0000000000000..7771909ae4c5d --- /dev/null +++ b/tests/periph/fmc/Makefile @@ -0,0 +1,11 @@ +BOARD ?= stm32f429i-disc1 +include ../Makefile.periph_common + +USEMODULE += benchmark +USEMODULE += od + +FEATURES_REQUIRED += periph_fmc +FEATURES_OPTIONAL += periph_fmc_nor_sram +FEATURES_OPTIONAL += periph_fmc_sdram + +include $(RIOTBASE)/Makefile.include diff --git a/tests/periph/fmc/README.md b/tests/periph/fmc/README.md new file mode 100644 index 0000000000000..814ea6fcd7682 --- /dev/null +++ b/tests/periph/fmc/README.md @@ -0,0 +1,24 @@ +Peripheral STM32 FMC Test Application +===================================== + +This application tests basic STM32 FMC functionality: + +- 8-bit, 16-bit and 32-bit write and read access +- availability of the whole memory + +Configuration +------------- + +The bank to be used is defined by environment variable `FMC_BANK`. For example, +if the board configures two banks with RAM, the second bank can be tested by +specifying the `FMC_BANK` variable as follows: +``` +FMC_BANK=1 BOARD=... make -j8 -C tests/periph/fmc flash test +``` + +Expected Output on Success +-------------------------- + + main(): This is RIOT! (Version: ) + ... + [SUCCESS] diff --git a/tests/periph/fmc/app.config.test b/tests/periph/fmc/app.config.test new file mode 100644 index 0000000000000..b651e9e402f14 --- /dev/null +++ b/tests/periph/fmc/app.config.test @@ -0,0 +1,3 @@ +CONFIG_MODULE_BENCHMARK=y +CONFIG_MODULE_PERIPH_FMC=y +CONFIG_MODULE_OD=y diff --git a/tests/periph/fmc/main.c b/tests/periph/fmc/main.c new file mode 100644 index 0000000000000..dce95cc1219db --- /dev/null +++ b/tests/periph/fmc/main.c @@ -0,0 +1,166 @@ +/* + * Copyright (C) 2023 Gunar Schorcht + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @file + * @brief Test for memories connected to the STM32 FMC/FSMC peripheral + * + * @author Gunar Schorcht + */ + +#include +#include + +#include "benchmark.h" +#include "od.h" +#include "periph_conf.h" + +#ifndef FMC_BANK +#define FMC_BANK 0 +#endif + +#ifndef MEM_BUFSIZ +#define MEM_BUFSIZ 1024 +#endif + +uint8_t mem_buf[MEM_BUFSIZ] = {}; + +void bench_mem_write(uint32_t addr, uint32_t len) +{ + uint8_t *mem = (uint8_t *)addr; + uint32_t i = 0; + + while (i < len) { + memcpy(mem + i, mem_buf, MEM_BUFSIZ); + i += MEM_BUFSIZ; + } +} + +int main(void) +{ + printf("FMC HCLK freq %lu MHz\n", CLOCK_AHB/MHZ(1)); + + uint8_t *data8 = (uint8_t *)(fmc_bank_config[FMC_BANK].address); + uint16_t *data16 = (uint16_t *)((fmc_bank_config[FMC_BANK].address) + 256); + uint32_t *data32 = (uint32_t *)((fmc_bank_config[FMC_BANK].address) + + (fmc_bank_config[FMC_BANK].size) - 256); + + printf("8-bit data @%p, 16-bit data @%p, 32-bit data @%p\n", + data8, data16, data32); + puts("------------------------------------------------------------------------"); + + for (unsigned i = 0; i < 256; i++) { + data8[i] = i; + } + od_hex_dump_ext(data8, 256, 16, fmc_bank_config[FMC_BANK].address); + + for (unsigned i = 0; i < 256; i++) { + if (data8[i] != i) { + printf("ERROR: memory content did not match @%p\n", &data8[i]); + return 1; + } + } + puts("------------------------------------------------------------------------"); + + for (unsigned i = 0; i < 128; i++) { + data16[i] = ((128 + i) << 8) + i; + } + od_hex_dump_ext(data16, 256, 16, fmc_bank_config[FMC_BANK].address + 256); + + for (unsigned i = 0; i < 128; i++) { + if (data16[i] != ((128 + i) << 8) + i) { + printf("ERROR: memory content did not match @%p\n", &data16[i]); + return 1; + } + } + puts("------------------------------------------------------------------------"); + + for (unsigned i = 0; i < 64; i++) { + data32[i] = ((192 + i) << 24) + ((128 + i) << 16) + ((64 + i) << 8) + i; + } + od_hex_dump_ext(data32, 256, 16, (fmc_bank_config[FMC_BANK].address) + + (fmc_bank_config[FMC_BANK].size) - 256); + + for (unsigned i = 0; i < 64; i++) { + if (data32[i] != ((192 + i) << 24) + ((128 + i) << 16) + ((64 + i) << 8) + i) { + printf("ERROR: memory content did not match @%p\n", &data32[i]); + return 1; + } + } + puts("------------------------------------------------------------------------"); + + + printf("fill complete memory of %lu kByte, a . represents a 16 kByte block\n", + fmc_bank_config[FMC_BANK].size >> 10); + + data32 = (uint32_t *)(fmc_bank_config[FMC_BANK].address); + + for (uint32_t i = 0; i < (fmc_bank_config[FMC_BANK].size >> 2); i++) { + *data32 = (uint32_t)data32; + data32++; + if (((i << 2) % KiB(16)) == 0) { + printf("."); + } + } + puts("\nready"); + puts("check memory content, a + represents one 16 kByte block of matching data"); + + data32 = (uint32_t *)(fmc_bank_config[FMC_BANK].address); + + for (uint32_t i = 0; i < (fmc_bank_config[FMC_BANK].size >> 2); i++) { + if (*data32 != (uint32_t)data32) { + printf("ERROR: memory content did not match @%p, " + "should be %p but was 0x%08"PRIx32"\n", + data32, data32, *data32); + return 1; + } + data32++; + if (((i << 2) % KiB(16)) == 0) { + printf("+"); + } + } + puts("\nready"); + puts("------------------------------------------------------------------------"); + + puts("Doing some benchmarks\n"); + + volatile uint8_t val8 = 0xf0; + volatile uint16_t val16 = 0x5555; + volatile uint32_t val32 = 0xaaaaaaaa; + + BENCHMARK_FUNC("write 8 bit", fmc_bank_config[FMC_BANK].size, + ((uint8_t *)(fmc_bank_config[FMC_BANK].address))[i] = val8); + BENCHMARK_FUNC("write 16 bit", fmc_bank_config[FMC_BANK].size / 2, + ((uint16_t *)(fmc_bank_config[FMC_BANK].address))[i] = val16); + BENCHMARK_FUNC("write 32 bit", fmc_bank_config[FMC_BANK].size / 4, + ((uint32_t *)(fmc_bank_config[FMC_BANK].address))[i] = val32); + + BENCHMARK_FUNC("read 8 bit", fmc_bank_config[FMC_BANK].size, + val8 = ((uint8_t *)(fmc_bank_config[FMC_BANK].address))[i]); + BENCHMARK_FUNC("read 16 bit", fmc_bank_config[FMC_BANK].size / 2, + val16 = ((uint16_t *)(fmc_bank_config[FMC_BANK].address))[i]); + BENCHMARK_FUNC("read 32 bit", fmc_bank_config[FMC_BANK].size / 4, + val32 = ((uint32_t *)(fmc_bank_config[FMC_BANK].address))[i]); + + puts("\nready"); + puts("------------------------------------------------------------------------"); + + puts("print first and last memory block after benchmark, should be 0xaa\n"); + od_hex_dump_ext(data8, 256, 16, fmc_bank_config[FMC_BANK].address); + puts(""); + + data8 = (uint8_t *)((fmc_bank_config[FMC_BANK].address) + + (fmc_bank_config[FMC_BANK].size) - 256); + od_hex_dump_ext(data8, 256, 16, (fmc_bank_config[FMC_BANK].address) + + (fmc_bank_config[FMC_BANK].size) - 256); + puts("------------------------------------------------------------------------"); + + puts("\n[SUCCESS]\n"); + + return 0; +} diff --git a/tests/periph/fmc/tests/01-run.py b/tests/periph/fmc/tests/01-run.py new file mode 100755 index 0000000000000..d8bccef43b4b2 --- /dev/null +++ b/tests/periph/fmc/tests/01-run.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +# Copyright (C) 2023 Gunar Schorcht +# +# This file is subject to the terms and conditions of the GNU Lesser +# General Public License v2.1. See the file LICENSE in the top level +# directory for more details. + +import sys +from testrunner import run + + +def testfunc(child): + child.expect_exact("[SUCCESS]") + + +if __name__ == "__main__": + sys.exit(run(testfunc))