changeset 2976:758f9d488949

NAND: Add timing instrumentation hooks (disabled by default)
author Ross Younger <wry@ecoscentric.com>
date Tue, 10 Nov 2009 18:25:39 +0000
parents 7050b2f79b6e
children 7a3f0b89a102
files packages/io/nand/current/ChangeLog packages/io/nand/current/cdl/nand.cdl packages/io/nand/current/misc/timetag_to_csv.pl packages/io/nand/current/src/nand.c
diffstat 4 files changed, 167 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/packages/io/nand/current/ChangeLog
+++ b/packages/io/nand/current/ChangeLog
@@ -1,16 +1,22 @@
+2009-11-12  Ross Younger  <wry@eCosCentric.com>
+
+	* nand.c: Add timing instrumentation hooks.
+	* nand.cdl: Add CYGSEM_IO_NAND_INSTRUMENT_TIMING to mediate them.
+	* misc/timetag_to_csv.pl: Helper perl script for use with them.
+
 2009-11-09  Ross Younger  <wry@eCosCentric.com>
 
 	* nand.h: Rationalise interface:
 	  * read_page: now only reads a whole page and/or its spare.
 	  * read_part_page: Created.  Allows part-pages and optionally
-	    not checking ECC.
+	  not checking ECC.
 	  * write_page: now only writes a whole page and/or its spare.
 	  + nand_ecc.h: Drop earlier attempt to support sub-page ECC.
 	* tests/readwrite.c: Test out cyg_nand_read_part_page().
 	* tests: Update for interface tweak.
 	* nand_bbt.c: Rationalise BBT finding into one place and fix to 
-	  better cope with the situation where only one BBT block had
-	  been written. Cure an internal race.
+	better cope with the situation where only one BBT block had
+	been written. Cure an internal race.
 	* nand_ecc_mtd_fast.c: Speed up checking phase.
 
 2009-11-06  Ross Younger  <wry@eCosCentric.com>
--- a/packages/io/nand/current/cdl/nand.cdl
+++ b/packages/io/nand/current/cdl/nand.cdl
@@ -239,6 +239,15 @@ cdl_package CYGPKG_IO_NAND {
             played with lightly."
     }
 
+    cdl_option CYGSEM_IO_NAND_INSTRUMENT_TIMING {
+        display     "Instrument NAND call timings"
+        default_value 0
+        description "This option enables timing instrumentation within
+            the NAND library. It is intended only to assist driver
+            authors during development.
+            Normal users should not set this option."
+    }
+
     cdl_option CYGBLD_IO_NAND_UTILS {
         display "The list of utilities to build."
 		flavor data
new file mode 100644
--- /dev/null
+++ b/packages/io/nand/current/misc/timetag_to_csv.pl
@@ -0,0 +1,71 @@
+#!/usr/bin/perl -ln
+#
+# timetag_to_csv.pl
+# Reformats some timetags as output from gdb ("p tagslist") as CSV
+# for easy import into your analysis tool of choice.
+#
+# ####ECOSGPLCOPYRIGHTBEGIN####                                            
+# -------------------------------------------                              
+# This file is part of eCos, the Embedded Configurable Operating System.   
+# Copyright (C) 2009 eCosCentric Limited.
+#
+# eCos is free software; you can redistribute it and/or modify it under    
+# the terms of the GNU General Public License as published by the Free     
+# Software Foundation; either version 2 or (at your option) any later      
+# version.                                                                 
+#
+# eCos is distributed in the hope that it will be useful, but WITHOUT      
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or    
+# FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License    
+# for more details.                                                        
+#
+# You should have received a copy of the GNU General Public License        
+# along with eCos; if not, write to the Free Software Foundation, Inc.,    
+# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.            
+#
+# As a special exception, if other files instantiate templates or use      
+# macros or inline functions from this file, or you compile this file      
+# and link it with other works to produce a work based on this file,       
+# this file does not by itself cause the resulting work to be covered by   
+# the GNU General Public License. However the source code for this file    
+# must still be made available in accordance with section (3) of the GNU   
+# General Public License v2.                                               
+#
+# This exception does not invalidate any other reasons why a work based    
+# on this file might be covered by the GNU General Public License.         
+# -------------------------------------------                              
+# ####ECOSGPLCOPYRIGHTEND####                                              
+#
+# ####DESCRIPTIONBEGIN####
+#
+# Author(s):   wry
+# Date:        2009-11-10
+#
+# ###DESCRIPTIONEND####
+#
+
+# input (stdin or filename): paste from gdb ("p tagslist")
+# output (stdout): csv
+
+BEGIN { my $state=0; my @data = (); }
+
+$state = 1 if s/.*{//;
+
+my $nextstate = $state;
+$nextstate = 0 if s/}.*//;
+
+s/^\s+//;
+s/\s+$//;
+if ($state == 1) {
+    push @data, split /,\s*/;
+}
+$state = $nextstate;
+
+END {
+    while (@data) {
+        $a = shift @data;
+        $b = shift @data;
+        $c = shift @data;
+        print "$a, $b, $c";
+    }
+}
--- a/packages/io/nand/current/src/nand.c
+++ b/packages/io/nand/current/src/nand.c
@@ -58,6 +58,58 @@
 #include <string.h>
 
 /* ============================================================ */
+// Timing instrumentation hooks; or "where is all the time going?"
+// timetag_to_csv.pl will turn gdb output ("p tagslist") into CSV,
+// for ease of analysis.
+
+#ifdef CYGSEM_IO_NAND_INSTRUMENT_TIMING
+# ifndef HAL_CLOCK_READ
+#  error HAL_CLOCK_READ required
+# endif
+# define TAGSIZE 1024
+cyg_uint32 tagslist[TAGSIZE];
+cyg_uint32 tags_next;
+static cyg_int64        rtc_resolution[] = CYGNUM_KERNEL_COUNTERS_RTC_RESOLUTION;
+static cyg_int64        rtc_period = CYGNUM_KERNEL_COUNTERS_RTC_PERIOD;
+
+# define TAG(_x) do {                           \
+    tagslist[tags_next++] = _x;                 \
+    if (tags_next >= TAGSIZE) tags_next = 0;    \
+} while(0)
+
+# define TIMETAG() do {     \
+    cyg_uint64 tick;        \
+    cyg_uint32 haltick;     \
+    tick=cyg_current_time();\
+    HAL_CLOCK_READ(&haltick);\
+    TAG(__LINE__);          \
+    TAG(tick&0xFFFFFFFF);   \
+    TAG(haltick);           \
+} while(0)
+
+# define TIMETAG_INIT() do {        \
+    int _i;                         \
+    for (_i=0; _i<TAGSIZE; _i++)    \
+        tagslist[_i]=0;             \
+    tags_next = 0;                  \
+    cyg_uint32 _tt, _tu;            \
+    HAL_CLOCK_READ(&_tt);           \
+    _tu = _tt;                      \
+    while (_tt == _tu)              \
+        HAL_CLOCK_READ(&_tu);       \
+    TIMETAG();                      \
+    (void) rtc_period;              \
+    (void) rtc_resolution;          \
+} while(0)
+
+#else // ! CYGSEM_IO_NAND_INSTRUMENT_TIMING
+
+# define TIMETAG_INIT() CYG_EMPTY_STATEMENT
+# define TIMETAG() CYG_EMPTY_STATEMENT
+
+#endif
+
+/* ============================================================ */
 
 /* We have a global ("devinit") lock, protects the nanddevtab and all 
  * writes to device->isInited.
@@ -263,6 +315,7 @@ int cyg_nand_read_page(cyg_nand_partitio
                 void * dest, void * spare, size_t spare_size)
 {
     int rv, locked=0;
+    TIMETAG_INIT();
     PARTITION_CHECK(prt);
     cyg_nand_device *dev = prt->dev;
     DEV_INIT_CHECK(dev);
@@ -285,6 +338,7 @@ int cyg_nand_read_page(cyg_nand_partitio
 
 err_exit:
     if (locked) UNLOCK_DEV(dev);
+    TIMETAG();
     return rv;
 }
 
@@ -368,7 +422,9 @@ int nandi_read_whole_page_raw(cyg_nand_d
         ++tries;
         remain = NAND_BYTES_PER_PAGE(dev);
 
+        TIMETAG();
         EG(dev->fns->read_begin(dev, page));
+        TIMETAG();
 
         if (dest) {
             int step;
@@ -376,22 +432,28 @@ int nandi_read_whole_page_raw(cyg_nand_d
                 step = read_data_stride;
                 CYG_ASSERTC(remain >= read_data_stride);
 
+                TIMETAG();
                 if (do_hw_ecc && dev->ecc->init) dev->ecc->init(dev);
                 EG(dev->fns->read_stride(dev, data_dest, read_data_stride));
+                TIMETAG();
 
                 if (do_hw_ecc) {
                     dev->ecc->calc(dev, 0, ecc_dest);
                     ecc_dest += ecc_stride;
+                    TIMETAG();
                 }
 
                 data_dest += read_data_stride;
                 remain -= read_data_stride;
             }
 
+            TIMETAG();
             EG(dev->fns->read_finish(dev, oob_buf, dev->spare_per_page));
+            TIMETAG();
             nand_oob_unpack(dev, spare, spare_size, ecc_read, oob_buf);
 
             if (check_ecc) {
+                TIMETAG();
                 if (!do_hw_ecc) {
                     // Calculate software ECC in one go to try and take
                     // advantage of the cache.
@@ -433,9 +495,12 @@ int nandi_read_whole_page_raw(cyg_nand_d
                     ecc_calc_p += ecc_stride;
                     remain -= ecc_data_stride;
                 }
+                TIMETAG();
             }
         } else { // !dest: very simple case
+            TIMETAG();
             EG(dev->fns->read_finish(dev, oob_buf, dev->spare_per_page));
+            TIMETAG();
             rv = 0;
             nand_oob_unpack(dev, spare, spare_size, ecc_read, oob_buf);
         }
@@ -471,6 +536,7 @@ int cyg_nand_write_page(cyg_nand_partiti
         const void * src, const void * spare, size_t spare_size)
 {
     int rv, locked = 0;
+    TIMETAG_INIT();
     PARTITION_CHECK(prt);
     cyg_nand_device *dev = prt->dev;
     DEV_INIT_CHECK(dev);
@@ -490,6 +556,7 @@ int cyg_nand_write_page(cyg_nand_partiti
 
 err_exit:
     if (locked) UNLOCK_DEV(dev);
+    TIMETAG();
     return rv;
 }
 
@@ -517,6 +584,7 @@ int nandi_write_page_raw(cyg_nand_device
     if (spare) CYG_CHECK_DATA_PTRC(spare);
     CYG_ASSERTC(NAND_BYTES_PER_PAGE(dev) % ecc_data_stride == 0);
 
+    TIMETAG();
     // If we're doing software ECC, do it all in one go now.
     if (src && !do_hw_ecc) {
         const CYG_BYTE *data_src = src;
@@ -532,15 +600,18 @@ int nandi_write_page_raw(cyg_nand_device
             ecc_dest += ecc_stride;
         }
     }
+    TIMETAG();
 
     EG(dev->fns->write_begin(dev, page));
+    TIMETAG();
     if (src) {
         remain = NAND_BYTES_PER_PAGE(dev);
         while (remain) {
             CYG_ASSERTC(remain >= write_data_stride);
+            TIMETAG();
             if (do_hw_ecc && dev->ecc->init) dev->ecc->init(dev);
-
             EG(dev->fns->write_stride(dev, src, write_data_stride));
+            TIMETAG();
             if (do_hw_ecc) {
                 dev->ecc->calc(dev, 0, ecc_dest);
                 ecc_dest += ecc_stride;
@@ -549,10 +620,12 @@ int nandi_write_page_raw(cyg_nand_device
             remain -= write_data_stride;
         }
     }
+    TIMETAG();
 
     nand_oob_pack(dev, spare, spare_size, ecc, oob_packed);
     NAND_CHATTER(8,dev,"Write page %u\n", page);
     EG(dev->fns->write_finish(dev, oob_packed, dev->spare_per_page));
+    TIMETAG();
 
     /* N.B. We don't read-back to verify; drivers may do so themselves if
      * they wish. Typically the spec sheet says that a read-back test
@@ -575,7 +648,9 @@ int cyg_nand_erase_block(cyg_nand_partit
     int rv;
     DEV_INIT_CHECK(dev);
 
+    TIMETAG();
     LOCK_DEV(dev);
+    TIMETAG();
 
     EG(valid_block_addr(prt, blk));
 #ifdef CYGSEM_IO_NAND_USE_BBT
@@ -593,7 +668,9 @@ int cyg_nand_erase_block(cyg_nand_partit
         EG(rv);
     }
 err_exit:
+    TIMETAG();
     UNLOCK_DEV(dev);
+    TIMETAG();
     return rv;
 #endif
 }