Introduction to FILEX

Revision as of 06:13, 6 June 2022 by Registered User (Escoda Michael moved page FILEX overview to Introduction to FILEX)

1 Introduction

Azure® RTOS 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[1]

Info white.png Information
For frequently asked questions, visit the general FAQ section in STM32CoreMW_overview article.


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 Flash 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-levelling, bad block management, and so on). FileX is hardware agnostic in this case, as LevelX offers a common porting layer for NOR and NAND Flash 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 white.png Information
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 FileX advanced features

  • Logical sector cache:

By reducing the copy paste of the entire sector, the FileX logic sector cache significantly improves performance. FileX maintains a logical sector cache for each open medium. The depth of the logical sector cache depends on the amount of memory supplied to FileX via the FX_MEDIA_OPEN API call.

  • Continuous file support (file access time determinism):

FileX provides continuous file support through the fx_file_allocate function . This function attempts to allocate the number of consecutive clusters required to satisfy the user's request. If there are enough clusters, the clusters are allocated and linked to the file. Otherwise, an error code is returned to the caller. This feature can significantly improve performance and reduces access time.

  • Fault tolerance support (power-down protection):

FileX fault tolerance is designed to prevent file system damage due to interrupts occurring during file or directory update. For example, when adding data to a file, FileX needs to update the content of files, directory entries, and possible FAT entries. If this update process is interrupted (for example by a power failure or by media popping up during the update process), the file system is inconsistent, which may affect the integrity of the entire file system, resulting in corruption of files.

The FileX fault tolerance function works by logging all the steps required to update the file or directory during the update process. The log entries are stored in a dedicated sector that FileX can find and access. The location of the log data can be accessed even if there is no appropriate file system. Therefore, in the event of a file system interruption, FileX can still find a log entry and restore the file system to a good condition.

This fault tolerance function can be used for all the FAT file systems supported by FileX, including FAT12, FAT16, FAT32, and exFAT. By default, fault tolerance is not enabled in FileX. To enable it, use the fx_fault_tolerant_enable macro definition to build FileX. At run time, the application starts fault-tolerant service by calling fx_fault_tolerant_enable. After the service is started, all files and directory write operations are performed through the fault tolerant module.

  • Wear levelling:

LevelX provides NAND and NOR Flash memory wear levelling capabilities to embedded applications. Since both NAND and NOR Flash memories can only be erased a finite number of times, it is critical to distribute the Flash memory use evenly. This is typically called "wear levelling" and it is the purpose behind LevelX.

2.3 FileX Standalone mode

ThreadX is typically required to run FileX. However, it is possible to use FileX in Standalone mode, that is without ThreadX integration, by enabling the FX_STANDALONE_ENABLE flag. Using FileX in Standalone mode allows the reduction of code footprint by removing ThreadX files and libraries from the project. Application complexity can also be reduced by removing constraints around thread management.

When FX_STANDALONE_ENABLE flag is activated, local path logic and ThreadX timer setup are disabled.

Unless FX_SINGLE_THREAD or FX_STANDALONE_ENABLE flags are activated, all FileX operations are thread safe.

2.4 Known limitations

  • No known limitations so far. More details are in the official FileX documentation[1]

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 Flash memory 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 Octo-SPI interface: #define LX_NOR_OSPI_DRIVER
  • NOR Flash Quad-SPI 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                  /* Quad-SPI 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 Octo-SPI 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 Quad-SPI 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 Migration from FatFS to FileX

Both FileX and FatFS are universal file systems (FAT/exFAT) stacks used to implement FAT file system in relatively small embedded devices.

4.1 Main features

Feature FileX FatFS
FAT 12/16/32 and exFAT support Yes Yes
Platform independent Yes Yes
Multiple partition support Yes Yes
Long file name in ANSI/OEM or Unicode Yes Yes
Fault tolerance: Power failure recovery capability Yes No
Wear levelling support Yes No

4.2 Main API group correspondence

The following tables summarize the most commonly used APIs for FatFS and their equivalent on FileX side.

Description of file APIs FatFS FileX
Open/Create a file f_open fx_file_open
Close a file f_close fx_file_close
Read data from a file f_read fx_file_read
Write date to a file f_write fx_file_write
Move the read/write pointer (or expand size) f_lseek fx_file_relative_seek
Get the current read/write pointer f_tell fx_file_seek
Truncate file size f_truncate fx_file_truncate
Flush cached data f_sync fx_file_media_flush
Read a string f_gets N/A
Write a character f_putc N/A
Write a string f_puts N/A
Write a formatted string f_printf N/A
Test for end of file f_eof N/A
Get object size f_size N/A
Check for errors f_error N/A


Description of volume APIs FatFS FileX
Register/Unregister the work area of the volume f_mount fx_media_open / fx_media_close
Create a FAT volume on the logical drive f_mkfs fx_media_format
Create partitions on the physical driver f_fdisk fx_file_media_open
Get free space on the volume f_getfree fx_media_space_available
Get volume label f_getlabel fx_media_volume_get
Set volume label f_setlabel fx_media_volume_set
Set active code page f_setcp N/A


Description of media interface APIs FatFS FileX
Get device status disk_status fx_media_check
Initialize device disk_initialize fx_system_initialize
Read data disk_read fx_media_read
Write data disk_wirte fx_media_write
Control device dependent functions disk_ioctl N/A
Get current time get_fattime fx_system_date_get

5 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 [2] 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.
Fx_SRAM_File_Edit_Standalone [3] This application provides an example of FileX stack usage in Standalone mode (without ThreadX). It demonstrates how to create a FAT File system in internal SRAM using FileX API.
Fx_MultiAccess [4] Demonstrates the FileX 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.
Fx_DualInstance[5] Demonstrates the coexistence capability of two FileX/LevelX stacks running independently on each core.
Fx_IAP [6] Demonstrates how to implement an in-application programming (IAP) using FileX 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.
Fx_NoR_Write_Read_File[7] Demonstrates how to create a Fat file system in NOR Flash memory using FileX alongside LevelX. The application is designed to execute file operations on the MX25LM51245G NOR Flash memory device. The code provides all required software code to properly manage it.
Fx_NAND_Write_Read_File [8] Demonstrates how to create a Fat File system on the NAND Flash memory 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.

6 References