This is the mail archive of the ecos-discuss@sourceware.org mailing list for the eCos project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Improved flash erase algorithm


I had a problem with my EB40A ARM7 board, when I tried to use Redboot to
erase the flash. I finally tracked it down, and found that the problem had
to do with the erasure of boot blocks, meaning the subdivided erase blocks
that some flash chips have for use as boot sectors or parameter areas.

The Atmel flash driver uses a data structure to describe the way some blocks
are subdivided. The EB40A board HAL specifies an AT49LV1614A chip, which is
described in the data structure as subdividing the first 64K block into
eight 8K blocks. However, my board has the AT49LV1614 chip on it, which also
divides the second 64K block into two 32K blocks.

There also exist parts that have a T suffix, which have the boot blocks at
the top of the memory instead of the bottom. On top of that, the EB40A has a
jumper which inverts the top address bit, swapping the two halves of the
chip, putting the boot blocks in the middle. Thus there are conceivably
eight possible layouts that need to be dealt with.

It seems to me, though, that it isn't necessary to have any data structure
at all to describe even the existence, let alone the layout, of these boot
blocks. I fixed the problem by writing a new version of flash_erase_block,
which is found in
packages/devs/flash/atmel/at49xxxx/v2_0_41/include/flash_at49xxxx.inl. It
simply has a blankcheck loop which reads each location in the logical block
(64K in this case) until it finds some data, then issues a Sector Erase
command to that particular location, then resumes the blank check loop.

If the non-blank location is contained in a 64K block, the command will
erase the entire block, and the rest of the blankcheck loop will find
nothing. If it is contained in an 8K block, the blankcheck loop will
eventually find more data, and issue another Erase command to the next
block. This has the added advantage that if only some of the sub-blocks have
data in them, the algorithm won't waste time erasing the rest of them.
Indeed, if the entire block is already erased, it won't do any erase
operation at all.

Can anyone think of any problems with using this algorithm? Source code (not
thoroughly tested, but it seems to work) follows:

//--------------------------------------------------------------------------
--
// Erase Block
int
flash_erase_block(void* block, unsigned int size)
{
    volatile flash_data_t* ROM;
    volatile flash_data_t* b_p = (volatile flash_data_t*) block;

    int res;
    unsigned int i;

    // diag_printf("\nERASE: Block %p, size %u\n",block,size);

    // Base address of device(s) being programmed.
    ROM = (volatile flash_data_t*) ((unsigned long)block &
flash_dev_info->base_mask);

#if defined(CYGHWR_DEVS_FLASH_ATMEL_AT49XXXX_ERASE_BUG_WORKAROUND)

    // Before erasing the data, overwrite it with all zeroes. This is a
    // workaround for a silicon bug that affects erasing of some devices.
    for (i = 0; i < size; i += sizeof(*b_p)) {

        // Program data [byte] - 4 step sequence
        ROM[FLASH_Setup_Addr1] = FLASH_Setup_Code1;
        ROM[FLASH_Setup_Addr2] = FLASH_Setup_Code2;
        ROM[FLASH_Setup_Addr1] = FLASH_Program;
        *b_p = ~FLASH_BlankValue;
        res = wait_while_busy(5000000, b_p, 0);
        if (res != FLASH_ERR_OK)
           return res;
        if (*b_p != ~FLASH_BlankValue)
            return FLASH_ERR_DRV_VERIFY;
        ++b_p;
    }

    b_p = (volatile flash_data_t*) block;

#endif // defined(CYGHWR_DEVS_FLASH_ATMEL_AT49XXXX_ERASE_BUG_WORKAROUND)

    // If chip can only be erased in entirety...
    if (flash_dev_info->chip_erase) {

        // Erase must be addressed to the bottom of the chip.
        if (b_p != ROM)
            return FLASH_ERR_DRV_VERIFY;

        // Issue 6-byte erase sequence, return result.
        ROM[FLASH_Setup_Addr1] = FLASH_Setup_Code1;
        ROM[FLASH_Setup_Addr2] = FLASH_Setup_Code2;
        ROM[FLASH_Setup_Addr1] = FLASH_Setup_Erase;
        ROM[FLASH_Setup_Addr1] = FLASH_Setup_Code1;
        ROM[FLASH_Setup_Addr2] = FLASH_Setup_Code2;
        ROM[FLASH_Setup_Addr1] = FLASH_Chip_Erase;
        return wait_while_busy(66000000, b_p, FLASH_BlankValue);
    }

    // Blank check each location.
    for (i = 0; i < size; i += sizeof(*b_p)) {
        if (*b_p != FLASH_BlankValue) {

            //  diag_printf("\nERASE:   Issuing erase command to %p\n",
b_p);

            // If nonblank location found, issue 6-byte erase sequence to
it.
            ROM[FLASH_Setup_Addr1] = FLASH_Setup_Code1;
            ROM[FLASH_Setup_Addr2] = FLASH_Setup_Code2;
            ROM[FLASH_Setup_Addr1] = FLASH_Setup_Erase;
            ROM[FLASH_Setup_Addr1] = FLASH_Setup_Code1;
            ROM[FLASH_Setup_Addr2] = FLASH_Setup_Code2;
            *b_p = FLASH_Sector_Erase;
            res = wait_while_busy(66000000, b_p, FLASH_BlankValue);
            if (res != FLASH_ERR_OK)
                return res;

            // If location still isn't blank, return error code.
            if (*b_p != FLASH_BlankValue)
                return FLASH_ERR_DRV_VERIFY;
        }

        // Next location.
        ++b_p;
    }
    return FLASH_ERR_OK;
}

--

Ciao,               Paul D. DeRocco
Paul                mailto:pderocco@ix.netcom.com


-- 
Before posting, please read the FAQ: http://ecos.sourceware.org/fom/ecos
and search the list archive: http://ecos.sourceware.org/ml/ecos-discuss


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]