YAFFS Direct User Guide
Table of Contents
- Purpose
- What are YAFFS and YAFFS Direct Interface?
- Why use YAFFS?
- System Requirements
- How to integrate YAFFS with an RTOS
- Application Interface
- RTOS Integration Interface
- NAND Configuration and Access Interface
- YAFFS NAND Model
- Pre-loading a YAFFS device
- Using YAFFS with NOR flash
Purpose
The purpose of this document is to describe the interfacing of the YAFFS Direct Interface, as well as provide sufficient information to allow a preliminary evaluation of YAFFS.What are YAFFS and YAFFS Direct Interface?
YAFFS stands for Yet Another Flash File System. YAFFS is the first file system that has been designed, from the ground up, for NAND storage.In 2002 Aleph One set out to identify file system options for using NAND Flash as a file system. Various file systems available at the time were evaluated and all were found lacking in one way or another. The need for a suitable NAND storage file system was identified and YAFFS was designed to fill that need1.
Although YAFFS was originally designed for NAND flash, it has been used successfully with NOR flash systems and even as a RAM file system. This allows high reliability file systems to be constructed using NOR flash, with a future migration path to NAND flash for higher density and performance.
YAFFS was originally designed for use with the Linux operating system, but was designed in a very modular way. The file-system-specific code was kept separate from the main YAFFS file system code. This allowed YAFFS to be ported quite cleanly to other operating systems through operating system personality modules. One such personality module is the YAFFS Direct Interface (YDI) which allows YAFFS to be simply integrated with embedded systems, with or without an RTOS. YAFFS has been used for many products using various operating systems including Windows CE and various RTOSs.
Other example OSs ?
YAFFS2, a more recent release of YAFFS, supports a wider range of NAND flash components including 2k page devices.
Any other changes notable ? Define YAFFS Direct Interface and say it is beyond the scope of tise version ?
YAFFS was originally released for Linux under the GNU Public License (GPL). Various embedded developers soon identified that YAFFS would be ideal for their applications, but were not using GPL based code. Aleph One has alternative licensing arrangements to support these applications.
Why use YAFFS?
YAFFS is the first, and perhaps only, file system designed specifically for NAND flash. This means that YAFFS has been designed to work around the various limitations and quirks of NAND flash, as well as exploit the various features of NAND to achieve an effective file system.Some features to consider:
- YAFFS has been well proven and has been used to ship in large volumes in several products.
- YAFFS provides bad block handling and ECC algorithms to handle deficiencies in NAND flash.
- YAFFS is a log-structured file system which makes it very robust to power failures etc.
- YAFFS has highly optimised and predictable garbage collection strategies. This makes it high performance and deterministic.
- YAFFS has lower memory footprints than most other log-structured flash file systems.
- YAFFS provides a wide range of POSIX-style file system support including directories, symbolic and hard links etc. through standard file system interface calls.
- YAFFS is highly configurable to work with various flash geometries, various ECC options, caching options etc.
System Requirements
Determining minimum system requirements is often quite difficult. The following are presented as a guideline only. Contact Aleph One for more detailed analysis if required.YAFFS is endian-neutral and works fine with little-endian and big-endian processors. Insert -
YAFFS code has been used successfully with many different 32-bit and 64-bit CPUs including 68000, ARM, ColdFire, PowerPC and x86 variants. YAFFS should work with 16-bit CPUs too, but this is generally untested and might need some tuning.
Because YAFFS is log structured, RAM is required to build up runtime data structures. As a rule of thumb, budget approximately 2 bytes per chunk of NAND flash, where a chunk is typically one page of NAND. For NAND with 512byte pages, budget approximately 4kbytes of RAM per 1Mbyte of NAND. For 2kbyte page devices budget approximately 1kbyte per 1Mbyte of NAND.
How to integrate YAFFS with an RTOS
The three parts to YAFFS Direct Interface are:-
- Application Interface: This is the interface that the application code uses to access the YAFFS file system.
- RTOS Integration Interface: These are the functions that much be provided for YAFFS to access the RTOS system resources.
- NAND Configuration and Access Interface: These are the functions that must be provided for YAFFS to access the NAND flash.
Application Interface
The application interface is defined in yaffsfs.h.For the most part, this interface consists of the standard clib function names prefixed with yaffs_. For example, yaffs_open, yaffs_close etc.
There is a lot of flexibility in how these functions are used and the system integrator needs to determine the best strategy for the system.
These functions can be used directly, with the code written using these names. For example
int main(...)
{
// initialisation
f = yaffs_open(...);
yaffs_read(f,...);
yaffs_close(f);
}
The yaffs_ functions can also be wrapped in some way to allow existing code to use yaffs without modification. For example:
#define open(path, oflag, mode) yaffs_open(path, oflag, mode)
..
int main(...)
{
// initialisation
f = open(...);
read(f,...);
close(f);
}
A more complex approach that can be used if more than one file-system is used is to provide
redirection functions. For example:
int open(const char *path,...)
{
// Determine which file system is being used if(it_is_a_yaffs_path(path))
return yaffs_open(...);
else
{
// Do something else
}
}
RTOS Integration Interface
Before YAFFS can be used, it needs to be integrated with the [see above] system and configured so that the correct actions are performed.To do this, you need to modify the configuration file. An example configuration file is presented in yaffscfg.c Where is that available ? URL ?
This is a relatively straight forward process and defines the RTOS access functions.
The RTOS access functions are:
void yaffsfs_SetError(int err): Called by YAFFS to set the system error.
void yaffsfs_Lock(void): Called by YAFFS to lock YAFFS from multithreaded access.
void yaffsfs_Unlock(void): Called by YAFFS to unlock YAFFS.
__u32 yaffsfs_CurrentTime(void): Get current time from RTOS.
void yaffsfs_LocalInitialisation(void): Called to initialise RTOS context.
If YAFFS is being used in a multi-threaded environment, then typically yaffs_LocalInitialisation() will initialise a suitable RTOS semaphore and yaffs_Lock and yaffs_Unlock will call the appropriate functions to lock and release the semaphore.
yaffs_CurrentTime can be any time increment of use to the system. If this is not required, then it is fine to just use a function that returns zero.
Although not shown here, YAFFS also requires memory allocation/free functions which default to malloc() and free(). These, and some other functions can be tuned in ydirectenv.h
Before the application code uses YAFFS, the yaffs_StartUp() function must be called and the appropriate partitions must be mounted. This is typically done in the system boot code:
// System boot code: Start up YAFFS.
yaffs_StartUp();
yaffs_mount("/boot");
NAND Configuration and Access Interface
This part of the configuration involves configuring the chips and suitable access functions.This is done in yaffs_StartUp() (see yaffscfg.c).
A yaffs_DeviceConfiguration is an entry that has two parts: a mount point name and a yaffs_Device structure. The mount point name is used to determine where the particular yaffs_Device may be found in the directory structure. There can be any number of mount points, but these must all be named off the root (ie. /boot and /flash are acceptable, but /boot/flash is not).
A yaffs_Device is a logical entity and can correspond to:
- A whole flash device
- Part of a flash device
- Something other than a flash device (e.g. perhaps a RAM emulation). During testing, this emulation capability is often employed to use a host system hard-disk file or nfs-mounted file as an alternative storage mechanism.
Two or more yaffs_Devices can reside in a single physical flash device by specifying different start and end blocks. For example here is a case where a single device is split into two partitions:
// /boot
......
bootDev.startBlock = 1; // Can't use block 0
bootDev.endBlock = 127; // Last block in 2MB.
// /disk
...
diskDev.startBlock = 128; // First block after 2MB
diskDev.endBlock = 1023; // Last block in 16MB
Each yaffs_Device mush be configured with the following fields:
nBytesPerChunk: Number of bytes per chunk. This must be 512 bytes for YAFFS1.
nChunksPerBlock: Number of chunks per erasable block.
nReservedBlocks: Number of erasable blocks that YAFFS must keep in reserve for garbage collection and to cover for block failures. This must be a minimum of 2, but 5 or so is better. If you are using a medium that is not expected to fail (eg. RAM for a RAM disk, or a host file system emulation then 2 is OK).
startBlock: Start block for this yaffs_Device. NB You cannot use block 0.
endBlock: Last block in this yaffs_Device.
useNANDECC: If this is non-zero, then YAFFS will not perform ECC and it is assumed that the hardware ECC or specified NAND access functions will perform EC checks.
nShortOpCaches: This configures the number of entries in the YAFFS cache for this device. A value of zero defeats the cache. A value of 10 to 20 is recommended for most systems.
genericDevice: This is an arbitrary value used to identify the device context. It is important that each yaffs_Device has a different value for fstat to work correctly. Typically using (void *) n, is sufficient, but this can also be a pointer or some other value as appropriate to establish a context.
The yaffs_Device configuration also specifies pointers to five functions that YAFFS calls to access the NAND for this yaffs_Device. For a better understanding of these functions, see the section on the YAFFS NAND Model. These are:
int (*writeChunkToNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)
YAFFS calls this function with the following parameters:
dev: Pointer to the yaffs_Device.
chunkInNAND: the identifier for the chunk = page.
data: Pointer to a buffer holding the page of data to be written. This will be NULL if only the spare area is being written.
spare: Pointer to buffer holding the spare info to be written. This will never be NULL.
int (*readChunkFromNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)
YAFFS calls this function when reading a chunk from flash. The meanings of the arguments are obvious from the above, but note that the data and spare fields might be NULL.
int (*eraseBlockInNAND)(struct yaffs_DeviceStruct *dev,int blockInNAND)
YAFFS calls this function to erase a block of flash.
int (*initialiseNAND)(struct yaffs_DeviceStruct *dev)
YAFFS calls this function at start up before other functions get called to access the device. This allows the system integrator a control point to perform any required initialisation (eg. set up chip selects etc.).
YAFFS NAND Model
YAFFS uses a fairly abstract model for NAND flash. This allows a lot of flexibility in the way YAFFS can be used.YAFFS is designed for NAND flash and makes the following assumptions and definitions:
- NAND flash is arranged in blocks. Each block is the same size and comprises an integer number of chunks. Each block is treated as a single erasable item.
- A chunk equates to the allocation units of flash. For YAFFS1, each chunk equates to a 512-byte NAND page (that is, a 512-byte data portion and a 16-byte spare area). For YAFFS2, a chunk can be larger (eg. on 2k page devices, a chunk will typically be a single 2k page : 2kbytes of data + 64-bytes of spare).
- All accesses (reads and writes) are page (chunk) aligned.
- When programming a NAND flash, only the zero bits in the pattern being programmed are relevant, and one bits are "don't care". For example, if a byte already contains the binary pattern 1010, then programming 1001 will result in the pattern which is the logical AND of these two patterns ie. 1000. This is different to NOR flash.
YAFFS identifies blocks by their block number and chunks by the chunk Id. A chunk Id is calculated as:
chunkId = block_id * YAFFS_CHUNKS_PER_BLOCK + chunk Offset in this block.
YAFFS assumes that the spare information is set out according to the variable yaffs_Spare. Particular note must be taken of bad block marker positions etc.
YAFFS treats a blank (0xFF filled) block as being free or erased. Thus, the equivalent of formatting for a YAFFS partition is to erase all the blocks that are not bad.
Pre-loading a YAFFS device
Use mkyaffsimg and mkyaffs. - TBCUsing YAFFS with NOR flash
Although YAFFS was designed for NAND flash, it has been used with great success on NOR flash.It should be noted, however, that NOR flash typically takes far longer to erase and program than NAND flash which can make a NOR-based file system slow for writing.
As per the YAFFS NAND model above, YAFFS needs pages with data and spare areas. NOR flash is not arranged like this, so what now?
To get this working requires that the pages and spare area be emulated.
For example, let us consider a system which has NOR flash in 128kB erasable blocks. Here's what we can do:
Each chunk is going to be 512bytes of data area + 16 bytes of spare area = 528 bytes.
Each block is arranged as (128*1024)/528 = 248 chunks (with a few bytes wasted per block).
Now let's look at the NAND access functions, presented in a skeleton form below:
#define NOR_BASE xxxxx // Base address for NOR device
#define NOR_BLOCK_SIZE (128*1024)
#define NOR_PAGE_DATA_SIZE 512
#define NOR_PAGE_SPARE_SIZE 16
#define NOR_PAGE_WHOLE_SIZE (512+16)
#define NOR_CHUNKS_PER_BLOCK (NOR_BLOCK_SIZE/NOR_PAGE_WHOLE_SIZE)
#define chunkToAddr(c) (NOR_BASE + \
NOR_BLOCK_SIZE * ((c)/NOR_CHUNKS_PER_BLOCK) + \
NOR_PAGE_SIZE * ((c)%NOR_CHUNKS_PER_BLOCK))
int nor_writeChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare);
{
// Remember, we're dealing with NAND programming semantics, so we need to read back
// and AND in the pattern for NOR. Don't have to do this for NAND.
__u8 buffer[NOR_PAGE_DATA_SIZE];
int i;
__u8 * norAddr;
if(data)
{
norAddr = chunkToAddr(chunkInNAND);
memcpy(buffer, norAddr,NOR_PAGE_DATA_SIZE);
for(i = 0; i < NOR_PAGE_DATA_SIZE; i++)
buffer[i] &= data[i];
write_nor(norAddr,buffer,NOR_PAGE_DATA_SIZE);
}
if(spare)
{
norAddr = chunkToAddr(chunkInNAND) + NOR_PAGE_DATA_SIZE;
memcpy(buffer, norAddr,NOR_PAGE_SPARE_SIZE);
for(i = 0; i < NOR_PAGE_SPARE_SIZE; i++)
buffer[i] &= spare[i];
write_nor(norAddr,buffer,NOR_PAGE_SPARE_SIZE);
}
return YAFFS_OK;
}
int nor_readChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)
{
__u8 * norAddr;
if(data)
{
norAddr = chunkToAddr(chunkInNAND);
memcpy(data, norAddr,NOR_PAGE_DATA_SIZE);
}
if(spare)
{
norAddr = chunkToAddr(chunkInNAND) + NOR_PAGE_DATA_SIZE;
memcpy(spare, norAddr,NOR_PAGE_SPARE_SIZE);
}
return YAFFS_OK;
}
int nor_eraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND);
{
....__u8 *norAddr = NOR_BASE + NOR_BLOCK_SIZE * blockInNAND;
erase_nor(norAddr);
return YAFFS_OK;
}
int nor_initialiseNAND(struct yaffs_DeviceStruct *dev)
{
// Set up chip selects etc.
}
?
YAFFS