aboutsummaryrefslogtreecommitdiffstats
path: root/testhal/STM32/STM32F4xx/FSMC_NAND/main.c
diff options
context:
space:
mode:
authorbarthess <barthess@yandex.ru>2014-10-18 16:34:12 +0300
committerbarthess <barthess@yandex.ru>2014-10-18 16:34:12 +0300
commit7355cbd461771f0d38cc2066f7a93b081ce38394 (patch)
treef34e8d6685247acd528e3d3d677a40261fa17313 /testhal/STM32/STM32F4xx/FSMC_NAND/main.c
parent0214eb9bccecfee4598c8aa00b5af8c6f053ee31 (diff)
downloadChibiOS-Contrib-7355cbd461771f0d38cc2066f7a93b081ce38394.tar.gz
ChibiOS-Contrib-7355cbd461771f0d38cc2066f7a93b081ce38394.tar.bz2
ChibiOS-Contrib-7355cbd461771f0d38cc2066f7a93b081ce38394.zip
Added fsmc code
Diffstat (limited to 'testhal/STM32/STM32F4xx/FSMC_NAND/main.c')
-rw-r--r--testhal/STM32/STM32F4xx/FSMC_NAND/main.c642
1 files changed, 642 insertions, 0 deletions
diff --git a/testhal/STM32/STM32F4xx/FSMC_NAND/main.c b/testhal/STM32/STM32F4xx/FSMC_NAND/main.c
new file mode 100644
index 0000000..e695894
--- /dev/null
+++ b/testhal/STM32/STM32F4xx/FSMC_NAND/main.c
@@ -0,0 +1,642 @@
+/*
+ ChibiOS/RT - Copyright (C) 2006-2014 Giovanni Di Sirio
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+*/
+/*
+ Concepts and parts of this file have been contributed by Uladzimir Pylinsky
+ aka barthess.
+ */
+
+/*
+ * Hardware notes.
+ *
+ * Use external pullup on ready/busy pin of NAND IC for a speed reason.
+ *
+ * Chose MCU with 140 (or more) pins package because 100 pins packages
+ * has no dedicated interrupt pins for FSMC.
+ *
+ * If your hardware already done using 100 pin package than you have to:
+ * 1) connect ready/busy pin to GPIOD6 (NWAIT in terms of STM32)
+ * 2) set GPIOD6 pin as input with pullup and connect it to alternate
+ * function0 (not function12)
+ * 3) set up EXTI to catch raising edge on GPIOD6 and call NAND driver's
+ * isr_handler() function from an EXTI callback.
+ *
+ * If you use MLC flash memory do NOT use ECC to detect/correct
+ * errors because of its weakness. Use Rid-Solomon on BCH code instead.
+ * Yes, you have to realize it in sowftware yourself.
+ */
+
+/*
+ * Software notes.
+ *
+ * For correct calculation of timing values you need AN2784 document
+ * from STMicro.
+ */
+
+#include "ch.h"
+#include "hal.h"
+
+#include "dma_storm.h"
+#include "string.h"
+#include "stdlib.h"
+
+/*
+ ******************************************************************************
+ * DEFINES
+ ******************************************************************************
+ */
+
+#define USE_KILL_BLOCK_TEST FALSE
+
+#define FSMCNAND_TIME_SET ((uint32_t) 2) //(8nS)
+#define FSMCNAND_TIME_WAIT ((uint32_t) 6) //(30nS)
+#define FSMCNAND_TIME_HOLD ((uint32_t) 1) //(5nS)
+#define FSMCNAND_TIME_HIZ ((uint32_t) 4) //(20nS)
+
+#define NAND_BLOCKS_COUNT 8192
+#define NAND_PAGE_DATA_SIZE 2048
+#define NAND_PAGE_SPARE_SIZE 64
+#define NAND_PAGE_SIZE (NAND_PAGE_SPARE_SIZE + NAND_PAGE_DATA_SIZE)
+#define NAND_PAGES_PER_BLOCK 64
+#define NAND_ROW_WRITE_CYCLES 3
+#define NAND_COL_WRITE_CYCLES 2
+
+#define NANF_TEST_START_BLOCK 1200
+#define NAND_TEST_END_BLOCK 1220
+
+#if USE_KILL_BLOCK_TEST
+#define NAND_TEST_KILL_BLOCK 8000
+#endif
+
+#if STM32_NAND_USE_FSMC_NAND1
+ #define NAND NANDD1
+#elif STM32_NAND_USE_FSMC_NAND2
+ #define NAND NANDD2
+#else
+#error "You should enable at least one NAND interface"
+#endif
+
+/*
+ ******************************************************************************
+ * EXTERNS
+ ******************************************************************************
+ */
+
+/*
+ ******************************************************************************
+ * PROTOTYPES
+ ******************************************************************************
+ */
+#if STM32_NAND_USE_EXT_INT
+static void ready_isr_enable(void);
+static void ready_isr_disable(void);
+static void nand_ready_cb(EXTDriver *extp, expchannel_t channel);
+#endif
+
+/*
+ ******************************************************************************
+ * GLOBAL VARIABLES
+ ******************************************************************************
+ */
+/*
+ *
+ */
+static uint8_t nand_buf[NAND_PAGE_SIZE];
+static uint8_t ref_buf[NAND_PAGE_SIZE];
+
+/*
+ *
+ */
+static time_measurement_t tmu_erase;
+static time_measurement_t tmu_write_data;
+static time_measurement_t tmu_write_spare;
+static time_measurement_t tmu_read_data;
+static time_measurement_t tmu_read_spare;
+
+#if NAND_USE_BAD_MAP
+static uint32_t badblock_map[NAND_BLOCKS_COUNT / 32];
+#endif
+
+/*
+ *
+ */
+static const NANDConfig nandcfg = {
+ &FSMCD1,
+ NAND_BLOCKS_COUNT,
+ NAND_PAGE_DATA_SIZE,
+ NAND_PAGE_SPARE_SIZE,
+ NAND_PAGES_PER_BLOCK,
+#if NAND_USE_BAD_MAP
+ badblock_map,
+#endif
+ NAND_ROW_WRITE_CYCLES,
+ NAND_COL_WRITE_CYCLES,
+ /* stm32 specific fields */
+ ((FSMCNAND_TIME_HIZ << 24) | (FSMCNAND_TIME_HOLD << 16) | \
+ (FSMCNAND_TIME_WAIT << 8) | FSMCNAND_TIME_SET),
+#if STM32_NAND_USE_EXT_INT
+ ready_isr_enable,
+ ready_isr_disable
+#endif
+};
+
+/**
+ *
+ */
+#if STM32_NAND_USE_EXT_INT
+static const EXTConfig extcfg = {
+ {
+ {EXT_CH_MODE_DISABLED, NULL}, //0
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL}, //4
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_RISING_EDGE | EXT_MODE_GPIOD, nand_ready_cb},
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL}, //8
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL}, //12
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL}, //16
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL}, //20
+ {EXT_CH_MODE_DISABLED, NULL},
+ {EXT_CH_MODE_DISABLED, NULL},
+ }
+};
+#endif /* STM32_NAND_USE_EXT_INT */
+
+static uint32_t BackgroundThdCnt = 0;
+
+#if USE_KILL_BLOCK_TEST
+static uint32_t KillCycle = 0;
+#endif
+
+/*
+ ******************************************************************************
+ ******************************************************************************
+ * LOCAL FUNCTIONS
+ ******************************************************************************
+ ******************************************************************************
+ */
+static void nand_wp_assert(void) {palClearPad(GPIOB, GPIOB_NAND_WP);}
+static void nand_wp_release(void) {palSetPad(GPIOB, GPIOB_NAND_WP);}
+static void red_led_on(void) {palSetPad(GPIOI, GPIOI_LED_R);}
+static void red_led_off(void) {palClearPad(GPIOI, GPIOI_LED_R);}
+
+#if STM32_NAND_USE_EXT_INT
+static void nand_ready_cb(EXTDriver *extp, expchannel_t channel){
+ (void)extp;
+ (void)channel;
+
+ NAND.isr_handler(&NAND);
+}
+
+static void ready_isr_enable(void) {
+ extChannelEnable(&EXTD1, GPIOD_NAND_RB_NWAIT);
+}
+
+static void ready_isr_disable(void) {
+ extChannelDisable(&EXTD1, GPIOD_NAND_RB_NWAIT);
+}
+#endif /* STM32_NAND_USE_EXT_INT */
+
+/**
+ *
+ */
+static THD_WORKING_AREA(BackgroundThreadWA, 128);
+static THD_FUNCTION(BackgroundThread, arg) {
+ (void)arg;
+
+ while(true){
+ BackgroundThdCnt++;
+ }
+ return 0;
+}
+
+/*
+ *
+ */
+static bool is_erased(NANDDriver *dp, size_t block){
+ uint32_t page = 0;
+ size_t i = 0;
+
+ for (page=0; page<NAND.config->pages_per_block; page++){
+ nandReadPageData(dp, block, page, nand_buf, NAND.config->page_data_size, NULL);
+ nandReadPageSpare(dp, block, page, &nand_buf[2048], NAND.config->page_spare_size);
+ for (i=0; i<sizeof(nand_buf); i++) {
+ if (nand_buf[i] != 0xFF)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+/*
+ *
+ */
+static void pattern_fill(void) {
+
+ size_t i;
+
+ srand(chSysGetRealtimeCounterX());
+
+ for(i=0; i<NAND_PAGE_SIZE; i++){
+ ref_buf[i] = rand() & 0xFF;
+ }
+
+ /* protect bad mark */
+ ref_buf[NAND_PAGE_DATA_SIZE] = 0xFF;
+ ref_buf[NAND_PAGE_DATA_SIZE + 1] = 0xFF;
+ memcpy(nand_buf, ref_buf, NAND_PAGE_SIZE);
+
+ /* paranoid mode ON */
+ osalDbgCheck(0 == memcmp(ref_buf, nand_buf, NAND_PAGE_SIZE));
+}
+
+/*
+ *
+ */
+#if USE_KILL_BLOCK_TEST
+static void kill_block(NANDDriver *nandp, uint32_t block){
+
+ size_t i = 0;
+ size_t page = 0;
+ uint8_t op_status;
+
+ /* This test requires good block.*/
+ osalDbgCheck(!nandIsBad(nandp, block));
+
+ while(true){
+ op_status = nandErase(&NAND, block);
+ if (0 != (op_status & 1)){
+ if(!is_erased(nandp, block))
+ osalSysHalt("Block successfully killed");
+ }
+ if(!is_erased(nandp, block))
+ osalSysHalt("Block block not erased, but erase operation report success");
+
+ for (page=0; page<nandp->config->pages_per_block; page++){
+ memset(nand_buf, 0, NAND_PAGE_SIZE);
+ op_status = nandWritePageWhole(nandp, block, page, nand_buf, NAND_PAGE_SIZE);
+ if (0 != (op_status & 1)){
+ nandReadPageWhole(nandp, block, page, nand_buf, NAND_PAGE_SIZE);
+ for (i=0; i<NAND_PAGE_SIZE; i++){
+ if (nand_buf[i] != 0)
+ osalSysHalt("Block successfully killed");
+ }
+ }
+
+ nandReadPageWhole(nandp, block, page, nand_buf, NAND_PAGE_SIZE);
+ for (i=0; i<NAND_PAGE_SIZE; i++){
+ if (nand_buf[i] != 0)
+ osalSysHalt("Page write failed, but write operation report success");
+ }
+ }
+ KillCycle++;
+ }
+}
+#endif /* USE_KILL_BLOCK_TEST */
+
+/*
+ *
+ */
+typedef enum {
+ ECC_NO_ERROR = 0,
+ ECC_CORRECTABLE_ERROR = 1,
+ ECC_UNCORRECTABLE_ERROR = 2,
+ ECC_CORRUPTED = 3,
+} ecc_result_t;
+
+/*
+ *
+ */
+static ecc_result_t parse_ecc(uint32_t ecclen,
+ uint32_t ecc1, uint32_t ecc2, uint32_t *corrupted){
+
+ size_t i = 0;
+ uint32_t corr = 0;
+ uint32_t e = 0;
+ uint32_t shift = (32 - ecclen);
+ uint32_t b0, b1;
+
+ ecc1 <<= shift;
+ ecc1 >>= shift;
+ ecc2 <<= shift;
+ ecc2 >>= shift;
+ e = ecc1 ^ ecc2;
+
+ if (0 == e){
+ return ECC_NO_ERROR;
+ }
+ else if (((e - 1) & e) == 0){
+ return ECC_CORRUPTED;
+ }
+ else {
+ for (i=0; i<ecclen/2; i++){
+ b0 = e & 1;
+ e >>= 1;
+ b1 = e & 1;
+ e >>= 1;
+ if ((b0 + b1) != 1)
+ return ECC_UNCORRECTABLE_ERROR;
+ corr |= b1 << i;
+ }
+ *corrupted = corr;
+ return ECC_CORRECTABLE_ERROR;
+ }
+}
+
+/*
+ *
+ */
+static void invert_bit(uint8_t *buf, uint32_t byte, uint32_t bit){
+ osalDbgCheck((byte < NAND_PAGE_DATA_SIZE) && (bit < 8));
+ buf[byte] ^= ((uint8_t)1) << bit;
+}
+
+/*
+ *
+ */
+static void ecc_test(NANDDriver *nandp, uint32_t block){
+
+ uint32_t corrupted;
+ uint32_t byte, bit;
+ const uint32_t ecclen = 28;
+ uint32_t ecc_ref, ecc_broken;
+ uint8_t op_status;
+ ecc_result_t ecc_result = ECC_NO_ERROR;
+
+ /* This test requires good block.*/
+ osalDbgCheck(!nandIsBad(nandp, block));
+ if (!is_erased(nandp, block))
+ nandErase(&NAND, block);
+
+ pattern_fill();
+
+ /*** Correctable errors ***/
+ op_status = nandWritePageData(nandp, block, 0,
+ nand_buf, nandp->config->page_data_size, &ecc_ref);
+ osalDbgCheck(0 == (op_status & 1)); /* operation failed */
+ nandReadPageData(nandp, block, 0,
+ nand_buf, nandp->config->page_data_size, &ecc_broken);
+ ecc_result = parse_ecc(ecclen, ecc_ref, ecc_broken, &corrupted);
+ osalDbgCheck(ECC_NO_ERROR == ecc_result); /* unexpected error */
+
+ /**/
+ byte = 0;
+ bit = 7;
+ invert_bit(nand_buf, byte, bit);
+ op_status = nandWritePageData(nandp, block, 1,
+ nand_buf, nandp->config->page_data_size, &ecc_broken);
+ osalDbgCheck(0 == (op_status & 1)); /* operation failed */
+ invert_bit(nand_buf, byte, bit);
+ ecc_result = parse_ecc(ecclen, ecc_ref, ecc_broken, &corrupted);
+ osalDbgCheck(ECC_CORRECTABLE_ERROR == ecc_result); /* this error must be correctable */
+ osalDbgCheck(corrupted == (byte * 8 + bit)); /* wrong correction code */
+
+ /**/
+ byte = 2047;
+ bit = 0;
+ invert_bit(nand_buf, byte, bit);
+ op_status = nandWritePageData(nandp, block, 2,
+ nand_buf, nandp->config->page_data_size, &ecc_broken);
+ osalDbgCheck(0 == (op_status & 1)); /* operation failed */
+ invert_bit(nand_buf, byte, bit);
+ ecc_result = parse_ecc(ecclen, ecc_ref, ecc_broken, &corrupted);
+ osalDbgCheck(ECC_CORRECTABLE_ERROR == ecc_result); /* this error must be correctable */
+ osalDbgCheck(corrupted == (byte * 8 + bit)); /* wrong correction code */
+
+ /**/
+ byte = 1027;
+ bit = 3;
+ invert_bit(nand_buf, byte, bit);
+ op_status = nandWritePageData(nandp, block, 3,
+ nand_buf, nandp->config->page_data_size, &ecc_broken);
+ osalDbgCheck(0 == (op_status & 1)); /* operation failed */
+ invert_bit(nand_buf, byte, bit);
+ ecc_result = parse_ecc(ecclen, ecc_ref, ecc_broken, &corrupted);
+ osalDbgCheck(ECC_CORRECTABLE_ERROR == ecc_result); /* this error must be correctable */
+ osalDbgCheck(corrupted == (byte * 8 + bit)); /* wrong correction code */
+
+ /*** Uncorrectable error ***/
+ byte = 1027;
+ invert_bit(nand_buf, byte, 3);
+ invert_bit(nand_buf, byte, 4);
+ op_status = nandWritePageData(nandp, block, 4,
+ nand_buf, nandp->config->page_data_size, &ecc_broken);
+ osalDbgCheck(0 == (op_status & 1)); /* operation failed */
+ invert_bit(nand_buf, byte, 3);
+ invert_bit(nand_buf, byte, 4);
+ ecc_result = parse_ecc(28, ecc_ref, ecc_broken, &corrupted);
+ osalDbgCheck(ECC_UNCORRECTABLE_ERROR == ecc_result); /* This error must be NOT correctable */
+
+ /*** make clean ***/
+ nandErase(&NAND, block);
+}
+
+/*
+ *
+ */
+static void general_test (NANDDriver *nandp, size_t first,
+ size_t last, size_t read_rounds){
+
+ size_t block, page, round;
+ bool status;
+ uint8_t op_status;
+ uint32_t recc, wecc;
+
+ red_led_on();
+
+ /* initialize time measurement units */
+ chTMObjectInit(&tmu_erase);
+ chTMObjectInit(&tmu_write_data);
+ chTMObjectInit(&tmu_write_spare);
+ chTMObjectInit(&tmu_read_data);
+ chTMObjectInit(&tmu_read_spare);
+
+ /* perform basic checks */
+ for (block=first; block<last; block++){
+ if (!nandIsBad(nandp, block)){
+ if (!is_erased(nandp, block)){
+ op_status = nandErase(nandp, block);
+ osalDbgCheck(0 == (op_status & 1)); /* operation failed */
+ }
+ }
+ }
+
+ /* write block with pattern, read it back and compare */
+ for (block=first; block<last; block++){
+ if (!nandIsBad(nandp, block)){
+ for (page=0; page<nandp->config->pages_per_block; page++){
+ pattern_fill();
+
+ chTMStartMeasurementX(&tmu_write_data);
+ op_status = nandWritePageData(nandp, block, page,
+ nand_buf, nandp->config->page_data_size, &wecc);
+ chTMStopMeasurementX(&tmu_write_data);
+ osalDbgCheck(0 == (op_status & 1)); /* operation failed */
+
+ chTMStartMeasurementX(&tmu_write_spare);
+ op_status = nandWritePageSpare(nandp, block, page,
+ nand_buf + nandp->config->page_data_size,
+ nandp->config->page_spare_size);
+ chTMStopMeasurementX(&tmu_write_spare);
+ osalDbgCheck(0 == (op_status & 1)); /* operation failed */
+
+ /* read back and compare */
+ for (round=0; round<read_rounds; round++){
+ memset(nand_buf, 0, NAND_PAGE_SIZE);
+
+ chTMStartMeasurementX(&tmu_read_data);
+ nandReadPageData(nandp, block, page,
+ nand_buf, nandp->config->page_data_size, &recc);
+ chTMStopMeasurementX(&tmu_read_data);
+ osalDbgCheck(0 == (recc ^ wecc)); /* ECC error detected */
+
+ chTMStartMeasurementX(&tmu_read_spare);
+ nandReadPageSpare(nandp, block, page,
+ nand_buf + nandp->config->page_data_size,
+ nandp->config->page_spare_size);
+ chTMStopMeasurementX(&tmu_read_spare);
+
+ osalDbgCheck(0 == memcmp(ref_buf, nand_buf, NAND_PAGE_SIZE)); /* Read back failed */
+ }
+ }
+
+ /* make clean */
+ chTMStartMeasurementX(&tmu_erase);
+ op_status = nandErase(nandp, block);
+ chTMStopMeasurementX(&tmu_erase);
+ osalDbgCheck(0 == (op_status & 1)); /* operation failed */
+
+ status = is_erased(nandp, block);
+ osalDbgCheck(true == status); /* blocks was not erased successfully */
+ }/* if (!nandIsBad(nandp, block)){ */
+ }
+ red_led_off();
+}
+
+
+/*
+ ******************************************************************************
+ * EXPORTED FUNCTIONS
+ ******************************************************************************
+ */
+
+/*
+ * Application entry point.
+ */
+int main(void) {
+
+ /* performance counters */
+ int32_t adc_ints = 0;
+ int32_t spi_ints = 0;
+ int32_t uart_ints = 0;
+ int32_t adc_idle_ints = 0;
+ int32_t spi_idle_ints = 0;
+ int32_t uart_idle_ints = 0;
+ uint32_t background_cnt = 0;
+ systime_t T = 0;
+
+ /*
+ * System initializations.
+ * - HAL initialization, this also initializes the configured device drivers
+ * and performs the board-specific initializations.
+ * - Kernel initialization, the main() function becomes a thread and the
+ * RTOS is active.
+ */
+ halInit();
+ chSysInit();
+
+#if STM32_NAND_USE_EXT_INT
+ extStart(&EXTD1, &extcfg);
+#endif
+ nandStart(&NAND, &nandcfg);
+
+ chThdSleepMilliseconds(4000);
+
+ chThdCreateStatic(BackgroundThreadWA,
+ sizeof(BackgroundThreadWA),
+ NORMALPRIO - 20,
+ BackgroundThread,
+ NULL);
+
+ nand_wp_release();
+
+ /*
+ * run NAND test in parallel with DMA load and background thread
+ */
+ dma_storm_adc_start();
+ dma_storm_uart_start();
+ dma_storm_spi_start();
+ T = chVTGetSystemTimeX();
+ general_test(&NAND, NANF_TEST_START_BLOCK, NAND_TEST_END_BLOCK, 1);
+ T = chVTGetSystemTimeX() - T;
+ adc_ints = dma_storm_adc_stop();
+ uart_ints = dma_storm_uart_stop();
+ spi_ints = dma_storm_spi_stop();
+ chSysLock();
+ background_cnt = BackgroundThdCnt;
+ BackgroundThdCnt = 0;
+ chSysUnlock();
+
+ /*
+ * run DMA load and background thread _without_ NAND test
+ */
+ dma_storm_adc_start();
+ dma_storm_uart_start();
+ dma_storm_spi_start();
+ chThdSleep(T);
+ adc_idle_ints = dma_storm_adc_stop();
+ uart_idle_ints = dma_storm_uart_stop();
+ spi_idle_ints = dma_storm_spi_stop();
+
+ /*
+ * ensure that NAND code have negligible impact on other subsystems
+ */
+ osalDbgCheck(background_cnt > (BackgroundThdCnt / 4));
+ osalDbgCheck(abs(adc_ints - adc_idle_ints) < (adc_idle_ints / 20));
+ osalDbgCheck(abs(uart_ints - uart_idle_ints) < (uart_idle_ints / 20));
+ osalDbgCheck(abs(spi_ints - spi_idle_ints) < (spi_idle_ints / 10));
+
+ /*
+ * perform ECC calculation test
+ */
+ ecc_test(&NAND, NAND_TEST_END_BLOCK);
+
+#if USE_KILL_BLOCK_TEST
+ kill_block(&NAND, NAND_TEST_KILL_BLOCK);
+#endif
+
+ nand_wp_assert();
+
+ /*
+ * Normal main() thread activity, in this demo it does nothing.
+ */
+ while (TRUE) {
+ chThdSleepMilliseconds(500);
+ }
+}
+
+