//! Returns the first free block seen, scanning downstream. //! //! It uses g_curr_block_addr (current block address of each device). //! //! @param i_dev device number on which we look for a free block //! //! @return the physical block number //! static U16 nf_fetch_free_block(U8 i_dev) { U16 block_addr= g_curr_block_addr[i_dev]; while ( block_addr>=g_nf_first_block ) { nfc_read_spare_byte( g_byte, 8, nf_block_2_page( block_addr ) ); if(( 0xFF ==g_byte[G_OFST_BLK_STATUS ] ) // the block is valid && ( NFC_BLK_ID_DATA==g_byte[NFC_SPARE_OFST_1_BLK_ID ] ) // the block is a data block && ( 0xFF ==g_byte[NFC_SPARE_OFST_6_LBA ] ) // and is not affected && ( 0xFF ==g_byte[NFC_SPARE_OFST_6_LBA+1 ] )) { // Since we rebuild the flash, we should not see any of these blocks // Assert( NFC_BLK_ID_SUBLUT!=g_byte[NFC_SPARE_OFST_1_BLK_ID] ); Assert( NFC_BLK_ID_FBB !=g_byte[NFC_SPARE_OFST_1_BLK_ID] ); // Find a free and valid block addr. Store the current position // g_curr_block_addr[i_dev] = block_addr-1; return block_addr; } block_addr-=1 ; } // This situation is dramatic: it should never happen ! // Force Rebuild on next startup nfc_erase_block( nf_block_2_page( g_fbb_block_addr ), TRUE ); while(1); Assert( FALSE ) ; // Not enough free blocks: fatal error! }
//! Cleanup the memory by erasing all the management blocks. //! //! The sub-LUT blocks, the recovery block and the free-blocks block //! will be erased on any devices. //! //! @param none //! void nf_cleanup_memory(void) { U8 i_dev =0; U16 i_block=0; U8 block_valid; U8 block_id; // Scan all the devices and looks for: // - the sub-LUT // - the recovery block // - the free-blocks block // for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { // Select the devices // Nfc_action(NFC_ACT_DEV_SELECT, i_dev); for( i_block=g_nf_first_block ; i_block<G_N_BLOCKS ; i_block++ ) { nfc_open_page_read( nf_block_2_page(i_block), NF_SPARE_POS); block_valid = Nfc_rd_data_fetch_next(); block_id = Nfc_rd_data() ; if ( block_valid!=0xFF ) { continue; // The block is bad } if(( NFC_BLK_ID_SUBLUT==block_id ) || ( NFC_BLK_ID_FBB ==block_id )) { nfc_erase_block( nf_block_2_page(i_block), TRUE ) ; if ( FAIL==nfc_check_status() ) { nfc_mark_bad_block( nf_block_2_page(i_block) ); } } } // for( i_block... } // for( i_dev... } // nf_cleanup_memory
//! Refines the position of the 'block' index according to a particular //! pattern. This allow to find exactely where are stored the last //! sub-LUT, Free_blocks or Recovery entry. //! The index is roughly initialized at the beginning of the block that holds //! the 'pattern' at the location 1 of the spare zone. The function parses the //! block until the pattern is no more found. //! //! @param block_addr physical block address //! @param inc increment //! @param pattern pattern which is scanned //! //! @return the offset (in page) of the last valid entry in the block //! static U8 nf_refine_index( U16 block_addr , U8 inc , U8 pattern) { _MEM_TYPE_SLOW_ U8 u8_tmp; _MEM_TYPE_SLOW_ U8 val=0; do { val+= inc; // Assume that the pattern has already be seen previously if( val>=SIZE_BLOCK_PAGE ) { break; } nfc_open_page_read( nf_block_2_page(block_addr) + val , NF_SPARE_POS+NFC_SPARE_OFST_1_BLK_ID ); u8_tmp = Nfc_rd_data(); } while( pattern==u8_tmp ); val-= inc; // come back to last valid entry Assert( val<(1<<G_SHIFT_BLOCK_PAGE) ); // The offset shall not be outside the block return val; }
//! @brief Rebuild the memory and create LUT, Recovery and Free-blocks blocks. //! //! TBD //! //! It uses s_n_invalid_blocks (number of invalid blocks) and //! g_curr_block_addr (current block address of each device). //! //! @return a status: //! PASS if there are no error; //! FAIL a programmation error occured: the block is marked as bad. The //! function must be recall. //! static Status_bool nf_rebuild ( void ) { Status_bool status_bool=PASS; Bool b_duplicate; U8 i_sub_lut; U8 i_dev =0; _MEM_TYPE_SLOW_ U16 i_block=0; _MEM_TYPE_SLOW_ U16 u16_tmp; _MEM_TYPE_SLOW_ U16 sub_lut_log_sz; _MEM_TYPE_SLOW_ U16 log_block_addr; _MEM_TYPE_SLOW_ U16 log_block_addr_min; _MEM_TYPE_SLOW_ U16 log_block_addr_max; // Refine the computation // s_n_invalid_blocks[S_MNGT_DEV] += 1 // Need a block for the Free-blocks block + (G_N_BLOCKS*NF_N_DEVICES)/NF_SUBLUT_SIZE // and one for each sub-LUT ; // Take the max number of invalid blocks of each devices // u16_tmp=s_n_invalid_blocks[0] ; for ( i_dev=1 ; i_dev<NF_N_DEVICES ; i_dev++ ) { u16_tmp=Max( u16_tmp, s_n_invalid_blocks[i_dev] ); } // Take the max number of quarantine blocks of each devices // i_sub_lut=s_n_quarantine_blocks[0] ; for ( i_dev=1 ; i_dev<NF_N_DEVICES ; i_dev++ ) { i_sub_lut=Max( i_sub_lut, s_n_quarantine_blocks[i_dev] ); } sub_lut_log_sz = (U16)NF_N_DEVICES*(G_N_BLOCKS -g_nf_first_block -u16_tmp); // Finally compute the number of exportable physical blocks and free blocks // Assert( u16_tmp<(G_N_BLOCKS -g_nf_first_block) ); g_n_export_blocks= (U16)( ((U32)( (U32)sub_lut_log_sz ) * 1000) / 1024); g_n_export_blocks= Align_down( g_n_export_blocks, NF_N_DEVICES); g_n_free_blocks = (U16)sub_lut_log_sz - g_n_export_blocks; g_n_free_blocks -= (U16)NF_N_DEVICES*i_sub_lut; if( g_n_free_blocks<=NF_LOW_N_FREE_THRESHOLD ) { while(1); // TBD } Assert( g_n_free_blocks>0 ); Assert( g_n_free_blocks<(1L<<NF_SHIFT_PAGE_BYTE) ); // limit the free blocks in order to fit in 1 page // Compute the number of needed sub-LUT // Affect to each management block a free block address // Nfc_action(NFC_ACT_DEV_SELECT, S_MNGT_DEV); g_fbb_block_addr = nf_fetch_free_block(S_MNGT_DEV); nfc_erase_block( nf_block_2_page( g_fbb_block_addr ), TRUE ); g_n_sub_lut= 0; u16_tmp = g_n_export_blocks; //#error il faut positionner les index(LUT, FBB, RCV...) while(1) { Assert( g_n_sub_lut<N_SUBLUT ); g_lut_block_addr [g_n_sub_lut]=nf_fetch_free_block(S_MNGT_DEV); g_lut_block_index[g_n_sub_lut]=0; nfc_erase_block( nf_block_2_page( g_lut_block_addr [g_n_sub_lut] ), TRUE ); g_n_sub_lut++; if( u16_tmp>NF_SUBLUT_SIZE ) u16_tmp-=NF_SUBLUT_SIZE; else break; } g_last_sub_lut_log_sz=u16_tmp/NF_N_DEVICES; // Build the sub-LUTs // for ( i_sub_lut=0 ; i_sub_lut<g_n_sub_lut ; ) { U8 n_sublut_in_buf = g_n_sub_lut - i_sub_lut; // Count remaining sublut to build log_block_addr_max = log_block_addr_min = (U16)i_sub_lut<<(NF_SHIFT_SUBLUT_PHYS-NF_SHIFT_N_DEVICES); // first included if( n_sublut_in_buf>(NF_PAGE_BUFFER_SIZE/(2*NF_SUBLUT_SIZE)) ) { n_sublut_in_buf = NF_PAGE_BUFFER_SIZE/(2*NF_SUBLUT_SIZE); log_block_addr_max += ((U16)n_sublut_in_buf)*g_sub_lut_log_sz; // last not included } else { log_block_addr_max += ((U16)n_sublut_in_buf-1)*g_sub_lut_log_sz +g_last_sub_lut_log_sz; // last not included } nf_init_buffer(); // Report affected logical blocks // u16_tmp=g_n_export_blocks/NF_N_DEVICES; // Number of logical blocks used for the mass storage b_duplicate=FALSE; for ( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { Nfc_action(NFC_ACT_DEV_SELECT, i_dev); g_block_to_kill[i_dev]=0xFFFF; for ( i_block=g_nf_first_block ; i_block<G_N_BLOCKS ; i_block++ ) { nfc_read_spare_byte( g_byte, 8, nf_block_2_page(i_block) ); if(( 0xFF !=g_byte[G_OFST_BLK_STATUS ] ) // The block is bad || ( NFC_BLK_ID_DATA!=g_byte[NFC_SPARE_OFST_1_BLK_ID ] ) // or is not a data block || ( ( 0xFF ==g_byte[NFC_SPARE_OFST_6_LBA ] ) // or is not affected && ( 0xFF ==g_byte[NFC_SPARE_OFST_6_LBA+1 ] ) ) ) { continue; } MSB(log_block_addr) = g_byte[NFC_SPARE_OFST_6_LBA ]; LSB(log_block_addr) = g_byte[NFC_SPARE_OFST_6_LBA+1]; if( log_block_addr>=u16_tmp ) { // The LBA seems bad: it does not fit in any LUT. This happens when unplugging the player. // Block is erased. // Anyway, stay in the loop to track similar problems. nfc_erase_block( nf_block_2_page(i_block), TRUE ); status_bool=FAIL; } if(( log_block_addr>=log_block_addr_min ) && ( log_block_addr< log_block_addr_max )) { U16 ofst=2*((U16)i_dev + (log_block_addr%((U16)NF_PAGE_BUFFER_SIZE/2/NF_N_DEVICES))*NF_N_DEVICES) ; if( ( 0xFF==g_page_buffer[ ofst ] ) && ( 0xFF==g_page_buffer[ ofst +1 ] ) ) { // no redundant phys blocks Assert( ( ofst +1 ) < NF_PAGE_BUFFER_SIZE ); g_page_buffer[ ofst ] = MSB(i_block); g_page_buffer[ ofst +1 ] = LSB(i_block); } else { // A duplicated logical block is detected. This happens when unplugging the player. // Anyway, stay in the loop to track any other redundant blocks, for that sub-LUT. _MEM_TYPE_SLOW_ U16 tmp_addr; MSB(tmp_addr)=g_page_buffer[ ofst ]; LSB(tmp_addr)=g_page_buffer[ ofst +1 ]; //trace("Dupl "); trace_hex32(tmp_addr); trace("-"); trace_hex32(i_block);; trace("\n\r"); if(0xFFFF!=g_block_to_kill[i_dev]) { // !!! There are more than 1 duplicated block on the device. This should never happen... nfc_erase_block( nf_block_2_page(g_block_to_kill[i_dev]), TRUE ); return FAIL; } b_duplicate=TRUE; g_log_block_id=log_block_addr; nfc_open_page_read( nf_block_2_page(i_block) , NF_SPARE_POS+NFC_SPARE_OFST_3_BYTE_3 ); if( NFC_OFST_3_DATA_DST!=Nfc_rd_data_fetch_next() ) { trace("1. Src block="); trace_hex16(i_block); trace_nl(); trace("1. Dst block="); trace_hex16(tmp_addr); trace_nl(); //nfc_print_block(i_block, 0); //nfc_print_block(tmp_addr, 0); //while(1); g_block_to_kill[i_dev]=i_block; // source block g_phys_page_addr[i_dev] = nf_block_2_page( tmp_addr ); // recipient block } else { trace("2. Src block="); trace_hex16(tmp_addr); trace_nl(); trace("2. Dst block="); trace_hex16(i_block); trace_nl(); //nfc_print_block(tmp_addr, 0); //nfc_print_block(i_block, 0); //while(1); g_block_to_kill[i_dev]= tmp_addr ; // source block g_page_buffer[ ofst ]=MSB(i_block); g_page_buffer[ ofst +1 ]=LSB(i_block); g_phys_page_addr[i_dev] = nf_block_2_page( i_block ); // recipient block } } } } // for ( i_block ../.. } // for ( i_dev ../.. if( b_duplicate ) { U8 i_page; U8 i_sect; trace("recovery\n\r"); // Test that recovery can be done for ( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { if( 0xFFFF==g_block_to_kill[i_dev] ) { // !Ooops... we can not recover from that case since there are duplication // only on on device for ( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { if( 0xFFFF!=g_block_to_kill[i_dev] ) { nfc_erase_block( nf_block_2_page(g_block_to_kill[i_dev]), TRUE ); } } return FAIL; } } // Initialize variable for nf_copy_tail g_curr_dev_id=0; g_last_log_sector= ((U32)g_log_block_id) << S_SHIFT_LOG_BLOCK_SECTOR; // Look for last written sector for( i_page=0 ; i_page<SIZE_BLOCK_PAGE ; i_page++ ) { Nfc_action(NFC_ACT_DEV_SELECT, g_curr_dev_id); // open the current device for( i_sect=0 ; i_sect<SIZE_PAGE_SECTOR ; i_sect++ ) { nfc_open_page_read( g_phys_page_addr[g_curr_dev_id] , NF_SPARE_POS + (((U16)i_sect)*16) + NFC_SPARE_OFST_6_LBA ); if(( 0xFF==Nfc_rd_data_fetch_next() ) && ( 0xFF==Nfc_rd_data_fetch_next() )) goto recovery_exit; else { g_last_log_sector++; trace("g_last_log_sector="); trace_hex32(g_last_log_sector); trace_nl(); } } g_phys_page_addr[g_curr_dev_id]++; // update the current physical page of the current device g_curr_dev_id++; // update the current device if( g_curr_dev_id==NF_N_DEVICES ) { g_curr_dev_id=0; } trace("g_curr_dev_id="); trace_hex(g_curr_dev_id); trace_nl(); trace("g_phys_page_addr="); trace_hex32(g_phys_page_addr[g_curr_dev_id]); trace_nl(); } recovery_exit: trace("recovery stop on g_last_log_sector="); trace_hex32(g_last_log_sector); trace_nl(); trace("g_curr_dev_id="); trace_hex(g_curr_dev_id); trace_nl(); trace("g_phys_page_addr="); trace_hex32(g_phys_page_addr[g_curr_dev_id]); trace_nl(); //while(1); nf_copy_tail(); return FAIL; } // At least one redundant have been found: the LUT must be rebuilt since the fetch of free block // may not have seen that affected block (redundant) are in fact free. if( PASS!=status_bool ) { return FAIL; } // Affect a free physical block to the logical block // for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { Nfc_action(NFC_ACT_DEV_SELECT, i_dev); for(u16_tmp=0 ; u16_tmp<(log_block_addr_max-log_block_addr_min) ; u16_tmp++ ) { U16 ofst=2*((U16)i_dev + u16_tmp*NF_N_DEVICES); if(( 0xFF==g_page_buffer[ofst ] ) && ( 0xFF==g_page_buffer[ofst+1] )) { i_block=nf_fetch_free_block(i_dev); Assert( ofst+1<NF_PAGE_BUFFER_SIZE); g_page_buffer[ofst ] = MSB(i_block); g_page_buffer[ofst+1] = LSB(i_block); } } } // for ( i_dev ../.. // Each sub-LUT will fit in a physical page and will be of the same size // except the last one which contains less // for( ; n_sublut_in_buf!=0 ; n_sublut_in_buf--, i_sub_lut++ ) { sub_lut_log_sz= ( i_sub_lut==(g_n_sub_lut-1) ) ? g_last_sub_lut_log_sz : g_sub_lut_log_sz ; // Write the sub-LUT in the page // status_bool = nf_write_lut(i_sub_lut%(NF_PAGE_BUFFER_SIZE/(2*NF_SUBLUT_SIZE)), i_sub_lut, sub_lut_log_sz); if ( PASS!=status_bool ) { nfc_mark_bad_block( nf_block_2_page( g_lut_block_addr[i_sub_lut] ) ); return FAIL; } } } //#error: si recovery, il faut effacer la lut en question. Il faut donc la reconstruire. // Pour cela, il faut trouver des blocs libres. // 1ere methode: effacer aussi le free-blocks block et le reconstruire, ainsi que la sub-LUT // 2eme methode: marquer les free block pour les reconnaitre et reconstruire la sub lut // Build the free-blocks block // First, fill the internal buffer with the free blocks // for ( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { Nfc_action(NFC_ACT_DEV_SELECT, i_dev); for ( u16_tmp=0 ; u16_tmp<(g_n_free_blocks/NF_N_DEVICES) ; u16_tmp++ ) { // This define is better than using a variable that holds the expression... #define OFST (2*(i_dev + u16_tmp*NF_N_DEVICES)) i_block=nf_fetch_free_block(i_dev); nfc_erase_block( nf_block_2_page(i_block), TRUE ); Assert( OFST <NF_PAGE_BUFFER_SIZE); Assert( OFST +1<NF_PAGE_BUFFER_SIZE); Assert( i_block>=g_nf_first_block ); Assert( i_block< G_N_BLOCKS ); g_page_buffer[OFST ] = MSB(i_block); g_page_buffer[OFST +1] = LSB(i_block); #undef OFST } } // Then write the buffer in the free-blocks block // Note that the list of free-blocks holds on one page only; the // algo is thus made for both 512B and 2kB pages. // g_fbb_block_index=0; status_bool = nf_write_fbb(); if ( PASS!=status_bool ) { nfc_mark_bad_block( nf_block_2_page( g_fbb_block_addr ) ); return FAIL; } //#error Effacer les free blocks !!!!!! //#error il faut determiner s_lut_index[all] pour les sub-lut existantes //#error si il existe un bloc de recovery, alors la lut associée n'est plus valide //#error rendre parametrable la taille du buffer (actuellement 2k). Si <512 et no partial prog: fatal error //nf_init_buffer(); // Cleanup the buffer return PASS; }
//! Scan the memory and looks for sub-LUT, free-blocks block and recovery blocks //! //! @param none //! //! @return a status: //! PASS if the scan has been succesfully executed; //! FAIL if the scan encountered any problem //! static Status_bool nf_scan( void ) { U8 i_dev =0; U16 i_block=0; U8 n_quarantine_blocks=0; U16 n_invalid_blocks=0; g_last_sub_lut_log_sz =(U16)-1; g_sub_lut_log_sz =(U16)NF_SUBLUT_SIZE/NF_N_DEVICES; // Initialize the recovery structure. This should be done by the startup ! // g_is_found_lut =FALSE; g_is_found_fbb =FALSE; g_fatal =FALSE; g_n_real_sub_lut=0; trace("nf_scan"); trace_nl(); // Scan all the devices and looks for: // - the sub-LUT blocks // - the recovery block // - the free-blocks block // for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { n_invalid_blocks = 0; n_quarantine_blocks= 0; g_curr_block_addr[i_dev]= G_N_BLOCKS -1; // points on the last block trace("Device "); trace_hex(i_dev); trace("\n\r"); Nfc_action(NFC_ACT_DEV_SELECT, i_dev); for( i_block=g_nf_first_block ; i_block<G_N_BLOCKS ; i_block++ ) { nfc_read_spare_byte( g_byte, 16, nf_block_2_page(i_block) ); if ( g_byte[G_OFST_BLK_STATUS]!=0xFF ) { n_invalid_blocks +=1 ; trace_hex16(i_block); trace(" ("); trace_u32(i_block); trace("): bad Block\n\r"); continue; // The block is bad } if(( g_byte[NFC_SPARE_OFST_1_BLK_ID]!=NFC_BLK_ID_SUBLUT ) && ( g_byte[NFC_SPARE_OFST_1_BLK_ID]!=NFC_BLK_ID_FBB ) && ( g_byte[NFC_SPARE_OFST_1_BLK_ID]!=NFC_BLK_ID_QUARANTINE ) && ( g_byte[NFC_SPARE_OFST_1_BLK_ID]!=NFC_BLK_ID_DATA )) { n_invalid_blocks +=1; trace_hex16(i_block); trace(" ("); trace_u32(i_block); trace("): Unknown\n\r"); continue; } else if ( g_byte[NFC_SPARE_OFST_1_BLK_ID]==NFC_BLK_ID_QUARANTINE ) { n_quarantine_blocks +=1; trace_hex16(i_block); trace(" ("); trace_u32(i_block); trace("): Quarantine\n\r"); continue; } else if ( g_byte[NFC_SPARE_OFST_1_BLK_ID]==NFC_BLK_ID_SUBLUT ) { n_invalid_blocks +=1; if ( i_dev==S_MNGT_DEV ) { U8 sub_lut_id = g_byte[NFC_SPARE_OFST_2_BYTE_2]; g_n_sub_lut = g_byte[NFC_SPARE_OFST_4_BYTE_4]; g_is_found_lut = TRUE; g_n_real_sub_lut++; g_lut_block_addr[sub_lut_id] = i_block; if ( sub_lut_id==(g_n_sub_lut-1) ) { MSB(g_last_sub_lut_log_sz)= g_byte[NFC_SPARE_OFST_6_LBA ]; LSB(g_last_sub_lut_log_sz)= g_byte[NFC_SPARE_OFST_6_LBA+1]; } g_lut_block_index[sub_lut_id]= nf_refine_index(i_block, 1, NFC_BLK_ID_SUBLUT); trace_hex16(i_block); trace(" ("); trace_u32(i_block); trace("): SUB-LUT (id ");trace_hex(sub_lut_id);trace(" ofst "); trace_hex(g_lut_block_index[sub_lut_id]); trace(")\n\r"); continue ; } else { // LUT found on bad NF g_fatal=TRUE; break; } } else if ( g_byte[NFC_SPARE_OFST_1_BLK_ID]==NFC_BLK_ID_FBB ) { n_invalid_blocks +=1; if ( i_dev==S_MNGT_DEV ) { if ( TRUE==g_is_found_fbb ) { g_fatal=TRUE; // already found break; } g_fbb_block_addr = i_block; g_fbb_block_index = nf_refine_index(i_block, 1, NFC_BLK_ID_FBB); nfc_read_spare_byte( g_byte, 16, nf_block_2_page(i_block) + (U32)g_fbb_block_index ); // Reload if( NFC_OFST_6_FBB_VALID!=g_byte[NFC_SPARE_OFST_6_LBA] ) { g_fatal=TRUE; // FBB not valid. Force rebuild break; } MSB(g_n_free_blocks) = g_byte[NFC_SPARE_OFST_2_BYTE_2]; LSB(g_n_free_blocks) = g_byte[NFC_SPARE_OFST_3_BYTE_3]; s_nfd_rev = g_byte[NFC_SPARE_OFST_4_BYTE_4]; MSB(g_n_export_blocks) = g_byte[NFC_SPARE_OFST_EXPORT]; LSB(g_n_export_blocks) = g_byte[NFC_SPARE_OFST_EXPORT+1]; trace(" g_n_free_blocks="); trace_hex16(g_n_free_blocks); trace_nl(); trace(" g_n_export_blocks="); trace_hex16(g_n_export_blocks); trace_nl(); g_is_found_fbb=TRUE; trace_hex16(i_block); trace(" ("); trace_u32(i_block); trace("): FBB (ofst "); trace_hex( g_fbb_block_index ); trace(")\n\r"); continue ; } else { g_fatal=TRUE; break; } } } // for ( i_block // A fatal error on one device is enough to cleanup all the devices ! // s_n_invalid_blocks[ i_dev]= n_invalid_blocks; s_n_quarantine_blocks[i_dev]= n_quarantine_blocks; if ( TRUE==g_fatal ) { break; } } // for ( i_dev return (g_fatal==TRUE) ? FAIL: PASS; } // nf_scan
// main function int main(void) { bool valid_block_found[NF_N_DEVICES]; U32 u32_nf_ids, i_dev, i_block; U8 maker_id, device_id; U32 i, j; // ECCHRS options. static const ecchrs_options_t ECCHRS_OPTIONS = { .typecorrect = ECCHRS_TYPECORRECT_4_BIT, .pagesize = ECCHRS_PAGESIZE_4_BIT_2112_W }; // switch to oscillator 0 pcl_switch_to_osc(PCL_OSC0, FOSC0, OSC0_STARTUP); // init debug serial interface init_dbg_rs232(FOSC0); nf_init(FOSC0); // init the nand flash driver with the correct HSB frequency nf_unprotect(); print_dbg("\r\nECCHRS example using Nand Flash.\r\n================================\r\n\r\n"); // - Simple test of the NF communication through the SMC. // - Find all bad blocks. // - Find a valid block for the remaining tests. nf_reset_nands(NF_N_DEVICES); print_dbg("\tCPU, HSB is at 12000000Mhz.\r\n"); print_dbg("\tPBA, PBB is at 12000000Mhz.\r\n"); print_dbg("\tDetecting Nand Flash device(s).\r\n"); for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { // Performs some init here... valid_block_found[i_dev] = false; // Test Maker and Device ids u32_nf_ids = nf_read_id( NF_READ_ID_CMD, i_dev ); maker_id = MSB0(u32_nf_ids); device_id = MSB1(u32_nf_ids); print_dbg("\t\tNF"); print_dbg_hex(i_dev); print_dbg(" [Maker="); print_dbg_hex(maker_id); print_dbg("] [Device="); print_dbg_hex(device_id); print_dbg("]\r\n"); if( maker_id==M_ID_MICRON ) { print_dbg("\t\t Micron chip"); if( device_id==0xDA ){ print_dbg("- MT29F2G08AACWP device\r\n"); } else { print_dbg("- *** Error: unexpected chip detected. Please check the board settings and hardware.\r\n"); return -1; } } else { print_dbg("\t\t *** Error: unexpected chip detected. Please check the board settings and hardware.\r\n"); return -1; } } // Looking for valid blocks for the test. for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { nf_select(i_dev); for( i_block=0 ; i_block<G_N_BLOCKS ; i_block++ ) { nf_open_page_read( nf_block_2_page(i_block), NF_SPARE_POS + G_OFST_BLK_STATUS ); if( (nf_rd_data()==0xFF) ) { // The block is valid. print_dbg("\tValid block found ("); print_dbg_ulong(i_block); print_dbg(") on NF "); print_dbg_hex(i_dev); print_dbg("\r\n"); valid_block_found[i_dev]= true; valid_block_addr[i_dev] = i_block; break; } } } for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) if( !valid_block_found[i_dev] ) { print_dbg("Error: no valid blocks found.\r\n"); return 0; } print_dbg("\tECCHRS IP ver "); print_dbg_ulong(ecchrs->version); print_dbg("("); print_dbg_hex(ecchrs->version); print_dbg(")"); print_dbg("\r\n"); // Work with NF 0 from now... nf_select(0); // Setup the ECCHRS. ecchrs_init(&AVR32_ECCHRS, &ECCHRS_OPTIONS); // Ensures that the block is erased for the test. print_dbg("\tErasing a free block for the test.\r\n"); nf_erase_block(nf_block_2_page(valid_block_addr[0]), false); // Reset the ECCHRS state machine. ecchrs_reset(&AVR32_ECCHRS); // Program a simple patterns in the first page. print_dbg("\tProgramming the first page with a simple pattern.\r\n"); nf_open_page_write( nf_block_2_page(valid_block_addr[0]), 0); for ( i = 0; i < 2048/2; i++ ) { U16 val = i; nf_wr_data(MSB(val)); nf_wr_data(LSB(val)); } // Extract the ECCs and store them at the end of the page. // [2054; 2063]: codeword 0:9 // [2070; 2079]: codeword 10:19 // [2086; 2095]: codeword 20:29 // [2102; 2111]: codeword 30:39 // Since we use the Random Data Output command, we need to freeze // the ECCHRS in order to keep our ECC codewords unchanged. print_dbg("\tExtracting the ECCHRS codewords and store them in the page.\r\n"); ecchrs_freeze(&AVR32_ECCHRS); // not needed if ECCHRS reaches the end of page. for ( i=0 ; i<4 ; i++ ) { U16 offset = 2048+6+16*i; nf_wr_cmd(NF_RANDOM_DATA_INPUT_CMD); nf_wr_addr( LSB(offset) ); nf_wr_addr( MSB(offset) ); for ( j=0 ; j<10 ; j++ ) nf_wr_data( ecchrs_get_cw(&AVR32_ECCHRS, i*10+j) ); } ecchrs_unfreeze(&AVR32_ECCHRS); nf_wr_cmd(NF_PAGE_PROGRAM_CMD); // Now let's test the ECC verification. print_dbg("\tReading the first page.\r\n"); nf_open_page_read( nf_block_2_page(valid_block_addr[0]), 0); for( i=0 ; i<2048 ; i++ ) nf_rd_data(); print_dbg("\tChecking if the data are valid thanks to the ECCHRS codewords.\r\n"); for ( i=0 ; i<4 ; i++ ) { U16 offset = 2048+6+16*i; ecchrs_freeze(&AVR32_ECCHRS); nf_wr_cmd(NF_RANDOM_READ_CMD_C1); nf_wr_addr( LSB(offset) ); nf_wr_addr( MSB(offset) ); nf_wr_cmd(NF_RANDOM_READ_CMD_C2); ecchrs_unfreeze(&AVR32_ECCHRS); for ( j=0 ; j<10 ; j++ ) nf_rd_data(); } // Check if there is any errors after the read of the page. i = ecchrs_4bit_check_error(&AVR32_ECCHRS); print_dbg("\tSR1 is "); print_dbg_ulong(i); print_dbg("("); print_dbg_hex(i); print_dbg(")"); print_dbg("\r\n"); if(i&(15)) { print_dbg("\tERROR: ECCHRS detects some errors in the sectors 1, 2, 3 or 4\r\n"); } else print_dbg("\tNo error detected.\r\n"); // Let the block free. nf_erase_block(nf_block_2_page(valid_block_addr[0]), false); return 0; }
/*! \brief Main function. */ int main(void) { bool valid_block_found[NF_N_DEVICES]; U32 u32_nf_ids, i_dev, i_block; U8 maker_id, device_id, byte3; U32 i, time_s, time_e; U8 data; // start init sysclk_init(); // Enable clock for require module sysclk_enable_hsb_module(SYSCLK_EBI); sysclk_enable_pbb_module(SYSCLK_SMC_REGS); sysclk_enable_pbb_module(SYSCLK_HMATRIX); // Initialize the board. // The board-specific conf_board.h file contains the configuration of the board // initialization. board_init(); init_stdio(); #ifdef NF_ADDON // addon nandflash board for EVK1104 (not mounted by default) // Unselect NF on board gpio_set_gpio_pin(AVR32_PIN_PX53); gpio_set_gpio_pin(AVR32_PIN_PX52); #endif nf_init(sysclk_get_cpu_hz()); nf_unprotect(); printf("\x0C"); printf("Nand Flash example.\r\n===================\r\n\r\n"); // - Simple test of the NF communication through the SMC. // - Find all bad blocks. // - Find a valid block for the remaining tests. nf_reset_nands(NF_N_DEVICES); printf("\tDetecting Nand Flash device(s).\r\n"); for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { // Performs some init here... valid_block_found[i_dev] = false; // Test Maker and Device ids u32_nf_ids = nf_read_id( NF_READ_ID_CMD, i_dev ); maker_id = MSB0(u32_nf_ids); device_id = MSB1(u32_nf_ids); byte3 = MSB3(u32_nf_ids); printf("\t\tNF %ld: [Maker=0x%02x] [Device=0x%02x] [byte3=0x%02x]\r\n", i_dev, maker_id, device_id, byte3); if( maker_id==M_ID_MICRON ) { printf("\t\t Micron chip"); if( (device_id==0xDA) && (byte3==0x15) ) printf("- MT29F2G08AACWP device\r\n"); else if( (device_id==0xDA) && (byte3==0x95) ) printf("- MT29F2G08AADWP device\r\n"); else { printf("- *** Error: unexpected chip detected. Please check the board settings and hardware.\r\n"); return -1; } } else { printf("\t\t *** Error: unexpected chip detected. Please check the board settings and hardware.\r\n"); return -1; } } printf("\r\n\tTesting bad blocks.\r\n"); for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { printf("\t\tNF %ld:\r\n", i_dev); nf_select(i_dev); for( i_block=0 ; i_block<G_N_BLOCKS ; i_block++ ) { nf_open_page_read( nf_block_2_page(i_block), NF_SPARE_POS + G_OFST_BLK_STATUS ); if( (nf_rd_data()!=0xFF) ) { // The block is bad. printf("\t\t\tBlock %ld (0x%lx) is bad.\r\n", i_block, i_block); } else { if( !valid_block_found[i_dev] ) { valid_block_found[i_dev]= true; valid_block_addr[i_dev] = i_block; printf("\t\t\tFirst valid block is at address %ld (0x%lx).\r\n", i_block, i_block); } } } } for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) if( !valid_block_found[i_dev] ) { printf("Error %d\r\n", __LINE__); return 0; } // - Ensure good NF behaviour through simple commands. // Erase, Program, Read // - Measures NF timings. printf("\r\n\tMeasuring NF timings.\r\n"); for( i_dev=0 ; i_dev<NF_N_DEVICES ; i_dev++ ) { printf("\t\tNF %ld:\r\n", i_dev); nf_select(i_dev); nf_erase_block( nf_block_2_page(valid_block_addr[0]), false); time_s = Get_sys_count(); nf_wait_busy(); time_e = Get_sys_count(); // Verify that the block is erased. nf_open_page_read( nf_block_2_page(valid_block_addr[0]), 0); for( i= 0; i<2048 ; i++ ) { data = nf_rd_data(); if( data!=0xFF ) { printf("\tError: offset %d is not erased (read %d).\r\n", (U8)i, data); return 0; } } printf("\t\t\tTime to erase a page:%ld cy (%ld us)\r\n", time_e-time_s, cpu_cy_2_us(time_e-time_s, sysclk_get_cpu_hz())); nf_open_page_write( nf_block_2_page(valid_block_addr[0]), 0); for( i=0 ; i<2048 ; i++ ) nf_wr_data(i%256); nf_wr_cmd(NF_PAGE_PROGRAM_CMD); time_s = Get_sys_count(); nf_wait_busy(); time_e = Get_sys_count(); printf("\t\t\tTime to program a page:%ld cy (%ld us)\r\n", time_e-time_s, cpu_cy_2_us(time_e-time_s, sysclk_get_cpu_hz())); time_s = Get_sys_count(); nf_open_page_read( nf_block_2_page(valid_block_addr[0]), 0); time_e = Get_sys_count(); printf("\t\t\tTime to access to a page:%ld cy (%ld us)\r\n", time_e-time_s, cpu_cy_2_us(time_e-time_s, sysclk_get_cpu_hz())); for( i= 0; i<2048 ; i++ ) { data = nf_rd_data(); if( data!= i%256) { printf("\tError: expect %d, read %d\r\n", (U8)i, data); return 0; } } } printf("Example DONE\r\n"); return 0; }