cancel
Showing results for 
Search instead for 
Did you mean: 

XSPI / XSPIM / EXTMem in Application code for FileX / LevelX

musicmindmachine
Associate II

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.

Screenshot 2025-07-08 081434.pngScreenshot 2025-07-08 081421.pngScreenshot 2025-07-08 081330.pngScreenshot 2025-07-08 081241.pngScreenshot 2025-07-08 081038.png

0 REPLIES 0