2025-07-08 6:44 AM - last edited on 2025-07-08 7:45 AM by KDJEM.1
I have a basic FSBL Load and Run application on the NUCLEO-N657 that loads an application from flash into the internal SRAM of the MCU.
Since the N6 has no internal flash, I would like to allocate a portion of the external flash memory to a Fat/FileX volume that the loaded application can use.
What's the preferred method of creating a custom LevelX nor driver in Application for using the OSPI Flash? Should I use the EXTMem manager to abstract the read/writes, or should I integrate the XSPI2 driver directly into the custom LevelX nor controller?
Current Attempt, using EXTMem_manager within custom nor controller:
In my FSBL, I successfully init XSPI2, XSPIM, and the EXTMEM_Manager to load and run the application from flash. (Tutorial)
However, once the application begins initialization, the app cannot re-init XSPI2 and times out with a busy error. If I bypass the init of XSPI2, then the EXTMEM manager times out trying to access the XSPI2 peripheral.
// appli main.c
int main(void)
{
HAL_Init();
MX_GPIO_Init();
MX_ETH1_Init();
MX_USB1_OTG_HS_USB_Init();
MX_HASH_Init();
MX_RNG_Init();
MX_XSPI2_Init(); // <- Unmodified, throws an error on HAL_XSPI_Init()
SystemIsolation_Config();
MX_EXTMEM_MANAGER_Init();
MX_ThreadX_Init();
}
Seems like trying to re-init the peripheral is causing errors, since the peripheral was initialized and used to load the application. If I return early in XSPI2_Init(), then the app runs, but the XSPI2 bus has a timeout after the standard 5000 ms.
// lx_stm32_nor_custom_driver.c
#include "lx_stm32_nor_custom_driver.h"
/* USER CODE BEGIN Includes */
#include "stm32_extmem_conf.h"
#include "extmem_manager.h"
#include "stm32_extmem.h"
/* USER CODE END Includes */
/* Private typedef -----------------------------------------------------------*/
/* USER CODE BEGIN PTD */
typedef enum
{
FLASH_OK,
FLASH_ERROR_UNSUPPORTED_MEMORY, /* !< unsupported memory type */
FLASH_ERROR_NOBASEADDRESS, /* !< not base address for the memory */
FLASH_ERROR_MAPPEDMODEFAIL, /* !< */
FLASH_ERROR_COPY,
} FLASHStatus_TypeDef;
/* USER CODE END PTD */
/* USER CODE BEGIN PV */
static uint8_t block_erase_verify_buf[LX_STM32_NOR_FLASH_BLOCK_SIZE] = {0};
/* USER CODE END PV */
static UINT lx_nor_driver_read(ULONG *flash_address, ULONG *destination, ULONG words);
static UINT lx_nor_driver_write(ULONG *flash_address, ULONG *source, ULONG words);
static UINT lx_nor_driver_block_erase(ULONG block, ULONG erase_count);
static UINT lx_nor_driver_block_erased_verify(ULONG block);
#ifndef LX_DIRECT_READ
#ifndef NOR_SECTOR_BUFFER_SIZE
#define NOR_SECTOR_BUFFER_SIZE 512
#endif
static ULONG nor_sector_memory[NOR_SECTOR_BUFFER_SIZE];
#endif
UINT lx_stm32_nor_custom_driver_initialize(LX_NOR_FLASH *nor_flash)
{
UINT ret = LX_SUCCESS;
ULONG total_blocks = 0;
ULONG words_per_block = 0;
/* USER CODE BEGIN Init_Section_0 */
/* USER CODE END Init_Section_0 */
nor_flash->lx_nor_flash_total_blocks = total_blocks;
nor_flash->lx_nor_flash_words_per_block = words_per_block;
/* USER CODE BEGIN Init_Section_1 */
/* USER CODE END Init_Section_1 */
nor_flash->lx_nor_flash_driver_read = lx_nor_driver_read;
nor_flash->lx_nor_flash_driver_write = lx_nor_driver_write;
nor_flash->lx_nor_flash_driver_block_erase = lx_nor_driver_block_erase;
nor_flash->lx_nor_flash_driver_block_erased_verify = lx_nor_driver_block_erased_verify;
#ifndef LX_DIRECT_READ
nor_flash->lx_nor_flash_sector_buffer = nor_sector_memory;
#endif
/* USER CODE BEGIN Init_Section_2 */
/* USER CODE END Init_Section_2 */
return ret;
}
/* USER CODE BEGIN USER_CODE_SECTION_2 */
/* USER CODE END USER_CODE_SECTION_2 */
static UINT lx_nor_driver_read(ULONG *flash_address, ULONG *destination, ULONG words)
{
UINT ret = LX_SUCCESS;
const uint32_t MapAddress = (uint32_t)(flash_address + LX_STM32_NOR_FLASH_FS_START_ADDRESS_OFFSET);
/* USER CODE BEGIN NOR_READ */
if (EXTMEM_OK != EXTMEM_Read(EXTMEMORY_1,
MapAddress,
(uint8_t *)destination,
words * sizeof(ULONG)))
{
printf("lx_nor_driver_read: EXTMEM_Read failed!\r\n");
ret = LX_ERROR;
}
printf("lx_nor_driver_read: Read %lu words from address 0x%08lX\r\n", words, (unsigned long)flash_address + LX_STM32_NOR_FLASH_FS_START_ADDRESS_OFFSET);
/* USER CODE END NOR_READ */
return ret;
}
static UINT lx_nor_driver_write(ULONG *flash_address, ULONG *source, ULONG words)
{
UINT ret = LX_SUCCESS;
/* USER CODE BEGIN NOR_DRIVER_WRITE */
if (EXTMEM_OK != EXTMEM_Write(EXTMEMORY_1,
(uint32_t)(flash_address + LX_STM32_NOR_FLASH_FS_START_ADDRESS_OFFSET),
(const uint8_t *)source,
words * sizeof(ULONG)))
{
printf("lx_nor_driver_write: EXTMEM_WriteInMappedMode failed!\r\n");
ret = LX_ERROR;
}
printf("lx_nor_driver_write: Written %lu words to address 0x%08lX\r\n", words, (unsigned long)flash_address + LX_STM32_NOR_FLASH_FS_START_ADDRESS_OFFSET);
/* USER CODE END NOR_DRIVER_WRITE */
return ret;
}
static UINT lx_nor_driver_block_erase(ULONG block, ULONG erase_count)
{
UINT ret = LX_SUCCESS;
/* USER CODE BEGIN NOR_DRIVER_BLOCK */
if (EXTMEM_OK != EXTMEM_EraseSector(EXTMEMORY_1,
((uint32_t)(block * LX_STM32_NOR_FLASH_BLOCK_SIZE) + LX_STM32_NOR_FLASH_FS_START_ADDRESS_OFFSET),
LX_STM32_NOR_FLASH_BLOCK_SIZE))
{
printf("lx_nor_driver_block_erase: EXTMEM_EraseSector failed!\r\n");
ret = LX_ERROR;
}
printf("lx_nor_driver_block_erase: Erased block %lu, erase_count %lu\r\n", block, erase_count);
/* USER CODE END NOR_DRIVER_BLOCK */
return ret;
}
static UINT lx_nor_driver_block_erased_verify(ULONG block)
{
UINT ret = LX_SUCCESS;
/* USER CODE BEGIN NOR_DRIVER_VERIFY */
EXTMEM_Read(EXTMEMORY_1,
(uint32_t)((block * LX_STM32_NOR_FLASH_BLOCK_SIZE) + LX_STM32_NOR_FLASH_FS_START_ADDRESS_OFFSET),
(uint8_t *)block_erase_verify_buf,
LX_STM32_NOR_FLASH_BLOCK_SIZE);
for (int i = 0; i < LX_STM32_NOR_FLASH_BLOCK_SIZE; i++)
{
if (block_erase_verify_buf[i] != 0xFF)
{
printf("lx_nor_driver_block_erased_verify: Block %lu not erased at word %d!\r\n", block, i);
ret = LX_ERROR;
break;
}
}
/* USER CODE END NOR_DRIVER_VERIFY */
return ret;
}
For reference, here's the FileX implementation to load the custom LevelX NOR driver:
// app_filex.c
#include "app_filex.h"
/* Main thread stack size */
#define FX_APP_THREAD_STACK_SIZE 1024 * 2
/* Main thread priority */
#define FX_APP_THREAD_PRIO 10
/* Main thread global data structures. */
TX_THREAD fx_app_thread;
/* USER CODE BEGIN PV */
FX_MEDIA nor_flash_disk;
/* FileX file instance */
FX_FILE fx_file;
uint32_t media_memory[LX_STM32_NOR_FLASH_BLOCK_SIZE / sizeof(uint32_t)];
/* USER CODE END PV */
void fx_app_thread_entry(ULONG thread_input);
UINT MX_FileX_Init(VOID *memory_ptr)
{
UINT ret = FX_SUCCESS;
TX_BYTE_POOL *byte_pool = (TX_BYTE_POOL *)memory_ptr;
VOID *pointer;
/*Allocate memory for the main thread's stack*/
ret = tx_byte_allocate(byte_pool, &pointer, FX_APP_THREAD_STACK_SIZE, TX_NO_WAIT);
/* Check FX_APP_THREAD_STACK_SIZE allocation*/
if (ret != FX_SUCCESS)
{
return TX_POOL_ERROR;
}
/* Create the main thread. */
ret = tx_thread_create(&fx_app_thread, FX_APP_THREAD_NAME, fx_app_thread_entry, 0, pointer, FX_APP_THREAD_STACK_SIZE,
FX_APP_THREAD_PRIO, FX_APP_PREEMPTION_THRESHOLD, FX_APP_THREAD_TIME_SLICE, FX_APP_THREAD_AUTO_START);
/* Check main thread creation */
if (ret != FX_SUCCESS)
{
return TX_THREAD_ERROR;
}
/* Initialize FileX. */
fx_system_initialize();
return ret;
}
void fx_app_thread_entry(ULONG thread_input)
{
UNUSED(thread_input);
/* Format the NOR flash as FAT */
UINT status = fx_media_format(&nor_flash_disk,
fx_stm32_levelx_nor_driver, // Driver entry
(VOID *)NOR_CUSTOM_DRIVER_ID, // Device info pointer
media_memory, // Media buffer pointer
sizeof(media_memory), // Media buffer size
"NOR_FLASH_DISK", // Volume Name
1, // Number of FATs
32, // Directory Entries
0, // Hidden sectors
LX_STM32_NOR_FLASH_FS_PARTITION_SIZE /
LX_STM32_NOR_FLASH_BLOCK_SIZE, // Total sectors
LX_STM32_NOR_FLASH_BLOCK_SIZE, // Sector size
8, // Sectors per cluster
1, // Heads
1); // Sectors per track
/* Check if the format status */
if (status != FX_SUCCESS)
{
Error_Handler();
}
/* Open the octo-SPI NOR Flash disk driver. */
status = fx_media_open(&nor_flash_disk, "FX_LX_NOR_DISK", fx_stm32_levelx_nor_driver, (VOID *)NOR_CUSTOM_DRIVER_ID, media_memory, sizeof(media_memory));
/* Check the media open status. */
if (status != FX_SUCCESS)
{
Error_Handler();
}
}
No matter what I try, XSPI2 times out on either infit or in EXTMem_Read in the application.