3
\$\begingroup\$

For my project, I am writing some code that gets some results and I want to store these results on external flash memory. The external flash memory in question is a MX25R8035F. I found that writing without erasing only works the first time I wrote something to an address.

But because of this I'm running into some memory issues. The smallest erase is a sector erase (4096 bytes.) If I want to write to a specific address I would first need to read the sector that contains that address, change the bytes that I want to be changed and then write the whole sector (writing is only per page.)

Is there a more efficient way of using this external flash memory? Specifically using less memory to change one or two bytes.

The function I use now this can be found below:

uint8_t EXT_FLASH_write(size_t address, uint8_t *buf, size_t length) {

/*
 * The entire sector will be erased when writing to an offset inside that sector
 * Therefore this function will first retrieve all data inside the sector and update the retrieved
 * data with the values inside buff which and will then write the entire content back to the sector
 */

    uint8_t wbuf[4];
    SPI_Transaction transaction;
    uint32_t sectorBaseAddr;
    uint8_t temp[EXT_FLASH_ERASE_SECTOR_SIZE];
    uint8_t tries;
    size_t ilen; /* interim length per instruction */
    uint32_t bufIndex = 0;
    uint8_t pageIterations;

    while (length > 0) {
        // first retrieve entire sector so it can be erased on the chip
        sectorBaseAddr = EXT_FLASH_SECTOR_BASE_ADDR(address);
        EXT_FLASH_read(sectorBaseAddr, temp, EXT_FLASH_ERASE_SECTOR_SIZE);

        // Erase the sector on the chip
        EXT_FLASH_erase(address, EXT_FLASH_ERASE_SECTOR_SIZE);

        ilen = EXT_FLASH_PROGRAM_PAGE_SIZE
                - (address % EXT_FLASH_PROGRAM_PAGE_SIZE);
        if (length < ilen)
            ilen = length;

        memcpy(temp + (address - sectorBaseAddr), buf + bufIndex, length);

        bufIndex += ilen;
        address += ilen;
        length -= ilen;

        tries = 0;
        if (EXT_FLASH_writeEnable() != 0) {
            if (tries == EXT_FLASH_MAX_TRIES) {
                return 1;
            }
            tries++;
        }
        tries = 0;
        while (EXT_FLASH_waitReady() != 2) {
            if (tries == EXT_FLASH_MAX_TRIES) {
                return 1;
            }
            EXT_FLASH_writeEnable();
            tries++;
        }

        // programming the external flash can only be done in pages
        // so divide current sector into pages and write each page separately
         for (pageIterations = 0;
                 pageIterations * EXT_FLASH_PROGRAM_PAGE_SIZE
                         < EXT_FLASH_ERASE_SECTOR_SIZE; pageIterations++) {
            EXT_FLASH_select();

            wbuf[0] = EXT_FLASH_CODE_PROGRAM;
            wbuf[1] = ((sectorBaseAddr
                    + (pageIterations * EXT_FLASH_PROGRAM_PAGE_SIZE)) >> 16)
                    & 0xff;
            wbuf[2] = ((sectorBaseAddr
                    + (pageIterations * EXT_FLASH_PROGRAM_PAGE_SIZE)) >> 8)
                    & 0xff;
            wbuf[3] = (sectorBaseAddr
                    + (pageIterations * EXT_FLASH_PROGRAM_PAGE_SIZE)) & 0xff;

            // Configure the transaction
            transaction.count = sizeof(wbuf);
            transaction.txBuf = wbuf;
            transaction.rxBuf = NULL;

            if (!SPI_transfer(masterSpi, &transaction)) {
                /* failure */
                EXT_FLASH_deselect();
                return 1;
            }
            // Configure the transaction
            transaction.count = EXT_FLASH_PROGRAM_PAGE_SIZE;
            transaction.txBuf = temp
                    + (pageIterations * EXT_FLASH_PROGRAM_PAGE_SIZE);
            transaction.rxBuf = NULL;

            if (!SPI_transfer(masterSpi, &transaction)) {
                /* failure */
                EXT_FLASH_deselect();
                return 1;
            }

            EXT_FLASH_deselect();
        }
    }
    return 0;
}
\$\endgroup\$
  • 3
    \$\begingroup\$ You have mis-characterized the part and mis-titled your question, this is not an EEPROM, but rather a flash device. With such a memory you don't "change a few bytes" as that is indeed very wasteful. Re-think how you are doing things and find a method that doesn't require that, perhaps something of a journal. \$\endgroup\$ – Chris Stratton Apr 30 '20 at 8:54
  • 1
    \$\begingroup\$ Um, Flash is a type of EEPROM \$\endgroup\$ – Marcus Müller Apr 30 '20 at 10:11
  • 1
    \$\begingroup\$ The big erase size is a well-known problem with flash memories, often making them unsuitable to store changing data. Memory manufacturers typically have special kinds of memory marketed as "data flash" with smaller erase sizes, similar to what was previously called EEPROM back in the days. For the purpose of logging etc with a lot of writes, you might wanna consider alternative memory types though, such as "FRAM" or "MRAM" that various companies have developed in later years. They don't have the write cycle limitation and data retention problem of flash memories. \$\endgroup\$ – Lundin Apr 30 '20 at 13:30
4
\$\begingroup\$

FLASH is cheap and provides lot of memory, but comes with the cost of sector erase. So you might want to use EEPROM instead.

Of course it is possible to overcome this flash limitation with some clever software, which tracks the bits and bytes with a journal (i.e. you don't overwrite a byte value but write a new copy of it and note in the journal that the new byte is the recent one). But this is a really big software overhead and you might not want to do that.

\$\endgroup\$
  • \$\begingroup\$ Since I don't really have a choice on what hardware I am using. I guess the journal is the better solution. \$\endgroup\$ – Frank Ridder Apr 30 '20 at 9:26
  • 2
    \$\begingroup\$ Do not underestimate the work required to get a fully functioning and bug-free journal implementation. If a journal is even feasible for your application is also not trivial to decide. How much space do you have? How much will be used at maximum? How many rewrites per page are expected? Can your application even work with a time in-deterministic write-function? Unless you're already in pre-production, it will make much more sense to change the BOM to something that more closely fits your application. \$\endgroup\$ – markus-nm Apr 30 '20 at 10:56
  • \$\begingroup\$ Any books or artikels you would recommend to read up on journals? \$\endgroup\$ – Frank Ridder May 11 '20 at 13:32
  • \$\begingroup\$ The application note by @Mike below looks like a useful starting point. Also, as your page-erase size is 4096 bytes, maybe you could simply keep the whole page in memory until it's full, then program it to the next available flash page and repeat? Considering your model seems to be "add-only" plus maybe "bulk erase". \$\endgroup\$ – markus-nm May 12 '20 at 10:55
2
\$\begingroup\$

There is an really nice application note by microchip. The algorithm in this application note supports selectable,multiple emulated data EEPROMs with a total size of upto multiples of 255 locations. The overhead in this journal is not so big and it's really easy and comfortable to use.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.