FileX

1 Introduction

FileX is a fully compliant FAT library for media storage and file system management. It supports standard filesystem operations such as media formatting and file/directory management. FileX is designed in a modular way that facilitates the integration of any media storage.

FileX Overview

FileX supports FAT16, FAT32 and extFAT formats. It offers a set of APIs to deal with files and directories such as create, delete and read/write. FileX supports both utf8 and Unicode coding as well as the "Long File Name” features to ensure filesystem compatibility between MCU and modern PCs. It also ensures the integrity of the file system via the failsafe feature (fault tolerance) especially for Flash memories where “power loss” may damage the data content and break the whole file system.

Further details are available in the FileX official documentation

2 STM32 integration

FileX supports common media storage devices such as µSD. Combined with LevelX it can also support file systems on NAND and NOR Flash memories.

The media devices below are supported in the context of the STM32:

  • SRAM memories: a FAT file system can be created on the MCU internal memories. This is useful for applications with a small footprint and for fast testing and prototyping.
  • SDMMC: this is the typical media storage device used in MCU-based applications. It requires both an SDMMC HW IP in the MCU and a uSD connector on the board. Therefore, dedicated BSP APIs are required. The availability of this media depends on the MCU and the board. (The Nucleo-144 board does not have µSD connectors, unless an external shield is used).
  • NOR/NAND memories:(via SPI/QSPI/OSPI/FMC). In order to support this type of memory, FileX shall be combined with LevelX. FileX manages the filesystem logic, while LevelX ensures efficient access to these memories in read and write modes (wear-leveling, bad block management, and so on). FileX is hardware agnostic in this case, as LevelX offers a common porting layer for NOR and NAND memories.

In all cases, FileX requires a low-level driver to interact with the underlining media storage device.

FileX Folders
  • fx_stm32_xxx_driver_template.c/fx_stm32_xxx_driver_template.h: these files represent the driver interfaces (that is, a driver skeleton without any functional API). It is used to implement the actual driver. These files are copied in the user application source tree then customized depending on the application targets.
  • fx_stm32_*_driver.c: these are driver patterns that implement FileX drivers for specific media devices. (ppp can be, sdmmc, sram and so on). These drivers are referenced directly by the applications.
  • fx_stm32_*_driver_template.h: to make the patterns usable across different platforms, a configuration step is required. For this reason, the user needs to provide a configuration file at application level to tune the driver. The fx_stm32_*_driver_template.h are header file templates that contain the driver configuration options. Config flags should be defined the in xxx_driver_template.h. This file should then be renamed fx_stm32_*_driver.h and copied under the user application source tree.
#ifndef FX_STM32_SD_DRIVER_H
#define FX_STM32_SD_DRIVER_H
#ifdef __cplusplus
extern "C" {
#endif

/*------------Includes------------*/
#include "fx_api.h"
#include "stm32xxxx_(discovery,eval)_sd.h"
#endif

#define DEFAULT_TIMEOUT					(10 * TX_TIMER_TICKS_PER_SECOND)
#define SD_INSTANCE						0

#define DEFAULT_SECTOR_SIZE				512
#define ENABLE_CACHE_MAINTENANCE		1

#define FX_DRIVER_CALLS_BSP_SD_INIT		0

VOID fx_stm32_sd_driver(FX_MEDIA *media_ptr);

#ifdef __cplusplus
}
#endif

#endif /* FX_STM32_SD_DRIVER_H */
Info.png The driver list is not exhaustive and depends on the MCU and boards.



2.1 FileX and LevelX integration

The figure below illustrates how FileX is used in conjunction with LevelX to read and write NAND and NOR Flash memories :

FileX LevelX Integration

2.2 Known limitations

3 How to use

A FileX based application has two methods to instantiate a media storage driver:

  • Formatting the media storage device using one of the following APIs:
UINT _fx_media_format(FX_MEDIA *media_ptr, VOID (*driver)(FX_MEDIA *media), 
                                       VOID *driver_info_ptr, UCHAR *memory_ptr, UINT memory_size,
                                      CHAR *volume_name, UINT number_of_fats, UINT directory_entries, 
                                      UINT hidden_sectors, ULONG total_sectors, UINT bytes_per_sector, 
                                      UINT sectors_per_cluster, UINT heads, UINT sectors_per_track);

or:

UINT _fx_media_exFAT_format(FX_MEDIA *media_ptr, VOID (*driver)(FX_MEDIA *media), 
                                                    VOID *driver_info_ptr, UCHAR *memory_ptr, UINT memory_size,
                                                   CHAR *volume_name, UINT number_of_fats, ULONG64 hidden_sectors, 
                                                   ULONG64 total_sectors, UINT bytes_per_sector, UINT sectors_per_cluster, 
                                                   UINT volume_serial_number, UINT boundary_unit);
  • Open the media storage if it already contains a valid FAT file system:
UINT _fx_media_open(FX_MEDIA *media_ptr, CHAR *media_name,
                                     VOID (*media_driver)(FX_MEDIA *), VOID *driver_info_ptr,
                                     VOID *memory_ptr, ULONG memory_size);

The two main items used in the above APIs are:

  • struct FX_MEDIA: This structure holds all the information needed about the media storage device.
  • VOID (* media_driver) (FX_MEDIA *): This is the pointer to the unique entry point to the low-level driver.

Note: fx_system_initialize() has to be called before calling any other fx_xxx() API

3.1 Example 1: SRAM interface

  • SRAM driver implementation:
VOID  fx_stm32_sram_driver(FX_MEDIA *media_ptr)
{

UCHAR *source_buffer;
UCHAR *destination_buffer;
UINT   bytes_per_sector;

    /* Process the driver request specified in the media control block.  */
    switch (media_ptr->fx_media_driver_request)
    {

        case FX_DRIVER_INIT:
        {
            /*
             * the FX_DRIVER_INIT can be requested either from the fx_media_format() or fx_media_open()
             * as the RAM meory should be always formatted before being used, by memset'ing it to '\0'
             * we need to avoid double initialization to keep the file system integrity.
             */
            if (is_initialized == 0)
            {
                _fx_utility_memory_set((UCHAR *)FX_SRAM_DISK_BASE_ADDRESS, '\0', FX_SRAM_DISK_SIZE);
                is_initialized = 1;
            }
            media_ptr -> fx_media_driver_status =  FX_SUCCESS;
            break;
        }

        case FX_DRIVER_UNINIT:
        {
            /* there is nothing to do for FX_DRIVER_UNINIT request
             *  set the media driver status to FX_SUCCESS.
             */
            media_ptr -> fx_media_driver_status =  FX_SUCCESS;
            break;
        }

        case FX_DRIVER_READ:
        {

            /* Calculate the RAM disk sector offset.*/
            source_buffer = ((UCHAR *)FX_SRAM_DISK_BASE_ADDRESS) +
                             ((media_ptr->fx_media_driver_logical_sector + media_ptr->fx_media_hidden_sectors) * media_ptr->fx_media_bytes_per_sector);

            /* Copy the RAM sector into the destination.  */
            _fx_utility_memory_copy(source_buffer, media_ptr -> fx_media_driver_buffer,
                                     media_ptr->fx_media_driver_sectors * media_ptr->fx_media_bytes_per_sector);

            /* Successful driver request.  */
            media_ptr->fx_media_driver_status = FX_SUCCESS;
            break;
        }

        case FX_DRIVER_WRITE:
        {

            /* Calculate the RAM disk sector offset */
            destination_buffer =  (UCHAR *)FX_SRAM_DISK_BASE_ADDRESS +
                                  ((media_ptr->fx_media_driver_logical_sector +  media_ptr->fx_media_hidden_sectors) * media_ptr->fx_media_bytes_per_sector);

            /* Copy the source to the RAM sector.  */
            _fx_utility_memory_copy(media_ptr->fx_media_driver_buffer, destination_buffer,
                                    media_ptr->fx_media_driver_sectors * media_ptr->fx_media_bytes_per_sector);

            /* Successful driver request.  */
            media_ptr -> fx_media_driver_status =  FX_SUCCESS;
            break;
        }

        case FX_DRIVER_FLUSH:
        {

            /*
             * Nothing to do for the FX_DRIVER_FLUSH Return driver success.
             */
            media_ptr->fx_media_driver_status =  FX_SUCCESS;
            break;
        }

        case FX_DRIVER_ABORT:
        {

            /*
             * Nothing to do for the FX_DRIVER_ABORT Return driver success.
             */
            media_ptr->fx_media_driver_status =  FX_SUCCESS;
            break;
        }

        case FX_DRIVER_BOOT_READ:
        {

            /* Calculate the RAM disk boot sector offset, which is at the very beginning of
             * the RAM disk.
             */
            source_buffer =  (UCHAR *)FX_SRAM_DISK_BASE_ADDRESS;
            /* For RAM disk only, pickup the bytes per sector.*/

            bytes_per_sector =  _fx_utility_16_unsigned_read(&source_buffer[FX_BYTES_SECTOR]);

            /* Ensure this is less than the media memory size.  */
            if (bytes_per_sector > media_ptr->fx_media_memory_size)
            {
                media_ptr->fx_media_driver_status =  FX_BUFFER_ERROR;
                break;
            }

            /* Copy the RAM boot sector into the destination.  */
            _fx_utility_memory_copy(source_buffer, media_ptr -> fx_media_driver_buffer,
                                    bytes_per_sector);

            /* Successful driver request.  */
            media_ptr -> fx_media_driver_status =  FX_SUCCESS;
            break;
        }

        case FX_DRIVER_BOOT_WRITE:
        {

            /* 
             * Calculate the RAM disk boot sector offset, which is at the very beginning of the RAM disk.
             */ 
            destination_buffer =  (UCHAR *)FX_SRAM_DISK_BASE_ADDRESS;

            /* Copy the RAM boot sector into the destination.  */
            _fx_utility_memory_copy(media_ptr->fx_media_driver_buffer, destination_buffer,
                                    media_ptr->fx_media_bytes_per_sector);

            /* Successful driver request.  */
            media_ptr -> fx_media_driver_status =  FX_SUCCESS;
            break;
        }

        default:
        {
            /* Invalid driver request.  */
            media_ptr -> fx_media_driver_status =  FX_IO_ERROR;
            break;
        }
    }
}


  • SRAM driver loading:
    /* Format the RAM disk - the memory for the RAM disk was defined above.  */
    status = _fx_media_format(&ram_disk, 
                            _fx_ram_driver,                  /* Driver entry                */
                            ram_disk_memory,                 /* RAM disk memory pointer     */
                            ram_disk_sector_cache,           /* Media buffer pointer        */
                            sizeof(ram_disk_sector_cache),   /* Media buffer size           */
                            "MY_RAM_DISK",                   /* Volume Name                 */
                            1,                               /* Number of FATs              */
                            32,                              /* Directory Entries           */
                            0,                               /* Hidden sectors              */
                            256,                             /* Total sectors               */
                            128,                             /* Sector size                 */
                            1,                               /* Sectors per cluster         */
                            1,                               /* Heads                       */
                            1);                              /* Sectors per track           */

    /* Check status.  */
    if (status != NX_SUCCESS)
    {
        error_counter++;
        return;
    }

    /* Open the RAM disk.  */
    status = fx_media_open(&ram_disk, "RAM DISK", _fx_ram_driver, ram_disk_memory, ram_disk_sector_cache, sizeof(ram_disk_sector_cache));

    /* Check status.  */
    if (status != NX_SUCCESS)
    {
        error_counter++;
        return;
    }

3.2 Example 2: NOR interface

The FileX NOR Flash low-level interface APIs have a modular generic multi-instance architecture that allows simultaneous use of several IP instances. To use the multi-instances feature, the instances shall be defined in the fx_stm32_levelx_nor_driver.h file.

Supported instances are:

  • Nor Flash simulator: #define LX_NOR_SIMULATOR_DRIVER
  • Nor Flash OctoSPI interface: #define LX_NOR_OSPI_DRIVER
  • Nor Flash QuadSPI interface: #define LX_NOR_QSPI_DRIVER
  • Nor Flash custom interface: #define LX_NOR_USE_CUSTOM_DRIVER

The main steps to use FileX NOR driver are:

  • NOR driver implementation:
VOID  fx_stm32_levelx_nor_driver(FX_MEDIA *media_ptr)
{
    ULONG i;
    UINT status;
    UCHAR *source_buffer;
    UCHAR *destination_buffer;
    ULONG logical_sector;


    /* Process the driver request specified in the media control block.*/
#ifdef USE_LX_NOR_DEFAULT_DRIVER
    i = find_driver_id(NOR_DEFAULT_DRIVER);
#else
    if (media_ptr->fx_media_driver_info == NULL)
    {
        i = UNKNOWN_DRIVER_ID;
    }
    else
    {
        i = find_driver_id((UINT)media_ptr->fx_media_driver_info);
    }

#endif

    if (i == UNKNOWN_DRIVER_ID)
    {
        /* No Driver found return an error */
        media_ptr->fx_media_driver_status = FX_MEDIA_INVALID;
        return;
    }
    else
    {
        current_driver = &fx_lx_nor_drivers[i];
    }

    switch(media_ptr->fx_media_driver_request)
    {

        case FX_DRIVER_INIT:
            {
                if (current_driver->initialized == FX_FALSE)
                {
                    /* Open flash instance*/
                    status = lx_nor_flash_open(&current_driver->flash_instance, current_driver->name, current_driver->nor_driver_initialize);


  • NOR driver Instantiation (ie. in "fx_stm32_levelx_nor_driver.h"):
/*--------------- Includes ---------------*/
#include "fx_api.h"
#include "lx_api.h"

#define LX_NOR_QSPI_DRIVER                  /* QuadSPI Interface */
#define LX_NOR_OSPI_DRIVER                  /* OCTO-SPI Interface */
#define LX_NOR_SIMULATOR_DRIVER      /* Built-in NOR Simulator interface */
#define USE_LX_NOR_CUSTOM_DRIVER  /* Custom user NOR interface */

#ifdef LX_NOR_SIMULATOR_DRIVER
#include "lx_stm32_nor_simulator_driver.h"

#define LX_NOR_SIMULATOR_DRIVER_ID        0x01
#define LX_NOR_SIMULATOR_DRIVER_NAME      "FX Levelx NOR flash Simulator"
#endif

#ifdef LX_NOR_OSPI_DRIVER
#include "lx_stm32_ospi_driver.h"

#define LX_NOR_OSPI_DRIVER_ID            0x02
#define LX_NOR_OSPI_DRIVER_NAME          "FX Levelx OctoSPI driver"
#endif

#ifdef LX_NOR_QSPI_DRIVER
#include "lx_stm32_qspi_driver.h"

#define LX_NOR_QSPI_DRIVER_ID            0x03
#define LX_NOR_QSPI_DRIVER_NAME          "FX Levelx QuadSPI driver"
#endif

/* uncomment the define below to support custom drivers */
/* #define USE_LX_NOR_CUSTOM_DRIVER */

#ifdef USE_LX_NOR_CUSTOM_DRIVER
/*
 * define the Custom levelx nor drivers to be supported by the filex
  • NOR driver loading:
  /* Format the NOR flash as FAT */
  status =  fx_media_format(&nor_flash_disk,
                            fx_stm32_levelx_nor_driver,   // Driver entry
                            (VOID*)LX_NOR_QSPI_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
                            qspi_info.FlashSize/512,      // Total sectors
                            512,                          // 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 QUAD-SPI NOR Flash disk driver.  */
  status =  fx_media_open(&nor_flash_disk, "FX_LX_NOR_DISK", fx_stm32_levelx_nor_driver,(VOID*)LX_NOR_QSPI_DRIVER_ID , media_memory, sizeof(media_memory));

  /* Check the media open status.  */
  if (status != FX_SUCCESS)
  {
    Error_Handler();
  }

  /* Get the available usable space */
  status =  fx_media_space_available(&nor_flash_disk, &available_space_pre);


4 STM32 FileX applications

STM32 Packages provide the following set of applications (the supported applications list may differ between products and boards):

Application Short description
Fx_uSD_File_Edit Demonstrates how to develop a basic SD Card file operations application. The application is designed to handle SD Card insertion/removal events, and depending on that state, it starts and stops file operations from and into the SD Card. [readme]
Fx_MultiAccess Demonstrates the FileX's concurrent file access capabilities. The application is designed to execute file operations on the SD Card device, the code provides all required software code for handling SD card I/O operations. [readme]
Fx_NoR_Write_Read_File Demonstrates how to create a Fat file system on the NOR flash using FileX alongside LevelX. The application is designed to execute file operations on the MX25LM51245G NOR Flash device, the code provides all required software code to properly manage it. [readme]
Fx_DualInstance Demonstrates the coexistence capability of two FileX/Levelx stacks running independently on each core. [readme]
Fx_IAP Demonstrates how to implement an in-application programming (IAP) using FileX's SD file access capabilities. The application is designed to erase and write to on-chip Flash memory, and provides all required software code for handling SD card and Flash memory I/O operations. This is a typical application on how to use the SD card peripheral for firmware upgrade application or IAP, allowing user to erase and write to on-chip flash memory. [readme]
Fx_NAND_Write_Read_File Demonstrates how to create a Fat File system on the NAND flash using FileX alongside LevelX. The application is designed to execute file operations on the Micron MT29F2G16ABAEAWP NAND flash device, the code provides all the required software code to manage it properly. [readme]