FAL: Use of Flash abstraction layer

FAL: Use of Flash abstraction layer

FAL (Flash Abstraction Layer) Flash abstraction layer is the abstraction layer that manages and operates Flash and Flash-based partitions. It unifies Flash and the API for partition operations on the upper layer (as shown below) and has the following characteristics:

Supports static configurable partition tables and can associate multiple Flash devices;
Partition tables support automatic loading. Avoid problems where partition tables are defined multiple times in multiple firmware projects;
Code streamlining, operating system-independent, can run on bare-metal platforms, such as Bootloader with certain requirements for resources;
Unified operation interface guarantees that file system, OTA, NVM (e.g. EasyFlash) and other components depend on Flash, and the reusability of underlying Flash drivers;
Built-in Finsh/MSH-based test commands allow developers to debug and test Flash or partitions by Shell byte addressing.
FAL framework

It's also understandable that the middle tier idealizes Flash hardware devices as non-operational objects so that you don't need to manage whether the lower tier's operating objects are SPI-FLASH or EEPROM or in-chip FLASH.

transplant

  • Porting fal is based on the underlying drivers of SFUD, so if you haven't already ported SFUD, read the previous article

  • Same source code first.Gitee repository: https://gitee.com/RT-Thread-Mirror/fal/blob/master/docs/fal_api.md.

  • What happens after the project is imported

  • Note that because I'm using SPI-FLASH, I use the fal_flash_sfud_port.c file. If it's a flash in the movie, please choose another one, but generally this one is more useful.

  • Following the routine, change the fal_cfg.h file first

#ifndef _FAL_CFG_H_
#define _FAL_CFG_H_

#define FAL_DEBUG 1 //Start Printing
#define FAL_PART_HAS_TABLE_CFG //Startup Device Table
#define FAL_USING_SFUD_PORT //use sfud universal serial flash

/* ===================== Flash device Configuration ========================= */
extern struct fal_flash_dev nor_flash0;

/* flash device table */
#define FAL_FLASH_DEV_TABLE                                          \
{                                                                    \
    &nor_flash0,                                                     \
}

/* ====================== Partition Configuration ========================== */
#ifdef FAL_PART_HAS_TABLE_CFG
/* partition table */
#define FAL_PART_TABLE                                                                 \
{                                                                                      \
    {FAL_PART_MAGIC_WORD,  "fdb_tsdb1",       "norflash0",           0, 1024*1024, 0}, \
    {FAL_PART_MAGIC_WORD,  "fdb_kvdb1",       "norflash0",   1024*1024, 1024*1024, 0}, \
}
#endif /* FAL_PART_HAS_TABLE_CFG */

#endif /* _FAL_CFG_H_ */
  • The main function of the middle layer is to abstract the physical interface of the bottom layer, which is to abstract W25Q64 into two objects, which is called partition table.
  • FAL_PART_TABLE is the partition table that we defined. Later erasing, reading and writing are all for partition tables.
  • I made a demo myself
/**
 * fal demo for the first flash device test.
 *
 */
#define FAL_DEMO_TEST_BUFFER_SIZE	128
static uint8_t fal_demo_test_buf[FAL_DEMO_TEST_BUFFER_SIZE];
static void fal_demo(char* partition_name, uint8_t *data,size_t size)
{
	int i,result;
	const struct fal_partition *demo_partition;
	
	//Get partition table pointer
	demo_partition = fal_partition_find(partition_name);
	
	//Erase test
	result = fal_partition_erase( demo_partition,	//Partition table name, so partition table must not be duplicated
								  0,				//Starting address of erase
								  size				//Erased byte length
								);					//Returns the actual number received
	if ( result >= 0 ) {
		printf("Fal Erase The partition \"fdb_tsdb1\" is success...\n");
	} else {
		printf("Fal Erase The partition \"fdb_tsdb1\" is false!!!\n");
	}
	
	//Assign Initial Value Before Writing
	for(i=0;i<size;i++)
		data[i] = i;
	
	//Write Test
	result = fal_partition_write(demo_partition,	//Partition table name, so partition table must not be duplicated
								0, 					//Addresses are cheaper in the partition table, note that the partition table
								data,				//Receiving Address
								size				//Receive Length
								);					//Returns the actual number received
	if ( result >= 0 ) {
		printf("Fal write The partition \"fdb_tsdb1\" is success...\n");
	} else {
		printf("Fal write The partition \"fdb_tsdb1\" is false!!!\n");
	}
	
	
	//Read Test
	result = fal_partition_read(demo_partition,		//Partition table name, so partition table must not be duplicated
								0, 					//Addresses are cheaper in the partition table, note that the partition table
								data,				//Receiving Address
								size				//Receive Length
								);					//Returns the actual number received
	if ( result >= 0 ) {
		printf("Fal Read The partition \"fdb_tsdb1\" is success...\n");
	} else {
		printf("Fal Read The partition \"fdb_tsdb1\" is false!!!\n");
	}
  • I'm lazy here. Log printing isn't complete.

summary

1. The key is the partition table. As long as the partition table is clear, everything else will be clear.
2. We use the fal middle layer alone, not with Rt-Thread, no problem
3. With the middle layer, of course, the upper layer calls, so the next one...

Tags: stm32

Posted on Sun, 12 Sep 2021 14:15:47 -0400 by sigmon