changeset 2935:3c14144a4c7a

Add support for counting block erase and page write operations, to monitor wear levelling.
author bartv
date Fri, 31 Jul 2009 17:01:34 +0000
parents b190dd0f03b1
children aab00220ea52
files packages/devs/nand/synth/current/ChangeLog packages/devs/nand/synth/current/doc/synth_nand.sgml packages/devs/nand/synth/current/src/nand_synth.c
diffstat 3 files changed, 77 insertions(+), 19 deletions(-) [+]
line wrap: on
line diff
--- a/packages/devs/nand/synth/current/ChangeLog
+++ b/packages/devs/nand/synth/current/ChangeLog
@@ -1,3 +1,8 @@
+2009-07-31  Bart Veer  <bartv@ecoscentric.com>
+
+	* doc/synth_nand.sgml, src/nand_synth.c: add support for per-block
+	erase counts and per-page write counts to monitor wear-levelling.
+
 2009-07-14  Bart Veer  <bartv@ecoscentric.com>
 
 	* host/nand.tcl (dialog): avoid the use of max() for compatibility
--- a/packages/devs/nand/synth/current/doc/synth_nand.sgml
+++ b/packages/devs/nand/synth/current/doc/synth_nand.sgml
@@ -472,14 +472,14 @@ selected settings.
 
   <refsect1 id="devs-nand-synth-file-formats"><title>File Formats</title>
     <para>
-A NAND image is a binary file with four sections: a 64-byte header
-block; an array of the factory-bad blocks; a bitmap of the good and
-bad blocks; and the actual data. The header consists of the following
-data structure:
+A NAND image is a binary file with six sections: a 64-byte header
+block; an array of erase counts; an array of write counts; an array of
+the factory-bad blocks; a bitmap of the good and bad blocks; and the
+actual data. The header consists of the following data structure:
     </para>
     <programlisting width=72>
 struct header {
-  cyg_uint32 magic;           // 0xEC05A11E
+  cyg_uint32 magic;           // 0xEC05A11F
   cyg_uint32 page_size;       // CYGNUM_DEVS_NAND_SYNTH_PAGESIZE
   cyg_uint32 spare_size;      // CYGNUM_DEVS_NAND_SYNTH_SPARE_PER_PAGE
   cyg_uint32 pages_per_block; // CYGNUM_DEVS_NAND_SYNTH_PAGES_PER_BLOCK
@@ -490,20 +490,27 @@ struct header {
 };
     </programlisting>
     <para>
-All integers are stored in bigendian format.
-The <structfield>magic</structfield> field is used to check that a
-file really holds a NAND image. The next four fields specify the
-emulated device size and layout, as per the corresponding CDL options.
-The <structfield>tv_sec</structfield>
-and <structfield>tv_usec</structfield> fields are filled in with
-the result of a <function>gettimeofday()</function> system call during
+All integers in the header and in the following three sections are
+stored in bigendian format. The <structfield>magic</structfield> field
+is used to check that a file really holds a NAND image. The next four
+fields specify the emulated device size and layout, as per the
+corresponding CDL options. The <structfield>tv_sec</structfield> and
+<structfield>tv_usec</structfield> fields are filled in with the
+result of a <function>gettimeofday()</function> system call during
 driver initialization. These fields also appear in logfiles, so code
 can check that a logfile and an image file correspond to the same test
 run.
     </para>
     <para>
-The array of factory-bad blocks consists of 32 integers, again in
-bigendian format, holding the settings of the target definition's file
+Following the header is an array of BLOCK_COUNT integers holding the
+number of erase calls for each block. Next is an array of (BLOCK_COUNT
+&ast; PAGES_PER_BLOCK) integers holding the number of write calls for
+each page. These arrays can be used to check that higher-level code is
+performing wear levelling.
+    </para>
+    <para>
+The array of factory-bad blocks consists of 32 integers holding the
+settings of the target definition's file
 <varname>factory_bad</varname> setting at the time that the image file
 was created.
     </para>
--- a/packages/devs/nand/synth/current/src/nand_synth.c
+++ b/packages/devs/nand/synth/current/src/nand_synth.c
@@ -90,6 +90,8 @@ static struct cyg_hal_sys_timeval       
 // The current image file.
 static int              image_fd    = -1;
 static int*             image_header;
+static int*             image_erase_counts;
+static int*             image_write_counts;
 static int*             image_factory_bads;
 static unsigned char*   image_ok_blocks;
 static unsigned char*   image_data;
@@ -97,7 +99,11 @@ static unsigned char*   image_data;
 // Various size fields.
 #define HEADER_SIZE     64
 #define DATA_SIZE       (BLOCK_COUNT * PAGES_PER_BLOCK * (PAGESIZE + SPARE_PER_PAGE))
-#define IMAGE_SIZE      (HEADER_SIZE + (MAX_FACTORY_BAD * 4) + (BLOCK_COUNT / 8) + DATA_SIZE)
+#define IMAGE_SIZE      (HEADER_SIZE +                                      \
+                         (BLOCK_COUNT * sizeof(int)) +                      \
+                         (BLOCK_COUNT * PAGES_PER_BLOCK * sizeof(int)) +    \
+                         (MAX_FACTORY_BAD * sizeof(int)) +                  \
+                         (BLOCK_COUNT / 8) + DATA_SIZE)
 
 // For interacting with the I/O auxiliary. synth_buf can be used for
 // other things.
@@ -997,7 +1003,7 @@ erase_check_injections(cyg_nand_block_ad
 //
 // The file format is as follows:
 //   A 64-byte parameter block containing:
-//     A magic number 0xEC05A11E
+//     A magic number 0xEC05A11F
 //     Page size
 //     Spare (OOB) size
 //     Pages per block
@@ -1005,6 +1011,10 @@ erase_check_injections(cyg_nand_block_ad
 //     timeval.tv_sec
 //     timeval.tv_usec
 //     Spare fields.
+//   Erase counts
+//     Number_of_blocks integers
+//   Write counts
+//     (Number_of_blocks * Pages_per_block) integers
 //   Factory-bad blocks.
 //     32 integers. -1 indicates no entry.
 //   An array of good/bad blocks
@@ -1014,7 +1024,7 @@ erase_check_injections(cyg_nand_block_ad
 //       All pages without padding
 //         The data area followed by the spare area.
 
-#define NAND_SYNTH_MAGIC        0xec05a11d
+#define NAND_SYNTH_MAGIC        0xec05a11F
 
 
 static int
@@ -1057,6 +1067,19 @@ open_image(void)
             diag_printf("NAND synth_devinit: error, failed to write header to image file %s\n", IMAGE_FILENAME);
             return -EIO;
         }
+        // And the erase count and page write count arrays
+        memset(synth_buf, 0x00, SYNTH_BUFSIZE);
+        to_write    = BLOCK_COUNT * (PAGES_PER_BLOCK + 1) * sizeof(int);
+        while (to_write > 0) {
+            int this_write = (to_write > SYNTH_BUFSIZE) ? SYNTH_BUFSIZE : to_write;
+            written = cyg_hal_sys_write(image_fd, synth_buf, this_write);
+            if (written != this_write) {
+                cyg_hal_sys_close(image_fd);
+                diag_printf("NAND synth_devinit: error, failed to write counters to image file %s\n", IMAGE_FILENAME);
+                return -EIO;
+            }
+            to_write -= written;
+        }
 
         // And the factory-bad blocks.
         memset(synth_buf, 0x00FF, SYNTH_BUFSIZE);
@@ -1200,10 +1223,20 @@ open_image(void)
         return -EIO;
     }
     image_header        = (int*) i;
-    image_factory_bads  = (int*) (i + HEADER_SIZE);
-    image_ok_blocks     = (unsigned char*) (i + HEADER_SIZE + MAX_FACTORY_BAD * 4);
+    image_erase_counts  = (int*) (i + HEADER_SIZE);
+    image_write_counts  = image_erase_counts + BLOCK_COUNT;
+    image_factory_bads  = image_write_counts + (BLOCK_COUNT * PAGES_PER_BLOCK);
+    image_ok_blocks     = (unsigned char*) (image_factory_bads + MAX_FACTORY_BAD);
     image_data          = image_ok_blocks + (BLOCK_COUNT / 8);
 
+#if 0
+    diag_printf("Image header    @ %p\n", image_header);
+    diag_printf("Erase counts    @ %p\n", image_erase_counts);
+    diag_printf("Write counts    @ %p\n", image_write_counts);
+    diag_printf("Factory-bads    @ %p\n", image_factory_bads);
+    diag_printf("OK block bitmap @ %p\n", image_ok_blocks);
+    diag_printf("Image data      @ %p\n", image_data);   
+#endif    
     // Final sanity check. Make sure that all the factory bad blocks are marked bad.
     for (i = 0; i < MAX_FACTORY_BAD; i++) {
         int bad = CYG_BE32_TO_CPU(image_factory_bads[i]);
@@ -1429,6 +1462,7 @@ synth_writepage(cyg_nand_device *dev, cy
     int     i;
     int     idx;
     int     page_gone_bad   = 0;
+    int     counter;
     
     CYG_ASSERTC(image_fd >= 0);
     CYG_ASSERTC(dev != NULL);
@@ -1485,6 +1519,12 @@ synth_writepage(cyg_nand_device *dev, cy
         }
     }
     if (0 == result) {
+        counter                     = CYG_BE32_TO_CPU(image_write_counts[page]);
+        counter                    += 1;
+        image_write_counts[page]    = CYG_CPU_TO_BE32(counter);
+    }
+    
+    if (0 == result) {
         page_gone_bad = write_check_injections(page);
 
         if (src && (size > 0)) {
@@ -1540,6 +1580,7 @@ synth_eraseblock(cyg_nand_device *dev, c
     int     result  = 0;
     size_t  offset;
     int     block_gone_bad  = 0;
+    int     counter;
 
     CYG_ASSERTC(image_fd >= 0);
     CYG_ASSERTC(dev != NULL);
@@ -1561,6 +1602,11 @@ synth_eraseblock(cyg_nand_device *dev, c
         result = -EIO;
     }
     if (0 == result) {
+        counter                  = CYG_BE32_TO_CPU(image_erase_counts[blk]);
+        counter                 += 1;
+        image_erase_counts[blk]  = CYG_CPU_TO_BE32(counter);
+    }
+    if (0 == result) {
         block_gone_bad = erase_check_injections(blk);
         memset(&(image_data[offset]), 0x00FF, PAGES_PER_BLOCK * (PAGESIZE + SPARE_PER_PAGE));
         if (block_gone_bad) {