cancel
Showing results for 
Search instead for 
Did you mean: 

How to configure the FMC peripheral to interface an STM32 MCU with an external NOR flash memory

Lionking
ST Employee

Summary

In this article, we learn how to configure the FMC peripheral of the STM32 to interface with the NOR flash PC28F128M29EWLA from Micron®

Introduction

In embedded systems, NOR flash stands out for its speed and reliability. Unlike other flash memories, NOR flash allows for code execution directly from the memory due to its fast read speeds and random-access capability. This makes it perfect for storing critical code like bootloaders and firmware. Additionally, NOR flash boasts high endurance for reading and writing data, ensuring the integrity of information in applications where reliability is paramount. With a simpler interface compared to other options, NOR flash simplifies the design process for embedded systems. These combined strengths make NOR flash a compelling choice for various embedded applications that prioritize code execution speed, reliability, and design ease.

 

1. Hardware and software prerequisites 

Note: This example runs on a STM32F769I-EVAL Board but can be easily adapted to any other STM32 microcontroller.

2. Development

Let us start this demonstration by creating a new project for the STM32F769I-EVAL board using STM32CubeIDE and configuring the clocks and the FMC peripheral. Here are the steps to perform the initial configurations.

  • Open STM32CubeIDE

  • Click [File]-> [New] -> [STM32 Project]

  • Search for STM32F769I-EVAL under the board selector tab and click on the shown board selection and then click [Next]

Lionking_16-1722892729519.png

  •  Choose a name for the project and click on [Finish]

Lionking_2-1722891963905.png

  • A popup appears, click on [No] to keep all the peripherals present on the board in their default mode.

Lionking_3-1722891963907.png

  • Now, the.ioc file opens, click on [Clear Pinouts] under the the Pinout drop-down menu to start the peripheral initialization from scratch.

Lionking_4-1722891963915.png

  • Configure the clock by enabling the HSE clock. Go to the RCC section under [Pinout & configuration] and select [Crystal/Ceramic Resonator] as the HSE clock.

Lionking_5-1722891963921.png

Make sure that the core is running at the maximum frequency of 216 MHz. This can be done by going to the [Clock Configuration] tab and entering 216 in the HCLK box and pressing enter. STM32CubeMX automatically chooses the appropriate clock, calculate the values for the PLL multipliers and prescalars to generate this 216 MHz system clock.

Lionking_6-1722891963933.png

  • Set PI15 as GPIO_OUTPUT to use the onboard LED, which is useful later to verify if the NOR flash memory is working as expected.

Lionking_7-1722891963939.png

The MPU has to be configured to prevent speculative read access to normal memory regions. This behavior is very particular to the Cortex® M7 processor. It is unlike the other Cortex® M-based MCUs, where the speculative read access nature causes the MCU to access normal memory regions even when there is no actual memory connected.
The default memory type for the FMC and the QSPI is normal and should be changed to device memory type. The control registers of the FMC and QSPI should be of device memory type. The NOR flash data region can either be strongly ordered or device memory type

Note: Not making the normal type regions to device type or strongly ordered results in a memfault exception due to the speculative read access nature of Cortex® M7.

 

 

Lionking_17-1722893023155.png

 

Lionking_18-1722893049195.pngLionking_19-1722893049214.png

So, choosing this option sets the MPU_CTRL (MPU control register) with the value 0x5, which sets PRIVDEFENA and enables the MPU

Lionking_21-1722893151789.png

 

Now, configure the MPU on STM32CubeMX as shown below

Lionking_20-1722893142272.png

Lionking_22-1722893182530.png

  • Under [Pinout & configuration] start by configuring the NOR flash

  • Under [Connectivity], select FMC and enable the NOR flash by considering the chip select as NE1 and choosing the memory type as [NOR Flash]

Lionking_23-1722893226065.png

  • The NOR flash on the STM32F769I-EVAL Board is connected to the NE1 chip select signal as seen in the schematic below

Lionking_24-1722893338450.png

 

2.1. Set up the NOR flash

The NOR flash PC28F128M29EWLA has the following main features:

  • Asynchronous memory

  • 128 Mb: 128 main blocks of 128 KB each

  • A0-A23: 24-bit address lines

  • D0-D15: 16-bit data lines

The STM32F76xxx FMC peripheral can support NOR flash memories only on one bank: Bank 1. Bank 1 is mapped at address 0x6000 0000 - 0x6FFF FFFF which is 256 MB in size. Moreover, a maximum of four devices (NOR Flash/PSRAM/SRAM) can be connected to bank 1 addressed by unique chip select signals (NE[4:1]). As you can see in the figure below, the address space is divided equally for four devices, which can be connected to bank 1.

Lionking_25-1722893447266.png

As mentioned, the NOR flash PC28F128M29EWLA device has 24-bit address lines, 16-bit data lines, connected to chip select NE1. Since we have an asynchronous memory, the clock can stay disabled. Since the NOR flash does not have a wait signal, the wait can be disabled. Let us make the selections as shown below:

 Lionking_26-1722893447271.png

 

The FMC can support asynchronous and synchronous NOR flash memories. Only the required parameters have to be configured. The table below shows the minimum and maximum values of each programmable NOR flash timing parameter.

Lionking_27-1722893509094.png

As we are using an external asynchronous nonmultiplexed NOR flash, the following parameters have to be computed and set according to the information in the memory datasheet.

  • Address setup time (ADDSET)

  • Data setup time (DATASET)

  • Bus turnaround time (BURSTURN)

As seen on the memory datasheet PC28F128M29EWLA is a BGA package and the timings are based on the BGA package mentioned on the datasheet.

Lionking_28-1722893509099.png

Asynchronous static memories signals are synchronized by the internal AHB clock (HCLK). This clock is not issued to the memory. In this article, the clock frequency of the MCU is 216 MHz, so all the parameters are configured based on AHB clock cycles (HCLK) we have tHCLK= 1/216 MHz = 4.6 ns (1 HCLK). Now, let us look at what the timing parameters mean and how to calculate them:

  • Address setup time (ADDSET): This is the minimum amount of time the address needs to be stable on the address lines before the data becomes valid on the data lines during a read or write operation. It contributes to the overall access Time(tACC) as specified on the memory datasheet.

Let us consider the maximum value of tACC, which is 60 ns so that the address setup time satisfies all conditions.

ADDSET = tACC / tHCLK = (60 ns/4.6 ns) = 13

Lionking_29-1722893749038.png

 

Lionking_30-1722893749041.png

For synchronous accesses, this value is considered irrelevant. In muxed mode or mode D, the minimum value for ADDSET is 1.

  • Data setup time (DATAST):

For write operations, this parameter is measured between the falling edge and rising edge of the write signal. It defines the duration of the data phase used in asynchronous accesses. For synchronous accesses this value is considered irrelevant.

Usually denoted as tWP on the memory datasheet.

So, DATAST = tWP/tHCLK = (35ns/4.6ns) = 7.6 (approximately)

Lionking_31-1722893749044.png

Lionking_32-1722893749046.png

  • Bus Turnaround Time (BUSTURN):

Defines a delay inserted between memory access operations (reads and writes) on the data bus. It ensures reliable data transfer by giving the memory chip enough time to complete the previous operation before receiving a new request. Without it, there might be an occurrence of data corruption if the memory tries to handle two requests simultaneously.
On the memory datasheet, this delay aligns with the minimum time between consecutive transactions (tEHEL from NEx high to NEx low). The maximum time required for the memory to release the data bus after a read access (tEHQZ). This delay is applied between:

  • Asynchronous read/write (multiplexed I/O or mode D)

  • Other asynchronous/synchronous read or write to/from a static bank.

For reads the bank can be the same or different, but for writes it can be different except for multiplexed I/O or access mode D.

Lionking_33-1722893749046.png

 

Lionking_34-1722893749048.png

 

Lionking_35-1722893749049.png

 

 

Lionking_36-1722893749051.png

 

Even with the BUSTURN programmed, there are situations where the system automatically inserts a fixed delay regardless of the programmed value:

  • No delay between consecutive writes to the same bank (except modes A & D)

  • 1 HCLK cycle delay for most other read/write combinations (except modes A & D)

  • 2 HCLK cycle delay for consecutive synchronous writes or a synchronous write followed by an asynchronous access

  • 3 HCLK cycle delay for consecutive synchronous writes to different banks or a synchronous write followed by a synchronous read

According to the memory datasheet:

  • tEHEL = 20 ns (min)
  • tEHQZ = 20 ns (max)
  • BUSTURN = 20 ns/4.6 ns = 4.3 ns (approximately 5 ns)

 

  • Address hold phase duration (ADDHLD):

This parameter is used only for asynchronous memories when used in access mode D (nonmultiplexed or multiplexed). In synchronous accesses, this value is not used and always 1 memory clock period duration.

Lionking_37-1722893749054.png

 

Lionking_38-1722893749056.png

 

Lionking_39-1722893749058.png

  • Clock Divide Ratio (CLKDIV):

This parameter is only used in the case of synchronous NOR flash and defines a period of FMC_CLK clock output signal given as an input to the NOR flash. For asynchronous NOR flash this value is considered irrelevant.

  • Data latency (DATLAT):

This parameter is only used for synchronous memories for access with read/write burst mode enabled. This defines the number of memory clock cycles to issue to the memory before sampling(reading/writing) the first data.

The timings parameter is not expressed in HCLK periods but in FMC_CLK periods.
The number of latency clock cycles might be dependent on the operating NOR flash operating frequency.

For example, looking at the Infineon S26KL12S Hyperbus NOR flash memory datasheet, the read latencies required for this part are shown below:

Lionking_40-1722893749060.png

 

Lionking_41-1722893749062.png

This part does not require initial latency clock cycles for the write operations.

 

Under the NOR flash configurations tab, enable the write operation and write FIFO field so that write operations are enabled for the NOR flash bank by the FMC.

Note: Enabling the write FIFO enables the FIFO for all the 4 subbanks of Bank 1 of the FMC.

Extended mode is kept disabled. By default extended mode 2 is selected when NOR flash memory is used.

Note: Enabling the extended mode gives access to 3 additional modes (B, C, D). It is possible to mix A, B, C, and D modes for read and write operations. For example, read operation can be performed in mode B and write operation can be performed in mode C.

Enabling this mode opens up the FMC_BWTR register to program the write timings if we wish to have different write and read timings.
The read and write access waveforms for different modes can be referenced from RM0410.

When programming a synchronous NOR flash memory, the memory automatically switches between the synchronous and the asynchronous mode, so in this case, all parameters have to be set correctly.


Now, input all the calculated parameter values into their respective fields:

Lionking_47-1722894869758.png

 

 

2.2 Timing equations to be satisfied

  • (ADDSET + (DATAST + 1)) * tHCLK >= max (tWC, tRC)

 According to the datasheet max (tWC, tRC) = 60 ns

(13 + 8 * 4.6 ns = 96.6 ns, which is greater than 60 ns

Lionking_48-1722894869759.png

 

Lionking_49-1722894869761.png

  • DATAST * tHCLK >= tWP

 tWP = 35 ns

DATAST * tHCLK = 8 * 4.6 ns = 36.8 ns, which is greater than 35 ns

 

  • For read accesses, DATAST must be verified

DATAST >= ((tAVQV + tsu(Data_NE) + tv(A_NE)) / tHCLK) – ADDSET

tAVQV = 60 ns

tsu(Data_NE) + tv(A_NE)  = tHCLK – 0.5

8 >=  ((60 ns + 4.6 ns – 0.5) / 4.6 ns) - 13  

8 >= 1, which satisfies the condition.

Lionking_50-1722894869764.png

For synchronous NOR flash memories, the maximum frequency that the FMC supports depends on the VDD being supplied to the MCU and load capacitance on the data and address lines.

This value is mentioned on the datasheet documentation.

Lionking_51-1722894869765.png

2.3. Set up the GPIO

Finally, under the [GPIO Setting] tab, we can see that the STM32CubeMX default pinout for the NOR flash matches the STM32F769I-EVAL board schematic, except for the following pins: FMC_NWAIT and FMC_NE1

  • STM32CubeMX selects PD6 for FMC_NWAIT by default while the schematic uses PC6

  • STM32CubeMX selects PD7 for FMC_NWAIT by default while the schematic uses PC7

Hence, we need to change the mapping of FMC_NWAIT and FMC_NE1 to match the STM32F769I-EVAL board schematic.
There is a shortcut to do this: Hover the mouse pointer on the pin signal that you want to move.
Hold CTRL + left mouse button and move it to the desired alternate function pin that blinks in black color.

Lionking_52-1722895060465.png

Lionking_53-1722895060477.png

Verify that the GPIOs for the FMC are in high-speed mode. However, by default they are in high-speed mode in this example

Lionking_54-1722895060479.png

Now, all the configurations are done and generate code by clicking on this icon.

Lionking_55-1722895060481.png

 

2.4. Code to add in the main.c file

The code to add in between the respective user code comments in the main.c file are mentioned as below:

/* USER CODE BEGIN PTD */
typedef enum {FAILED = 0, PASSED = !FAILED} TestStatus;
/* USER CODE END PTD */

/* USER CODE BEGIN PD */
#define NOR_BANK_ADDR                 ((uint32_t)0x60000000)
#define PROGRAM_TIMEOUT               ((uint32_t)0x00004400)  /* NOR program timeout     */
#define ERASE_TIMEOUT                 ((uint32_t)0x00A00000)  /* NOR erase timeout     */

#define BUFFER_SIZE         ((uint32_t)0x1000)
#define WRITE_READ_ADDR     ((uint32_t)0x0800)
/* USER CODE END PD */

/* USER CODE BEGIN PFP */
static void Fill_Buffer(uint16_t *pBuffer, uint32_t uwBufferLenght, uint16_t uwOffset);
static TestStatus Buffercmp(uint16_t *pBuffer1, uint16_t *pBuffer2, uint16_t BufferLength);
/* USER CODE END PFP */

/* USER CODE BEGIN 0 */
/* Read/Write Buffers */
uint16_t aTxBuffer[BUFFER_SIZE];
uint16_t aRxBuffer[BUFFER_SIZE];

/* Status variables */
__IO uint32_t uwWriteReadStatus = 0;

/* Counter index */
uint32_t uwIndex = 0;
/* USER CODE END 0 */

  /* USER CODE BEGIN 2 */;

    /*##-2- NOR memory read/write access ######################################*/
      /* Fill the buffer to write */
      Fill_Buffer(aTxBuffer, BUFFER_SIZE, 0xD20F);

      /* Erase block */
      HAL_NOR_Erase_Block(&hnor1, WRITE_READ_ADDR, NOR_BANK_ADDR);

      /* Wait until NOR is ready */
      if(HAL_NOR_GetStatus(&hnor1, NOR_BANK_ADDR, ERASE_TIMEOUT) != HAL_NOR_STATUS_SUCCESS)
      {
        return HAL_NOR_STATUS_ERROR;
      }

      /* Write data to the NOR memory */
      for (uwIndex = 0; uwIndex < BUFFER_SIZE; uwIndex++)
      {
        /* Write data to NOR */
        HAL_NOR_Program(&hnor1, (uint32_t *)(NOR_BANK_ADDR + WRITE_READ_ADDR + 2*uwIndex), &aTxBuffer[uwIndex]);

        /* Read NOR device status */
        if(HAL_NOR_GetStatus(&hnor1, NOR_BANK_ADDR, PROGRAM_TIMEOUT) != HAL_NOR_STATUS_SUCCESS)
        {
          return HAL_NOR_STATUS_ERROR;
        }
      }

      /* Read back data from the NOR memory */
      if(HAL_NOR_ReadBuffer(&hnor1, NOR_BANK_ADDR + WRITE_READ_ADDR, &aRxBuffer[0], BUFFER_SIZE) != HAL_OK)
      {
        return HAL_ERROR;
      }

      /*##-3- Checking data integrity ############################################*/
      uwWriteReadStatus = Buffercmp(aTxBuffer, aRxBuffer, BUFFER_SIZE);

      if (uwWriteReadStatus != PASSED)
      {
        /* Set LED */
        while(1)
        {
          HAL_GPIO_WritePin(GPIOI, GPIO_PIN_15, GPIO_PIN_SET);
        }
      }
      else
      {
        /* OK */
      	/* Toggle LED */
      	while(1)
      	{
      		HAL_GPIO_TogglePin(GPIOI, GPIO_PIN_15);
      		HAL_Delay(500);
      	}
      }
  /* USER CODE END 2 */





/* USER CODE BEGIN 4 */
static void Fill_Buffer(uint16_t *pBuffer, uint32_t uwBufferLenght, uint16_t uwOffset)
{
  uint16_t tmpIndex = 0;

  /* Put in global buffer different values */
  for (tmpIndex = 0; tmpIndex < uwBufferLenght; tmpIndex++ )
  {
    pBuffer[tmpIndex] = tmpIndex + uwOffset;
  }
}

/**
  * @brief  Compares two buffers.
  *   pBuffer1, pBuffer2: buffers to be compared.
  *   BufferLength: buffer's length
  * @retval PASSED: pBuffer identical to pBuffer1
  *         FAILED: pBuffer differs from pBuffer1
  */
static TestStatus Buffercmp(uint16_t* pBuffer1, uint16_t* pBuffer2, uint16_t BufferLength)
{
  while (BufferLength--)
  {
    if (*pBuffer1 != *pBuffer2)
    {
      return FAILED;
    }

    pBuffer1++;
    pBuffer2++;
  }

  return PASSED;
}
/* USER CODE END 4 */

3. Results

Connect your board to the PC using a USB cable. Then load the binary file by clicking on the button shown below and the programming should be completed without any errors.

Lionking_56-1722895200811.png

Once the binary is loaded, you should see the green LED on the evaluation board toggling every 500 ms. This verifies that the FMC configurations for the NOR flash are done correctly. If you see the LED set permanently and not toggling, this shows that your FMC configurations were incorrect. This means that the FMC peripheral was not able to communicate with the memory device.

4. Related links

Version history
Last update:
‎2024-08-27 04:34 AM
Updated by: