Mercurial > nand-ecoscentric
annotate packages/io/nand/current/tests/nand_rwbenchmark.c @ 3383:060772fa9bb3
nand tests/nand_rwbenchmark.c: Fix infinite loop when all blocks are marked bad.
| author | Ross Younger <wry@ecoscentric.com> |
|---|---|
| date | Sat, 14 Jun 2014 19:11:12 +1200 |
| parents | b1e9145ae7cd |
| children | 631d86e5d620 |
| rev | line source |
|---|---|
| 3017 | 1 //============================================================================= |
| 2 // | |
| 3 // mounttime.c | |
| 4 // | |
| 5 // Mount timings exerciser, use on a filesystem filled by `makefiles' | |
| 6 // | |
| 7 //============================================================================= | |
| 8 // ####ECOSGPLCOPYRIGHTBEGIN#### | |
| 9 // ------------------------------------------- | |
| 10 // This file is part of eCos, the Embedded Configurable Operating System. | |
| 11 // Copyright (C) 2009 Free Software Foundation, Inc. | |
| 12 // | |
| 13 // eCos is free software; you can redistribute it and/or modify it under | |
| 14 // the terms of the GNU General Public License as published by the Free | |
| 15 // Software Foundation; either version 2 or (at your option) any later | |
| 16 // version. | |
| 17 // | |
| 18 // eCos is distributed in the hope that it will be useful, but WITHOUT | |
| 19 // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
| 20 // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
| 21 // for more details. | |
| 22 // | |
| 23 // You should have received a copy of the GNU General Public License | |
| 24 // along with eCos; if not, write to the Free Software Foundation, Inc., | |
| 25 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. | |
| 26 // | |
| 27 // As a special exception, if other files instantiate templates or use | |
| 28 // macros or inline functions from this file, or you compile this file | |
| 29 // and link it with other works to produce a work based on this file, | |
| 30 // this file does not by itself cause the resulting work to be covered by | |
| 31 // the GNU General Public License. However the source code for this file | |
| 32 // must still be made available in accordance with section (3) of the GNU | |
| 33 // General Public License v2. | |
| 34 // | |
| 35 // This exception does not invalidate any other reasons why a work based | |
| 36 // on this file might be covered by the GNU General Public License. | |
| 37 // ------------------------------------------- | |
| 38 // ####ECOSGPLCOPYRIGHTEND#### | |
| 39 //========================================================================== | |
| 40 //#####DESCRIPTIONBEGIN#### | |
| 41 // | |
| 42 // Author(s): wry | |
| 43 // Date: 2009-07-02 | |
| 44 // Description: Read, write and erase timing benchmark. | |
| 45 // Some timing code borrowed from mmfs tests. | |
| 46 // | |
| 47 //####DESCRIPTIONEND#### | |
| 48 //============================================================================= | |
| 49 | |
| 50 #include <cyg/infra/testcase.h> | |
| 51 #include <cyg/infra/diag.h> | |
| 52 #include <cyg/infra/cyg_ass.h> | |
| 53 #include <pkgconf/system.h> | |
| 54 #include <pkgconf/hal.h> | |
| 55 #include <cyg/hal/hal_intr.h> | |
| 56 | |
| 57 #ifndef HAL_CLOCK_READ | |
| 58 # error "HAL_CLOCK_READ not defined!" | |
| 59 #endif | |
| 60 | |
| 61 #if !defined(CYGPKG_LIBC_TIME) || !defined(CYGPKG_ERROR) || !defined(CYGPKG_LIBC_STDIO) || !defined(CYGPKG_KERNEL) | |
| 62 | |
| 63 void cyg_user_start(void) | |
| 64 { | |
|
3379
b1e9145ae7cd
packages/io/nand/current/tests/nand_rwbenchmark.c: Add CYG_TEST_INIT() before CYG_TEST_NA() to allow auto-testing cyg_test_init() breakpoint to work.
James G. Smith <jsmith@ecoscentric.com>
parents:
3017
diff
changeset
|
65 CYG_TEST_INIT(); |
| 3017 | 66 CYG_TEST_NA("Needs CYGPKG_KERNEL, CYGPKG_LIBC_TIME, CYGPKG_ERROR and CYGPKG_LIBC_STDIO"); |
| 67 } | |
| 68 | |
| 69 #else | |
| 70 | |
| 71 #include <cyg/nand/nand.h> | |
| 72 #include <cyg/nand/nand_device.h> | |
| 73 #include <cyg/nand/util.h> | |
| 74 | |
| 75 #include <string.h> | |
| 76 #include <stdio.h> | |
| 77 #include <unistd.h> | |
| 78 #include <sys/types.h> | |
| 79 #include <dirent.h> | |
| 80 #include <stdlib.h> | |
| 81 | |
| 82 #include <cyg/kernel/kapi.h> | |
| 83 | |
| 84 #include "ar4prng.inl" | |
| 85 ar4ctx rnd; | |
| 86 /* we'll use our text segment as seed to the prng; not crypto-secure but | |
| 87 * pretty good for running tests */ | |
| 88 extern unsigned char _stext[], _etext[]; | |
| 89 | |
| 90 | |
| 91 #ifdef CYGPKG_DEVS_NAND_SYNTH | |
| 92 #define DEVICE "synth" | |
| 93 #else | |
| 94 #define DEVICE "onboard" | |
| 95 #endif | |
| 96 | |
| 97 #define PARTITION 0 | |
| 98 | |
| 99 #define MUST(what) do { \ | |
| 100 if (0 != what) { \ | |
| 101 perror(#what); \ | |
| 102 CYG_TEST_FAIL(#what); \ | |
| 103 cyg_test_exit(); \ | |
| 104 } \ | |
| 105 } while(0) | |
| 106 | |
| 107 #define min(a,b) ( (a) > (b) ? (b) : (a) ) | |
| 108 | |
| 109 // -------------------------------------------------------------- | |
| 110 // Timing code cribbed from pvr5.c | |
| 111 | |
| 112 typedef struct | |
| 113 { | |
| 114 cyg_int64 ticks; | |
| 115 cyg_uint32 halticks; | |
| 116 } timestamp; | |
| 117 | |
| 118 typedef struct | |
| 119 { | |
| 120 timestamp start; | |
| 121 timestamp end; | |
| 122 cyg_int64 interval; // In HAL ticks | |
| 123 cyg_int64 us_interval; // Interval in microseconds | |
| 124 } timing; | |
| 125 | |
| 126 static cyg_int64 ticks_overhead = 0; | |
| 127 static cyg_int32 us_per_haltick = 0; | |
| 128 static cyg_int32 halticks_per_us = 0; | |
| 129 | |
| 130 static cyg_int64 rtc_resolution[] = CYGNUM_KERNEL_COUNTERS_RTC_RESOLUTION; | |
| 131 static cyg_int64 rtc_period = CYGNUM_KERNEL_COUNTERS_RTC_PERIOD; | |
| 132 | |
| 133 static void wait_for_tick( void ) | |
| 134 { | |
| 135 cyg_tick_count_t now = cyg_current_time(); | |
| 136 | |
| 137 while( cyg_current_time() == now ) | |
| 138 continue; | |
| 139 } | |
| 140 | |
| 141 static void get_timestamp( timestamp *ts ) | |
| 142 { | |
| 143 ts->ticks = cyg_current_time(); | |
| 144 HAL_CLOCK_READ( &ts->halticks ); | |
| 145 } | |
| 146 | |
| 147 static cyg_int64 ticks_to_us( cyg_int64 ticks ) | |
| 148 { | |
| 149 cyg_int64 us; | |
| 150 | |
| 151 if( us_per_haltick != 0 ) | |
| 152 us = ticks * us_per_haltick; | |
| 153 else | |
| 154 us = ticks / halticks_per_us; | |
| 155 | |
| 156 return us; | |
| 157 } | |
| 158 | |
| 159 static void calculate_interval( timing *t ) | |
| 160 { | |
| 161 t->interval = t->end.halticks - t->start.halticks; | |
| 162 | |
| 163 t->interval += (t->end.ticks - t->start.ticks) * rtc_period; | |
| 164 | |
| 165 t->interval -= ticks_overhead; | |
| 166 | |
| 167 t->us_interval = ticks_to_us( t->interval ); | |
| 168 } | |
| 169 | |
| 170 static void init_timing( void ) | |
| 171 { | |
| 172 timing t; | |
| 173 | |
| 174 cyg_thread_delay(2); // ensure clock running - asserts if not | |
| 175 | |
| 176 us_per_haltick = 1000000/(rtc_period * rtc_resolution[1]); | |
| 177 halticks_per_us = (rtc_period * rtc_resolution[1])/1000000; | |
| 178 | |
| 179 wait_for_tick(); | |
| 180 | |
| 181 get_timestamp( &t.start ); | |
| 182 get_timestamp( &t.end ); | |
| 183 | |
| 184 calculate_interval( &t ); | |
| 185 | |
| 186 ticks_overhead = t.interval; | |
| 187 | |
| 188 diag_printf("Timing overhead %lld ticks (%lluus), this will be factored out of all other measurements\n", ticks_overhead, t.us_interval ); | |
| 189 } | |
| 190 | |
| 191 #define disable_clock_latency_measurement() | |
| 192 #define enable_clock_latency_measurement() | |
| 193 | |
| 194 // -------------------------------------------------------------- | |
| 195 | |
| 196 void | |
| 197 show_times_hdr(void) | |
| 198 { | |
| 199 disable_clock_latency_measurement(); | |
| 200 diag_printf("\n"); | |
| 201 #ifdef _TM_BASIC_HAL_CLOCK_READ_UNDEFINED | |
| 202 diag_printf("HAL_CLOCK_READ() is not supported on this platform.\n"); | |
| 203 diag_printf("Timing results are meaningless.\n"); | |
| 204 #endif | |
| 205 diag_printf("All times are in microseconds (us) unless stated\n"); | |
| 206 diag_printf("\n"); | |
| 207 diag_printf(" Confidence\n"); | |
| 208 diag_printf(" Ave Min Max Var Ave Min Function\n"); | |
| 209 diag_printf(" ====== ====== ====== ====== ========== ========\n"); | |
| 210 enable_clock_latency_measurement(); | |
| 211 } | |
| 212 | |
| 213 // Display a time result | |
| 214 void | |
| 215 show_ns(cyg_uint64 ns) | |
| 216 { | |
| 217 if (ns > 999995000) { | |
| 218 ns += 5000000; // round to nearest 0.01s | |
| 219 diag_printf("%5u.%02us", (int)(ns/1000000000), (int)((ns%1000000000)/10000000)); | |
| 220 | |
| 221 } else if (ns >= 99999995) { | |
| 222 ns += 5000; // round to 0.01ms | |
| 223 diag_printf("%4u.%02ums", (int)(ns/1000000), (int)((ns%1000000)/10000)); | |
| 224 } else { | |
| 225 ns += 5; // round to .01us | |
| 226 diag_printf("%6u.%02u", (int)(ns/1000), (int)((ns%1000)/10)); | |
| 227 } | |
| 228 } | |
| 229 | |
| 230 void | |
| 231 show_times_detail(timing ft[], int nsamples, char *title, bool ignore_first) | |
| 232 { | |
| 233 int i; | |
| 234 int start_sample, total_samples; | |
| 235 cyg_uint64 delta, total, ave, min, max, ave_dev; | |
| 236 /* we measure in ticks, convert to us, but store as ns for good precision */ | |
| 237 cyg_int32 con_ave, con_min; | |
| 238 | |
| 239 if (ignore_first) { | |
| 240 start_sample = 1; | |
| 241 total_samples = nsamples-1; | |
| 242 } else { | |
| 243 start_sample = 0; | |
| 244 total_samples = nsamples; | |
| 245 } | |
| 246 | |
| 247 total = 0; | |
| 248 min = 0xFFFFffffFFFFffffULL; | |
| 249 max = 0; | |
| 250 for (i = start_sample; i < nsamples; i++) { | |
| 251 calculate_interval(&ft[i]); | |
| 252 delta = ft[i].us_interval * 1000; | |
| 253 total += delta; | |
| 254 if (delta < min) min = delta; | |
| 255 if (delta > max) max = delta; | |
| 256 } | |
| 257 ave = total / total_samples; | |
| 258 | |
| 259 ave_dev = 0; | |
| 260 for (i = start_sample; i < nsamples; i++) { | |
| 261 delta = ft[i].us_interval * 1000; | |
| 262 if (delta > ave) | |
| 263 delta = delta - ave; | |
| 264 else | |
| 265 delta = ave - delta; | |
| 266 ave_dev += delta; | |
| 267 } | |
| 268 ave_dev /= total_samples; | |
| 269 | |
| 270 con_ave = 0; | |
| 271 con_min = 0; | |
| 272 for (i = start_sample; i < nsamples; i++) { | |
| 273 delta = ft[i].us_interval * 1000; | |
| 274 if ((delta <= (ave+ave_dev)) && (delta >= (ave-ave_dev))) con_ave++; | |
| 275 if ((delta <= (min+ave_dev)) && (delta >= (min-ave_dev))) con_min++; | |
| 276 } | |
| 277 con_ave = (con_ave * 100) / total_samples; | |
| 278 con_min = (con_min * 100) / total_samples; | |
| 279 | |
| 280 disable_clock_latency_measurement(); | |
| 281 show_ns(ave); | |
| 282 show_ns(min); | |
| 283 show_ns(max); | |
| 284 show_ns(ave_dev); | |
| 285 diag_printf(" %3d%% %3d%%", con_ave, con_min); | |
| 286 diag_printf(" %s\n", title); | |
| 287 | |
| 288 CYG_ASSERT( ave <= max, "ave < max" ); | |
| 289 | |
| 290 enable_clock_latency_measurement(); | |
| 291 } | |
| 292 | |
| 293 void | |
| 294 show_times(timing ft[], int nsamples, char *title) | |
| 295 { | |
| 296 show_times_detail(ft, nsamples, title, false); | |
| 297 #ifdef STATS_WITHOUT_FIRST_SAMPLE | |
| 298 show_times_detail(ft, nsamples, "", true); | |
| 299 #endif | |
| 300 } | |
| 301 | |
| 302 // -------------------------------------------------------------- | |
| 303 | |
| 304 #define WORKDIR MOUNTPOINT "/" "test" | |
| 305 | |
| 306 #define NREADS 100 | |
| 307 #define NBULKREADS 10 | |
| 308 #define NBULKWRITES 10 | |
| 309 #define NWRITES 30 | |
| 310 #define NERASES 30 | |
| 311 | |
| 312 void show_test_parameters(cyg_nand_device *dev) | |
| 313 { | |
| 314 disable_clock_latency_measurement(); | |
| 315 diag_printf("\nTesting parameters:\n"); | |
| 316 diag_printf(" NAND reads: %5u\n", NREADS); | |
| 317 if (NWRITES) | |
| 318 diag_printf(" NAND writes: %5u\n", NWRITES); | |
| 319 if (NERASES) | |
| 320 diag_printf(" NAND erases: %5u\n", NERASES); | |
| 321 if (NBULKREADS) | |
| 322 diag_printf(" NAND bulk reads: %5u\n", NBULKREADS); | |
| 323 if (NBULKWRITES) | |
| 324 diag_printf(" NAND bulk writes: %5u\n", NBULKWRITES); | |
| 325 diag_printf(" Device page size: %5u bytes\n" | |
| 326 " Device block size: %5u pages\n", | |
| 327 CYG_NAND_BYTES_PER_PAGE(dev), | |
| 328 CYG_NAND_PAGES_PER_BLOCK(dev) ); | |
| 329 | |
| 330 enable_clock_latency_measurement(); | |
| 331 } | |
| 332 | |
| 333 cyg_nand_block_addr find_spare_block(cyg_nand_partition *part) | |
| 334 { | |
| 335 const int oobz = CYG_NAND_APPSPARE_PER_PAGE(part->dev); | |
| 336 unsigned char oob[oobz]; | |
| 337 int i,rv; | |
| 338 cyg_nand_block_addr b; | |
| 339 | |
|
3383
060772fa9bb3
nand tests/nand_rwbenchmark.c: Fix infinite loop when all blocks are marked bad.
Ross Younger <wry@ecoscentric.com>
parents:
3379
diff
changeset
|
340 for (b=CYG_NAND_PARTITION_NBLOCKS(part)-1; b>=1; b--) { |
|
060772fa9bb3
nand tests/nand_rwbenchmark.c: Fix infinite loop when all blocks are marked bad.
Ross Younger <wry@ecoscentric.com>
parents:
3379
diff
changeset
|
341 /* Testing for b>=1 means we don't check block 0 for |
|
060772fa9bb3
nand tests/nand_rwbenchmark.c: Fix infinite loop when all blocks are marked bad.
Ross Younger <wry@ecoscentric.com>
parents:
3379
diff
changeset
|
342 * availability, but if we get there then realistically |
|
060772fa9bb3
nand tests/nand_rwbenchmark.c: Fix infinite loop when all blocks are marked bad.
Ross Younger <wry@ecoscentric.com>
parents:
3379
diff
changeset
|
343 * the device is worn out anyway. So fail the test. */ |
| 3017 | 344 cyg_nand_page_addr pg = CYG_NAND_BLOCK2PAGEADDR(part->dev, b); |
| 345 rv = cyg_nandp_read_page(part, pg, 0, oob, oobz); | |
| 346 if (rv != 0) continue; // bad block? | |
| 347 | |
| 348 for (i=0; i<oobz; i++) | |
| 349 if (oob[i] != 0xff) | |
| 350 goto next; | |
| 351 | |
| 352 // oob all FF: take it! | |
| 353 return b; | |
| 354 next: | |
| 355 ; | |
| 356 } | |
| 357 | |
|
3383
060772fa9bb3
nand tests/nand_rwbenchmark.c: Fix infinite loop when all blocks are marked bad.
Ross Younger <wry@ecoscentric.com>
parents:
3379
diff
changeset
|
358 CYG_TEST_FAIL_EXIT("can't find an untagged block - device worn out?"); |
| 3017 | 359 } |
| 360 | |
| 361 CYG_BYTE databuf[CYGNUM_NAND_PAGEBUFFER], testbuf[CYGNUM_NAND_PAGEBUFFER]; | |
| 362 timing ft_read[NREADS]; | |
| 363 timing ft_write[NWRITES]; | |
| 364 timing ft_erase[NERASES]; | |
| 365 timing ft_bulkread[NBULKREADS]; | |
| 366 timing ft_bulkwrite[NBULKWRITES]; | |
| 367 | |
| 368 int fails = 0; | |
| 369 | |
| 370 void test_reads(cyg_nand_partition *part, cyg_nand_block_addr b) | |
| 371 { | |
| 372 cyg_nand_page_addr pgstart = CYG_NAND_BLOCK2PAGEADDR(part->dev, b), | |
| 373 pgend = CYG_NAND_BLOCK2PAGEADDR(part->dev, b+1)-1, | |
| 374 pg = pgstart; | |
| 375 int i, rv; | |
| 376 const int oobz = CYG_NAND_APPSPARE_PER_PAGE(part->dev); | |
| 377 unsigned char oob[oobz]; | |
| 378 #define ft ft_read | |
| 379 | |
| 380 /* First, set up the block the way we want it ... */ | |
| 381 cyg_nandp_erase_block(part, b); | |
| 382 memcpy(oob, databuf, oobz); | |
| 383 for (i=pgstart; i <= pgend; i++) { | |
| 384 rv = cyg_nandp_write_page(part, i, databuf, oob, oobz); | |
| 385 switch (rv) { | |
| 386 case 0: break; | |
| 387 case -EIO: | |
| 388 cyg_nandp_bbt_markbad(part, b); | |
| 389 CYG_TEST_FAIL_FINISH("Write failed; block now marked as bad. This run can't continue but should be OK to repeat."); | |
| 390 | |
| 391 default: | |
| 392 diag_printf("Unexpected write failure: %d\n", rv); | |
| 393 CYG_TEST_FAIL_FINISH("Pre-write failed"); | |
| 394 } | |
| 395 } | |
| 396 | |
| 397 #define CLEARDATA() memset(testbuf, 0, sizeof testbuf) | |
| 398 #define CHECKDATA() do { if (0 != memcmp(testbuf, databuf, sizeof testbuf)) { \ | |
| 399 CYG_TEST_FAIL("readback check failed"); \ | |
| 400 ++fails; \ | |
| 401 break; \ | |
| 402 } } while(0) | |
| 403 | |
| 404 #define CLEAROOB() memset(oob, 0, oobz) | |
| 405 #define CHECKOOB() do { if (0 != memcmp(oob, databuf, oobz)) { \ | |
| 406 CYG_TEST_FAIL("readback OOB check failed"); \ | |
| 407 ++fails; \ | |
| 408 break; \ | |
| 409 } } while(0) | |
| 410 | |
| 411 | |
| 412 for (i=0; i < NREADS; i++) { | |
| 413 CLEARDATA(); | |
| 414 wait_for_tick(); | |
| 415 get_timestamp(&ft[i].start); | |
| 416 cyg_nandp_read_page(part, pg, testbuf, 0, 0); | |
| 417 get_timestamp(&ft[i].end); | |
| 418 CHECKDATA(); | |
| 419 ++pg; | |
| 420 if (pg > pgend) pg = pgstart; | |
| 421 } | |
| 422 show_times(ft, NREADS, "NAND page reads (page data only)"); | |
| 423 | |
| 424 for (i=0; i < NREADS; i++) { | |
| 425 CLEAROOB(); | |
| 426 wait_for_tick(); | |
| 427 get_timestamp(&ft[i].start); | |
| 428 cyg_nandp_read_page(part, pg, 0, oob, oobz); | |
| 429 get_timestamp(&ft[i].end); | |
| 430 CHECKOOB(); | |
| 431 ++pg; | |
| 432 if (pg > pgend) pg = pgstart; | |
| 433 } | |
| 434 show_times(ft, NREADS, "NAND page reads (OOB only)"); | |
| 435 | |
| 436 for (i=0; i < NREADS; i++) { | |
| 437 CLEARDATA(); | |
| 438 CLEAROOB(); | |
| 439 wait_for_tick(); | |
| 440 get_timestamp(&ft[i].start); | |
| 441 cyg_nandp_read_page(part, pg, testbuf, oob, oobz); | |
| 442 get_timestamp(&ft[i].end); | |
| 443 CHECKDATA(); | |
| 444 CHECKOOB(); | |
| 445 ++pg; | |
| 446 if (pg > pgend) pg = pgstart; | |
| 447 } | |
| 448 show_times(ft, NREADS, "NAND page reads (page + OOB)"); | |
| 449 | |
| 450 #undef ft | |
| 451 #define ft ft_bulkread | |
| 452 for (i=0; i < NBULKREADS; i++) { | |
| 453 CLEARDATA(); | |
| 454 wait_for_tick(); | |
| 455 get_timestamp(&ft[i].start); | |
| 456 for (pg = pgstart; pg <= pgend; pg++) { | |
| 457 cyg_nandp_read_page(part, pg, testbuf, 0, 0); | |
| 458 } | |
| 459 get_timestamp(&ft[i].end); | |
| 460 CHECKDATA(); | |
| 461 } | |
| 462 // report the results later. | |
| 463 #undef ft | |
| 464 cyg_nandp_erase_block(part, b); | |
| 465 } | |
| 466 | |
| 467 | |
| 468 void test_writes(cyg_nand_partition *part, cyg_nand_block_addr b) | |
| 469 { | |
| 470 cyg_nand_page_addr pgstart = CYG_NAND_BLOCK2PAGEADDR(part->dev, b), | |
| 471 pgend = CYG_NAND_BLOCK2PAGEADDR(part->dev, b+1)-1, | |
| 472 pg = pgstart; | |
| 473 int i; | |
| 474 const int oobz = CYG_NAND_APPSPARE_PER_PAGE(part->dev); | |
| 475 unsigned char oob[oobz]; | |
| 476 #define ft ft_write | |
| 477 | |
| 478 cyg_nandp_erase_block(part, b); | |
| 479 for (i=0; i < NWRITES; i++) { | |
| 480 wait_for_tick(); | |
| 481 get_timestamp(&ft[i].start); | |
| 482 cyg_nandp_write_page(part, pg, databuf, databuf, oobz); | |
| 483 get_timestamp(&ft[i].end); | |
| 484 | |
| 485 // Read it back to confirm | |
| 486 CLEARDATA(); | |
| 487 CLEAROOB(); | |
| 488 cyg_nandp_read_page(part, pg, testbuf, oob, oobz); | |
| 489 CHECKDATA(); | |
| 490 CHECKOOB(); | |
| 491 | |
| 492 ++pg; | |
| 493 if (pg > pgend) { | |
| 494 cyg_nandp_erase_block(part, b); | |
| 495 pg = pgstart; | |
| 496 } | |
| 497 } | |
| 498 cyg_nandp_erase_block(part, b); | |
| 499 show_times(ft, NWRITES, "NAND full-page writes"); | |
| 500 | |
| 501 #undef ft | |
| 502 #define ft ft_bulkwrite | |
| 503 // N.B. Bulk writing times will likely improve later if we implement cache-programming mode as found on many a large-page device. | |
| 504 for (i=0; i < NBULKWRITES; i++) { | |
| 505 wait_for_tick(); | |
| 506 get_timestamp(&ft[i].start); | |
| 507 for (pg = pgstart; pg <= pgend; pg++) { | |
| 508 cyg_nandp_write_page(part, pg, testbuf, 0, 0); | |
| 509 } | |
| 510 get_timestamp(&ft[i].end); | |
| 511 cyg_nandp_erase_block(part, b); | |
| 512 } | |
| 513 // report later. | |
| 514 #undef ft | |
| 515 } | |
| 516 | |
| 517 | |
| 518 void test_erases(cyg_nand_partition *part, cyg_nand_block_addr b) | |
| 519 { | |
| 520 int i; | |
| 521 #define ft ft_erase | |
| 522 | |
| 523 for (i=0; i < NERASES; i++) { | |
| 524 wait_for_tick(); | |
| 525 get_timestamp(&ft[i].start); | |
| 526 cyg_nandp_erase_block(part, b); | |
| 527 get_timestamp(&ft[i].end); | |
| 528 | |
| 529 if (i==0) { | |
| 530 cyg_nand_page_addr pg = CYG_NAND_BLOCK2PAGEADDR(part->dev, b); | |
| 531 const int oobz = CYG_NAND_APPSPARE_PER_PAGE(part->dev); | |
| 532 unsigned char oob[oobz]; | |
| 533 int j; | |
| 534 // TODO: It only makes sense to check the one, unless we want | |
| 535 // to try writing out more dummy data each time. | |
| 536 CLEARDATA(); | |
| 537 CLEAROOB(); | |
| 538 cyg_nandp_read_page(part, pg, testbuf, oob, oobz); | |
| 539 for (j=0; j < sizeof testbuf; j++) { | |
| 540 if (testbuf[j] != 0xff) { | |
| 541 CYG_TEST_FAIL("readback check failed"); | |
| 542 ++fails; | |
| 543 break; | |
| 544 } | |
| 545 } | |
| 546 for (j=0; j < oobz; j++) { | |
| 547 if (oob[j] != 0xff) { | |
| 548 CYG_TEST_FAIL("readback OOB check failed"); | |
| 549 ++fails; | |
| 550 break; | |
| 551 } | |
| 552 } | |
| 553 } | |
| 554 } | |
| 555 show_times(ft, NERASES, "NAND block erases"); | |
| 556 #undef ft | |
| 557 } | |
| 558 | |
| 559 | |
| 560 | |
| 561 void rwbenchmark_main(void) | |
| 562 { | |
| 563 cyg_nand_device *dev; | |
| 564 cyg_nand_partition *part; | |
| 565 cyg_nand_block_addr block; | |
| 566 | |
| 567 cyg_nand_lookup(DEVICE, &dev); | |
| 568 if (!dev) | |
| 569 CYG_TEST_FAIL_EXIT("can't get device "DEVICE); | |
| 570 part = cyg_nand_get_partition(dev, PARTITION); | |
| 571 if (!part) | |
| 572 CYG_TEST_FAIL_EXIT("can't get partition"); | |
| 573 | |
| 574 block = find_spare_block(part); | |
| 575 diag_printf("Using block %d\n",block); | |
| 576 | |
| 577 show_test_parameters(dev); | |
| 578 show_times_hdr(); | |
| 579 | |
| 580 // TODO: For speed, refactor test_reads and test_writes into each other. | |
| 581 test_reads(part, block); | |
| 582 | |
| 583 // Freshen our test data for the second phase | |
| 584 ar4prng_many(&rnd, databuf, sizeof databuf); | |
| 585 test_writes(part,block); | |
| 586 | |
| 587 test_erases(part,block); | |
| 588 | |
| 589 show_times(ft_bulkread, NBULKREADS, "Bulk reads (block data)"); | |
| 590 show_times(ft_bulkwrite, NBULKWRITES, "Bulk writes (block data)"); | |
| 591 } | |
| 592 | |
| 593 int main(void) | |
| 594 { | |
| 595 CYG_TEST_INIT(); | |
| 596 init_timing(); | |
| 597 | |
| 598 ar4prng_init(&rnd,_stext, _etext-_stext); | |
| 599 ar4prng_many(&rnd, databuf, sizeof databuf); | |
| 600 | |
| 601 | |
| 602 #if defined(CYGVAR_KERNEL_COUNTERS_CLOCK_LATENCY) || defined(CYGVAR_KERNEL_COUNTERS_CLOCK_DSR_LATENCY) | |
| 603 CYG_TEST_INFO("WARNING: Clock or DSR latency instrumentation can mess with the results, recommend you turn it off."); | |
| 604 #endif | |
| 605 | |
| 606 | |
| 607 rwbenchmark_main(); | |
| 608 | |
| 609 if (fails) { | |
| 610 diag_printf("There were %d failures.\n",fails); | |
| 611 CYG_TEST_FAIL_FINISH("something went wrong, THESE RESULTS ARE INVALID"); | |
| 612 } | |
| 613 | |
| 614 CYG_TEST_PASS_FINISH("Benchmarking complete"); | |
| 615 } | |
| 616 | |
| 617 #endif | |
| 618 |
