Getting started with External memory manager custom driver using an external NOR Flash memory

Target description

This article provides an overview of the External Memory Manager (EMM) and External Memory Loader (EML) for BootFlash applications on STM32H7Rx/7Sx and STM32N6 devices, first introducing their main capabilities and then presenting a detailed tutorial on how to configure and use them with the EMM Custom driver. The first part of the article:

The first part of the article introduces the External Memory Manager (EMM) and External Memory Loader (EML) middleware features in BootFlash applications on STM32H7Rx/7Sx and STM32N6 products, in order to:

  • Provide a unique API that allows access to external memories.
  • Launch an application stored in an external memory (XIP (eXecute In Place) or LRUN (Load & Run)).
  • Generate an External Memory Loader file (.stldr) for STM32Cube tools and IDEs.


This Getting Started focuses on the EMM Custom driver, which is used when the memory configuration is provided by the user (instead of being discovered from SFDP tables). The example memories used are MX66UW1G45G[1] and M95P32[2].

The second part of the article:

The second part of the article presents a detailed tutorial for STM32H7R/H7S and STM32N6 on how to:

  • Configure the External Memory Manager with the Custom driver.
  • Select a selected memory configuration matching the external memory (examples: MX66UW1G45G[1], M95P32[2]).
  • Configure the External Memory Loader for the same memory.
  • Build a BootFlash application and generate a loader file.
  • Select each configuration parameter from the memory datasheet as used in the 'EXTMEM_<MEMORY>_INIT' configuration object.

Hardware

  • STM32H7Rx/7Sx or STM32N6

Software

  • STM32CubeMX.
  • An IDE (IAR Systems®, Keil®, STM32CubeIDE).
  • STM32CubeProgrammer (for loader usage).

Literature

  • Introduction to External Memory Manager and External Memory Loader Middleware for Boot Flash MCU[3]
  • External memory datasheet for the selected memory configuration (e.g., MX66UW1G45G[1], M95P32[2])

1. What is a BootFlash product

1.1. Definition

A BootFlash product is a device that contains a small embedded flash memory for the initial boot process and stores the application binary in an external memory.

1.2. What is a BootFlash application?

A BootFlash application is an application generated with STM32CubeMX that uses the External Memory Manager and External Memory Loader middleware to build a boot system capable of launching an application stored in external memory.

The project structure of a BootFlash application is divided into three different contexts:

  • Boot: boot code to run from the internal flash memory.
  • Appli: application code to run from the external memory.
  • ExtMemLoader: code dedicated to generating the External Loader.

When using external memories, loader configuration is a critical step. ST provides multiple drivers and enables customers to adapt the loader behavior through the Custom Driver within the Boot project. The remainder of this article focuses on a configuration example of this Custom Driver for the target memory.

2. External Memory Manager Custom driver

2.1. Why a Custom driver?

The EMM Custom driver is used when the memory configuration (commands, dummy cycles, sizes, timings, etc.) is provided by the user instead of being discovered from SFDP data stored in the device.

Typical reasons to select the Custom driver:

  • The memory does not support SFDP, or SFDP content is not usable (missing information).
  • Need to improve boot time (no discovery at runtime).
  • The memory configuration file is already available.

For an overview of the Custom driver APIs and the meaning of each configuration field, see:

2.2. Where the configuration file should be created

In a Custom-driver workflow, the memory configuration is expected to be written by the user as a C header that defines a 'EXTMEM_DRIVER_CUSTOM_ObjectTypeDef' initializer. The user could either create a new header file containing details about the used memory, or reuse memory configuration files as provided by ST.

For the MX66UW1G45G[1], create a new header or reuse the provided one (recommended location):

  • Middlewares\ST\STM32_ExtMem_Manager\custom\memories\stm32_mx66uw1g45g.h

For the M95P32[2], use:

  • Middlewares\ST\STM32_ExtMem_Manager\custom\memories\stm32_m95p32.h

This header should define:

  • 'EXTMEM_<MEMORY>_INIT' (initializer macro for 'EXTMEM_DRIVER_CUSTOM_ObjectTypeDef')
  • 'extmem_<memory>' (the object instance)

Examples:

Information
The MX66UW1G45G datasheet is not publically available, a request must be sent to have access to the datasheet, however except for the RWW (read while write) feature and Octal mode, the datasheet of the MX66U1G45G[5] is similar and publicly available
  • MX66UW1G45G[1]: 'EXTMEM_MX66UW1G45G_INIT', 'extmem_mx66uw1g45g'
  • M95P32[2]: 'EXTMEM_M95P32_INIT', 'extmem_m95p32'

Other files in Middlewares\ST\STM32_ExtMem_Manager\custom\memories\ can be used as examples of how to structure a memory descriptor (include guards, macros, and the '*_INIT' object layout).

The values inserted in 'EXTMEM_<MEMORY>_INIT' are derived from the memory datasheet (instruction set/opcodes, timing characteristics, erase/program timings, memory organization).

3. How EMM selects a Custom memory configuration

EMM uses the configuration table 'extmem_list_config[]' declared in the generated configuration header (typically 'stm32_extmem_conf.h'). Each extmem_list_config[] array element contains details about a memory managed by the External Memory Manager MW. Each element indicates:

  • the memory type ('MemType')
  • the instance/physical link ('Handle', 'ConfigType')
  • the driver object (for Custom driver: 'CustomObject')

At runtime, 'EXTMEM_Init()' dispatches to the correct driver implementation from the 'MemType' parameter (set to 'EXTMEM_CUSTOM' in our case).

For Custom driver the call path is:

  • 'EXTMEM_Init()' → 'EXTMEM_DRIVER_CUSTOM_Init(..., &extmem_list_config[MemId].CustomObject)'

The type layout showing 'CustomObject' in 'EXTMEM_DefinitionTypeDef' is defined in:

  • Code/stm32_extmem_type.h

Practical selection: using the MX66UW1G45G[1] object with 'EXTMEM_DRIVER_CUSTOM_Init()'

In a BootFlash application, the EMM middleware normally calls the Custom driver through 'EXTMEM_Init()' using the generated 'extmem_list_config[]' configuration.

For understanding/debug purposes (or for a minimal bring-up), you can also call the Custom driver directly using the MX66UW1G45G[1] object.

The MX66UW1G45G[1] object is defined in Middlewares\ST\STM32_ExtMem_Manager\custom\memories\stm32_mx66uw1g45g.h as 'extmem_mx66uw1g45g' and is initialized with 'EXTMEM_MX66UW1G45G_INIT'.

Minimal example (direct call):

#include "stm32_extmem_conf.h"        /* brings the HAL handle declaration (e.g. hxspi2) */
#include "custom/memories/stm32_mx66uw1g45g.h"
#include "custom/stm32_custom_driver_api.h"

/* Example: use the XSPI instance connected to MX66UW1G45G */
static void MX66UW1G45G_BringUp(void)
{
	/* ClockInput is the clock frequency of the XSPI, it can be retrieved using a clock fetch function or set directly*/
	uint32_t clockInputHz = HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2);

	/* Initialize the Custom driver with the user-provided memory object */
  (void)EXTMEM_DRIVER_CUSTOM_Init((void *)&hxspi2, clockInputHz, &extmem_mx66uw1g45g);
}

4. How to derive 'EXTMEM_<MEMORY>_INIT' from the datasheet

This section explains how each parameter of 'EXTMEM_MX66UW1G45G_INIT' (written by the user or taken from a provided configuration file) is chosen from the MX66UW1G45G[1] datasheet. The user can update some of those values according to the expected memory configuration.

For general guidance on what each field means and where it is used by the Custom driver, see:

General memory configuration

Field Description Datasheet section Value
MemType Select the NOR Flash/EEPROM-like command set. Cover page / features (serial SPI page EEPROM, erase commands, sectors/blocks) 'EXTMEM_CUSTOM_NOR_FLASH'
MemStandard Select the correct 'EXTMEM_CUSTOM_STD_*' enum matching the memory vendor. Vendor identification (part number, manufacturer ID) 'EXTMEM_CUSTOM_STD_MACRONIX'
MemSize Select the correct density enum. Features / memory organization (1 Gbit) 'EXTMEM_CUSTOM_SIZE_1GB'
ResetMethod Use the reset instruction sequence required by the device. Reset / software reset instructions 'EXTMEM_CUSTOM_RESET_METHOD_6699_9966'
ResetDelay Delay after reset before sending new commands. Adjust based on the datasheet worst-case if required. Reset / Reset Timing (Reset Recovery time for chip erase operation) '100' ms
SampleShiftCfg Select sample shift configuration depending on the board signal integrity and interface mode. Interface / timing recommendations 'EXTMEM_CUSTOM_SSHIFT_CFG_NONE'
MemChipSelectHighTimeCycle Ensure the chip-select high time between instructions respects the datasheet minimum deselect time. Choose this value so that tCSH >= tSHSL, where tCSH is derived from the XSPI clock period. AC characteristics (chip-select deselect time, often named like tSHSL) '8' cycles
Information
Short note: 'EXTMEM_CUSTOM_SIZE_1GB' refers to a 1-Gbit device (128 MBytes). The enum names use “MB/GB” but the comments in 'stm32_custom_driver_type.h' clarify the density.

Startup configuration (baseline SPI STR)

StartupConfig is typically a safe configuration that can be used for reset/bring-up and basic reads/writes.

Field Description Datasheet section Value
StartupConfig.Frequency Use a frequency compatible with the slowest required instruction class. Command set / instruction description (frequency limits for read instructions) '50000000' Hz
StartupConfig.CommandRead Select a read opcode compatible with the selected access mode. Instruction set / command table (read) '0x0C'
StartupConfig.DummyCycleRead One dummy byte = 8 dummy clock cycles in STR mode. Read command description (dummy cycles/dummy byte) '0x08'
StartupConfig.CommandWrite Use the program opcode supported by the memory in this mode. Instruction set / command table (program) '0x12'
StartupConfig.DummyCycleWrite No dummy cycles for page program. Page program command description '0x00'
StartupConfig.DummyRegisterRead Dummy cycles required when reading registers in this mode. Register read access description '0'
StartupConfig.AddressSize 32-bit addressing. Address protocol / 4-byte address command set 'EXTMEM_CUSTOM_ADDRESS_32_BITS'
StartupConfig.AccessMode Instruction/address/data are transferred in single line STR. Timing diagrams 'EXTMEM_CUSTOM_1S_1S_1S'
StartupConfig.InstructionSize Standard SPI opcode size. Instruction set / command table (opcode width) 'EXTMEM_CUSTOM_INSTRUCTION_8_BITS'
StartupConfig.DqsMode DQS is not used in SPI STR. Interface description (SPI STR) 'EXTMEM_CUSTOM_DQS_DISABLE'

Register configuration sequence before enabling 'OptionalConfig'

Before switching the driver to the octal DTR 'OptionalConfig', the MX66UW1G45G[1] example executes four register-configuration steps. The first two steps program Configuration Register 2 while the memory is still accessed in startup SPI STR mode, the third step applies 'OptionalConfig', and the fourth step verifies register access with the new protocol.

Parameter Description Datasheet section Value
NbRegisterConfig Number of configuration steps that must be executed before normal accesses start. The MX66UW1G45G[1] flow needs four steps: program dummy cycles, enable DTR OPI, apply 'OptionalConfig', then verify the new protocol. Configuration register 2 / protocol-switch procedure '4'

Step 0: Program the dummy-cycle selection before the protocol switch

This step uses the '0x71' / '0x72' / '0x71' read-write-read sequence on Configuration Register 2 at address '0x300' to program the dummy-cycle setting that matches the later high-speed read timing.

Parameter Description Datasheet section Value
RegisterConfig[0]. ConfigStepType Select a read-write-read register flow so the driver reads Configuration Register 2, updates it, then reads it again to verify the write while still in startup SPI STR mode. Configuration register 2 / configurable dummy cycle / register access sequence 'EXTMEM_CUSTOM_CFGSTEP_RWR_REG_ADDR'
RegisterConfig[0]. RWConfigStep.CommandRegisterWrite Register-write opcode used to program Configuration Register 2. Register write instruction / configuration register 2 access '0x72'
RegisterConfig[0]. RWConfigStep.CommandRegisterRead Register-read opcode used for the initial readback of Configuration Register 2. Register read instruction / configuration register 2 access '0x71'
RegisterConfig[0]. RWConfigStep.CommandRegisterReadAW Register-read opcode used for the post-write verification readback. Register read after write / configuration register 2 access '0x71'
RegisterConfig[0]. RWConfigStep.RegisterAddress Selects Configuration Register 2 sub-address '0x300', which is used here for dummy-cycle selection. Configuration register 2 / dummy-cycle field location '0x300'
RegisterConfig[0]. RWConfigStep.RegisterValue Programs the dummy-cycle field with value '0x00', matching the default code used by the descriptor before switching to the octal DTR read timing. Configuration register 2 / dummy-cycle encoding '0x00'
RegisterConfig[0]. RWConfigStep.RegisterMask Restricts the write to the dummy-cycle selection bits only. Configuration register 2 / dummy-cycle bit mask '0x07'

Step 1: Enable the DTR OPI protocol in Configuration Register 2

This step updates Configuration Register 2 at address '0x00' so the device accepts the octal DTR protocol used by 'OptionalConfig'.

Parameter Description Datasheet section Value
RegisterConfig[1]. ConfigStepType Select a read-write register flow so the driver reads the current register value, applies the requested masked update, and writes it back before leaving startup SPI STR mode. Configuration register 2 / protocol-selection bits / register access sequence 'EXTMEM_CUSTOM_CFGSTEP_RW_REG_ADDR'
RegisterConfig[1]. RWConfigStep.CommandRegisterWrite Register-write opcode used to set the protocol-selection bit. Register write instruction / configuration register 2 access '0x72'
RegisterConfig[1]. RWConfigStep.CommandRegisterRead Register-read opcode used to fetch the current register value before modification. Register read instruction / configuration register 2 access '0x71'
RegisterConfig[1]. RWConfigStep.RegisterAddress Selects Configuration Register 2 sub-address '0x00', used here for protocol selection. Configuration register 2 / protocol-selection field location '0x00'
RegisterConfig[1]. RWConfigStep.RegisterValue Sets the DTR OPI enable bit required before the memory can be accessed with the octal DTR commands from 'OptionalConfig'. Configuration register 2 / DTR OPI enable bit '0x02'
RegisterConfig[1]. RWConfigStep.RegisterMask Restricts the update to the protocol-selection bit only. Configuration register 2 / protocol-selection bit mask '0x02'

Step 2: Execute the Optional configuration

This step is the actual protocol switch from startup (StartupConfig) SPI STR settings to the octal DTR access parameters defined in 'OptionalConfig'.

Parameter Description Datasheet section Value
RegisterConfig[2]. ConfigStepType Tells the driver to apply the 'OptionalConfig' block after the prerequisite register writes are complete. This is where the access mode changes to octal DTR ('EXTMEM_CUSTOM_8D_8D_8D', 16-bit instructions, DQS enabled, '200000000' Hz). Mode-switch procedure / protocol transition to octal DTR 'EXTMEM_CUSTOM_CFGSTEP_EXEC_OPT_CFG'

Step 3: Verify register access after the protocol switch

After 'OptionalConfig' has been applied, the driver performs one more register read using the post-switch opcode to confirm that the memory now answers with the new protocol.

Parameter Description Datasheet section Value
RegisterConfig[3]. ConfigStepType Select a read-after-write style access using the new protocol, so the driver can verify register communication after the octal DTR switch. Register read after protocol switch / post-switch verification 'EXTMEM_CUSTOM_CFGSTEP_RAW_REG_ADDR'
RegisterConfig[3]. RWConfigStep.CommandRegisterReadAW Post-switch register-read opcode used once the device is already in the new octal DTR protocol. Register read after protocol switch / octal DTR register access '0x718E'
RegisterConfig[3]. RWConfigStep.RegisterAddress Reads back Configuration Register 2 at sub-address '0x00' to verify the new protocol selection. Configuration register 2 / protocol-selection field location '0x00'

Optional configuration (performance configuration: Octal mode)

The MX66UW1G45G[1] example switches to an octal access mode for higher throughput.

Field Description Datasheet section Value
OptionalConfig.Frequency Use the max frequency validated for this interface mode. Mode timing limits '200000000' Hz
OptionalConfig.CommandRead Select the octal read opcode for the selected mode. Instruction set / command table (octal read) '0xEE11'
OptionalConfig.CommandWrite Select the octal write/program opcode for the selected mode. Instruction set / command table (octal program) '0x12ED'
OptionalConfig.DummyCycleRead Configure dummy cycles to meet the memory timing at the selected frequency. Read command description (dummy cycles) '20'
OptionalConfig.DummyCycleWrite No dummy cycles are used for the write/program command in this mode. Page program command description '0'
OptionalConfig.DummyRegisterRead Dummy cycles required when reading registers in this mode. Register read access description '4'
OptionalConfig.AccessMode Instruction/address/data all use octal transfers. Timing diagrams 'EXTMEM_CUSTOM_8D_8D_8D'
OptionalConfig.InstructionSize Some octal modes use 16-bit opcodes. Opcode width in this mode 'EXTMEM_CUSTOM_INSTRUCTION_16_BITS'
OptionalConfig.AddressSize Keep 32-bit addressing enabled for this 1-Gbit density. 256Mb Address protocol / 4-byte address command set 'EXTMEM_CUSTOM_ADDRESS_32_BITS'
OptionalConfig.DqsMode Enable DQS if required/used by the selected mode. DQS usage for high-speed reads 'EXTMEM_CUSTOM_DQS_ENABLE'
Information
If the board/memory requires setting mode bits (for example to enable octal mode / DQS / protocol selection), add the required register read/write steps in 'RegisterConfig[]' before executing 'EXTMEM_CUSTOM_CFGSTEP_EXEC_OPT_CFG'.

NOR Flash specific configuration

Field Description Datasheet section Value
NorFlashConfig.PageSize Used by the driver to split writes. Features / memory organization (page size) / page program description (max bytes) '256' bytes
NorFlashConfig.SectorSize Used by erase operations. Features / memory organization (sector size) / sector erase description '0x1000' (4 KBytes)
NorFlashConfig.Startup.Cmd_EraseSector Sector erase opcode used while the memory is still in startup SPI STR mode. Sector erase (SE) / 4-byte address command set '0x21'
NorFlashConfig.Startup.Cmd_MassErase Chip erase opcode used while the memory is still in startup SPI STR mode. Chip erase (CE) '0x60'
NorFlashConfig.Startup.Cmd_RDSR Used for WIP/WEL polling while the memory is in startup SPI STR mode. Read status register (RDSR) '0x05'
NorFlashConfig.Startup.Cmd_WE Required before modify operations while the memory is in startup SPI STR mode. Write enable (WREN) '0x06'
MatchMask_WEL / MatchValue_WEL Confirms write enable latch is set. Status register description (WEL bit) '0x02' / '0x02'
MatchMask_WIP / MatchValue_WIP Poll until operation is complete. Status register description (WIP bit) '0x01' / '0x00'
NorFlashConfig.Startup.SRAccessDetails Status-register access details for startup SPI STR mode. The standard 'RDSR' command does not need an extra register address in this example. Read status register (RDSR) 'EXTMEM_CUSTOM_SR_ACCESS_CFG_UNDEFINED'
MaxSectorEraseTime / MaxChipEraseTime Choose conservative max values for timeout handling; adjust according to the datasheet worst-case. Programming / erase timing characteristics (sector erase time, chip erase time) '400 ms' / '300000 ms'
NorFlashConfig.OptionalConfigEnable Enables the dedicated NOR Flash command set to be used after the protocol switch, so erase/status/write-enable operations use the octal DTR values below instead of the startup SPI STR values. Configuration register 2 / protocol-switch procedure '1'
NorFlashConfig.Optional.Cmd_EraseSector Sector erase opcode used after 'OptionalConfig' switches the memory to octal DTR mode. Sector erase (SE) / protocol-specific opcode table '0x21DE'
NorFlashConfig.Optional.Cmd_MassErase Chip erase opcode used after the protocol switch to octal DTR mode. Chip erase (CE) / protocol-specific opcode table '0x609F'
NorFlashConfig.Optional.Cmd_RDSR Status-register read opcode used for WIP/WEL polling after the switch to octal DTR mode. Read status register (RDSR) / protocol-specific opcode table '0x05FA'
NorFlashConfig.Optional.Cmd_WE Write-enable opcode used before program or erase operations once octal DTR mode is active. Write enable (WREN) / protocol-specific opcode table '0x06F9'
NorFlashConfig.Optional.SRAccessDetails Status-register access details for octal DTR mode. This example uses register address '0x0' for status polling in the post-switch protocol. Read status register (RDSR) / register access address format 'EXTMEM_CUSTOM_SR_ACCESS_ADD_0'

Final MX66UW1G45G[1] configuration example (stm32_mx66uw1g45g.h)

This section provides a complete example of what the final MX66UW1G45G[1] memory descriptor could look like.

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32_MX66UW1G45G__H__
#define __STM32_MX66UW1G45G__H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "stm32_extmem.h"
#include "stm32_extmem_conf.h"
#if defined(EXTMEM_DRIVER_CUSTOM) && (EXTMEM_DRIVER_CUSTOM == 1)
#include "stm32_custom_driver_api.h"
#include "stm32_custom_driver_type.h"

/** @defgroup CUSTOM CUSTOM driver
  * @ingroup EXTMEM_DRIVER
  * @{
  */

/* Private Macro ------------------------------------------------------------*/
/** @defgroup CUSTOM_Private_Macro Private Macro
  * @{
  */
#define EXTMEM_MX66UW1G45G_CMD_READ                 0x0C    /*!< Command to read data */
#define EXTMEM_MX66UW1G45G_CMD_WRITE                0x12    /*!< Command to write data */
#define EXTMEM_MX66UW1G45G_DUMMY_READ               0x08    /*!< Number of dummy cycles for read operations */
#define EXTMEM_MX66UW1G45G_DUMMY_WRITE              0x00    /*!< Number of dummy cycles for write operations */
#define EXTMEM_MX66UW1G45G_DUMMY_REG_READ           0       /*!< Number of dummy cycles to read register */

#define EXTMEM_MX66UW1G45G_PAGE_SIZE                256     /*!< Page size 256 Bytes */
#define EXTMEM_MX66UW1G45G_SECTOR_SIZE              0x1000  /*!< Sector size in bytes (4K) */
#define EXTMEM_MX66UW1G45G_CMD_ERASE_SECTOR         0x21    /*!< Command to erase a sector */
#define EXTMEM_MX66UW1G45G_CMD_MASS_ERASE           0x60    /*!< Command to perform mass erase */
#define EXTMEM_MX66UW1G45G_CMD_RDSR                 0x05    /*!< Command to read status register */
#define EXTMEM_MX66UW1G45G_CMD_WE                   0x06    /*!< Command to enable write operations */
#define EXTMEM_MX66UW1G45G_MASK_WEL                 0x02    /*!< Mask for write enable latch */
#define EXTMEM_MX66UW1G45G_VALUE_WEL                0x02    /*!< Match Value for write enable latch */
#define EXTMEM_MX66UW1G45G_MASK_WIP                 0x01    /*!< Mask for write in progress */
#define EXTMEM_MX66UW1G45G_VALUE_WIP                0x00    /*!< Match Value for write in progress */

#define EXTMEM_MX66UW1G45G_CMD1_WRITE_CFG2          0x72    /*!< Command to write to a register */
#define EXTMEM_MX66UW1G45G_CMD1_READ_CFG2           0x71    /*!< Command to read from a register */
#define EXTMEM_MX66UW1G45G_CMD1_READAW_CFG2         0x71    /*!< Command to read after write operation */
#define EXTMEM_MX66UW1G45G_CMD1_ADDR_CFG2           0x300   /*!< Address of the register */
#define EXTMEM_MX66UW1G45G_CMD1_VALUE_CFG2          0x00    /*!< Value of the register */
#define EXTMEM_MX66UW1G45G_CMD1_MASK_CFG2           0x07    /*!< Mask for the register */

#define EXTMEM_MX66UW1G45G_CMD2_WRITE_CFG2          0x72    /*!< Command to write to a register */
#define EXTMEM_MX66UW1G45G_CMD2_READ_CFG2           0x71    /*!< Command to read from a register */
#define EXTMEM_MX66UW1G45G_CMD2_ADDR_CFG2           0x00    /*!< Address of the register */
#define EXTMEM_MX66UW1G45G_CMD2_VALUE_CFG2          0x02    /*!< Value of the register */
#define EXTMEM_MX66UW1G45G_CMD2_MASK_CFG2           0x02    /*!< Mask for the register */

#define EXTMEM_MX66UW1G45G_CMD3_READAW_CFG2         0x718E  /*!< Command to read after write operation */
#define EXTMEM_MX66UW1G45G_CMD3_ADDR_CFG2           0x00    /*!< Address of the register */

#define EXTMEM_MX66UW1G45G_OCTAL_CMD_ERASE_SECTOR   0x21DE  /*!< Command to erase a sector */
#define EXTMEM_MX66UW1G45G_OCTAL_CMD_MASS_ERASE     0x609F  /*!< Command to perform mass erase */
#define EXTMEM_MX66UW1G45G_OCTAL_CMD_READ           0xEE11  /*!< Command to read data */
#define EXTMEM_MX66UW1G45G_OCTAL_CMD_WRITE          0x12ED  /*!< Command to write data */
#define EXTMEM_MX66UW1G45G_OCTAL_DUMMY_READ         20      /*!< Number of dummy cycles for read operations */
#define EXTMEM_MX66UW1G45G_OCTAL_DUMMY_WRITE        0       /*!< Number of dummy cycles for write operations */
#define EXTMEM_MX66UW1G45G_OCTAL_DUMMY_REG_READ     4       /*!< Dummy cycles to read register */
#define EXTMEM_MX66UW1G45G_OCTAL_CMD_RDSR           0x05FA  /*!< Command to read status register in octal mode */
#define EXTMEM_MX66UW1G45G_OCTAL_CMD_WE             0x06F9  /*!< Command to enable write operations in octal mode */
#define EXTMEM_MX66UW1G45G_OCTAL_MASK_WEL           0x02    /*!< Mask for write enable latch */
#define EXTMEM_MX66UW1G45G_OCTAL_VALUE_WEL          0x02    /*!< Match Value for write enable latch */
#define EXTMEM_MX66UW1G45G_OCTAL_MASK_WIP           0x01    /*!< Mask for write in progress */
#define EXTMEM_MX66UW1G45G_OCTAL_VALUE_WIP          0x00    /*!< Match Value for write in progress */
/**
  * @}
  */

/* Private typedefs ---------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
/* Private functions ---------------------------------------------------------*/

/** @defgroup CUSTOM_Exported_Functions Exported Functions
  * @{
  */

#define EXTMEM_MX66UW1G45G_INIT { \
                                  .MemType                                  = EXTMEM_CUSTOM_NOR_FLASH,               /*!< Common field for memory type        */ \
                                  .MemStandard                              = EXTMEM_CUSTOM_STD_MACRONIX,            /*!< e.g., Macronix, Micron, etc.        */  \
                                  .MemSize                                  = EXTMEM_CUSTOM_SIZE_1GB,                /*!< Common field for memory Size        */ \
                                  .ResetMethod                              = EXTMEM_CUSTOM_RESET_METHOD_6699_9966,  /*!< Reset method based on 6699h and 9966h instructions */ \
                                  .ResetDelay                               = 100,                                   /*!< Time needed after Reset (in ms, 0 if no required delay */ \
                                  .SampleShiftCfg                           = EXTMEM_CUSTOM_SSHIFT_CFG_NONE,         /*!< Sample Shift setting */ \
                                  .MemChipSelectHighTimeCycle               = 8,                                     /*!< Common field for memory Chip select high time */ \
                                  .StartupConfig.Frequency                  = 50000000,                              /*!< Operating frequency in Hz*/ \
                                  .StartupConfig.CommandRead                = EXTMEM_MX66UW1G45G_CMD_READ,           /*!< Command to read data */ \
                                  .StartupConfig.CommandWrite               = EXTMEM_MX66UW1G45G_CMD_WRITE,          /*!< Command to write data */ \
                                  .StartupConfig.DummyCycleRead             = EXTMEM_MX66UW1G45G_DUMMY_READ,         /*!< Number of dummy cycles for read operations */ \
                                  .StartupConfig.DummyCycleWrite            = EXTMEM_MX66UW1G45G_DUMMY_WRITE,        /*!< Number of dummy cycles for write operations */ \
                                  .StartupConfig.DummyRegisterRead          = EXTMEM_MX66UW1G45G_DUMMY_REG_READ,     /*!< Number of dummy cycles to read register */ \
                                  .StartupConfig.AccessMode                 = EXTMEM_CUSTOM_1S_1S_1S,                /*!< Access mode (e.g., 1S4D4D) */ \
                                  .StartupConfig.InstructionSize            = EXTMEM_CUSTOM_INSTRUCTION_8_BITS,      /*!< Size of the instruction */ \
                                  .StartupConfig.AddressSize                = EXTMEM_CUSTOM_ADDRESS_32_BITS,         /*!< Size of the address */ \
                                  .StartupConfig.DqsMode                    = EXTMEM_CUSTOM_DQS_DISABLE,             /*!< Data strobe mode */ \
                                  .NbRegisterConfig                         = 4,                                     /*!< Common field for memory Chip select */ \
                                  .RegisterConfig[0].ConfigStepType                      = EXTMEM_CUSTOM_CFGSTEP_RWR_REG_ADDR,   /*!< Config step to read/write/read a register */ \
                                  .RegisterConfig[0].RWConfigStep.CommandRegisterWrite   = EXTMEM_MX66UW1G45G_CMD1_WRITE_CFG2,   /*!< Command to write to a register */ \
                                  .RegisterConfig[0].RWConfigStep.CommandRegisterRead    = EXTMEM_MX66UW1G45G_CMD1_READ_CFG2,    /*!< Command to read from a register */ \
                                  .RegisterConfig[0].RWConfigStep.CommandRegisterReadAW  = EXTMEM_MX66UW1G45G_CMD1_READAW_CFG2,  /*!< Command to read from a register after write operation */ \
                                  .RegisterConfig[0].RWConfigStep.RegisterAddress        = EXTMEM_MX66UW1G45G_CMD1_ADDR_CFG2,    /*!< Address of the register */ \
                                  .RegisterConfig[0].RWConfigStep.RegisterValue          = EXTMEM_MX66UW1G45G_CMD1_VALUE_CFG2,   /*!< Value of the register */ \
                                  .RegisterConfig[0].RWConfigStep.RegisterMask           = EXTMEM_MX66UW1G45G_CMD1_MASK_CFG2,    /*!< Mask for the register */ \
                                  .RegisterConfig[1].ConfigStepType                      = EXTMEM_CUSTOM_CFGSTEP_RW_REG_ADDR,    /*!< Config step to read/write/read a register */ \
                                  .RegisterConfig[1].RWConfigStep.CommandRegisterWrite   = EXTMEM_MX66UW1G45G_CMD2_WRITE_CFG2,   /*!< Command to write to a register */ \
                                  .RegisterConfig[1].RWConfigStep.CommandRegisterRead    = EXTMEM_MX66UW1G45G_CMD2_READ_CFG2,    /*!< Command to read from a register */ \
                                  .RegisterConfig[1].RWConfigStep.RegisterAddress        = EXTMEM_MX66UW1G45G_CMD2_ADDR_CFG2,    /*!< Address of the register */ \
                                  .RegisterConfig[1].RWConfigStep.RegisterValue          = EXTMEM_MX66UW1G45G_CMD2_VALUE_CFG2,   /*!< Value of the register */ \
                                  .RegisterConfig[1].RWConfigStep.RegisterMask           = EXTMEM_MX66UW1G45G_CMD2_MASK_CFG2,    /*!< Mask for the register */ \
                                  .RegisterConfig[2].ConfigStepType                      = EXTMEM_CUSTOM_CFGSTEP_EXEC_OPT_CFG,    /*!< Config step to use new Optional configuration */ \
                                  .RegisterConfig[3].ConfigStepType                      = EXTMEM_CUSTOM_CFGSTEP_RAW_REG_ADDR,    /*!< Config step to read/write/read a register */ \
                                  .RegisterConfig[3].RWConfigStep.CommandRegisterReadAW  = EXTMEM_MX66UW1G45G_CMD3_READAW_CFG2,  /*!< Command to read from a register after write operation */ \
                                  .RegisterConfig[3].RWConfigStep.RegisterAddress        = EXTMEM_MX66UW1G45G_CMD3_ADDR_CFG2,    /*!< Address of the register */ \
                                  .OptionalConfig.Frequency                 = 200000000,                             /*!< Operating frequency in Hz*/ \
                                  .OptionalConfig.CommandRead               = EXTMEM_MX66UW1G45G_OCTAL_CMD_READ,     /*!< Command to read data */ \
                                  .OptionalConfig.CommandWrite              = EXTMEM_MX66UW1G45G_OCTAL_CMD_WRITE,    /*!< Command to write data */ \
                                  .OptionalConfig.DummyCycleRead            = EXTMEM_MX66UW1G45G_OCTAL_DUMMY_READ,   /*!< Number of dummy cycles for read operations */ \
                                  .OptionalConfig.DummyCycleWrite           = EXTMEM_MX66UW1G45G_OCTAL_DUMMY_WRITE,  /*!< Number of dummy cycles for write operations */ \
                                  .OptionalConfig.DummyRegisterRead         = EXTMEM_MX66UW1G45G_OCTAL_DUMMY_REG_READ,/*!< Number of dummy cycles to read register */ \
                                  .OptionalConfig.AccessMode                = EXTMEM_CUSTOM_8D_8D_8D,                /*!< Access mode (e.g., 1S4D4D) */ \
                                  .OptionalConfig.InstructionSize           = EXTMEM_CUSTOM_INSTRUCTION_16_BITS,     /*!< Size of the instruction */ \
                                  .OptionalConfig.AddressSize               = EXTMEM_CUSTOM_ADDRESS_32_BITS,         /*!< Size of the address */ \
                                  .OptionalConfig.DqsMode                   = EXTMEM_CUSTOM_DQS_ENABLE,              /*!< Data strobe mode */ \
                                  .NorFlashConfig.PageSize                  = EXTMEM_MX66UW1G45G_PAGE_SIZE,          /*!< Page Size */ \
                                  .NorFlashConfig.SectorSize                = EXTMEM_MX66UW1G45G_SECTOR_SIZE,        /*!< Sector Size */ \
                                  .NorFlashConfig.MaxSectorEraseTime        = 2000,                                  /*!< Max time expected for completing a Sector Erase operation (in ms) */ \
                                  .NorFlashConfig.MaxChipEraseTime          = 300000,                                /*!< Max time expected for completing a Chip Erase operation (in ms) */ \
                                  .NorFlashConfig.Startup.Cmd_EraseSector   = EXTMEM_MX66UW1G45G_CMD_ERASE_SECTOR,   /*!< Command to erase a sector */ \
                                  .NorFlashConfig.Startup.Cmd_MassErase     = EXTMEM_MX66UW1G45G_CMD_MASS_ERASE,     /*!< Command to perform mass erase */ \
                                  .NorFlashConfig.Startup.Cmd_RDSR          = EXTMEM_MX66UW1G45G_CMD_RDSR,           /*!< Command to read status register */ \
                                  .NorFlashConfig.Startup.Cmd_WE            = EXTMEM_MX66UW1G45G_CMD_WE,             /*!< Command to enable write operations */ \
                                  .NorFlashConfig.Startup.MatchMask_WEL     = EXTMEM_MX66UW1G45G_MASK_WEL,           /*!< Mask for write enable latch */ \
                                  .NorFlashConfig.Startup.MatchValue_WEL    = EXTMEM_MX66UW1G45G_VALUE_WEL,          /*!< Match Value for write enable latch */ \
                                  .NorFlashConfig.Startup.MatchMask_WIP     = EXTMEM_MX66UW1G45G_MASK_WIP,           /*!< Mask for write in progress */ \
                                  .NorFlashConfig.Startup.MatchValue_WIP    = EXTMEM_MX66UW1G45G_VALUE_WIP,          /*!< Match Value for write in progress */ \
                                  .NorFlashConfig.Startup.SRAccessDetails   = EXTMEM_CUSTOM_SR_ACCESS_CFG_UNDEFINED, /*!< No details for SR register access (no address required) */ \
                                  .NorFlashConfig.OptionalConfigEnable      = 1,                                   /*!< Optional config for NorFlash is used */ \
                                  .NorFlashConfig.Optional.Cmd_EraseSector  = EXTMEM_MX66UW1G45G_OCTAL_CMD_ERASE_SECTOR,  /*!< Command to erase a sector */ \
                                  .NorFlashConfig.Optional.Cmd_MassErase    = EXTMEM_MX66UW1G45G_OCTAL_CMD_MASS_ERASE,    /*!< Command to perform mass erase */ \
                                  .NorFlashConfig.Optional.Cmd_RDSR         = EXTMEM_MX66UW1G45G_OCTAL_CMD_RDSR,     /*!< Command to read status register in octal mode */ \
                                  .NorFlashConfig.Optional.Cmd_WE           = EXTMEM_MX66UW1G45G_OCTAL_CMD_WE,       /*!< Command to enable write operations in octal mode */ \
                                  .NorFlashConfig.Optional.MatchMask_WEL    = EXTMEM_MX66UW1G45G_OCTAL_MASK_WEL,     /*!< Mask for write enable latch */ \
                                  .NorFlashConfig.Optional.MatchValue_WEL   = EXTMEM_MX66UW1G45G_OCTAL_VALUE_WEL,    /*!< Match Value for write enable latch */ \
                                  .NorFlashConfig.Optional.MatchMask_WIP    = EXTMEM_MX66UW1G45G_OCTAL_MASK_WIP,     /*!< Mask for write in progress */ \
                                  .NorFlashConfig.Optional.MatchValue_WIP   = EXTMEM_MX66UW1G45G_OCTAL_VALUE_WIP,    /*!< Match Value for write enable latch */ \
                                  .NorFlashConfig.Optional.SRAccessDetails  = EXTMEM_CUSTOM_SR_ACCESS_ADD_0,         /*!< Access details for SR register in octal mode*/ \
                                  }

#if defined(EXTMEM_C)
EXTMEM_DRIVER_CUSTOM_ObjectTypeDef extmem_mx66uw1g45g = EXTMEM_MX66UW1G45G_INIT;
#else
extern EXTMEM_DRIVER_CUSTOM_ObjectTypeDef extmem_mx66uw1g45g;
#endif /* EXTMEM_C */

/**
  * @}
  */

/**
  * @}
  */
#endif /* EXTMEM_DRIVER_CUSTOM == 1 */

#ifdef __cplusplus
}
#endif

#endif /* __STM32_MX66UW1G45G__H__ */

This section explains how each parameter of 'EXTMEM_M95P32_INIT' (written by the user or taken from a provided configuration file) is chosen from the M95P32[2] datasheet.

For general guidance on what each field means and where it is used by the Custom driver, see:

General memory configuration

Field Description Datasheet section Value
MemType Select the NOR Flash command set (erase/program/status polling). Overview / features 'EXTMEM_CUSTOM_NOR_FLASH'
MemStandard Select the vendor enum used by the Custom driver for this descriptor. Identification / manufacturer information 'EXTMEM_CUSTOM_STD_MACRONIX'
MemSize Select the density enum matching the device. Memory organization / density 'EXTMEM_CUSTOM_SIZE_32MB'
ResetMethod Use the reset instruction sequence required by the device. Reset / software reset instructions 'EXTMEM_CUSTOM_RESET_METHOD_66_99'
ResetDelay Delay after reset before sending new commands. Reset recovery time characteristics '2' ms
SampleShiftCfg Keep default sample-shift behavior; tune if needed for the board. Interface / timing recommendations 'EXTMEM_CUSTOM_SSHIFT_CFG_UNDEFINED'
MemChipSelectHighTimeCycle Ensure the chip-select high time between commands respects the datasheet minimum. AC characteristics (chip-select deselect time) '5' cycles

Startup configuration (baseline SPI STR)

StartupConfig is typically a safe configuration that can be used for reset/bring-up and basic reads/writes.

Field Description Datasheet section Value
StartupConfig.Frequency Choose a frequency within the device limits. AC characteristics / timing limits '50000000' Hz
StartupConfig.CommandRead Use a standard STR read opcode. Instruction set / command table (fast read) '0x0B'
StartupConfig.DummyCycleRead One dummy byte = 8 dummy clock cycles in STR mode. Read command description (dummy cycles) '0x08'
StartupConfig.CommandWrite Use the page program opcode. Instruction set / command table (program) '0x0A'
StartupConfig.DummyCycleWrite No dummy cycles for program. Page program description '0x00'
StartupConfig.DummyRegisterRead Dummy cycles required when reading registers in this mode. Register read access description '0x00'
StartupConfig.AddressSize 24-bit addressing. Addressing / command description 'EXTMEM_CUSTOM_ADDRESS_24_BITS'
StartupConfig.AccessMode Instruction/address/data are transferred in single line STR. Timing diagrams 'EXTMEM_CUSTOM_1S_1S_1S'
StartupConfig.InstructionSize Standard SPI opcode size. Instruction set / command table 'EXTMEM_CUSTOM_INSTRUCTION_8_BITS'
StartupConfig.DqsMode DQS is not used in SPI STR. Interface description (SPI STR) 'EXTMEM_CUSTOM_DQS_DISABLE'

Optional configuration (quad read)

The M95P32[2] example enables an optional configuration to use quad read for higher throughput.

Field Description Datasheet section Value
RegisterConfig[0].ConfigStepType Activate the OptionalConfig after startup initialization. Not from datasheet (driver configuration flow) 'EXTMEM_CUSTOM_CFGSTEP_EXEC_OPT_CFG'
OptionalConfig.Frequency Keep the same validated clock for this mode. AC characteristics / timing limits '50000000' Hz
OptionalConfig.CommandRead Select the quad read opcode. Instruction set / command table (quad read) '0x6B'
OptionalConfig.CommandWrite Keep writes in standard SPI. Instruction set / command table (program) '0x0A'
OptionalConfig.DummyCycleRead Configure dummy cycles required by the read opcode. Quad read command description (dummy cycles) '0x08'
OptionalConfig.AccessMode Quad data transfer for reads. Timing diagrams 'EXTMEM_CUSTOM_1S_1S_4S'
OptionalConfig.OptWriteAccessMode Explicitly keep writes in single line mode. Timing diagrams 'EXTMEM_CUSTOM_1S_1S_1S'
OptionalConfig.InstructionSize Standard SPI opcode size. Instruction set / command table 'EXTMEM_CUSTOM_INSTRUCTION_8_BITS'
OptionalConfig.AddressSize 24-bit addressing. Addressing / command description 'EXTMEM_CUSTOM_ADDRESS_24_BITS'
OptionalConfig.DqsMode DQS is not used for this mode. Interface description 'EXTMEM_CUSTOM_DQS_DISABLE'

NOR Flash specific configuration

Field Description Datasheet section Value
NorFlashConfig.PageSize Used by the driver to split writes. Memory organization / program operation '512' bytes
NorFlashConfig.SectorSize Used by erase operations. Memory organization / sector erase '0x1000' (4 KBytes)
Cmd_EraseSector Sector erase opcode. Instruction set / command table (sector erase) '0x20'
Cmd_MassErase Chip erase opcode. Instruction set / command table (chip erase) '0xC7'
Cmd_RDSR Used for WIP/WEL polling. Instruction set / command table (read status register) '0x05'
Cmd_WE Required before modify operations. Instruction set / command table (write enable) '0x06'
MatchMask_WEL / MatchValue_WEL Confirms write enable latch is set. Status register description (WEL bit) '0x02' / '0x02'
MatchMask_WIP / MatchValue_WIP Poll until operation is complete. Status register description (WIP bit) '0x01' / '0x00'
MaxSectorEraseTime / MaxChipEraseTime Choose conservative max values for timeout handling. Erase timing characteristics '2000 ms' / '100000 ms'

Final M95P32[2] configuration example (stm32_m95p32.h)

This section provides a complete example of what the final M95P32[2] memory descriptor could look like.

/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32_M95P32__H__
#define __STM32_M95P32__H__

#ifdef __cplusplus
extern "C" {
#endif

/* Includes ------------------------------------------------------------------*/
#include "stm32_extmem.h"
#include "stm32_extmem_conf.h"
#if defined(EXTMEM_DRIVER_CUSTOM) && (EXTMEM_DRIVER_CUSTOM == 1)
#include "stm32_custom_driver_api.h"
#include "stm32_custom_driver_type.h"

/* Private Macro ------------------------------------------------------------*/
#define EXTMEM_M95P32_CMD_READ                 0x0B
#define EXTMEM_M95P32_CMD_WRITE                0x0A
#define EXTMEM_M95P32_DUMMY_READ               0x08
#define EXTMEM_M95P32_DUMMY_WRITE              0x00
#define EXTMEM_M95P32_DUMMY_REG_READ           0x00

#define EXTMEM_M95P32_PAGE_SIZE                512
#define EXTMEM_M95P32_SECTOR_SIZE              0x1000
#define EXTMEM_M95P32_CMD_ERASE_SECTOR         0x20
#define EXTMEM_M95P32_CMD_MASS_ERASE           0xC7
#define EXTMEM_M95P32_CMD_RDSR                 0x05
#define EXTMEM_M95P32_CMD_WE                   0x06
#define EXTMEM_M95P32_MASK_WEL                 0x02
#define EXTMEM_M95P32_VALUE_WEL                0x02
#define EXTMEM_M95P32_MASK_WIP                 0x01
#define EXTMEM_M95P32_VALUE_WIP                0x00

#define EXTMEM_M95P32_QUAD_CMD_READ            0x6B
#define EXTMEM_M95P32_QUAD_DUMMY_READ          0x08

#define EXTMEM_M95P32_INIT { \
                               .MemType                                  = EXTMEM_CUSTOM_NOR_FLASH,               \
                               .MemStandard                              = EXTMEM_CUSTOM_STD_MACRONIX,            \
                               .MemSize                                  = EXTMEM_CUSTOM_SIZE_32MB,               \
                               .ResetMethod                              = EXTMEM_CUSTOM_RESET_METHOD_66_99,      \
                               .ResetDelay                               = 2,                                     \
                               .MemChipSelectHighTimeCycle               = 5,                                     \
                               .StartupConfig.Frequency                  = 50000000,                              \
                               .StartupConfig.CommandRead                = EXTMEM_M95P32_CMD_READ,                \
                               .StartupConfig.CommandWrite               = EXTMEM_M95P32_CMD_WRITE,               \
                               .StartupConfig.DummyCycleRead             = EXTMEM_M95P32_DUMMY_READ,              \
                               .StartupConfig.DummyCycleWrite            = EXTMEM_M95P32_DUMMY_WRITE,             \
                               .StartupConfig.DummyRegisterRead          = EXTMEM_M95P32_DUMMY_REG_READ,          \
                               .StartupConfig.AccessMode                 = EXTMEM_CUSTOM_1S_1S_1S,                \
                               .StartupConfig.InstructionSize            = EXTMEM_CUSTOM_INSTRUCTION_8_BITS,      \
                               .StartupConfig.AddressSize                = EXTMEM_CUSTOM_ADDRESS_24_BITS,         \
                               .StartupConfig.DqsMode                    = EXTMEM_CUSTOM_DQS_DISABLE,             \
                               .NbRegisterConfig                         = 1,                                     \
                               .RegisterConfig[0].ConfigStepType         = EXTMEM_CUSTOM_CFGSTEP_EXEC_OPT_CFG,    \
                               .OptionalConfig.Frequency                 = 50000000,                              \
                               .OptionalConfig.CommandRead               = EXTMEM_M95P32_QUAD_CMD_READ,           \
                               .OptionalConfig.CommandWrite              = EXTMEM_M95P32_CMD_WRITE,               \
                               .OptionalConfig.DummyCycleRead            = EXTMEM_M95P32_QUAD_DUMMY_READ,         \
                               .OptionalConfig.DummyCycleWrite           = EXTMEM_M95P32_DUMMY_WRITE,             \
                               .OptionalConfig.DummyRegisterRead         = EXTMEM_M95P32_DUMMY_REG_READ,          \
                               .OptionalConfig.AccessMode                = EXTMEM_CUSTOM_1S_1S_4S,                \
                               .OptionalConfig.OptWriteAccessMode        = EXTMEM_CUSTOM_1S_1S_1S,                \
                               .OptionalConfig.InstructionSize           = EXTMEM_CUSTOM_INSTRUCTION_8_BITS,      \
                               .OptionalConfig.AddressSize               = EXTMEM_CUSTOM_ADDRESS_24_BITS,         \
                               .OptionalConfig.DqsMode                   = EXTMEM_CUSTOM_DQS_DISABLE,             \
                               .NorFlashConfig.PageSize                  = EXTMEM_M95P32_PAGE_SIZE,               \
                               .NorFlashConfig.SectorSize                = EXTMEM_M95P32_SECTOR_SIZE,             \
                               .NorFlashConfig.MaxSectorEraseTime        = 2000,                                  \
                               .NorFlashConfig.MaxChipEraseTime          = 100000,                                \
                               .NorFlashConfig.Startup.Cmd_EraseSector   = EXTMEM_M95P32_CMD_ERASE_SECTOR,        \
                               .NorFlashConfig.Startup.Cmd_MassErase     = EXTMEM_M95P32_CMD_MASS_ERASE,          \
                               .NorFlashConfig.Startup.Cmd_RDSR          = EXTMEM_M95P32_CMD_RDSR,                \
                               .NorFlashConfig.Startup.Cmd_WE            = EXTMEM_M95P32_CMD_WE,                  \
                               .NorFlashConfig.Startup.MatchMask_WEL     = EXTMEM_M95P32_MASK_WEL,                \
                               .NorFlashConfig.Startup.MatchValue_WEL    = EXTMEM_M95P32_VALUE_WEL,               \
                               .NorFlashConfig.Startup.MatchMask_WIP     = EXTMEM_M95P32_MASK_WIP,                \
                               .NorFlashConfig.Startup.MatchValue_WIP    = EXTMEM_M95P32_VALUE_WIP,               \
}

#if defined(EXTMEM_C)
EXTMEM_DRIVER_CUSTOM_ObjectTypeDef extmem_m95p32 = EXTMEM_M95P32_INIT;
#else
extern EXTMEM_DRIVER_CUSTOM_ObjectTypeDef extmem_m95p32;
#endif /* EXTMEM_C */

#endif /* EXTMEM_DRIVER_CUSTOM == 1 */

#ifdef __cplusplus
}
#endif

#endif /* __STM32_M95P32__H__ */

5. How to create a BootFlash project with the STM32 ecosystem (H7R/H7S or N6)

5.1. STM32CubeMX Ecosystem Tool

STM32CubeMX is a graphical tool used to configure STM32 devices and generate initialization code. For BootFlash projects, it also generates the framework around:

  • External Memory Manager (EMM)
  • External Memory Loader (EML)
Important
Important note for the Custom driver: it is not available as a selectable option in STM32CubeMX

In practice, STM32CubeMX can still be used as the starting point for a Custom-driver BootFlash project

The recommended starting point to create a new EMM project remains the standard BootFlash workflow described in Getting started with External memory Manager and External memory loader.

The important points for the Custom driver are the following:

  • The overall BootFlash project structure is the same.
  • The calls to 'EXTMEM_Init()' are the same.
  • The main manual adaptation is in 'MX_EXTMEM_MANAGER_Init()', where the memory description is provided differently.

In other words, switching from NOR SFDP to Custom driver does not change the BootFlash architecture. It only changes how EMM receives the memory configuration.

5.2. Implementation differences

Note
A template is available as an example for using the EMM Custom driver and can be used directly with the memory configuration file  :
* For STM32H7R/H7S (XIP template) :Template_XIP_Custom.
* For STM32N6 (FSBL XIP template): Template_FSBL_XIP_Custom

For a NOR memory connected on XSPI, the differences are limited to the description of the memory entry inside 'extmem_list_config[]'.

With SFDP:

  • Set MemType to 'EXTMEM_NOR_SFDP'.
  • 'EXTMEM_DRIVER_NOR_SFDP' must be defined and set to 1 in "stm32_extmem_conf.h".
  • Set ConfigType depending on the number of lines connected to the memory (ex: 'EXTMEM_LINK_CONFIG_8LINES' for 8 lines).
  • In 'EXTMEM_Init', set XSPI clock speed using a RCC fetch function such as 'HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2)'.
  • the driver reads the memory SFDP tables at runtime to discover commands, timings, and capabilities.
/*** In stm32_extmem_conf.h ***/
#define EXTMEM_DRIVER_NOR_SFDP 1
/*** In MX_EXTMEM_MANAGER_Init function ***/
/* EXTMEMORY_1: NOR discovered through SFDP */
extmem_list_config[EXTMEMORY_1].MemType = EXTMEM_NOR_SFDP;
extmem_list_config[EXTMEMORY_1].Handle = (void *)&hxspi2;
extmem_list_config[EXTMEMORY_1].ConfigType = EXTMEM_LINK_CONFIG_8LINES;

With the Custom driver:

  • Set MemType to 'EXTMEM_CUSTOM'.
  • 'EXTMEM_DRIVER_CUSTOM' must be defined and set to 1 in "stm32_extmem_conf.h".
  • Set CustomObject using the one defined in the memory configuration file (ex: 'extmem_mx66uw1g45g' for the MX66UW1G45G memory).
  • In 'EXTMEM_Init', set XSPI clock speed using a RCC fetch function such as 'HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2)'.
/*** In stm32_extmem_conf.h ***/
#define EXTMEM_DRIVER_CUSTOM 1
/*** In MX_EXTMEM_MANAGER_Init function ***/
/* EXTMEMORY_1: NOR described by a user-provided custom object */
extmem_list_config[EXTMEMORY_1].MemType = EXTMEM_CUSTOM;
extmem_list_config[EXTMEMORY_1].Handle = (void *)&hxspi2;
extmem_list_config[EXTMEMORY_1].CustomObject = extmem_mx66uw1g45g;

After that, the application still calls:

EXTMEM_Init(EXTMEMORY_1, HAL_RCCEx_GetPeriphCLKFreq(RCC_PERIPHCLK_XSPI2));

So the initialization entry point is unchanged. Only the configuration source is different.

5.3. Example of the migration

This example has been taken from the provided template Template_XIP_Custom. For a BootFlash project using MX66UW1G45G[1] on 'hxspi2', the user should read the code as follows:

  • 'EXTMEM_DRIVER_NOR_SFDP' define must be set to 0 and 'EXTMEM_DRIVER_CUSTOM' must be defined and set to 1 in "stm32_extmem_conf.h".
  • 'extmem_list_config[EXTMEMORY_1].Handle = (void *)&hxspi2;' identifies the hardware interface.
  • 'extmem_list_config[EXTMEMORY_1].MemType = EXTMEM_CUSTOM;' selects the Custom driver path.
  • 'extmem_list_config[EXTMEMORY_1].CustomObject = extmem_mx66uw1g45g;' passes the complete memory description written from the datasheet.
  • 'EXTMEM_Init(...)' then initializes the memory using that object.
Note
The Custom driver also has its own debug trace, to enable it EXTMEM_DRIVER_CUSTOM_DEBUG_LEVEL must be defined it can also be with the desired detail level as its value (0-4)

6. References