1
0
mirror of https://github.com/RIOT-OS/RIOT.git synced 2024-12-29 04:50:03 +01:00
19843: cpu/stm32/periph: add FMC/FSMC support for STM32 r=aabadie a=gschorcht

### Contribution description

The PR provides 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                   |

The board has then to define
- the corresponding features according to the connected external device,
- the peripheral configuration of the FMC/FSMC of type `fmc_conf_t,`
- the configuration of the FMC banks which describe the connected external devices.

The PR includes the support for
- `stm32f429i-disc1` with 8 MByte SDRAM
- `stm32f746g-disco` with 16 MByte SDRAM
- `stm32l496g-disco` with 1 MByte SRAM
- `stm32f723e-disco` with 1 MByte SRAM.

To use the RAM connected to the FMC as heap, the board defines `FMC_RAM_ADDR` and ` FMC_RAM_LEN`. For that purpose:
- the linker symbols `_fmc_ram_addr` and `_fmc_ram_len` are set,
- a memory region `fcmram` is added in linker script for the FMC RAM based on these `_fcm_ram_*` linker symbols
- a section for the FMC RAM is defined in this memory region that defines the heap by setting `_sheap3` and `_eheap3` and
- the number of heaps is set to 4 to use `_sheap3` and `_eheap3` even though `_sheap1` and `_eheap1` (the backup RAM) and `_sheap2` and `_eheap2` (SRAM4) are not present.

Once the `drivers/st77xx` and `drivers/lcd` changes are merged, the display for boards like 
the `stm32l496g-disco` and `stm32f723e-disco` can also use the FMC peripheral.

~**NOTE**: The PR includes the fix in PR #19842 for now (commit 560fea17a0c50483214770855e22cc94df0e55b5).~

### Testing procedure

1. Use one of the boards above and flash `tests/driver/stm32_fmc`, for example:
   ```
   BOARD=stm32f429i-disc1 make -j8 -C tests/drivers/stm32_fmc flash test
   ```
   The test should succeed.
   
   **NOTE**: There is still a problem with `stm32f746g-disco`. It crashes with a hard-fault on accessing the upper 8 MByte of the 16 MByte.
   
2. Use the board above and flash `tests/sys/malloc`, for example:
   ```
   USEMODULE=periph_fmc CFLAGS='-DCHUNK_SIZE=4096 -DDEBUG_ASSERT_VERBOSE' \
   BOARD=stm32f429i-disc1 make -j8 -C tests/sys/malloc
   ```
   The FMC RAM should be used for `malloc`. On `stm32f746g-disco` for example
   ```
   ...
   Allocated 4096 Bytes at 0x2002d7c8, total 184672
   Allocated 4096 Bytes at 0x2002e7e0, total 188776
   Allocated 4096 Bytes at 0xd0000008, total 192880
   Allocated 4096 Bytes at 0xd0001010, total 196984
   Allocated 4096 Bytes at 0xd0002018, total 201088
   ...
   Allocated 4096 Bytes at 0xd07fd6d0, total 8544520
   Allocated 4096 Bytes at 0xd07fe6e8, total 8548624
   Allocations count: 2083
   ```

### Issues/PRs references

~Depends on PR #19842~

19847: .github: drop test-on-ryot workflow r=aabadie a=aabadie



Co-authored-by: Gunar Schorcht <gunar@schorcht.net>
Co-authored-by: Alexandre Abadie <alexandre.abadie@inria.fr>
This commit is contained in:
bors[bot] 2023-07-27 13:12:54 +00:00 committed by GitHub
commit c4a1802a9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1783 additions and 110 deletions

View File

@ -1,105 +0,0 @@
---
# Run 'compile_and_test_for_board.py' on connected boards.
#
# This workflow will run on a RYOT (Run Your Own Test) machine and launch
# all tests on all boards currently connected to that machine. This
# workflow relies on self-hosted runners, this means building and
# flashing happens on the self-hosted runner (all in docker). An alternative
# approach was used in https://github.com/RIOT-OS/RIOT/pull/14600, where
# the RYOT machine was only used for remote flashing, this is a better
# alternative if ssh access is possible.
#
# Documentation:
#
# * Setup a RYOT machine:
# https://github.com/fjmolinas/riot-ryot/blob/master/setup.md
#
# * Requirements (already filled by a RYOT machine):
# * Add one or more self-hosted runners:
# https://docs.github.com/en/actions/hosting-your-own-runners/adding-self-hosted-runners
# * All required flashing tools installed
# * udev rules that map all BOARD to /dev/riot/tty-$(BOARD), see
# http://riot-os.org/api/advanced-build-system-tricks.html#multiple-boards-udev
# * RIOT_MAKEFILES_GLOBAL_PRE that sets PORT and DEBUG_ADAPTER_ID for each
# BOARD
# * A list of connected BOARDs in JSON format so that fromJSON can be used
# to dynamically setup the matrix, e.g. make target providing this:
# https://github.com/fjmolinas/riot-ryot/blob/72fc9ad710a2219e942c5965a014e934822e9da5/template/conf/makefiles.pre#L19-L24
# * RYOT: https://github.com/fjmolinas/riot-ryot
name: test-on-ryot
on:
# Schedule weekly runs Saturday at 00:00 on master
schedule:
- cron: '0 0 * * 6'
push:
# Run on all new release candidates
tags:
- '[0-9][0-9][0-9][0-9].[0-9][0-9]-RC[0-9]*'
- '[0-9][0-9][0-9][0-9].[0-9][0-9]'
- '[0-9][0-9][0-9][0-9].[0-9][0-9].*'
env:
# self-hosted runners are started by a systemd which is not a "login" shell
# this means that no local environment variables are loaded (e.g. /etc/environment)
# when the runner is started. So explicitly set RIOT_MAKEFILES_GLOBAL_PRE
# to set PORT and DEBUG_ADAPTER_ID per BOARD
RIOT_MAKEFILES_GLOBAL_PRE: '/builds/conf/makefiles.pre'
jobs:
connected_boards:
name: Get Connected Boards
runs-on: self-hosted
outputs:
boards: ${{ steps.ci-connected-boards.outputs.boards }}
steps:
# Get all currently connected boards if not passed through an input
- id: ci-connected-boards
run: echo "::set-output name=boards::$(make -C /builds/boards/ list-boards-json --no-print-directory)"
# Runs all tests on connected boards
compile_and_test_for_board:
name: ${{ matrix.board }}
runs-on: self-hosted
needs: connected_boards
# ci-riot-tribe has 8 cores, parallelism will depend on actually configured
# runners
strategy:
max-parallel: 7
fail-fast: false
matrix:
board: ${{ fromJson(needs.connected_boards.outputs.boards) }}
env:
BUILD_IN_DOCKER: 1
COMPILE_AND_TEST_FOR_BOARD: /builds/boards/RIOT/dist/tools/compile_and_test_for_board/compile_and_test_for_board.py
# args for compile_and_test_for_board script
COMPILE_AND_TEST_ARGS: --with-test-only --report-xml --incremental
# environment vars for compile_and_test_for_board script to pass
# USEMODULE and CFLAGS use:
# DOCKER_ENVIRONMENT_CMDLINE=\'-e USEMODULE=<name>\'
# DOCKER_ENVIRONMENT_CMDLINE=\'-e CFLAGS=-D<flag>\'
COMPILE_AND_TEST_VARS: ''
APPLICATIONS: ''
# Exclude 'tests/periph/timer_short_relative_set' since its expected
# to fail on most BOARDs
APPLICATIONS_EXCLUDE: 'tests/periph/timer_short_relative_set'
steps:
- name: Checkout RIOT
uses: actions/checkout@main
with:
ref: ${{ github.event.inputs.riot_version }}
# Make sure it runs git clean -xdff before fetching
clean: true
- name: Run compile_and_test_for_board.py
run: |
${COMPILE_AND_TEST_VARS} ${COMPILE_AND_TEST_FOR_BOARD} . \
${{ matrix.board }} results-${{ matrix.board }} \
${COMPILE_AND_TEST_ARGS} \
--applications="${APPLICATIONS}" \
--applications-exclude="${APPLICATIONS_EXCLUDE}"
- name: Archive results
if: always()
uses: actions/upload-artifact@v2
with:
name: ${{ matrix.board }}
path: results-${{ matrix.board }}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -8,3 +8,6 @@ OPENOCD_DEBUG_ADAPTER ?= stlink
# openocd programmer is supported
PROGRAMMERS_SUPPORTED += openocd
FMC_RAM_ADDR=0xd0000000
FMC_RAM_LEN=8192K

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -13,3 +13,7 @@ endif
ifneq (,$(filter touch_dev,$(USEMODULE)))
USEMODULE += ft5x06
endif
ifneq (,$(filter periph_fmc,$(USEMODULE)))
FEATURES_REQUIRED += periph_fmc_16bit
endif

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)))

View File

@ -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

View File

@ -16,3 +16,6 @@ ifneq (,$(filter usbus_dfu tinyusb_dfu,$(USEMODULE)))
else
RIOTBOOT_LEN ?= 0x2000
endif
FMC_RAM_ADDR=0x64000000
FMC_RAM_LEN=1024K

View File

@ -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 | - | |

View File

@ -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
*

View File

@ -74,6 +74,8 @@ config RDP2
bool "RDP2"
endchoice
rsource "periph/Kconfig.fmc"
if TEST_KCONFIG
rsource "periph/Kconfig"

View File

@ -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

View File

@ -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)

View File

@ -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 <gunar@schorcht.net>
*/
#ifndef PERIPH_CPU_FMC_H
#define PERIPH_CPU_FMC_H
#include <stdint.h>
#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 */
/** @} */

View File

@ -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"

View File

@ -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

View File

@ -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.

575
cpu/stm32/periph/fmc.c Normal file
View File

@ -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 <gunar@schorcht.net>
* @}
*/
#include <assert.h>
#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);
}
}

View File

@ -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

View File

@ -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

6
tests/periph/fmc/Kconfig Normal file
View File

@ -0,0 +1,6 @@
config APPLICATION
bool
default y
imply MODULE_PERIPH_FMC_NOR_SRAM
imply MODULE_PERIPH_FMC_SDRAM
depends on TEST_KCONFIG

11
tests/periph/fmc/Makefile Normal file
View File

@ -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

View File

@ -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: <INSERT VERSION HERE>)
...
[SUCCESS]

View File

@ -0,0 +1,3 @@
CONFIG_MODULE_BENCHMARK=y
CONFIG_MODULE_PERIPH_FMC=y
CONFIG_MODULE_OD=y

166
tests/periph/fmc/main.c Normal file
View File

@ -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 <gunar@schorcht.net>
*/
#include <stdio.h>
#include <string.h>
#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;
}

View File

@ -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))