Chapter 3 - Azure RTOS LevelX NAND support
NAND flash memory is commonly utilized for large data storage, which is typical of file systems. NAND memory consists of blocks. Within each NAND block is a series of pages. NAND blocks are erasable, which means that all pages within the NAND block are erased (set to all ones). Each NAND block page has a set of spare bytes that are utilized by Azure RTOS LevelX for bookkeeping, bad block management, and error detection. NAND block pages are available in a variety of sizes. The most common page sizes are:
| Page Size | Spare Bytes |
|---|---|
| 256 | 8 |
| 512 | 16 |
| 2048 | 64 |
NAND memory differs from NOR memory in that there is no direct access, i.e., NAND memory cannot be read directly from the processor like NOR memory. NAND memory can only be written to after an erase a limited number of times. Again, this differs from NOR memory that can be written an unlimited number of times providing the write request is clearing set bits. Finally, the spare bytes associated with each page are unique to NAND flash. Typical spare byte configurations are as shown in the table below.
| Spare Bytes | Byte numbers | Configuration |
|---|---|---|
| 8 | Bytes 0-2: | ECC bytes |
| Bytes 3,4,6,7: | LevelX Sector Mapping | |
| Byte 5: | Bad block flag | |
| 16 | Bytes 0-3,6-7: | ECC bytes |
| Bytes 8-11: | LevelX Sector Mapping | |
| Bytes 12-15: | Unused | |
| Byte 5: | Bad block flag | |
| 64 | Byte 0: | Bad block flag |
| Bytes 2-5: | LevelX Sector Mapping | |
| Bytes 6-39: | Unused | |
| Bytes 40-63: | ECC bytes |
LevelX Utilizes 4 of the spare bytes of each NAND page for keeping track of the logical sector mapped to the physical NAND page. These 4 bytes are used to implement a 32-bit unsigned integer with a LevelX proprietary format. The upper bit of the 32-bit field (bit 31) is used to indicate the logical sector-to-page mapping is valid. If this bit is 0, the information in this page is no longer valid. The next bit—bit 30—is used to indicate this page is in the process of becoming obsolete and a new sector is being written. Bit 29 is used to indicate when the mapping entry write is complete. If bit 29 is 0, the mapping entry write is complete. If bit 29 is set, the mapping entry was in the process of being written. Bits 30 and 29 are used in recovering from a potential power loss while updating a new flash page. Finally, the lower 29-bits (28-0) contain the logical sector number for the page.
LevelX Mapping Entry
| Bit(s) | Meaning |
|---|---|
| 31 | Valid flag. When set and logical sector is not all ones indicates mapping is valid |
| 30 | Obsolete flag. When clear, this mapping is either obsolete or is in the process of becoming obsolete. |
| 29 | Mapping entry write is complete when this bit is 0 |
| 0-28 | Logical sector mapped to this physical page—when not all ones. |
LevelX also utilizes the first page of each NAND block for the block erase count as well as the list of mapped pages when the block is full. The format of the first page of a NAND block in LevelX is shown below:
| LevelX Block Page 0 Format |
|---|
| [Block Erase Count] |
| [Page 1 Sector Mapping] |
| ... |
| [Page "n" Sector Mapping] |
| [0xF0F0F0F0] |
Note
The page mapping information is only written when the block is full, i.e., all the pages of the block have been written to. This enables faster search for free pages and logical sector mapping during run-time.
NAND Bad Block Support
NAND memory is also more likely to have bad blocks than NOR memory. This is largely because NAND manufacturers can increase yield by allowing bad blocks and requiring software to work-around such bad blocks. LevelX handles NAND bad block management by simply mapping around bad blocks.
LevelX also provides APIs for 256-byte Hamming Error Correction Codes (ECC) for the underlying LevelX driver to utilize for calculating new ECC codes or to perform 1-bit error correction on page reading within each 256-byte section of the page.
NAND Driver Requirements
LevelX requires an underlying NAND flash driver that is specific to the underlying flash part and hardware implementation. The driver is specified to LevelX during initialization via the API lx_nand_flash_open. The prototype of the LevelX driver is as follows.
INT nand_driver_initialize(LX_NAND_FLASH *instance);
The instance parameter specifies the LevelX NAND control block. The driver initialization function is responsible for setting up all the other driver-level services for the associated LevelX instance. The services required for each LevelX NAND instance are shown in the list below.
- Read Page
- Write Page
- Block Erase
- Block Erased Verify
- Page Erased Verify
- Block Status Get
- Block Status Set
- Block Extra Bytes Get
- Block Extra Bytes Set
- System Error Handler
Driver Initialization
These services are setup via setting function pointers in the LX_NAND_FLASH instance within the driver's initialization function. The driver initialization function also specifies the total number of block, pages per block, bytes per page, and a RAM area large enough to read one page into memory. The driver initialization function likely also performs additional device and/or implementation-specific initialization duties before returning LX_SUCCESS.
Driver Read Page
The LevelX NAND driver "read page" service is responsible for reading a specific page in a specific block of the NAND flash. All error checking and correcting logic is the responsibility of the driver service. If successful, the LevelX NAND driver returns LX_SUCCESS. If not successful, the LevelX NAND driver returns LX_ERROR. The prototype of the LevelX NAND driver "read page" service is given below.
INT nand_driver_read_page(
ULONG block,
ULONG page,
ULONG *destination,
ULONG words);
Where block and page identify which page to read and destination and words specify where to place the page contents and how many 32-bit words to read.
Driver Write Page
The LevelX NAND driver "write page" service is responsible for writing a specific page into the specified block of the NAND flash. All error checking and ECC computation is the responsibility of the driver service. If successful, the LevelX NAND driver returns LX_SUCCESS. If not successful, the LevelX NAND driver returns LX_ERROR. The prototype of the LevelX NAND driver "write page" service is shown below.
INT nand_driver_write_page(
ULONG block,
ULONG page,
ULONG *source,
ULONG words);
Where block and page identify which page to write and source and words specify the source of the write and how many 32-bit words to write.
Note
LevelX relies on the driver for low-level error detection when writing to the flash page, which typically involves reading back the page and comparing with the write buffer to ensure the write was successful.
Driver Block Erase
The LevelX NAND driver "block erase" service is responsible for erasing the specified block of the NAND flash. If successful, the LevelX NAND driver returns LX_SUCCESS. If not successful, the LevelX NAND driver returns LX_ERROR. The prototype of the LevelX NAND driver "block erase" service is as follows.
INT nand_driver_block_erase(ULONG block,
ULONG erase_count);
Where block identifies which block to erase. The parameter erase_count is provided for diagnostic purposes. For example, the driver may want to alert another portion of the application software when the erase count exceeds a specific threshold.
Note
LevelX relies on the driver for low-level error detection when the block is erased, which typically involves ensuring that all pages of the block are all ones.
Driver Block Erased Verify
The LevelX NAND driver "block erased verify" service is responsible for verifying that the specified block of the NAND flash is erased. If it is erased, the LevelX NAND driver returns LX_SUCCESS. If the block is not erased, the LevelX NAND driver returns LX_ERROR. The prototype of the LevelX NAND driver "block erased verify" service is:
INT nand_driver_block_erased_verify(ULONG block);
Where block specifies which block to verify that it is erased.
Note
LevelX relies on the driver to examine all pages and all bytes of each page – including spare and data bytes – to ensure they are erased (contain all ones).
Driver Page Erased Verify
The LevelX NAND driver "page erased verify" service is responsible for verifying that the specified page of the specified block of the NAND flash is erased. If it is erased, the LevelX NAND driver returns LX_SUCCESS. If the page is not erased, the LevelX NAND driver returns LX_ERROR. The prototype of the LevelX NAND driver "page erased verify" service is:
INT nand_driver_page_erased_verify(
ULONG block,
ULONG page);
Where block specifies which block and page specifies the page to verify that it is erased.
Note
LevelX relies on the driver to examine all bytes of the specified page – including spare and data bytes – to ensure they are erased (contain all ones).
Driver Block Status Get
The LevelX NAND driver "block status get" service is responsible for retrieving the bad block flag of the specified block of the NAND flash. If it is successful, the LevelX NAND driver returns LX_SUCCESS. If it is not successful, the LevelX NAND driver returns LX_ERROR. The prototype of the LevelX NAND driver "block status get" service is: shown below.
INT nand_driver_block_status_get(
ULONG block,
UCHAR *bad_block_byte);
Where block specifies which block and bad_block_byte specifies the destination for the bad block flag.
Driver Block Status Set
The LevelX NAND driver "block status set" service is responsible for setting the bad block flag of the specified block of the NAND flash. If it is successful, the LevelX NAND driver returns LX_SUCCESS. If it is not successful, the LevelX NAND driver returns LX_ERROR. The prototype of the LevelX NAND driver "block status set" service is:
INT nand_driver_block_status_set(
ULONG block,
UCHAR bad_block_byte);
Where block specifies which block and bad_block_byte specifies the value of the bad block flag.
Driver Block Extra Bytes Get
The LevelX NAND driver "block extra bytes get" service is responsible for retrieving extra bytes associated with a specific page of a specific block of the NAND flash. If it is successful, the LevelX NAND driver returns LX_SUCCESS. If it is not successful, the LevelX NAND driver returns LX_ERROR. The prototype of the LevelX NAND driver "block extra bytes get" service is:
INT nand_driver_block_extra_bytes_get(
ULONG block,
ULONG page,
UCHAR *destination,
UINT size);
Where block specifies which block, page specifies the specific page and destination specifies the destination for the extra bytes. The parameter size specifies how many extra bytes to get.
Driver Block Extra Bytes Set
The LevelX NAND driver "block extra bytes set" service is responsible for setting extra bytes in a specific page of a specific block of the NAND flash. If it is successful, the LevelX NAND driver returns LX_SUCCESS. If it is not successful, the LevelX NAND driver returns LX_ERROR. The prototype of the LevelX NAND driver "block extra bytes set" service is:
INT nand_driver_block_extra_bytes_set(
ULONG block,
ULONG page,
UCHAR *source,
UINT size);
Where block specifies which block, page specifies the specific page and source specifies the source of the extra bytes. The parameter size specifies how many extra bytes to set.
Driver System Error
The LevelX NAND driver "system error handler" service is responsible for setting handling system errors detected by LevelX. The processing in this routine is application dependent. If it is successful, the LevelX NAND driver returns LX_SUCCESS. If it is not successful, the LevelX NAND driver returns LX_ERROR. The prototype of the LevelX NAND driver "system error" service is:
INT nand_driver_system_error(
UINT error_code,
ULONG block,
ULONG page);
Where block specifies which block, and page specifies the specific page the error represented by error_code occurred.
NAND Simulated Driver
LevelX provides a simulated NAND flash driver that simply uses RAM to simulate the operation of a NAND flash part. By default, the NAND simulated driver provides 8 NAND flash blocks with 16 pages per block and 2048 bytes per page.
The simulated NAND flash driver initialization function is lx_nand_flash_simulator_initialize and is defined in lx_nand_flash_simulator.c. This driver also provides a good template for writing specific NAND flash drivers.
NAND FileX Integration
As mentioned earlier, LevelX does not rely on FileX for operation. All the LevelX APIs may be called directly by the application software to store/retrieve raw data to the logical sectors provided by LevelX. However, LevelX also supports FileX.
The file fx_nand_flash_simulated_driver.c contains an example FileX driver for use with the NAND flash simulation. An interesting aspect of this driver is that it combines 512-byte logical sectors typically used by FileX into single logical sector read/write requests to the LevelX simulator using 2048-byte pages. This results in more efficient use of the NAND flash memory. The NAND flash FileX driver for LevelX provides a good starting point for writing custom FileX drivers.
Note
The FileX NAND flash format should be one full block size of sectors less than the NAND flash provides. This will help ensure best performance during the wear level processing. Additional techniques to improve write performance in the LevelX wear leveling algorithm include the following.
- Ensure that all writes are exactly one or more clusters in size and start on exact cluster boundaries.
- Pre-allocate clusters before performing large file write operations via the FileX fx_file_allocate class of APIs.
- Ensure the FileX driver is enabled to receive release sector information and requests made to the driver to release sectors are handled in the driver by calling lx_nor_flash_sector_release.
- Periodic use of lx_nand_flash_defragment to free up as many NAND blocks as possible and thus improve write performance.
- Utilize the lx_nand_flash_extended_cache_enable API to provide a RAM cache of various NAND block resources for faster performance.