8

Flash Memory Block Device Driver

Optional Component TrueFFS



8.1    Introduction

TrueFFS for Tornado is an optional product that provides a block device interface to a wide variety of flash memory devices. TrueFFS is a VxWorks-compatible implementation of M-Systems FLite, version 2.0. This system is reentrant, thread-safe, and supported on all CPU architectures that host VxWorks.

This chapter begins with a brief introduction to flash memory, followed by a step-by-step outline of the procedure for building a system with TrueFFS. Then, the main part of the chapter describes these steps in detail, including sections devoted to writing your own socket driver and MTD components. The chapter ends with a description of the functionality of flash memory block devices.


*      
NOTE: This version of the TrueFFS product is a block device driver to VxWorks that, although intended to be file system neutral, is guaranteed to work only with the MS-DOS compatible file system offered with this product.

8.1.1   Choosing TrueFFS as a Medium

TrueFFS applications can read and write from flash memory just as they would from an MS-DOS file system resident on a magnetic-medium mechanical disk drive. However, the underlying storage media are radically different. While these differences are completely transparent to the high-level developer, it is critical that you be aware of them when designing an embedded system.

Flash memory stores data indefinitely, even while unpowered. The physical components of flash memory are solid-state devices1 that consume little energy and leave a small foot print. Thus, flash memory is ideal for mobile devices, for hand-held devices, and for embedded systems that require reliable, non-volatile environments that are too harsh for mechanical disks.

However, flash does have a limited life, due to the finite number of erase cycles; and, TrueFFS only supports flash devices that are symmetrically blocked. In addition, some features common to block device drivers for magnetic media are not available with flash memory. Unequal read and write time is a typical characteristic of flash memory, in which reads are always faster than writes. For more information, see 8.13 Flash Memory Functionality. Also, TrueFFS does not support ioctl.

The unique qualities that distinguish flash memory from magnetic-media disk drives make it an ideal choice for some types of applications, yet impractical for others.


*      
NOTE: Although you can write in any size chunks of memory, ranging from bytes to words and double words, you can only erase in blocks. The best approach to extending the life of the flash is to ensure that all blocks wear evenly. For more information, see 8.13 Flash Memory Functionality.


*      
NOTE: The TrueFFS optional product does have not support for partition tables.


*      
CAUTION: VxWorks favors task priorities over I/O sequence when servicing I/O requests. Thus, if a low priority task and a high priority task request an I/O service while the resource is busy, the high priority task gets the resource when it becomes available--even if the low priority got its request in before the high priority task. To VxWorks, a flash device is just another resource.

8.1.2   TrueFFS Layers

TrueFFS is comprised of a core layer and three functional layers-the translation layer, the Memory Technology Driver (MTD) layer, and the socket layer-as illustrated in Figure 8-1. The three functional layers are provided in source code form or in binary form, or in both, as noted below. For more detailed information about the functionality of these layers, see 8.13 Flash Memory Functionality.

Figure 8-1:   TrueFFS is a Layered Product

This layer connects other layers to each other. It also channels work to the other layers and handles global issues, such as "backgrounding", garbage collection, timers, and other system resources. The core layer is provided in binary form only.

This layer maintains the map that associates the file system's view of the storage medium with the erase blocks in flash. The Block Allocation Map is the basic building block for implementing wear leveling and error recovery. The translation layer is media specific (NOR or SSFDC). The translation layer is provided in binary form only.

The MTD implements the low-level programming (map, read, write, and erase) of flash medium. MTDs are provided in both source and binary form.

The socket layer provides the interface between TrueFFS and the board hardware, providing board-specific hardware access routines. It is responsible for power management, card detection, window management, and socket registration. The socket drivers are provided in source code only.



8.2    Building Systems with TrueFFS

This section presents a high-level overview of the development process, outlining the steps required to configure and build a VxWorks system that supports TrueFFS. This process applies to VxWorks systems that can be used with either bootable or downloadable applications.

Step 1:   Select an MTD Component

Choose an MTD, appropriate to your hardware, from those provided with the TrueFFS product. You may prefer to (or, in rare cases, need to) write your own. For details, see 8.3 Selecting an MTD Component.

Step 2:   Identify the Socket Driver

Ensure that you have a working socket driver. The socket driver is a source code component, implemented in the file sysTffs.c. For some BSPs, the socket driver is fully defined and located in the BSP directory. If it is not, you can port a generic file containing skeleton code to your hardware. For details, see 8.4 Identifying the Socket Driver.

Step 3:   Configure the System

Configure your system for TrueFFS by adding the appropriate components. Minimum support requires components for dosFs and the four TrueFFS layers. For details, see 8.5 Configuring and Building the Project.


*      
NOTE: Component descriptions use the abbreviation TFFS, rather than TRUEFFS, as might be expected.

Step 4:   Build the Project

Before you build the system, the binaries for the MTDs need to be up to date and the socket driver file located in the BSP directory must be a working version. For details, see 8.5.7 Building the System Project.

Step 5:   Boot the Target and Format the Drives

Next, boot the target. Then, from the shell, format the drives. For details, see 8.6 Formatting the Device.


*      
NOTE: To preserve a region of flash for boot code, see 8.7 Creating a Region for Writing a Boot Image.

Step 6:   Mount the Drive

Mount the VxWorks DOS file system on a TrueFFS flash drive. For details, see 8.8 Mounting the Drive.

Step 7:   Test the Drive

Test your drive(s).

One way to do this is to perform a quick sanity check by copying a text file from the host (or from another type of storage medium) to the flash file system on the target; then copy the file to the console or to a temporary file for comparison, and verify the content. The following example is run from the shell as a sanity check:

%->@copy "host:/home/panloki/.cshrc" "/flashDrive0/myCshrc" 
Copy Ok: 4266 bytes copied 
Value = 0 = 0x0 
%->@copy "/flashDrive0/myCshrc" 
... 
... 
... 
Copy Ok: 4266 bytes copied 
Value = 0 = 0x0


*      
NOTE: The copy command requires the appropriate configuration of dosFs support components. For details, see Optional dosFs Components.



8.3    Selecting an MTD Component

The directory installDir/target/src/drv/tffs contains the source code for the following types of MTD components:

  • MTDs that work with several of the devices provided by Intel, AMD, Fujitsu, and Sharp.

  • Two generic MTDs that can be used for devices complying with CFI.

To better support the out-of-box experience, these MTDs attempt to cover the widest possible range of devices (in their class) and of bus architectures. Consequently, the drivers are bulky and slow in comparison to drivers written specifically to address the runtime environment that you want to target. If the performance and size of the drivers provided do not match your requirements, you can modify them to better suit your needs. The section 8.12 Writing MTD Components has been provided exclusively to address this issue.

For a complete list of these MTDs, see 8.11 Using the MTD-Supported Flash Devices. In addition, section 8.11.1 Supporting the Common Flash Interface (CFI) describes the CFI MTDs in detail. Evaluate whether any of these drivers support the device that you intend to use for TrueFFS. Devices are usually identified by their JEDEC IDs. If you find an MTD appropriate to your flash device, you can use that MTD. These drivers are also provided in binary form; so you do not need to compile the MTD source code unless you have modified it.


*      
NOTE: For a list of the MTD components and details about adding the MTD component to your system project, see 8.5.4 Including the MTD Component.



8.4    Identifying the Socket Driver

The socket driver that you include in your system must be appropriate for your BSP. Some BSPs include socket drivers, others do not. The socket driver file is sysTffs.c and, if it exists, it is located in your BSP directory. If your BSP provides a socket driver, you can use it.

If your BSP does not provide this file, follow the procedure described in 8.10 Writing Socket Drivers, which explains how to port a stub version to your hardware.

In either case, the build process requires that a working socket driver (sysTffs.c) be located in the BSP directory. For more information, see 8.5.6 Adding the Socket Driver.



8.5    Configuring and Building the Project

VxWorks systems configured with TrueFFS include:

  • configuration to fully support the dosFs file system

  • a core TrueFFS component, INCLUDE_TFFS

  • at least one software module from each of the three TrueFFS layers

You can configure and build your system either from the command line or by using the Tornado IDE project facility. When choosing a method for configuring and building systems with TrueFFS, consider the following criteria:

  • Configuring and building from the command line involves editing text files that contain component listings and parameters for initialization, and calling the make utility to build a system image for you. This process, while possibly faster than using the project facility, requires that you provide the list of dependent components accurately.

  • Configuring and building through the Tornado project facility provides an easy and accurate method of adding needed components, while the build process can take longer than when using the command line.

For both configuration and build methods, special consideration must be given to cases where either the socket driver or the MTD, or both, are not provided. The drivers need to be registered and MTDs need appropriate component descriptions. For more information, see 8.10 Writing Socket Drivers, and 8.12.4 Defining Your MTD as a Component.

For general information on configuration procedures, see the Tornado User's Guide: Configuration and Build and the Tornado User's Guide: Projects.


*      
NOTE: Included with TrueFFS for Tornado are sources for several MTDs and socket drivers. The MTDs are in target/src/drv/tffs. The socket drivers are defined in the sysTffs.c files provided in the target/config/bspname directory for each BSP that supports TrueFFS.

8.5.1   Including File System Components

A system configuration with TrueFFS is essentially meaningless without the VxWorks compatible file system, MS-DOS. Therefore, both dosFs support, and all components that it depends upon, need to be included in your TrueFFS system. For information on this file system and support components, see 5.2.2 Configuring Your System.

In addition, there are other file system components that are not required, but which may be useful. These components add support for the basic functionality that one needs to use a file system, such as the commands ls, cd, copy, and so.

8.5.2   Including the Core Component

All systems must include the TrueFFS core component, INCLUDE_TFFS.

Figure 8-2:   Adding TrueFFS Components from the Project Facility

Defining this component triggers the correct sequence of events, at boot time, for initializing this product. It also ensures that the socket driver is included in your project (see 8.5.6 Adding the Socket Driver).

You can include this component using the project facility, as shown in Figure 8-2, or by defining INCLUDE_TFFS in config.h.

8.5.3   Including Utility Components

This section describes TrueFFS utility components, their purpose, and their default configuration options. You do not need to modify the default configuration for these components in order to create a functioning TrueFFS system.

INCLUDE_TFFS_SHOW


*      
NOTE: INCLUDE_TFFS_BOOT_IMAGE is defined by default in the socket driver, sysTffs.c. It is used for configuring flash-resident boot images. Defining this constant automatically includes tffsBootImagePut( ) in your sysTffs.o. This routine is used to write the boot image to flash memory (see 8.7.3 Writing the Boot Image to Flash).

8.5.4   Including the MTD Component

Add the MTD component appropriate to your flash device (8.3 Selecting an MTD Component) to your system project. If you have written your own MTD, see 8.12.4 Defining Your MTD as a Component to ensure that it is defined correctly for inclusion. You can add the MTD component either through the project facility or, for a command-line build, by defining it in the socket driver file (see 8.5.7 Building the System Project). Whichever method is used, it must be the same for configuring the MTD component and building the project.

The MTD components provided by TrueFFS, for flash devices from Intel, AMD, Fujitsu, and Sharp, are listed below:

INCLUDE_MTD_CFISCS

INCLUDE_MTD_CFIAMD

INCLUDE_MTD_I28F016

INCLUDE_MTD_I28F008

INCLUDE_MTD_AMD

INCLUDE_MTD_WAMD

INCLUDE_MTD_I28F008_BAJA

MTDs defined in the component descriptor file (that is, included through the project facility) usually make it explicit to require the translation layer. However, if you are building from the command-line, or writing your own MTD, you may need to explicitly include the translation layer.


*      
NOTE: Although components can also be defined in config.h, this is not recommended for the MTD component because it can conflict with the project facility configurations.

8.5.5   Including the Translation Layer

The translation layer is selected according to the technology used by your flash medium. The two types of flash are NOR and NAND. This product only supports NAND devices that conform to the SSFDC specification. For more information, see 8.11.3 Obtaining Disk On Chip Support.

The translation layer is provided in binary form only. The translation layer components that are provided are listed below.

INCLUDE_TL_FTL

INCLUDE_TL_SSFDC

The component descriptor files (for the project facility) specify the dependency between the translation layers and the MTDs; therefore, when configuring through the project facility, you do not need to explicitly select a translation layer. The build process handles it for you.

If you are not using the project facility, you are responsible for selecting the correct translation layer. As with the MTD, when configuring and building from the command line, you define the translation layer in sysTffs.c within the conditional clause #ifndef PROJECT_BUILD. For details, see nConditional Compilation.

For more information, see 8.12.4 Defining Your MTD as a Component.

8.5.6   Adding the Socket Driver

To include the socket driver in your project, a working version of the socket driver, sysTffs.c, must be located in you BSP directory.

Inclusion of the socket driver is relatively automatic. By including the core TrueFFS component, INCLUDE_TFFS, into your project, the build process checks for a socket driver file, sysTffs.c, in the BSP directory and includes that file in the system project.

If your BSP does not provide a socket driver, follow the procedure described in 8.10 Writing Socket Drivers.

8.5.7   Building the System Project

Build the system project, from either the command line or the Tornado IDE. When building the system project, consider the following two issues.

The file sysTffs.c defines constants within a conditional clause #ifndef PROJECT_BUILD. By default, these constants include definitions for all the MTD components provided with the TrueFFS product. This PROJECT_BUILD clause conditionally includes all of these constants for a command-line build (adds them to sysTffs.o), and excludes them for a project facility build (because you include them from the GUI). Therefore, the same method must be used to both configure the MTD and the translation layer components, and to build the project.

If you are building from the command line, and want to save on memory space, you can undefine the constants for any MTDs that your hardware does not support.

Before you build the project, the binaries for the MTDs must be up to date, and the sysTffs.c that is present in the BSP directory must be a working version.

MTDs are provided in source and binary form. When writing your own MTD, rebuilding the directory is the recommended way to transform the source to binary. This way the binary is placed in the right location and added to the appropriate library. If any files in the group installDir/target/src/drv/tffs/*.c are newer than corresponding files in the library installDir/target/lib/archFamily/arch/common/libtffs.a, then rebuild them before building the project.



8.6    Formatting the Device

Boot the system. After the system boots and registers the socket driver(s), bring up the shell. From the shell, run tffsDevFormat( ) to format the flash memory for use with TrueFFS. This routine, as defined in tffsDrv.h, takes two arguments, a drive number and a format argument:

tffsDevFormat (int tffsDriveNo, int formatArg);


*      
NOTE: You can format the flash medium even though there is not yet a block device driver associated with the flash.


*      
CAUTION: Running tffsDevFormat( ) on a device that is sharing boot code with the file system will leave the board without boot code at the end of it. The board then becomes unusable until some alternative method is provided for reflashing the lost image. Once you do that, the file system that you created by formatting is destroyed.

8.6.1   Specifying the Drive Number

The first argument, tffsDriveNo, is the drive number (socket driver number). The drive number identifies the flash medium to be formatted and is determined by the order in which the socket drivers were registered. Most common systems have a single flash drive, but TrueFFS supports up to five. Drive numbers are assigned to the flash devices on your target hardware by the order in which the socket drivers are registered in sysTffsInit( ) during boot. The first to be registered is drive 0, the second is drive 1, and so on up to 4. Therefore, the socket registration process determines the drive number. (Details of this process are described in Socket Registration.) You use this number to specify the drive when you format it.

8.6.2   Formatting the Device

The second argument, formatArg, is a pointer to a tffsDevFormatParams structure (cast to an int). This structure describes how the volume should be formatted. The tffsDevFormatParams structure is defined in installDir\target\h\tffs\tffsDrv.h as:

typedef struct 
    { 
     tffsFormatParams  formatParams; 
     unsigned          formatFlags; 
    }tffsDevFormatParams;

The first member, formatParams, is of type tffsFormatParams. The second member, formatFlags, is an unsigned int.

TFFS_STD_FORMAT_PARAMS Macro

To facilitate calling tffsDevFormat( ) from a target shell, you can simply pass zero (or a NULL pointer) for the second argument, formatArg. Doing so will use a macro, which defines default values for the tffsDevFormatParams structure. The macro, TFFS_STD_FORMAT_PARAMS, defines the default values used in formatting a flash disk device. This macro, TFFS_STD_FORMAT_PARAMS, is defined in tffsDrv.h as:

#define TFFS_STD_FORMAT_PARAMS {{0, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL}, FTL_FORMAT_IF_NEEDED}

If the second argument, formatArg, is zero, tffsDevFormat( ) uses the default values from this macro.

The macro passes values for both the first and second members of the tffsDevFormatParams structure. These are:

formatParams = {0, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL} 
formatFlags = FTL_FORMAT_IF_NEEDED

The meaning of these default values, and other possible arguments for the members of this structure, are described below.

formatParams Member

The formatParams member is of the type tffsFormatParams. Both this structure, and the default values used by the TFFS_STD_FORMAT_PARAMS macro, are defined in installDir\target\h\tffs\tffsDrv.h.

If you use the TFFS_STD_FORMAT_PARAMS macro, the default values will format the entire flash medium for use with TrueFFS. The most common reason for changing formatParams is to support a boot region. If you want to create a boot image region that excludes TrueFFS, you need to modify these default values by changing only the first member of the tffsFormatParams structure, bootImageLen. For details, see 8.7 Creating a Region for Writing a Boot Image.

formatFlags Member

The second member of the tffsDevFormatParams structure, formatFlags, determines the option used to format the drive. There are several possible values for formatFlags, which are listed in Table 8-1.

Table 8-1:   Option for formatFlags


Macro
Value
Meaning

FTL_FORMAT
1
FAT and FTL formatting
FTL_FORMAT_IF_NEEDED
2
FAT formatting, FTL formatting if needed
NO_FTL_FORMAT
0
FAT formatting only

The default macro TFFS_STD_FORMAT_PARAMS passes FTL_FORMAT_IF_NEEDED as the value for this argument.



8.7    Creating a Region for Writing a Boot Image

Although the translation services of TrueFFS provide many advantages for managing the data associated with a file system, those same services also complicate the use of flash memory as a boot device. The only practical solution to first create a boot image region that excludes TrueFFS, and then write the boot image to that region. This section describes, first, the technical details of the situation, then, how to create the region, and finally, how to write the boot image to it.

8.7.1   Write Protecting Flash

TrueFFS requires that all flash devices that interact with the file system, including boot image regions and NVRAM regions, not be write protected via the MMU. This is because it is essential to the proper working of the product that all commands being issued to the device reach it. Write-protecting the device would impact this behavior, since it would also prevent commands being issued, that are not write-oriented, from reaching the device. For related information, see rfaWriteProtected.

You can, however, reserve a fallow region that is not tampered with by the file system. TrueFFS supports boot code by allowing the user to specify a fallow area when formatting the device. Fallow areas are always reserved at the start of the flash. There have been a few instances where architecture ports have required the fallow area to be at the end of flash. This was accomplished by faking the size of the identification process in the MTD (that is, by telling the MTD that it has less memory than is actually available). The format call is then told that no fallow area is required. TrueFFS does not care how the fallow area is managed, nor is it affected by any faking.

8.7.2   Creating the Boot Image Region

To create the boot image region, format the flash memory so that the TrueFFS segment starts at an offset. This creates a fallow area within flash that is not formatted for TrueFFS. This preserves a boot image region. If you want to update the boot image, you can write a boot image into this fallow area, as described in 8.7.3 Writing the Boot Image to Flash.

Formatting at an Offset

To format the flash at an offset, you need to initialize the tffsFormatParams structure to values that leave a space on the flash device for a boot image. You do this by specifying a value for the bootImageLen member (of the structure) that is at least as large as the boot image. The bootImageLen member specifies the offset after which to format the flash medium for use with TrueFFS. For details on bootImageLen and other members of the structure, see the comments in the header file installDir\target\h\tffs\tffsDrv.h.

The area below the offset determined by bootImageLen is excluded from TrueFFS. This special region is necessary for boot images because the normal translation and wear-leveling services of TrueFFS are incompatible with the needs of the boot program and the boot image it relies upon. When tffsDevFormat( ) formats flash, it notes the offset, then erases and formats all erase units with starting addresses higher than the offset. The erase unit containing the offset address (and all previous erase units) are left completely untouched. This preserves any data stored before the offset address.

For more information on wear leveling, see 8.13 Flash Memory Functionality.

Using a BSP Helper Routine

Some BSPs provide an optional, BSP-specific, helper routine, sysTffsFormat( ), which can be called externally to create or preserve the boot image region. This routine first sets up a pointer to a tffsFormatParams structure that has been initialized with a value for bootImageLen that formats at an offset, creating the boot image region; then it calls tffsDevFormat( ).

Several BSPs, among them the ads860 BSP, include a sysTffsFormat( ) routine that reserves 0.5 MB for the boot image. Following is an example:

STATUS sysTffsFormat (void) 
    { 
    STATUS status; 
    tffsDevFormatParams params = 
        { 
#define HALF_FORMAT  
/* lower 0.5MB for bootimage, upper 1.5MB for TFFS */ 
 
#ifdef  HALF_FORMAT 
    {0x80000l, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL}, 
#else 
    {0x000000l, 99, 1, 0x10000l, NULL, {0,0,0,0}, NULL, 2, 0, NULL}, 
#endif  /* HALF_FORMAT */ 
 
    FTL_FORMAT_IF_NEEDED 
    }; 
 
/* we assume that the drive number 0 is SIMM */ 
 
    status = tffsDevFormat (0, (int)&params); 
    return (status); 
    }

For examples of sysTffsFormat( ) usage, see the socket drivers in installDir/target/src/drv/tffs/sockets. If your BSP does not provide a sysTffsFormat( ) routine, then either create a similar routine, or pass the appropriate argument to tffsDevFormat( ).

8.7.3   Writing the Boot Image to Flash

If you have created a boot image region, write the boot image to the flash device. To do this you use tffsBootImagePut( ), which bypasses TrueFFS (and its translation layer) and writes directly into any location in flash memory. However, because tffsBootImagePut( ) relies on a call to tffsRawio( ), you cannot use this routine once the TrueFFS volume is mounted.


*      
WARNING: Because tffsBootImagePut( ) lets you write directly to any area of flash, it is possible to accidentally overwrite and corrupt the TrueFFS-managed area of flash. For more information about how to carefully use this utility, see the reference entry for tffsBootImagePut( ) in the VxWorks API Reference.

The tffsBootImagePut( ) routine is defined in installDir/target/src/drv/tffs/tffsConfig.c as:

STATUS tffsBootImagePut 
    ( 
    int    driveNo,                /* TFFS drive number */ 
    int    offset,                 /* offset in the flash chip/card */ 
    char * filename                /* binary format of the bootimage */ 
    )

This routine takes as arguments:

  • The driveNo parameter is the same drive number as the one used as input to the format routine.

  • The offset parameter is the actual offset from the start of flash at which the image is written (most often specified as zero).

  • The filename parameter is a pointer to the boot image (bootApp or boot ROM image).


*      
NOTE: For a detailed description of the bootImagePut( ) routine, see the comments in installDir/target/src/drv/tffs/tffsConfig.c.



8.8    Mounting the Drive

Next, use the usrTffsConfig( ) routine to mount the VxWorks DOS file system on a TrueFFS flash drive. This routine is defined in installDir/target/config/comps/src/usrTffs.c as:

STATUS usrTffsConfig 
    ( 
    int    drive,                 /* drive number of TFFS */ 
    int    removable,             /* 0 for nonremovable flash media */ 
    char * fileName               /* mount point */ 
    )

This routine takes three arguments:

  • The drive parameter specifies the drive number of the TFFS flash drive; valid values are 0 through the number of socket interfaces in BSP.

  • The removable parameter specifies whether the media is removable. Use 0 for non-removable, 1 for removable.

  • The fileName parameter specifies the mount point, for example, '/tffs0/'.

The following example runs usrTffsConfig( ) to attach a drive to dosFs, and then runs devs to list all drivers:

% usrTffsConfig 0,0,"/flashDrive0/" 
 
% devs 
drv     name 
0       /null 
1       /tyCo/0 
1       /tyCo/1 
5       host: 
6       /vio 
2       /flashDrive0/

Internally, usrTffsConfig( ) calls other routines, passing the parameters you input.

Among these routines is tffsDevCreate( ), which creates a TrueFFS block device on top of the socket driver. This routine takes, as input, a number (0 through 4, inclusive) that identifies the socket driver on top of which to construct the TrueFFS block device. The tffsDevCreate( ) call uses this number as an index into the array of FLSocket structures. This number is visible later to dosFs as the driver number.

After the TrueFFS block device is created, dcacheDevCreate( ) and then dosFsDevCreate( ) are called. This routine mounts dosFs onto the device. After mounting dosFs, you can read and write from flash memory just as you would from a standard disk drive.



8.9    Running the Shell Commands with Examples

Each of these examples assumes that you have built VxWorks and booted the target.

assabet with a Board-Resident Flash Array and a Boot Image

This example uses sysTffsFormat( ) to format board-resident flash, preserving the boot image region. It does not update the boot image, so no call is made to tffsBootImagePut( ). Then, it mounts the non-removable RFA medium as drive number 0.

At the target shell prompt, enter the following commands:

-> sysTffsFormat  
-> usrTffsConfig 0,0,"/RFA/" 
ads860 with a Board-Resident Flash Array and a PCMCIA Slot

This example formats RFA and PCMCIA flash for two drives.

The first lines of this example format the board-resident flash by calling the helper routine, sysTffsFormat( ), which preserves the boot image region. This example does not update the boot image. It then mounts the drive, numbering it as 0 and passing 0 as the second argument to usrTffsConfig( ). Zero is used because RFA is non-removable.

The last lines of the example format PCMCIA flash, passing default format values to tffsDevFormat( ) for formatting the entire drive. Then, it mounts that drive. Because PCMCIA is removable flash, it passes 1 as the second argument to usrTffsConfig( ). (See 8.8 Mounting the Drive for details on the arguments to usrTffsConfig( ).)

Insert a flash card in the PCMCIA socket. At the target shell prompt, enter the following commands:

-> sysTffsFormat  
-> usrTffsConfig 0,0,"/RFA/"  
-> tffsDevFormat 1,0  
-> usrTffsConfig 1,1,"/PCMCIA1/" 
mv177 with a Board-Resident Flash Array and No Boot Image Region Created

This example formats board-resident flash using the default parameters to tffsDevFormat( ), as described in 8.6 Formatting the Device. Then, it mounts the drive, passing 0 as the drive number and indicating that the flash is non-removable.

At the target shell prompt, enter the following commands:

-> tffsDevFormat 0,0  
-> usrTffsConfig 0,0,"/RFA/" 
x86 with Two PCMCIA Slots Using INCLUDE_PCMCIA

This example formats PCMCIA flash for two drives. Neither format call preserves a boot image region. Then, it mounts the drives, the first is numbered 0, and the second is numbered 1. PCMCIA is a removable medium.

Insert a flash card in each PCMCIA socket. At the target shell prompt, enter the following commands:

-> tffsDevFormat 0,0  
-> usrTffsConfig 0,1,"/PCMCIA1/"  
-> tffsDevFormat 1,0  
-> usrTffsConfig 1,1,"/PCMCIA2/" 


8.10    Writing Socket Drivers

The socket driver is implemented in the file sysTffs.c. TrueFFS provides a stub version of the socket driver file for BSPs that do not include one. As a writer of the socket driver, your primary focus is on the following key contents of the socket driver file:

  • The sysTffsInit( ) routine, which is the main routine. This routine calls the socket registration routine.

  • The xxxRegister( ) routine, which is the socket registration routine. This routine is responsible for assigning routines to the member functions of the socket structure.

  • The routines assigned by the registration routine.

  • The macro values that should reflect your hardware.

In this stub file, all of the required routines are declared. Most of these routines are defined completely, although some use generic or fictional macro values that you may need to modify.

The socket register routine in the stub file is written for RFA (Resident Flash Array) sockets only. There is no stub version of the registration routine for PCMCIA socket drivers. If you are writing a socket driver for RFA, you can use this stub file and follow the steps described in 8.10.1 Porting the Socket Driver Stub File. If you are writing a PCMCIA socket driver, see the example in installDir/target/src/drv/tffs/sockets/pc386-sysTffs.c and the general information in 8.10.2 Understanding Socket Driver Functionality.


*      
NOTE: Examples of other RFA socket drivers are in installDir/target/src/drv/tffs/sockets.

8.10.1   Porting the Socket Driver Stub File

If you are writing your own socket driver, it is assumed that your BSP does not provide one. When you run the build, a stub version of the socket driver, sysTffs.c, is copied from installDir/target/config/comps /src to your BSP directory. Alternatively, you can copy this version manually to your BSP directory before you run a build. In either case, edit only the file copied to the BSP directory; do not modify the original stub file.

This stub version is the starting point for you, to help you port the socket driver to your BSP. As such, it contains incomplete code and does not compile. The modifications you need to make are listed below. They are not extensive and all are noted by /* TODO */ clauses.

  1. Replace "fictional" macro values, such as FLASH_BASE_ADRS, with correct values that reflect your hardware. Then, remove the following line:
#error "sysTffs: Verify system macros and function before first use"
  1. Add calls to the registration routine for each additional device (beyond one) that your BSP supports. Therefore, if you have only one device, you do not need to do anything for this step. For details, see Call the Socket Register Routines.
  1. Review the implementation for the two routines marked /* TODO */. You may or may not need to add code for them. For details, see Implement the Socket Structure Member Functions.


*      
CAUTION: Do not edit the original copy of the stub version of sysTffs.c in installDir/target/config/comps /src, since you may need it for future ports.

Call the Socket Register Routines

The main routine in sysTffs.c is sysTffsInit( ), which is automatically called at boot time. The last lines of this routine call the socket register routines for each device supported by your system. The stub sysTffs.c file specifically calls the socket register routine rfaRegister( ).

If your BSP supports only one (RFA) flash device, you do not need to edit this section. However, if your BSP supports several flash devices, you are to edit the stub file to add calls for each socket's register routine. The place to do this is indicated by the /* TODO */ comments in the sysTffsInit( ) routine.

If you have several socket drivers, you can encapsulate each xxxRegister( ) call in pre-processor conditional statements, as in the following example:

#ifdef  INCLUDE_SOCKET_PCIC0  
    (void) pcRegister (0, PC_BASE_ADRS_0);   /* flash card on socket 0 */ 
#endif  /* INCLUDE_SOCKET_PCIC0 */ 
 
#ifdef  INCLUDE_SOCKET_PCIC1 
    (void) pcRegister (1, PC_BASE_ADRS_1);   /* flash card on socket 1 */ 
#endif  /* INCLUDE_SOCKET_PCIC1 */ 

Define the constants in the BSP's sysTffs.c. Then, you can use them to selectively control which calls are included in sysTffsInit( ) at compile time.

Implement the Socket Structure Member Functions

The stub socket driver file also contains the implementation for the rfaRegister( ) routine, which assigns routines to the member functions of the FLSocket structure, vol. TrueFFS uses this structure to store the data and function pointers that handle the hardware (socket) interface to the flash device. For the most part, you need not be concerned with the FLSocket structure, only with the routines assigned to it. Once these routines are implemented, you never call them directly; they are called automatically by TrueFFS.

All of the routines assigned to the socket structure member functions by the registration routine are defined in the stub socket driver module. However, only the rfaSocketInit( ) and rfaSetWindow( ) routines are incomplete. When you are editing the stub file, note the #error and /* TODO */ comments in the code. These indicate where and how you modify the code.

Following is a list of all of the routines assigned by the registration routine, along with a description of how each was implemented in the stub file. The two routines that require your attention are listed with descriptions of how they are to be implemented.


*      
NOTE: More detailed information on the functionality of each routine is provided in 8.10.2 Understanding Socket Driver Functionality. However, this information is not necessary for you to port the socket driver.

rfaCardDetected

This routine always returns TRUE in RFA environments, since the device is not removable. Implementation is complete in the stub file.

rfaVccOn

Vcc must be known to be good on exit. It is assumed to be ON constantly in RFA environments. This routine is simply a wrapper. While the implementation is complete in the stub file, you may want to add code as described below.

When switching Vcc on, the VccOn( ) routine must not return until Vcc has stabilized at the proper operating voltage. If necessary, your function should delay execution with an idle loop, or with a call to the flDelayMsec( ) routine, until the Vcc has stabilized.

rfaVccOff

Vcc is assumed to be ON constantly in RFA environments. This routine is simply a wrapper and is complete in the stub file.

rfaVppOn

Vpp must be known to be good on exit and is assumed to be ON constantly in RFA environments. This routine is not optional, and must always be implemented. Therefore, do not delete this routine. While the implementation in the stub file is complete, you may want to add code, as described below.

While When switching Vpp on, the VppOn( ) function must not return until Vpp has stabilized at the proper voltage. If necessary, your VppOn( ) function should delay execution with an idle loop or with a call to the flDelayMsec( ) routine, until the Vpp has stabilized.

rfaVppOff

Vpp is assumed to be ON constantly in RFA environments. This routine is complete in the stub file; however, it is not optional, and must always be implemented. Therefore, do not delete this routine.

rfaSocketInit

Contains a /* TODO */ clause.

This routine is called each time TrueFFS is initialized (the drive is accessed). It is responsible for ensuring that the flash is in a usable state (that is, board-level initialization). If, for any reason, there is something that must be done prior to such an access, this is the routine in which you perform that action. For more information, see nrfaSocketInit.

rfaSetWindow

Contains a /* TODO */ clause.

This routine uses the FLASH_BASE_ADRS and FLASH_SIZE values that you set in the stub file. As long as those values are correct, the implementation for this routine in the stub file is complete.

TrueFFS calls this routine to initialize key members of the window structure, which is a member of the FLSocket structure. For most hardware, the setWindow function does the following, which is already implemented in the stub file:

  • Sets the window.baseAddress to the base address in terms of 4 KB pages.

  • Calls flSetWindowSize( ), specifying the window size in 4 KB units (window.baseAddress). Internally, the call to flSetWindowSize( ) sets window.size, window.base, and window.currentPage for you.

This routine sets current window hardware attributes: base address, size, speed and bus width. The requested settings are given in the vol.window structure. If it is not possible to set the window size requested in vol.window.size, the window size should be set to a larger value, if possible. In any case, vol.window.size should contain the actual window size (in 4 KB units) on exit.

For more information, see nrfaSetWindow and Socket Windowing and Address Mapping.


*      
CAUTION: On systems with multiple socket drivers (to handle multiple flash devices), make sure that the window base address is different for each socket. In addition, the window size must be taken into account to verify that the windows do not overlap.

rfaSetMappingContext

TrueFFS calls this routine to set the window mapping register. Because board-resident flash arrays usually map the entire flash in memory, they do not need this function. In the stub file it is a wrapper, thus implementation is complete.

rfaGetAndClearChangeIndicator

Always return FALSE in RFA environments, since the device is not removable. This routine is complete in the stub file.

rfaWriteProtected

This routine always returns FALSE for RFA environments. It is completely implemented in the stub file.

8.10.2   Understanding Socket Driver Functionality

Socket drivers in TrueFFS are modeled after the PCMCIA socket services. As such, they must provide the following:

  • services that control power to the socket (be it PCMCIA, RFA, or any other type)

  • criteria for setting up the memory windowing environment

  • support for card change detection

  • a socket initialization routine

This section describes details about socket registration, socket member functions, and the windowing and address mapping set by those functions. This information is not necessary to port the stub RFA file; however, it may be useful for writers of PCMCIA socket drivers.

Socket Registration

The first task the registration routine performs is to assign drive numbers to the socket structures. This is fully implemented in the stub file. You only need to be aware of the drive number when formatting the drives (8.6.1 Specifying the Drive Number ).

The drive numbers are index numbers into a pre-allocated array of FLSocket structures. The registration sequence dictates the drive number associated with a drive, as indicated in the first line of code from the rfaRegister( ) routine:

FLSocket vol = flSockeOf (noOfDrives);

Here, noOfDrives is the running count of drives attached to the system. The function flSocketOf( ) returns a pointer to socket structure, which is used as the volume description and is incremented by each socket registration routine called by the system. Thus, the TrueFFS core in the socket structures are allocated each of the (up to) 5 drives supported for the system.2 When TrueFFS invokes the routines that you implement to handle its hardware interface needs, it uses the drive number as an index into the array to access the socket hardware for a particular flash device.

Socket Member Functions

This routine reports whether there is a flash memory card in the PCMCIA slot associated with this device. For non-removable media, this routine should always return TRUE. Internally, TrueFFS for Tornado calls this function every 100 milliseconds to check that flash media is still there. If this function returns FALSE, TrueFFS sets cardChanged to TRUE.

TrueFFS can call this routine to turn on Vcc, which is the operating voltage. For the flash memory hardware, Vcc is usually either 5 or 3.3 Volts. When the media is idle, TrueFFS conserves power by turning Vcc off at the completion of an operation. Prior to making a call that accesses flash memory, TrueFFS uses this function to turn the power back on again.

However, when socket polling is active, a delayed Vcc-off mechanism is used, in which Vcc is turned off only after at least one interval has passed. If several flash-accessing operations are executed in rapid sequence, Vcc remains on during the sequence, and is turned off only when TrueFFS goes into a relatively idle state.

TrueFFS can call this routine to turn off the operating voltage for the flash memory hardware. When the media is idle, TrueFFS conserves power by turning Vcc off. However, when socket polling is active, Vcc is turned off only after a delay. Thus, if several flags accessing operations are executed in rapid sequence, Vcc is left on during the sequence. Vcc is turned off only when TrueFFS goes into a relatively idle state. Vcc is assumed to be ON constantly in RFA environments.

This routine is not optional, and must always be implemented. TrueFFS calls this routine to apply Vpp, which is the programming voltage. Vpp is usually 12 Volts to the flash chip. Because not all flash chips require this voltage, the member is included only if SOCKET_12_VOLTS is defined.

Vpp must be known to be good on exit and is assumed to be ON constantly in RFA environments.


*      
NOTE: The macro SOCKET_12_VOLTS is only alterable by users that have source to the TrueFFS core.

TrueFFS calls this routine to turn off a programming voltage (Vpp, usually 12 Volts) to the flash chip. Because not all flash chips require this voltage, the member is included only if SOCKET_12_VOLTS is defined. This routine is not optional, and must always be implemented. Vpp is assumed to be ON constantly in RFA environments.

TrueFFS calls this function before it tries to access the socket. TrueFFS uses this function to handle any initialization that is necessary before accessing the socket, especially if that initialization was not possible at socket registration time. For example, if you did no hardware detection at socket registration time, or if the flash memory medium is removable, this function should detect the flash memory medium and respond appropriately, including setting cardDetected to FALSE if it is missing.

TrueFFS uses window.base to store the base address of the memory window on the flash memory, and window.size to store the size of the memory window. TrueFFS assumes that it has exclusive access to the window. That is, after it sets one of these window characteristics, it does not expect your application to directly change any of them, and could crash if you do. An exception to this is the mapping register. Because TrueFFS always reestablishes this register when it accesses flash memory, your application may map the window for purposes other than TrueFFS. However, do not do this from an interrupt routine.

TrueFFS calls this routine to set the window mapping register. This routine performs the sliding action by setting the mapping register to an appropriate value. Therefore, this routine is meaningful only in environments such as PCMCIA, that use the sliding window mechanism to view flash memory. Flash cards in the PCMCIA slot use this function to access/set a mapping register that moves the effective flash address into the host's memory window. The mapping process takes a "card address", an offset in flash, and produces real address from it. It also wraps the address around to the start of flash if the offset exceeds flash length. The latter is the only reason why the flash size is a required entity in the socket driver. On entry to setMappingContext, vol.window.currentPage is the page already mapped into the window (meaning that it was mapped in by the last call to setMappingContext).

This routine reads the hardware card-change indication and clears it. It serves as a basis for detecting media-change events. If you have no such hardware capability, return FALSE for this routine (set this function pointer to NULL).

TrueFFS can call this routine to get the current state of the media's write-protect switch (if available). This routine returns the write-protect state of the media, if available, and always returns FALSE for RFA environments. For more information, see 8.7.1 Write Protecting Flash.

Socket Windowing and Address Mapping

The FLSocket structure (defined in installDir/target/h/tffs/flsocket.h) contains an internal window state structure. If you are porting the socket driver, the following background information about this window structure may be useful when implementing the xxxSetWindow( ) and xxxSetMappingContext( ) routines.

The concept of windowing derives from the PCMCIA world, which formulated the idea of a Host Bus Adapter. The host could allow one of the following situations to exist:

  • The PCMCIA bus could be entirely visible in the host's address range.

  • Only a segment of the PCMCIA address range could be visible in the host's address space.

  • Only a segment of the host's address space could be visible to the PCMCIA.

To support these concepts, PCMCIA specified the use of a "window base register" that may be altered to adjust the view from the window. In typical RFA scenarios, where the device logic is NOR, the window size is that of the amount of flash on the board. In the PCMCIA situation, the window size is implementation-specific. The book PCMCIA Systems Architecture by Don Anderson provides an good explanation of this concept, with illustrations.



8.11    Using the MTD-Supported Flash Devices

This section lists the flash devices that are supported by the MTDs that are provided with the product.

8.11.1   Supporting the Common Flash Interface (CFI)

TrueFFS supports devices that use the Scalable Command Set (SCS) from Intel, and devices that use the AMD command set. Both of these command sets conform to the Common Flash Interface (CFI). System that requires support for both command sets are rare. Therefore, to facilitate code readability, support for each command set is provided in a separate MTD. To support both command sets, simply configure your system to include both MTDs (see 8.5.4 Including the MTD Component). The command sets are described below.

  • Intel/Sharp Command Set .  

This is the CFI specification listing for the SCS command set. The driver file for this MTD is installDir/target/src/drv/tffs/cfiscs.c. The support for the Intel/Sharp command set was largely derived from the Application Note 646, available at the Intel web site.

  • AMD/Fujitsu Command Set .  

This is the Embedded Program Algorithm and flexible sector architecture listing for the SCS command set. The driver file is installDir/target/src/drv/tffs/cfiamd.c. Details about support for this MTD are described in AMD/Fujitsu CFI Flash Support.

Common Functionality

Both drivers support 8 and 16 bit devices, and 8- and 16-bit wide interleaves. Configuration macros (described in the code) are used to control these and other configuration issues, and must be defined specifically for your system. If modifications are made to the code, it must be rebuilt. Noteworthy are the following macros:

INTERLEAVED_MODE_REQUIRES_32BIT_WRITES

SAVE_NVRAM_REGION

CFI_DEBUG

BUFFER_WRITE_BROKEN

DEBUG_PRINT


*      
NOTE: These macros are only configurable by defining them in the source file, not through the IDE project tool.

CFI/SCS Flash Support

The MTD defined in cfiscs.c supports flash components that follow the CFI/SCS specification. CFI stands for Common Flash Interface. SCS stands for Scalable Command Set. CFI is a standard method for querying flash components for their characteristics. SCS is a second layer built on the CFI specification. This lets a single MTD handle all CFI/SCS flash technology in a common manner.

The joint CFI/SCS specification is currently adopted by Intel Corporation and Sharp Corporation for all new flash components starting in 1997.

The CFI document can be downloaded from:

http://www.intel.com/design/flcomp/applnots/292204.htm

or can be found by searching for CFI at:

http://www.intel.com/design

Define INCLUDE_MTD_CFISCS in your BSP's sysTffs.c file to include this MTD in TrueFFS for Tornado.

In some of the more recent target boards we have observed that non-volatile RAM circuitry does not exist, and that the BSP developers have opted to use the high end of flash for this purpose. The last erase block of each flash part is used to make up this region. The CFISCS MTD supports this concept by providing the compiler constant SAVE_NVRAM_REGION. If this is defined, the driver reduces the device's size by the erase block size times the number of devices; this results in the NVRAM region being preserved and never over-written. The ARM BSPs, in particular, use flash for NVRAM and for the boot image.

AMD/Fujitsu CFI Flash Support

In AMD and Fujitsu devices, the flexible sector architecture, also called boot block devices, is only supported when erasing blocks. However, the TrueFFS core and translation layers have no knowledge of the subdivision within the boot block because the MTD presents this division transparently. According to the data sheet for the 29LV160 device, it is comprised of 35 sectors. However, the 4 boot block sectors appear to the core and translation layer as yet another, single (64 KB) sector. Thus, the TrueFFS core detects only 32 sectors. Consequently, the code that supports boot images also has no knowledge of the boot block, and cannot provide direct support for it.

The AMD and Fujitsu devices include the concept of Top and Bottom boot devices. However, the CFI interrogation process does not provide a facility for distinguishing between the two. Thus, in order to determine the boot block type the driver embeds the JEDEC device IDs in it. This limits the number of supported devices to the ones that are registered in it, requiring verification that the device in use is listed in the registry.

8.11.2   Supporting Other MTDs

If you are not using a CFI-compliant MTD, the following MTDs are also provided.

Intel 28F016 Flash Support

The MTD defined in i28f016.c supports Intel 28F016SA and Intel 28F008SV flash components. Any flash array or card based on these chips is recognized and supported by this MTD. This MTD also supports interleaving factors of 2 and 4 for BYTE-mode 28F016 component access.

For WORD-mode component access, only non-interleaved (interleave 1) mode is supported. The list of supported flash media includes the following:

  • Intel Series-2+ PC Cards
  • M-Systems Series-2+ PC Cards

Define INCLUDE_MTD_I28F016 in your BSP's sysTffs.c file to include this MTD in TrueFFS for Tornado.

Intel 28F008 Flash Support

The MTD defined in I28F008.c supports the Intel 28F008SA, Intel 28F008SC, and Intel 28F016SA/SV (in 8-mbit compatibility mode) flash components. Any flash array or card based on these chips is recognized and supported by this MTD. However, the WORD-mode of 28F016SA/SV is not supported (BYTE-mode only). This MTD also supports all interleaving factors (1, 2, 4, ...). Interleaving of more than 4 is recognized, although the MTD does not access more than 4 flash parts simultaneously. The list of supported flash media includes the following:

  • M-Systems D-Series PC Cards
  • M-Systems S-Series PC Cards
  • Intel Series-2 (8-mbit family) PC Cards
  • Intel Series-2+ (16-mbit family) PC Cards
  • Intel Value Series 100 PC Cards
  • Intel Miniature cards
  • M-Systems PC-FD, PC-104-FD, Tiny-FD flash disks

Define INCLUDE_MTD_I28F008 in your BSP's sysTffs.c file to include this MTD in TrueFFS for Tornado.

AMD/Fujitsu Flash Support

The MTD defined in amdmtd.c (8-bit) supports AMD flash components of the AMD Series-C and Series-D flash technology family, as well as the equivalent Fujitsu flash components. The flash types supported are:

  • Am29F040 (JEDEC IDs 01a4h, 04a4h)
  • Am29F080 (JEDEC IDs 01d5h, 04d5h)
  • Am29LV080 (JEDEC IDs 0138h, 0438h)
  • Am29LV008 (JEDEC IDs 0137h, 0437h)
  • Am29F016 (JEDEC IDs 01adh, 04adh)
  • Am29F016C (JEDEC IDs 013dh, 043dh)

Any flash array or card based on these chips is recognized and supported by this MTD. The MTD supports interleaving factors of 1, 2, and 4. The list of supported flash media includes the following:

  • AMD and Fujitsu Series-C PC cards
  • AMD and Fujitsu Series-D PC cards
  • AMD and Fujitsu miniature cards

Define INCLUDE_MTD_AMD in your BSP's sysTffs.c file to include the 8-bit MTD in TrueFFS for Tornado.

8.11.3   Obtaining Disk On Chip Support

The previous demand for NAND devices has been in one of two forms: SSFDC/ Smart Media devices and Disk On Chip from M-Systems. Each of these is supported by a separate translation layer. To provide M-Systems the capability of adding Disk On Chip specific optimizations within the product that do not effect other supported devices, support for M-Systems devices must now be obtained directly from M-Systems and is no longer distributed with the Tornado product. This version of Tornado only supports NAND devices that conform to the SSFDC specification. 8.5.5 Including the Translation Layer.



8.12    Writing MTD Components

An MTD is a software module that provides TrueFFS with data, and with pointers to the routines that it uses to program the flash memory. All MTDs must provide the following three routines: a write routine, an erase routine, and an identification routine. The MTD module uses an identification routine to evaluate whether the type of the flash device is appropriate for the MTD. If you are writing your own MTD, you need to define it as a component and register the identification routine.

For source code examples of MTDs, see the installDir/target/src/drv/tffs directory.

8.12.1   Writing the MTD Identification Routine

TrueFFS provides a flash structure in which information about each flash part is maintained. The identification process is responsible for setting up the flash structure correctly.


*      
NOTE: Many of the MTDs previously developed by M-Systems or Wind River are provided in source form as examples of how one might write an MTD (in installDir/target/src/drv/tffs). This section provides additional information about writing them.

In the process of creating a logical block device for a flash memory array, TrueFFS tries to match an MTD to the flash device. To do this, TrueFFS calls the identification routine from each MTD until one reports a match. The first reported match is the one taken. If no MTD reports a match, TrueFFS falls back on a default read-only MTD that reads from the flash device by copying from the socket window.

The MTD identification routine is guaranteed to be called prior to any other routine in the MTD. An MTD identification routine is of the following format:

FLStatus xxxIdentify(FLFlash vol) 

Within an MTD identify routine, you must probe the device to determine its type. How you do this depends on the hardware. If the type is not appropriate to this MTD, return failure. Otherwise, set the members of the FLFlash structure listed below.

The identification routine for every MTD must be registered in the mtdTable[ ] defined in installDir/target/src/drv/tffs/tffsConfig.c. Each time a volume is mounted, the list of identification routines is traversed to find the MTD suitable for the volume. This provides better service for hot-swap devices; no assumption is made about a previously identified device being the only device that will work for a given volume.

Device identification can be done in a variety of ways. If your device conforms to Joint Electronic Device Engineering Council (JEDEC) or Common Flash Interface (CFI) standards, you can use their identification process. You might want your MTD to identify many versions of the device, or just simply one.

Initializing the FLFLash Structure Members

At the end of the identification process, the ID routine needs to set all data elements in the FlFlash structure, except the socket member. The socket member is set by functions internal to TrueFFS. The FLFlash structure is defined in installDir/h/tffs/flflash.h. Members of this structure are described as follows:

type

The JEDEC ID for the flash memory hardware. This member is set by the MTD's identification routine.

erasableBlockSize

The size, in bytes, of an erase block for the attached flash memory hardware. This value takes interleaving into account. Thus, when setting this value in an MTD, the code is often of the form:

vol.erasableBlockSize = aValue * vol.interleaving;

Where aValue is the erasable block size of a flash chip that is not interleaved with another.

chipSize

The size (storage capacity), in bytes, of one of the flash memory chips used to construct the flash memory array. This value is set by the MTD, using your flFitInSocketWindow( ) global routine.

noOfChips

The number of flash memory chips used to construct the flash memory array.

interleaving

The interleaving factor of the flash memory array. This is the number of devices that span the data bus. For example, on a 32-bit bus we can have four 8-bit devices or two 16-bit devices.

flags

Bits 0-7 are reserved for the use of TrueFFS (it uses these flags to track things such as the volume mount state). Bits 8-15 are reserved for the use of the MTDs.

mtdVars

This field, if used by the MTD, is initialized by the MTD identification routine to point to a private storage area. These are instance-specific. For example, suppose you have an Intel RFA based on the I28F016 flash part; suppose you also have a PCMCIA socket into which you decide to plug a card that has the same flash part. The same MTD is used for both devices, and the mtdVars are used for the variables that are instance-specific, so that an MTD may be used more than once in a system.

socket

This member is a pointer to the FLSocket structure for your hardware device. This structure contains data and pointers to the socket layer functions that TrueFFS needs to manage the board interface for the flash memory hardware. The functions referenced in this structure are installed when you register your socket driver (see 8.10 Writing Socket Drivers). Further, because TrueFFS uses these socket driver functions to access the flash memory hardware, you must register your socket driver before you try to run the MTD identify routine that initializes the bulk of this structure.

map

A pointer to the flash memory map function, the function that maps flash into an area of memory. Internally, TrueFFS initializes this member to point to a default map function appropriate for all NOR (linear) flash memory types. This default routine maps flash memory through simple socket mapping. Flash should replace this pointer to the default routine with a reference to a routine that uses map-through-copy emulation.

read

A pointer to the flash memory read function. On entry to the MTD identification routine, this member has already been initialized to point to a default read function that is appropriate for all NOR (linear) flash memory types. This routine reads from flash memory by copying from a mapped window. If this is appropriate for your flash device, leave read unchanged. Otherwise, the MTD identify routine should update this member to point to a more appropriate function.

write

A pointer to the flash memory write function. Because of the dangers associated with an inappropriate write function, the default routine for this member returns a write-protect error. The MTD identification routine must supply an appropriate function pointer for this member.

erase

A pointer to the flash memory erase function. Because of the dangers associated with an inappropriate erase function, the default routine for this member returns a write-protect error. The MTD identification routine must supply an appropriate function pointer for this member.

setPowerOnCallback

A pointer to the function TrueFFS should execute after the flash hardware device powers up. TrueFFS calls this routine when it tries to mount a flash device. Do not confuse this member of FLFlash with the powerOnCallback member of the FLSocket structure. For many flash memory devices, no such function is necessary. However, this member is used by the MTD defined in installDir/target/src/drv/tffs/nfdc2048.c.

Return Value

The identification routine must return flOK or an appropriate error code defined in flbase.h. The stub provided is:

FLStatus myMTDIdentification 
    ( 
    FLFlash vol 
    ) 
    { 
    /* Do what is needed for identification */   
    
    /* If identification fails return appropriate error */ 
 
    return flOK; 
    }

After setting the members listed above, this function should return flOK.

Call Sequence

Upon success, the identification routine updates the FLFlash structure, which also completes the initialization of the FLSocket structure referenced within this FLFlash structure.

Figure 8-3:   Identifying an MTD for the Flash Technology

8.12.2   Writing the MTD Map Function

MTDs need to provide a map function only when a RAM buffer is required for windowing. No MTDs are provided for devices of this kind in this release. If the device you are using requires such support, you need to add a map function to your MTD and assign a pointer to it in FLFlash.map. The function takes three arguments, a pointer to the volume structure, a "card address", and a length field, and returns a void pointer.

static void FAR0 * Map  
    (FLFlash vol, 
     CardAddress address, 
     int length 
    ) 
    { 
    /* implement function */ 
    }

8.12.3   Writing the MTD Read, Write, and Erase Functions

Typically, your read, write, and erase functions should be as generic as possible. This means that they should:

  • Read, write, or erase only a character, a word, or a long word at a time.

  • Be able to handle an unaligned read or write.

  • Be able to handle a read, write, or erase that crosses chip boundaries.

When writing these functions, you probably want to use the MTD helper functions flNeedVpp( ), flDontNeedVpp( ), and flWriteProtected( ). The interfaces for these routines are as follows:

FLStatus  flNeedVpp(FLSocket vol) 
void  flDontNeedVpp(FLSocket vol) 
FLBoolean flWriteProtected(FLSocket vol)

Use flNeedVpp( ) if you need to turn on the Vpp (programming voltage) for the chip. Internally, flNeedVpp( ) bumps a counter, FLSocket.VppUsers, and then calls the function referenced in FLSocket.VppOn. After calling flNeedVpp( ), check its return status to verify that it succeeded in turning on Vpp.

When done with the write or erase that required Vpp, call flDontNeedVpp( ) to decrement the FLSocket.VppUsers counter. This FLSocket.VppUsers counter is part of a delayed-off system. While the chip is busy, TrueFFS keeps the chip continuously powered. When the chip is idle, TrueFFS turns off the voltage to conserve power. 3

Use flWriteProtected( ) to test that the flash device is not write protected. The MTD write and erase routines must not do any flash programming before checking that writing to the card is allowed. The boolean function flWriteProtected( ) returns TRUE if the card is write-protected and FALSE otherwise.

Read Routine

If the flash device can be mapped directly into flash memory, it is generally a simple matter to read from it. TrueFFS supplies a default function that performs a remap, and simple memory copy, to retrieve the data from the specified area. However, if the mapping is done through a buffer, you must provide your own read routine.

Write Routine

The write routine must write a given block at a specified address in flash. Its arguments are a pointer to the flash device, the address in flash to write to, a pointer to the buffer that must be written, and the buffer length. The last parameter is boolean, and if set to TRUE implies that the destination has not been erased prior to the write request. The routine is declared as static since it is only called from the volume descriptor. The stub provided is:

static FLStatus myMTDWrite 
    (  
    FLFlash vol, 
    CardAddress address, 
    const void FAR1 *buffer, 
    int length, 
    FLBoolean overwrite 
    ) 
    { 
    /* Write routine */ 
    return flOK; 
    }

The write routine must do the following:

  • Check to see if the device is write protected.

  • Turn on Vpp by calling flNeedVpp( ).

  • Always "map" the "card address" provided to a flashPtr before you write.

When implementing the write routine, iterate through the buffer in a way that is appropriate for your environment. If writes are permitted only on word or double word boundaries, check to see whether the buffer address and the card address are so aligned. Return an error if they are not.

The correct algorithms usually follow a sequence in which you:

  • Issue a "write setup" command at the card address.

  • Copy the data to that address.

  • Loop on the status register until either the status turns OK or you time out.

Device data sheets usually provide flow charts for this type of algorithm. AMD devices require an unlock sequence to be performed as well.

The write routine is responsible for verifying that what was written matches the content of the buffer from which you are writing. The file flsystem.h has prototypes of compare functions that can be used for this purpose.

Erase Routine

The erase routine must erase one or more contiguous blocks of a specified size. This routine is given a flash volume pointer, the block number of the first erasable block and the number of erasable blocks. The stub provided is:

Static FLStatus myMTDErase 
    (  
    FLFlash vol, 
    int     firstBlock, 
    int     numOfBlocks 
    ) 
    { 
    volatile UINT32 * flashPtr; 
    int                     iBlock; 
     
    if (flWriteProtected(vol.socket)) 
               return flWriteProtected; 
    for (iBlock = firstBlock; iBlock < iBlock + numOfBlocks; Iblock++) 
        { 
        flashPtr = vol.map (&vol, iBlock * vol.erasableBlockSize, 0); 
         
        /* Perform erase operation here */ 
         
        /* Verify if erase succeeded */ 
         
        /* return flWriteFault if failed*/ 
        } 
    return flOK; 
    }

As input, the erase can expect a block number. Use the value of the erasableBlockSize member of the FLFlash structure to translate this block number to the offset within the flash array.

8.12.4   Defining Your MTD as a Component

Once you have completed the MTD, you need to add it as a component to your system project. By convention, MTD components are named INCLUDE_MTD_someName; for example, INCLUDE_MTD_USR. You can include the MTD component either through the project facility or, for a command-line configuration and build, by defining it in the socket driver file, sysTffs.c.

Adding Your MTD to the Project Facility

In order to have the MTD recognized by the project facility, a component description of the MTD is required. To add your own MTD component to your system by using the project facility, edit the installDir\target\config\comps\vxworks\00tffs.cdf file to include it. MTD components are defined in that file using the following format:

Component INCLUDE_MTD_type { 
    NAME            name 
    SYNOPSIS        type devices 
    MODULES         filename.o 
    HDR_FILES       tffs/flflash.h tffs/backdrnd.h 
    REQUIRES        INCLUDE_TFFS \ 
                    INCLUDE_TL_type 
}

Once you define your MTD component in the 00tffs.cdf file, it appears in the project facility the next time you run Tornado.

Defining the MTD in the Socket Driver File

For a command-line configuration and build, you can include the MTD component simply by defining it in the socket driver file, sysTffs.c, as follows:

#define INCLUDE_MTD_USR 

Add your MTD definition to the list of those defined between the conditional clause, as described in nConditional Compilation. Then, define the correct translation layer for your MTD. If both translation layers are defined in the socket driver file, undefine the one you are not using. If both are undefined, define the correct one. For other examples, see the type-sysTffs.c files in installDir\target\src\drv\tffs\sockets.


*      
CAUTION: Be sure that you have the correct sysTffs.c file before changing the defines. For more information, see 8.10.1 Porting the Socket Driver Stub File.

8.12.5   Registering the Identification Routine

The identification routine for every MTD must be registered in the mtdTable[ ]. Each time a volume is mounted, TrueFFS searches this list to find an MTD suitable for the volume (flash device). For each component that has been defined for your system, TrueFFS executes the identification routine referenced in mtdTable[ ], until it finds a match to the flash device. The current mtdTable[ ] as defined in installDir/target/src/drv/tffs/tffsConfig.c is:

MTDidentifyRoutine mtdTable[] =         /* MTD tables */ 
    { 
#ifdef  INCLUDE_MTD_I28F016 
    i28f016Identify, 
#endif                    /* INCLUDE_MTD_I28F016 */ 
 
#ifdef  INCLUDE_MTD_I28F008 
    i28f008Identify, 
#endif                    /* INCLUDE_MTD_I28F008 */ 
 
#ifdef  INCLUDE_MTD_AMD 
    amdMTDIdentify, 
#endif                    /* INCLUDE_MTD_AMD */ 
 
#ifdef  INCLUDE_MTD_CDSN 
    cdsnIdentify, 
#endif                    /* INCLUDE_MTD_CDSN */ 
 
#ifdef  INCLUDE_MTD_DOC2 
    doc2Identify, 
#endif                    /* INCLUDE_MTD_DOC2 */ 
 
#ifdef  INCLUDE_MTD_CFISCS 
    cfiscsIdentify, 
#endif                    /* INCLUDE_MTD_CFISCS */ 
    };

If you write a new MTD, list its identification routine in mtdTable[ ]. For example:

#ifdef INCLUDE_MTD_USR 
    usrMTDIdenitfy, 
#endif                    /* INCLUDE_MTD_USR */

It is recommended that you surround the component name with conditional include statements, as shown above. The symbolic constants that control these conditional includes are defined in the BSP config.h file. Using these constants, your end users can conditionally include specific MTDs.

When you add your MTDs identification routine to this table, you should also add a new constant to the BSP's config.h.



8.13    Flash Memory Functionality

This section discusses flash memory functionality, and the ways in which it protects data integrity, extends the lifetime of the medium, and supports fault recovery.

8.13.1   Block Allocation and Data Clusters

As is required of a block device driver, TrueFFS maps flash memory into an apparently contiguous array of storage blocks, upon which a file system can read and write data. These blocks are numbered from zero to one less than the total number of blocks. Currently, the only supported file system is dosFs (see 5.2 MS-DOS-Compatible File System: dosFs).

Block Allocation Algorithm

To promote more efficient data retrieval, TrueFFS uses a flexible allocation strategy, which clusters related data into a contiguous area in a single erase unit. These clusters might be, for example, the blocks that comprise the sectors of a file. TrueFFS follows a prioritized procedure for attempting to cluster the related data. In this order:

  1. First, it tries to maintain a pool of physically consecutive free blocks that are resident in the same erase unit.
  1. If that fails, it then tries to assure that all the blocks in the pool reside in the same erase unit.
  1. If that fails, it finally tries to allocate a pool of blocks in the erase unit that has the most space available.

Benefits of Clustering

Clustering related data in this manner has several benefits, listed and described below.

  • Allows Faster Retrieval Times.  

For situations that require TrueFFS to access flash through a small memory window, clustering related data minimizes the number of calls needed to map physical blocks into the window. This allow faster retrieval times for files accessed sequentially.

  • Minimizes Fragmentation. .  

Clustering related data cuts down on fragmentation because deleting a file tends to free up complete blocks that can be easily reclaimed.

  • Speeds Garbage Collection.  

Minimizing fragmentation means that garbage collection is faster.

  • Localizes Static File Blocks. .  

Localizing blocks that belong to static files significantly facilitates transferring these blocks when the wear-leveling algorithm decides to move static areas.

8.13.2   Read and Write Operations

One of the characteristics of flash memory that differs considerably from the more common magnetic-medium mechanical disks is the way in which it writes new data to the medium. When using traditional magnetic storage media, writing new data to a previously written storage area simply overwrites the existing data, essentially obliterating it; whereas flash does not. This section describes how flash reads from, and writes to, memory.

Reading from Blocks

Reading the data from a block is straightforward. The file system requests the contents of a particular block. In response, TrueFFS translates the block number into flash memory coordinates, retrieves the data at those coordinates, and returns the data to the file system.

Writing to Previously Unwritten Blocks

Writing data to a block is straightforward, if the target block is previously unwritten. TrueFFS translates the block number into flash memory coordinates and writes to the location. However, if the write request seeks to modify the contents of a previously written block, the situation is more complex.

If any write operation fails, TrueFFS attempts a second write. For more information, see Recovering During a Write Operation.


*      
NOTE: Storing data in flash memory requires the use of a manufacturer-supplied programming algorithm, which is defined in the MTD. Consequently, writing to flash is often referred to as programming flash.

Writing to Previously Written Blocks

If the write request is to an area of flash that already contains data, TrueFFS finds a different, writable area of flash instead--one that is already erased and ready to receive data. TrueFFS then writes the new data to that free area. After the data is safely written, TrueFFS updates its block-to-flash mapping structures, so that the block now maps to the area of flash that contains the modified data. This mapping information is protected, even during a fault. For more information on fault recovery and mapping information, see Recovering Mapping Information.

8.13.3   Erase Cycles and Garbage Collection

The write operation is intrinsically linked to the erase operation, since data cannot be "over-written." Data must be erased, and then those erased units must be reclaimed before they are made available to a write operation. This section describes that process, as well as the consequences of over-erasing sections of flash.

Erasing Units

Once data is written to an area of flash memory, modifying blocks leaves behind block-sized regions of flash memory that no longer contain valid data. These regions are also unwritable until erased. However, the erase cycle does not operate on individual bytes or even blocks. Erasure is limited to much larger regions called erase units. The size of these erase units depends on the specific flash technology, but a typical size is 64 KB.

Reclaiming Erased Blocks

To reclaim (erase) blocks that no longer contain valid data, TrueFFS uses a mechanism called garbage collection. This mechanism copies all the valid data blocks, from a source erase unit, into another erase unit known as a transfer unit. TrueFFS then updates the block-to-flash map and afterward erases the old (erase) unit. The virtual block presented to the outside world still appears to contain the same data even though that data now resides in a different part of flash.

For details on the algorithm used to trigger garbage collection, see Garbage Collection. For information about garbage collection and fault recovery, see Recovering During Garbage Collection.

Over-Programming

As a region of flash is constantly erased and rewritten, it enters an over-programmed state, in which it responds only very slowly to write requests. With rest, this condition eventually fixes itself, but the life of the flash memory is shortened. Eventually the flash begins to suffer from sporadic erase failures, which become more and more frequent until the medium is no longer erasable and, thus, no longer writable.4 Consequently, flash limits how often you can erase and rewrite the same area. This number, known as the cycling limit, depends on the specific flash technology, but it ranges from a hundred thousand to a million times per block.5

8.13.4   Optimization Methods

As mentioned, flash memory is not an infinitely reusable storage medium. The number of erase cycles per erase unit of flash memory is large but limited. Eventually, flash degrades to a read-only state. To delay this as long as possible, TrueFFS uses a garbage collection algorithm that works in conjunction with a technique called wear leveling.

Wear Leveling

One way to alleviate over-programming is to balance usage over the entire medium, so that the flash is evenly worn and no one part is over-used. This technique is known as wear leveling and it can extend the lifetime of the flash significantly. To implement wear leveling, TrueFFS uses a block-to-flash translation system that is based on a dynamically maintained map. This map is adjusted as blocks are modified, moved, or garbage collected.

Static-File Locking

By remapping modified blocks to new flash memory coordinates, a certain degree of wear leveling occurs. However, some of the data stored in flash may be essentially static, which means that if wear leveling only occurs during modifications, the areas of flash that store static data are not cycled at all. This situation, known as static file locking, exacerbates the wear on other areas of flash, which must be recycled more frequently as a consequence. If not countered, this situation can significantly lowers the medium's life-expectancy.

TrueFFS overcomes static file locking by forcing transfers of static areas. Because the block-to-flash map is dynamic, TrueFFS can manage these wear-leveling transfers in a way that is invisible to the file system.

Algorithm

Implementing absolute wear leveling can have a negative impact on performance because of all the required data moves. To avoid this performance hit, TrueFFS implements a wear-leveling algorithm that is not quite absolute. This algorithm provides an approximately equal number of erase cycles per unit. In the long run, you can expect the same number of erase cycles per erase unit without a degradation of performance. Given the large number of allowed erase cycles, this less than absolute approach to wear leveling is good enough.

Dead Locks

Finally, the TrueFFS wear-leveling algorithm is further enhanced to break a failure mode known as dead locks. Some simple wear-leveling algorithms have been shown to "flip-flop" transfers between only two or more units for very long periods, neglecting all other units. The wear-leveling mechanism used by TrueFFS makes sure that such loops are curbed. For optimal wear-leveling performance, TrueFFS requires at least 12 erase units.

Garbage Collection

Garbage collection, described in Reclaiming Erased Blocks, is used by the erase cycle to reclaim erased blocks. However, if garbage collection is done too frequently, it defeats the wear-leveling algorithm and degrades the overall performance of the flash disk. Thus, garbage collection uses an algorithm that relies on the block allocation algorithm (Block Allocation Algorithm), and is triggered only as needed.

The block allocation algorithm maintains a pool of free consecutive blocks that are resident in the same erase unit. When this pool becomes too small, the block allocation algorithm launches the garbage collection algorithm, which then finds and reclaims an erase unit that best matches the following criteria:

  • the largest number of garbage blocks
  • the least number of erase cycles
  • the most static areas

In addition to these measurable criteria, the garbage collection algorithm also factors in a random selection process. This helps guarantee that the reclamation process covers the entire medium evenly and is not biased due to the way applications use data.

8.13.5   Fault Recovery in TrueFFS

A fault can occur whenever data is written to flash; thus, for example, a fault can occur:

  • In response to a write request from the file system.
  • During garbage collection.
  • During erase operations.
  • During formatting.

TrueFFS can recover from the fault in all cases except when new data is being written to flash for the first time. This new data will be lost. However, once data is safely written to flash, it is essentially immune to power failures. All data already resident in flash is recoverable, and the file and directory structures of the disk are retained. In fact, the negative consequence of a power interruption or fault is the need to restart any incomplete garbage collection operation. This section describes fault occurrence and fault recovery in TrueFFS.

Recovering During a Write Operation

A write or erase operation can fail because of a hardware problem or a power failure. As mentioned in above, TrueFFS uses an "erase after write" algorithm, in which the previous data is not erased until after the update operation has successfully completed. To prevent the possible loss of data, TrueFFS monitors and verifies the success of each write operation, using a register in which the actual written data is read back and compared to the user data. Therefore, a data sector cannot be in a partially written state. If the operation completes, the new sector is valid; if the update fails, the old data is not lost or in any way corrupted.

TrueFFS verifies each write operation, and automatically attempts a second write to a different area of flash after any failure. This ensures the integrity of the data by making failure recovery automatic. This write-error recovery mechanism is especially valuable as the flash medium approaches its cycling limit (end-of-life). At that time, flash write/erase failures become more frequent, but the only user-observed effect is a gradual decline in performance (because of the need for write retries).

Recovering Mapping Information

TrueFFS stores critical mapping information in flash-resident memory, thus it is not lost during an interruption such as a power loss or the removal of the flash medium. TrueFFS does, however, use a RAM-resident mapping table to track the contents of flash memory. When power is restored or the medium reconnected, the the RAM-resident version of the flash mapping table is reconstructed (or verified) from flash-resident information.


*      
NOTE: Mapping information can reside anywhere on the medium.However, each erase unit in flash memory maintains header information at a predictable location. By carefully cross-checking the header information in each erase unit, TrueFFS is able to rebuild or verify a RAM copy of the flash mapping table.

Recovering During Garbage Collection

After a fault, any garbage collection in process at the time of the failure must be restarted. Garbage area is space that is occupied by sections that have been deleted by the host. TrueFFS reclaims garbage space by first moving data from one transfer unit to another, and then erasing the original unit.

If consistent flash failures prevent the necessary write operations to move data, or if it is not possible to erase the old unit, the garbage collection operation fails. To minimize a failure of the write part of the transfer, TrueFFS formats the flash medium to contain more than one transfer unit. Thus, if the write to one transfer unit fails, TrueFFS retries the write using a different transfer unit. If all transfer units fail, the medium no longer accepts new data and becomes a read-only device. This does not, however, have a direct effect on the user data, all of which is already safely stored.

Recovering During Formatting

In some cases, sections of the flash medium are found to be unusable when flash is first formatted. Typically, this occurs because those sections are not erasable. As long as the number of bad units does not exceed the number of transfer units, the medium is considered usable as a whole and can be formatted. The only noticeable adverse effect is the reduced capacity of the formatted flash medium.


1:  Meaning that they have no moving parts.

2:  TrueFFS only supports a maximum of 5 drives.

3:  An MTD does not need to touch Vcc. TrueFFS turns Vcc on before calling an MTD function.

4:  The data already resident is still readable.

5:  This number is merely statistical in nature and should not be taken as an exact figure.