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 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]
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.
- 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 */
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 :
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(¤t_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