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.
|
|
|||||||||||||||||||
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.
|
||||||||||||||||||
|
|
|||||||||||||||||||
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.
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.
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.
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.
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.
|
|
|||||||||||||||||||
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.
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.
|
||||||||||||||||||
Mount the VxWorks DOS file system on a TrueFFS flash drive. For details, see 8.8 Mounting the Drive.
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.
|
||||||||||||||||||
The directory installDir/target/src/drv/tffs contains the source code for the following types of MTD components:
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.
|
||||||||||||||||||
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.
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:
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.
|
|
|||||||||||||||||||
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.
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.
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.
|
|
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).
|
||||||||||||||||||
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:
|
|
|||||||||||||||||||
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.
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.
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.
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.
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);
|
|
|||||||||||||||||||
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.
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.
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.
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.
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.
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
|
|
|||||||||||||||||||
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.
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.
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.
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.
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)¶ms);
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( ).
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.
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 */ )
|
|
|||||||||||||||||||
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 */ )
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.
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/"
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/"
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/"
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/"
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:
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.
|
|
|||||||||||||||||||
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.
#error "sysTffs: Verify system macros and function before first use"
|
|
|||||||||||||||||||
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.
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.
|
||||||||||||||||||
This routine always returns TRUE in RFA environments, since the device is not removable. Implementation is complete in the stub file.
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.
Vcc is assumed to be ON constantly in RFA environments. This routine is simply a wrapper and is complete in the stub file.
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.
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.
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.
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:
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.
|
|
|||||||||||||||||||
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.
Always return FALSE in RFA environments, since the device is not removable. This routine is complete in the stub file.
Socket drivers in TrueFFS are modeled after the PCMCIA socket services. As such, they must provide the following:
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.
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.
|
|
|||||||||||||||||||
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.
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:
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.
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.
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.
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.
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:
|
|
|||||||||||||||||||
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:
or can be found by searching for CFI at:
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.
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.
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:
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:
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:
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.
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.
|
|
|||||||||||||||||||
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.
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:
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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;
}
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 */
}
Typically, your read, write, and erase functions should be as generic as possible. This means that they should:
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.
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.
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;
}
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:
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.
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.
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.
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.
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.
|
||||||||||||||||||
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.
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).
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:
Clustering related data in this manner has several benefits, listed and described below.
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.
Clustering related data cuts down on fragmentation because deleting a file tends to free up complete blocks that can be easily reclaimed.
Minimizing fragmentation means that garbage collection is faster.
Localizing blocks that belong to static files significantly facilitates transferring these blocks when the wear-leveling algorithm decides to move static areas.
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.
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.
|
|
|||||||||||||||||||
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.
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.
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.
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.
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
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.
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.
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.
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.
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, 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:
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.
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).
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.
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.
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.
3: An MTD does not need to touch Vcc. TrueFFS turns Vcc on before calling an MTD function.
5: This number is merely statistical in nature and should not be taken as an exact figure.