static BOOL32 check_format_mark(void) { // This function reads a flash page from (bank #0, block #0) in order to check whether the SSD is formatted or not. #ifdef __GNUC__ extern UINT32 size_of_firmware_image; UINT32 firmware_image_pages = (((UINT32) (&size_of_firmware_image)) + BYTES_PER_FW_PAGE - 1) / BYTES_PER_FW_PAGE; #else extern UINT32 Image$$ER_CODE$$RO$$Length; extern UINT32 Image$$ER_RW$$RW$$Length; UINT32 firmware_image_bytes = ((UINT32) &Image$$ER_CODE$$RO$$Length) + ((UINT32) &Image$$ER_RW$$RW$$Length); UINT32 firmware_image_pages = (firmware_image_bytes + BYTES_PER_FW_PAGE - 1) / BYTES_PER_FW_PAGE; #endif UINT32 format_mark_page_offset = FW_PAGE_OFFSET + firmware_image_pages; UINT32 temp; flash_clear_irq(); // clear any flash interrupt flags that might have been set SETREG(FCP_CMD, FC_COL_ROW_READ_OUT); SETREG(FCP_BANK, REAL_BANK(0)); SETREG(FCP_OPTION, FO_E); SETREG(FCP_DMA_ADDR, FTL_BUF_ADDR); // flash -> DRAM SETREG(FCP_DMA_CNT, BYTES_PER_SECTOR); SETREG(FCP_COL, 0); SETREG(FCP_ROW_L(0), format_mark_page_offset); SETREG(FCP_ROW_H(0), format_mark_page_offset); // At this point, we do not have to check Waiting Room status before issuing a command, // because scan list loading has been completed just before this function is called. SETREG(FCP_ISSUE, NULL); // wait for the FC_COL_ROW_READ_OUT command to be accepted by bank #0 while ((GETREG(WR_STAT) & 0x00000001) != 0); // wait until bank #0 finishes the read operation while (BSP_FSM(0) != BANK_IDLE); // Now that the read operation is complete, we can check interrupt flags. temp = BSP_INTR(0) & FIRQ_ALL_FF; // clear interrupt flags CLR_BSP_INTR(0, 0xFF); if (temp != 0) { return FALSE; // the page contains all-0xFF (the format mark does not exist.) } else { return TRUE; // the page contains something other than 0xFF (it must be the format mark) } }
void ftl_isr(void) { // interrupt service routine for flash interrupts UINT32 bank; UINT32 bsp_intr_flag; for (bank = 0; bank < NUM_BANKS; bank++) { while (BSP_FSM(bank) != BANK_IDLE); bsp_intr_flag = BSP_INTR(bank); if (bsp_intr_flag == 0) { continue; } UINT32 fc = GETREG(BSP_CMD(bank)); CLR_BSP_INTR(bank, bsp_intr_flag); if (bsp_intr_flag & FIRQ_DATA_CORRUPT) { g_read_fail_count++; } if (bsp_intr_flag & (FIRQ_BADBLK_H | FIRQ_BADBLK_L)) { if (fc == FC_COL_ROW_IN_PROG || fc == FC_IN_PROG || fc == FC_PROG) { g_program_fail_count++; } else { ASSERT(fc == FC_ERASE); g_erase_fail_count++; } } } // clear the flash interrupt flag at the interrupt controller SETREG(APB_INT_STS, INTR_FLASH); }
// BSP interrupt service routine void ftl_isr(void) { UINT32 bank; UINT32 bsp_intr_flag; uart_print("BSP interrupt occured..."); // interrupt pending clear (ICU) SETREG(APB_INT_STS, INTR_FLASH); for (bank = 0; bank < NUM_BANKS; bank++) { while (BSP_FSM(bank) != BANK_IDLE); // get interrupt flag from BSP bsp_intr_flag = BSP_INTR(bank); if (bsp_intr_flag == 0) { continue; } UINT32 fc = GETREG(BSP_CMD(bank)); // BSP clear CLR_BSP_INTR(bank, bsp_intr_flag); // interrupt handling if (bsp_intr_flag & FIRQ_DATA_CORRUPT) { uart_printf("BSP interrupt at bank: 0x%x", bank); uart_print("FIRQ_DATA_CORRUPT occured..."); } if (bsp_intr_flag & (FIRQ_BADBLK_H | FIRQ_BADBLK_L)) { uart_printf("BSP interrupt at bank: 0x%x", bank); if (fc == FC_COL_ROW_IN_PROG || fc == FC_IN_PROG || fc == FC_PROG) { uart_print("find runtime bad block when block program..."); } else { uart_printf("find runtime bad block when block erase...vblock #: %d", GETREG(BSP_ROW_H(bank)) / PAGES_PER_BLK); ASSERT(fc == FC_ERASE); } } } }
void ftl_open(void) { sanity_check(); // STEP 1 - read scan lists from NAND flash scan_list_t* scan_list = (scan_list_t*) SCAN_LIST_ADDR; UINT32 bank; UINT32 bad_block, i , j ; // Since we are going to check the flash interrupt flags within this function, ftl_isr() should not be called. disable_irq(); flash_clear_irq(); // clear any flash interrupt flags that might have been set for (bank = 0; bank < NUM_BANKS; bank++) { //g_misc_meta[bank].g_merge_buff_sect = 0; SETREG(FCP_CMD, FC_COL_ROW_READ_OUT); // FC_COL_ROW_READ_OUT = sensing and data output SETREG(FCP_OPTION, FO_E); // scan list was written in 1-plane mode by install.exe, so there is no FO_P SETREG(FCP_DMA_ADDR, scan_list + bank); // target address should be DRAM or SRAM (see flash.h for rules) SETREG(FCP_DMA_CNT, SCAN_LIST_SIZE); // number of bytes for data output SETREG(FCP_COL, 0); SETREG(FCP_ROW_L(bank), SCAN_LIST_PAGE_OFFSET); // scan list was written to this position by install.exe SETREG(FCP_ROW_H(bank), SCAN_LIST_PAGE_OFFSET); // Tutorial FTL always uses the same row addresses for high chip and low chip flash_issue_cmd(bank, RETURN_ON_ISSUE); // Take a look at the source code of flash_issue_cmd() now. } // This while() statement waits the last issued command to be accepted. // If bit #0 of WR_STAT is one, a flash command is in the Waiting Room, because the target bank has not accepted it yet. while ((GETREG(WR_STAT) & 0x00000001) != 0); // Now, FC_COL_ROW_READ_OUT commands are accepted by all the banks. // Before checking whether scan lists are corrupted or not, we have to wait the completion of read operations. // This code shows how to wait for ALL the banks to become idle. while (GETREG(MON_CHABANKIDLE) != 0); // Now we can check the flash interrupt flags. for (bank = 0; bank < NUM_BANKS; bank++) { UINT32 num_entries = NULL; UINT32 result = OK; if (BSP_INTR(bank) & FIRQ_DATA_CORRUPT) { // Too many bits are corrupted so that they cannot be corrected by ECC. result = FAIL; } else { // Even though the scan list is not corrupt, we have to check whether its contents make sense. UINT32 i; num_entries = read_dram_16(&(scan_list[bank].num_entries)); if (num_entries > SCAN_LIST_ITEMS) { result = FAIL; // We cannot trust this scan list. Perhaps a software bug. } else { for (i = 0; i < num_entries; i++) { UINT16 entry = read_dram_16(&(scan_list[bank].list[i])); UINT16 pblk_offset = entry & 0x7FFF; if (pblk_offset == 0 || pblk_offset >= PBLKS_PER_BANK) { #if OPTION_REDUCED_CAPACITY == FALSE result = FAIL; // We cannot trust this scan list. Perhaps a software bug. #endif } else { // Bit position 15 of scan list entry is high-chip/low-chip flag. // Remove the flag in order to make is_bad_block() simple. write_dram_16(&(scan_list[bank].list[i]), pblk_offset); } } } } if (result == FAIL) { mem_set_dram(scan_list + bank, 0, SCAN_LIST_SIZE); g_misc_meta[bank].g_scan_list_entries = 0; } else { write_dram_16(&(scan_list[bank].num_entries), 0); g_misc_meta[bank].g_scan_list_entries = num_entries; } } // STEP 2 - If necessary, do low-level format // format() should be called after loading scan lists, because format() calls is_bad_block(). init_meta_data(); // save non bad block list for metadata block // block#0 : list, block#1 : misc meta // block#2 ~ map table meta and data for(i = 0 ;i < NUM_BANKS;i++){ bad_block = 2; for(j = 0 ;j < NUM_BANKS_MAX;j++){ while(is_bad_block(i, bad_block) && j < VBLKS_PER_BANK) { bad_block++; } g_bad_list[i][j] = bad_block++; } g_free_start[i] = g_bad_list[i][NUM_BANKS_MAX-1] + 1; } //if (check_format_mark() == FALSE) if( TRUE) { // When ftl_open() is called for the first time (i.e. the SSD is powered up the first time) // format() is called. format(); } else{ loading_misc_meta(); } //*Red// // STEP 3 - initialize sector mapping table pieces // The page mapping table is too large to fit in SRAM and DRAM. // gyuhwa // init_metadata(); // STEP 4 - initialize global variables that belong to FTL g_ftl_read_buf_id = 0; g_ftl_write_buf_id = 0; for (bank = 0; bank < NUM_BANKS; bank++) { g_misc_meta[bank].g_target_row = PAGES_PER_VBLK * (g_free_start[bank]); } flash_clear_irq(); // This example FTL can handle runtime bad block interrupts and read fail (uncorrectable bit errors) interrupts SETREG(INTR_MASK, FIRQ_DATA_CORRUPT | FIRQ_BADBLK_L | FIRQ_BADBLK_H); SETREG(FCONF_PAUSE, FIRQ_DATA_CORRUPT | FIRQ_BADBLK_L | FIRQ_BADBLK_H); enable_irq(); }
void loading_misc_meta() { /*int i; flash_finish(); disable_irq(); flash_clear_irq(); for(i = 0 ;i < NUM_BANKS;i++){ SETREG(FCP_CMD, FC_COL_ROW_READ_OUT); SETREG(FCP_DMA_CNT, sizeof(misc_metadata)); SETREG(FCP_COL, 0); SETREG(FCP_DMA_ADDR, FTL_BUF_ADDR); //SETREG(FCP_DMA_ADDR, &(g_misc_meta[i])); SETREG(FCP_OPTION, FO_P | FO_E ); SETREG(FCP_ROW_L(i), PAGES_PER_VBLK); SETREG(FCP_ROW_H(i), PAGES_PER_VBLK); flash_issue_cmd(i, RETURN_ON_ISSUE); flash_finish(); CLR_BSP_INTR(i,0xff); mem_copy(&(g_misc_meta[i]),FTL_BUF_ADDR,sizeof(misc_metadata)); } enable_irq();*/ UINT32 load_flag = 0; UINT32 bank, page_num; UINT32 load_cnt = 0; flash_finish(); disable_irq(); flash_clear_irq(); // clear any flash interrupt flags that might have been set // scan valid metadata in descending order from last page offset for (page_num = PAGES_PER_VBLK - 1; page_num != ((UINT32) -1); page_num--) { for (bank = 0; bank < NUM_BANKS; bank++) { if (load_flag & (0x1 << bank)) { continue; } // read valid metadata from misc. metadata area nand_page_ptread(bank, 1, page_num, 0, ((sizeof(misc_metadata) + BYTES_PER_SECTOR -1 ) / BYTES_PER_SECTOR), FTL_BUF_ADDR, RETURN_ON_ISSUE); flash_finish(); mem_copy(&g_misc_meta[bank], FTL_BUF_ADDR, sizeof(misc_metadata)); } for (bank = 0; bank < NUM_BANKS; bank++) { if (!(load_flag & (0x1 << bank)) && !(BSP_INTR(bank) & FIRQ_ALL_FF)) { load_flag = load_flag | (0x1 << bank); load_cnt++; } CLR_BSP_INTR(bank, 0xFF); } } ASSERT(load_cnt == NUM_BANKS); enable_irq(); }
void init_jasmine(void) { UINT32 i, bank; extern UINT32 Image$$ER_ZI$$ZI$$Base; extern UINT32 Image$$ER_ZI$$ZI$$Length; // PLL initialization SETREG(CLKSelCon, USE_BYPASS_CLK); SETREG(PllCon, PLL_PD); // power down delay(600); // at least 500ns SETREG(PllCon, PLL_CLK_CONFIG | PLL_PD); // change settings delay(600); // at least 1us SETREG(PllCon, PLL_CLK_CONFIG); // power up while ((GETREG(PllCon) & PLL_LD) == 0); // wait lock SETREG(CLKSelCon, USE_PLL_CLK); // reset hardware modules SETREG(PMU_ResetCon, RESET_SDRAM | RESET_BM | RESET_SATA | RESET_FLASH); // GPIO bits // There are 7 GPIO bits from 0 to 6. // 0: This bit is connected to J2 (Factory Mode jumper). The ROM firmware configures it as input mode. // While main firmware is running, it can be freely used for arbitrary purpose. Beware that a "1" output while // the Factory Mode jumper is set to Normal position (tied to ground) can lead to circuit damage. // A "0" output while the jumper is tied to Vcc will also lead to circuit damage. // 1: This bit is connected to J3 (Boot ROM). The controller hardware checks its status upon reset. // After the reset is done, you can remove the jumper and use the pin as output. // 2 through 5: The IO pins for these bits are shared between MAX3232C (UART chip) and J4. // In order to use J4, you have to turn on the switches of SW4 and turn off the switches 1 through 4 in SW2. // In order to use UART, you have to turn off the switches of SW4 and turn on the switches 1 through 4 in SW2. // 6: This bit is connected to D4 (LED) via SW2. #if OPTION_UART_DEBUG SETREG(GPIO_MOD, 0); SETREG(GPIO_DIR, BIT3 | BIT4 | BIT6); // output pins: 3(UART_TXD), 4(UART_RTS), 6(LED) #else SETREG(GPIO_MOD, 7); SETREG(GPIO_DIR, BIT2 | BIT3 | BIT4 | BIT5 | BIT6); #endif SETREG(GPIO_REG, 0); // initial state of LED is "off" // ZI region is zero-filled by hardware. mem_set_sram((UINT32) &Image$$ER_ZI$$ZI$$Base, 0x00000000, (UINT32) &Image$$ER_ZI$$ZI$$Length); SETREG(PHY_DEBUG, 0x40000139); while((GETREG(PHY_DEBUG) & BIT30) == 1); SETREG(SDRAM_INIT, SDRAM_PARAM_MAIN_FW_INIT); SETREG(SDRAM_REFRESH, SDRAM_PARAM_MAIN_FW_REFRESH); SETREG(SDRAM_TIMING, SDRAM_PARAM_MAIN_FW_TIMING); SETREG(SDRAM_MRS, SDRAM_PARAM_MAIN_FW_MRS); SETREG(SDRAM_CTRL, SDRAM_INITIALIZE); // initialization of SDRAM begins now while (GETREG(SDRAM_STATUS) & 0x00000010); // wait until the initialization completes (200us) for (i = 0; i < DRAM_SIZE / MU_MAX_BYTES; i++) { mem_set_dram(DRAM_BASE + i * MU_MAX_BYTES, 0x00000000, MU_MAX_BYTES); } #if OPTION_UART_DEBUG uart_init(); uart_print("Welcome to OpenSSD"); #endif SETREG(SDRAM_ECC_MON, 0xFFFFFFFF); // configure SDRAM interrupt SETREG(SDRAM_INTCTRL, SDRAM_INT_ENABLE); // clear interrupt flags in DRAM controller SETREG(SDRAM_INTSTATUS, 0xFFFFFFFF); // configure ICU SETREG(APB_ICU_CON, INTR_SATA); // SATA = FIQ, other = IRQ SETREG(APB_INT_MSK, INTR_SATA | INTR_FLASH | INTR_SDRAM | INTR_TIMER_1 | INTR_TIMER_2 | INTR_TIMER_3); // clear interrupt flags in ICU SETREG(APB_INT_STS, 0xFFFFFFFF); flash_reset(); SETREG(FCONF_PAUSE, 0); SETREG(INTR_MASK, 0); for (bank = 0; bank < NUM_BANKS; bank++) { flash_clear_irq(); SETREG(FCP_CMD, FC_COL_ROW_READ_OUT); SETREG(FCP_OPTION, 0x06); // FO_E SETREG(FCP_DMA_ADDR, g_temp_mem); SETREG(FCP_DMA_CNT, BYTES_PER_SECTOR); SETREG(FCP_COL, 0); SETREG(FCP_ROW_L(bank), STAMP_PAGE_OFFSET); SETREG(FCP_ROW_H(bank), STAMP_PAGE_OFFSET); flash_issue_cmd(bank, RETURN_WHEN_DONE); if ( (BSP_INTR(bank) & 0xFE)== 0 ) break; } #if OPTION_FTL_TEST == FALSE sata_reset(); #endif ftl_open(); #if OPTION_FTL_TEST == TRUE extern void ftl_test(); ftl_test(); led(1); while (1); #endif }
// misc + VCOUNT static void load_misc_metadata(void) { UINT32 misc_meta_bytes = NUM_MISC_META_SECT * BYTES_PER_SECTOR; UINT32 vcount_bytes = NUM_VCOUNT_SECT * BYTES_PER_SECTOR; UINT32 vcount_addr = VCOUNT_ADDR; UINT32 vcount_boundary = VCOUNT_ADDR + VCOUNT_BYTES; UINT32 load_flag = 0; UINT32 bank, page_num; UINT32 load_cnt = 0; flash_finish(); disable_irq(); flash_clear_irq(); // clear any flash interrupt flags that might have been set // scan valid metadata in descending order from last page offset for (page_num = PAGES_PER_BLK - 1; page_num != ((UINT32) -1); page_num--) { for (bank = 0; bank < NUM_BANKS; bank++) { if (load_flag & (0x1 << bank)) { continue; } // read valid metadata from misc. metadata area nand_page_ptread(bank, MISCBLK_VBN, page_num, 0, NUM_MISC_META_SECT + NUM_VCOUNT_SECT, FTL_BUF(bank), RETURN_ON_ISSUE); } flash_finish(); for (bank = 0; bank < NUM_BANKS; bank++) { if (!(load_flag & (0x1 << bank)) && !(BSP_INTR(bank) & FIRQ_ALL_FF)) { load_flag = load_flag | (0x1 << bank); load_cnt++; } CLR_BSP_INTR(bank, 0xFF); } } ASSERT(load_cnt == NUM_BANKS); for (bank = 0; bank < NUM_BANKS; bank++) { // misc. metadata mem_copy(&g_misc_meta[bank], FTL_BUF(bank), sizeof(misc_metadata)); // vcount metadata if (vcount_addr <= vcount_boundary) { mem_copy(vcount_addr, FTL_BUF(bank) + misc_meta_bytes, vcount_bytes); vcount_addr += vcount_bytes; } } enable_irq(); }
static void build_bad_blk_list(void) { UINT32 bank, num_entries, result, vblk_offset; scan_list_t* scan_list = (scan_list_t*) TEMP_BUF_ADDR; mem_set_dram(BAD_BLK_BMP_ADDR, NULL, BAD_BLK_BMP_BYTES); disable_irq(); flash_clear_irq(); for (bank = 0; bank < NUM_BANKS; bank++) { SETREG(FCP_CMD, FC_COL_ROW_READ_OUT); SETREG(FCP_BANK, REAL_BANK(bank)); SETREG(FCP_OPTION, FO_E); SETREG(FCP_DMA_ADDR, (UINT32) scan_list); SETREG(FCP_DMA_CNT, SCAN_LIST_SIZE); SETREG(FCP_COL, 0); SETREG(FCP_ROW_L(bank), SCAN_LIST_PAGE_OFFSET); SETREG(FCP_ROW_H(bank), SCAN_LIST_PAGE_OFFSET); SETREG(FCP_ISSUE, NULL); while ((GETREG(WR_STAT) & 0x00000001) != 0); while (BSP_FSM(bank) != BANK_IDLE); num_entries = NULL; result = OK; if (BSP_INTR(bank) & FIRQ_DATA_CORRUPT) { result = FAIL; } else { UINT32 i; num_entries = read_dram_16(&(scan_list->num_entries)); if (num_entries > SCAN_LIST_ITEMS) { result = FAIL; } else { for (i = 0; i < num_entries; i++) { UINT16 entry = read_dram_16(scan_list->list + i); UINT16 pblk_offset = entry & 0x7FFF; if (pblk_offset == 0 || pblk_offset >= PBLKS_PER_BANK) { #if OPTION_REDUCED_CAPACITY == FALSE result = FAIL; #endif } else { write_dram_16(scan_list->list + i, pblk_offset); } } } } if (result == FAIL) { num_entries = 0; // We cannot trust this scan list. Perhaps a software bug. } else { write_dram_16(&(scan_list->num_entries), 0); } g_bad_blk_count[bank] = 0; for (vblk_offset = 1; vblk_offset < VBLKS_PER_BANK; vblk_offset++) { BOOL32 bad = FALSE; #if OPTION_2_PLANE { UINT32 pblk_offset; pblk_offset = vblk_offset * NUM_PLANES; // fix bug@jasmine v.1.1.0 if (mem_search_equ_dram(scan_list, sizeof(UINT16), num_entries + 1, pblk_offset) < num_entries + 1) { bad = TRUE; } pblk_offset = vblk_offset * NUM_PLANES + 1; // fix bug@jasmine v.1.1.0 if (mem_search_equ_dram(scan_list, sizeof(UINT16), num_entries + 1, pblk_offset) < num_entries + 1) { bad = TRUE; } } #else { // fix bug@jasmine v.1.1.0 if (mem_search_equ_dram(scan_list, sizeof(UINT16), num_entries + 1, vblk_offset) < num_entries + 1) { bad = TRUE; } } #endif if (bad) { g_bad_blk_count[bank]++; set_bit_dram(BAD_BLK_BMP_ADDR + bank*(VBLKS_PER_BANK/8 + 1), vblk_offset); } } } }
void test_nand_blocks(void) { // This function is a utility that writes random data to flash pages and verifies them. // This function takes a long time to complete. UINT32 bank, vblk_offset, page_offset, data, bad; #define write_buffer_base DRAM_BASE #define read_buffer_base (DRAM_BASE + BYTES_PER_VBLK) disable_irq(); flash_clear_irq(); mem_set_sram(g_test_result, 0, sizeof(g_test_result)); // Configure the flash controller so that any FIRQ_* does not lead to pause state. SETREG(FCONF_PAUSE, 0); // STEP 1 - prepare random data srand(10); for (page_offset = 0; page_offset < PAGES_PER_VBLK; page_offset++) { data = (rand() & 0xFFFF) | (rand() << 16); mem_set_dram(write_buffer_base + page_offset * BYTES_PER_PAGE, data, BYTES_PER_PAGE); } for (vblk_offset = 1; vblk_offset < VBLKS_PER_BANK; vblk_offset++) { // STEP 2 - erase a block at each bank for (bank = 0; bank < NUM_BANKS; bank++) { UINT32 rbank = REAL_BANK(bank); SETREG(FCP_CMD, FC_ERASE); SETREG(FCP_BANK, rbank); SETREG(FCP_OPTION, FO_P); SETREG(FCP_ROW_L(bank), vblk_offset * PAGES_PER_VBLK); SETREG(FCP_ROW_H(bank), vblk_offset * PAGES_PER_VBLK); while ((GETREG(WR_STAT) & 0x00000001) != 0); SETREG(FCP_ISSUE, NULL); } // STEP 3 - write to every pages of the erased block for (page_offset = 0; page_offset < PAGES_PER_VBLK; page_offset++) { for (bank = 0; bank < NUM_BANKS; bank++) { UINT32 rbank = REAL_BANK(bank); SETREG(FCP_CMD, FC_COL_ROW_IN_PROG); SETREG(FCP_BANK, rbank); SETREG(FCP_OPTION, FO_P | FO_E | FO_B_W_DRDY); SETREG(FCP_DMA_ADDR, write_buffer_base + page_offset * BYTES_PER_PAGE); SETREG(FCP_DMA_CNT, BYTES_PER_PAGE); SETREG(FCP_COL, 0); SETREG(FCP_ROW_L(bank), vblk_offset * PAGES_PER_VBLK + page_offset); SETREG(FCP_ROW_H(bank), vblk_offset * PAGES_PER_VBLK + page_offset); while ((GETREG(WR_STAT) & 0x00000001) != 0); SETREG(FCP_ISSUE, NULL); } } // STEP 4 - check the FC_ERASE and FC_COL_ROW_IN_PROG results. bad = 0; while (GETREG(MON_CHABANKIDLE) != 0); for (bank = 0; bank < NUM_BANKS; bank++) { if (BSP_INTR(bank) & (FIRQ_BADBLK_H | FIRQ_BADBLK_L)) { bad |= (1 << bank); CLR_BSP_INTR(bank, 0xFF); g_test_result[bank].erase_prog_fail++; } } // STEP 5 - read and verify // We check ECC/CRC results for verification. for (page_offset = 0; page_offset < PAGES_PER_VBLK; page_offset++) { for (bank = 0; bank < NUM_BANKS; bank++) { UINT32 rbank = REAL_BANK(bank); if (bad & (1 << bank)) continue; SETREG(FCP_CMD, FC_COL_ROW_READ_OUT); SETREG(FCP_BANK, rbank); SETREG(FCP_OPTION, FO_P | FO_E); SETREG(FCP_DMA_ADDR, read_buffer_base + bank * BYTES_PER_PAGE); SETREG(FCP_DMA_CNT, BYTES_PER_PAGE); SETREG(FCP_COL, 0); SETREG(FCP_ROW_L(bank), vblk_offset * PAGES_PER_VBLK + page_offset); SETREG(FCP_ROW_H(bank), vblk_offset * PAGES_PER_VBLK + page_offset); while ((GETREG(WR_STAT) & 0x00000001) != 0); SETREG(FCP_ISSUE, NULL); } } // STEP 6 - check the FC_COL_ROW_READ_OUT results while (GETREG(MON_CHABANKIDLE) != 0); for (bank = 0; bank < NUM_BANKS; bank++) { if (BSP_INTR(bank) & FIRQ_DATA_CORRUPT) { bad |= (1 << bank); CLR_BSP_INTR(bank, 0xFF); g_test_result[bank].read_fail++; } } // STEP 7 - erase the blocks, but not the bad ones for (bank = 0; bank < NUM_BANKS; bank++) { UINT32 rbank = REAL_BANK(bank); if (bad & (1 << bank)) continue; SETREG(FCP_CMD, FC_ERASE); SETREG(FCP_BANK, rbank); SETREG(FCP_OPTION, FO_P); SETREG(FCP_ROW_L(bank), vblk_offset * PAGES_PER_VBLK); SETREG(FCP_ROW_H(bank), vblk_offset * PAGES_PER_VBLK); while ((GETREG(WR_STAT) & 0x00000001) != 0); SETREG(FCP_ISSUE, NULL); } } // Now that bad blocks contain non-0xFF data, it is a good time to use install.exe to scan bad blocks. }