YAFFS Direct User Guide

Table of Contents

  1. Purpose
  2. What are YAFFS and YAFFS Direct Interface?
  3. Why use YAFFS?
  4. System Requirements
  5. How to integrate YAFFS with an RTOS
  6. Application Interface
  7. RTOS Integration Interface
  8. NAND Configuration and Access Interface
  9. YAFFS NAND Model
  10. Pre-loading a YAFFS device
  11. 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:

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

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:


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:

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. - TBC

Using 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.
}

?