Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > comp.arch.embedded > #27820

Re: Serial EEPROM or Serial Flash?

From pozz <pozzugno@gmail.com>
Newsgroups comp.arch.embedded
Subject Re: Serial EEPROM or Serial Flash?
Date 2018-06-15 22:54 +0200
Organization A noiseless patient Spider
Message-ID <pg192b$ifa$1@dont-email.me> (permalink)
References (3 earlier) <pg00mh$cmj$1@dont-email.me> <pg0ds2$sal$1@dont-email.me> <pg0i0n$nlr$1@dont-email.me> <pg0lfs$es1$1@dont-email.me> <0rSUC.342343$br4.37911@fx34.iad>

Show all headers | View raw


Il 15/06/2018 19:11, Richard Damon ha scritto:
 > On 6/15/18 11:20 AM, pozz wrote:
 >> Il 15/06/2018 16:21, David Brown ha scritto:
 >>> On 15/06/18 15:10, pozz wrote:
 >>>> Il 15/06/2018 11:25, David Brown ha scritto:
 >>>>> On 15/06/18 09:38, pozz wrote:
 >>>>>> Il 14/06/2018 12:49, David Brown ha scritto:
 >>>>>>> On 14/06/18 12:20, pozz wrote:
 >>>>>>>> I need to save some data on a non-volatile memory. They are some
 >>>>>>>> parameters that the user could change infrequently, for example 10
 >>>>>>>> times
 >>>>>>>> per day at a maximum.  In the range 100-2kB.
 >>>>>>>>
 >>> <snip>
 >>>>>>>
 >>>>>>> Multiple copies (at least 2) are key, along with timestamps or
 >>>>>>> counters
 >>>>>>
 >>>>>> Two should be sufficient to prevent corruption caused by 
interruption
 >>>>>> during the writing of a block of data.
 >>>>>> Maybe three (or more) are needed to face memory *physical* 
corruption
 >>>>>> (maybe for many writings).
 >>>>>> I think I can ignore this event in my actual project.
 >>>>>>
 >>>>>
 >>>>> You use more for wear levelling.  Often your flash chip is /way/ 
bigger
 >>>>> than you need - perhaps by a factor of 1000 simply because that's 
what
 >>>>> you have on stock, or that's the cheapest device in the package you
 >>>>> want.  Spread your writes over 1000 blocks instead of 2, and you have
 >>>>> 500 times the endurance.  Use a good checksum and accept that 
sometimes
 >>>>> a block will be worn out and move onto the next one, and you have
 >>>>> perhaps a million times the endurance (because most blocks last a lot
 >>>>> longer than the guaranteed minimum).
 >>>>
 >>>> So the writing process should be:
 >>>>
 >>>> 1. write the data at block i+1 (where i is the block of the current
 >>>>     data in RAM)
 >>>> 2. read back the block i+1 and check if checksum is ok
 >>>> 3. if ok, writing process is finished
 >>>> 4. if not, go to block i+2 and start again from 1.
 >>>>
 >>>
 >>> Yes.
 >>>
 >>> Then comes step 5 (for flash with separate erasing) :
 >>>
 >>> 5. If you have written block x, check if block x+1 (modulo the size of
 >>> the device) is erased.  If not, then erase it ready for the next write.
 >>>
 >>> Note that it does not matter if the erase block size is bigger than the
 >>> program block size - if it is, then your "erase block x+1" command will
 >>> cover the next few program blocks.
 >>>
 >>>>>>> and checksums.
 >>>>>>
 >>>>>> This is interesting.  Why do I need a checksum?  My approach is 
to use
 >>>>>> only a magic number plus a counter... and two memory areas.
 >>>>>> At first startup magic number isn't found on any areas, so the 
device
 >>>>>> starts with default and write data on Area1 (magic/0, where 0 is the
 >>>>>> counter).
 >>>>>
 >>>>> You use checksums to ensure that you haven't had a power-out or
 >>>>> reset in
 >>>>> the middle of writing,
 >>>>
 >>>> Only for this thing, you can write the counter as the last byte. 
If the
 >>>> writing is interrupted in the middle, counter hasn't written yet, 
so the
 >>>> block is not valid (because considered too old or empty).
 >>>
 >>> Nope.  You can't rely on that, unless you are absolutely sure that you
 >>> have a simple, old-fashioned E²PROM device.  Many devices are /not/ 
like
 >>> that, even if they provide an interface that matches it logically.
 >>>
 >>> A common structure for a modern device is to have 32-byte pages as a
 >>> compromise between density, cost, and flexibility.  (Bigger pages are
 >>> more efficient in device area and cost.)  When you send a command to
 >>> write a byte, the device reads the old 32-byte page into ram, 
erases the
 >>> old page, updates the ram with the new data, the writes the whole 32
 >>> byte page back in.
 >>>
 >>> The write process is done by a loop that writes all the data, reads it
 >>> back at a low voltage to see if it has stuck, and writes again as 
needed
 >>> until the data looks good.  Then it writes again a few times for safety
 >>> - either a fixed number, or a percentage of the number of writes taken.
 >>>
 >>> So it is /entirely/ possible for an interrupted write to give you a
 >>> valid counter, but invalid data.  It is also entirely possible to get
 >>> some bits of the counter as valid while others are still erased (giving
 >>> ones on most devices).
 >>>
 >>> And that is just for simple devices that don't do any fancy wear
 >>> levelling, packing, garbage collection, etc.
 >>>
 >>>
 >>>>
 >>>>> and that the flash has not worn out.
 >>>>>
 >>>>>>
 >>>>>> When the configuration is changed, Area2 is written with magic/1,
 >>>>>> being
 >>>>>> careful to save magic/1 only at the end of area writing.
 >>>>>>
 >>>>>> At startup magics and counters from both area are loaded, and 
one area
 >>>>>> is chosen (magic should be valid and counter should be the maximum).
 >>>>>>
 >>>>>> I think this approach works, even when the area writing is 
interrupted
 >>>>>> at the middle.
 >>>>>>
 >>>>>> Why do I need checksum?  The only thing that comes in mind is to
 >>>>>> prevent
 >>>>>> writing errors: for example, I want to write 0x00 but the value 
really
 >>>>>> written is 0x01, maybe for a noise on the serial bus.
 >>>>>>
 >>>>>> To solve this situation, I need checksum... but also I need to 
re-read
 >>>>>> and re-calculate the checksum at *every* area writing... and start
 >>>>>> a new
 >>>>>> writing if something was wrong.
 >>>>>>
 >>>>>> Do you have a better strategy?
 >>>>>
 >>>>> You calculate the checksum for a block before writing it, and you 
check
 >>>>> it when reading it.  Simple.
 >>>>
 >>>> Do you calc the checksum of all the data block in RAM, including 
padding
 >>>> bytes?
 >>>
 >>> Yes, of course.  The trick is not to have unknown padding bytes.  I 
make
 >>> a point of /never/ having compiler-generated padding in my structs.
 >>>
 >>> So you have something like this:
 >>>
 >>> #define sizeOfRawBlock 32
 >>> #define noOfRawBlocks 4
 >>> #define magicNumber 0x9185be91
 >>> #define dataStructVersionExpected 0x0001
 >>>
 >>> typedef union {
 >>>      uint8_t raw8[sizeOfRawBlock * noOfRawBlocks];
 >>>      uint16_t raw16[sizeOfRawBlock * noOfRawBlocks / 2];
 >>>      struct {
 >>>          uint32_t magic;
 >>>          uint16_t dataStructVersion;
 >>>          uint16_t crc;
 >>>          uint32_t count;
 >>>
 >>>          // real data
 >>>      }
 >>> } nvmData_t;
 >>>
 >>> static_assert(sizeof(nvmData_t) == (sizeofRawBlock * noOfRawBlocks),
 >>>       "Check size of nvnData!");
 >>
 >> Why raw8[]?
 >>
 >> I think you can avoid raw16[] too. If you have the function:
 >>
 >>     uint16_t calc_crc(const void *data, size_t size);
 >>
 >> you can simply call:
 >>
 >>     nvmData.crc =
 >>       calc_crc( ((unsigned char *)&nvmData) + 8, sizeof(nvmData) );
 >>
 >>
 >
 > I typically also do something like this. The data structure is a union
 > of the basic data structure with a preamble that includes (in very fixed
 > locations) a data structure version, checksum/crc, and if a versioning
 > store a timestamp/data generation number. A 'Magic Number' isn't often
 > needed unless it is a removable media as it will either be or not the
 > expected data, nothing else could be there (if the unit might have
 > different sorts of programs, then a piece of the data version would be a
 > program ID.)
 >
 > Often I will have TWO copies of the data packet.

TWO copies in EEPROM or in RAM?


 > Often organized something like this:
 >
 > struct SystemParameters {
 >     // List of data parameters that need to be saved
 > } parms;
 >
 > union {
 >    uint8_t	raw_data[DATA_SIZE];
 >    struct {
 >        struct StandardHeader header;
 >        struct SystemParameters parms;
 >
 > } flash_parms;
 >
 > /* Check that sizeof(flash_parms) == DATA_SIZE, and that DATA_SIZE is
 > a multiple of the flash sector size */

Do you need raw_data[] array only to set a well-known size to flash_parms?
Do you alloc an entire (or a multiple of) flash sector in RAM?  Even if 
your params in flash take only 1/5 or 1/2 or 1/10 of a sector?


 > And a request to save the data will fill flash_parms with a 'NULL'
 > fill (0 or all ones, or whatever I want unused flash locations to be),

Is it really necessary?  What happens if you don't care about unused
bytes in flash_parms?  I think... nothing.


 > then
 > copy the parms into flash_parms.parms, then fill in the header, and then
 > program the flash. Only the part that copys parms to flash_parms.parms
 > needs to be protected from other operation in the system updating the
 > main parms structure.
 >
 > The flash_parms structure can be further modified to be a union of the
 > current data structure of the SystemParameters structure and any
 > previous 'incompatible' version of it. (A version from just prior to
 > adding elements at the end doesn't need to be listed). This way at
 > startup I can read the values into that same structure, and if it was
 > old, parse to old version in flash_parms to get the new version in
 > parms, defaulting new parameters as needed.

It seems difficult, if the changes from one version to the other are 
"important".

struct {
   uint16_t foo;
   uint16_t bar;
   uint16_t dummy;
} SystemParameters;

struct {
   uint8_t foo;
   uint8_t bar;
   uint8_t dummy;
} SystemParametersOld;

union {
    uint8_t	raw_data[DATA_SIZE];
    struct {
        struct StandardHeader header;
        struct SystemParameters parms;
        struct SystemParametersOld parms_old;
} flash_parms;


for (uint8_t sector = 0; sector < SECTORS_NUM; sector++) {
   uint16_t memory_address = OFFSET + sector * SECTOR_SIZE;
   sermem_read(&flash_params, memory_address, sizeof(flash_params));
   uint16_t chk_read = flash_parms.header.crc;
   flash_parms.header.crc = 0xFFFF;
   if (checksum_calc(&flash_parms, sizeof(flash_parms)) == chk_read) {
     /* Datablock is valid */
     if (flash_parms.header.counter > prev_counter) {
       /* Datablock is newer */
       prev_counter = flash_parms.header.counter;
       if (flash_parms.header.version == FLASH_PARMS_OLD) {
         /* How to convert old version in new version in place? */
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
       } else {
         /* Ok, new version. No convertion is needed. */
       }
     }
   }
}

As I wrote in the comment, how to convert old version to new version, 
using the same union in RAM?  Do I need another union?

 > Checksums / CRC are always over the whole data array (skipping over,
 > or using a fixed value, the value of it stored in the Header).
 >
 >
 >>> When you want to store the data, run your crc calculation over the
 >>> raw16[] from index 4 upwards, and set it in the crc field.  Set the
 >>> magic field with a different fixed value per program.  Use the
 >>> dataStructVersion to track versions of the structure of the data - so
 >>> that updated firmware can recognise old data and quietly update it.
 >>
 >> This is interesting.  Suppose you have a new struct version and you are
 >> reading an old version from the memory.  How do you read it?  Do you put
 >> it completely in RAM and after copy it in the final struct making the
 >> changes from one version to the other?  In this case you need a double
 >> space in RAM, for old and new version.
 >>
 >> Or you declare a "unsigned char reserved_for_future[1024]" big array in
 >> the struct, so the CRC is calculated over the full area?
 >>
 >>
 > (see above)
 >
 >>> Set the count to a new number each time.
 >>>
 >>> Leave plenty of room to add new fields to your nvm structure.
 >>>
 >>>
 >>>>
 >>>>
 >>>>>>>> This means I need to copy&paste an entire block everytime, even
 >>>>>>>> for a
 >>>>>>>> single byte change.  And this is similar to Flash approach, 
where I
 >>>>>>>> *need* a sector erase before changing a single byte.
 >>>>>>>
 >>>>>>> Yes.
 >>>>>>>
 >>>>>>> Of course, it is possible to have multiple smaller blocks 
rather than
 >>>>>>> just one big one.
 >>>>>>
 >>>>>> This is good to reduce writing time of a block of data.
 >>>>>> In my case I have 2kB of data, around 62 32-bytes EEPROM pages,
 >>>>>> that is
 >>>>>> 5*62=300ms.  I don't like to block a task (in a cooperative OS) 
for so
 >>>>>> long time.
 >>>>>
 >>>>> Your writing should be done in the background.  The background writer
 >>>>> task is blocked - the system is not.
 >>>>
 >>>> Are you thinking of a preemption OS?  I'm using a cooperative OS.
 >>>>
 >>>
 >>> It doesn't matter.  In a pre-emptive OS, your background writer is:
 >>                             ^^^^^^^^^^^
 >> You mean cooperative.
 >>
 > You can use cooperative or pre-emptive. If cooperative then your flash
 > write routine should yield (like the example seems to show) while it is
 > waiting for the flash to do the actual operations. (If pre-emptive you
 > just need to block for a bit and let the pre-emption do its thing, or
 > make the flash routine a background level operation so a spin wait
 > doesn't impact other operations)
 >
 >>>
 >>> static enum { idle, writing, checking, erasing } writerState;
 >>>
 >>> void doWriter(void) {
 >>>      switch (writerState) {
 >>>          case idle :
 >>>              if (writeTriggered) {
 >>>                  writeTriggered = false;
 >>>                  startWriting();
 >>>                  writerState = writing;
 >>>              }
 >>>              break;
 >>>          case writing :
 >>>              bool stillWorking = pollNVMdevice();
 >>>              if (stillWorking) return;
 >>>              startChecking();
 >>>              writerState = checking;
 >>>              break;
 >>>          case checking :
 >>>              bool stillWorking = pollNVMdevice();
 >>>              if (stillWorking) return;
 >>>              startErasingNextBlock();
 >>>              writerState = erasing;
 >>>              break;
 >>>          case erasing :
 >>>              bool stillWorking = pollNVMdevice();
 >>>              if (stillWorking) return;
 >>>              writerState = idle;
 >>>              break;
 >>>      }
 >>> }
 >>>
 >>> There - half your program is done :-)
 >>>
 >>> Cooperative multitasking does not mean busy waiting for long-running
 >>> tasks.  It means using state machines, interrupt-driven SPI/I²C 
drivers,
 >>> polling, etc., so that you can mix fast and slow tasks.
 >>
 >> Yes, ok.
 >>
 >

Back to comp.arch.embedded | Previous | Next — Previous in thread | Next in thread | Find similar | Unroll thread


Thread

Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-14 12:20 +0200
  Re: Serial EEPROM or Serial Flash? David Brown <david.brown@hesbynett.no> - 2018-06-14 12:49 +0200
    Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-15 09:38 +0200
      Re: Serial EEPROM or Serial Flash? Jack <jack4747@gmail.com> - 2018-06-15 00:54 -0700
        Re: Serial EEPROM or Serial Flash? Reinhardt Behm <rbehm@hushmail.com> - 2018-06-15 16:26 +0800
          Re: Serial EEPROM or Serial Flash? Jack <jack4747@gmail.com> - 2018-06-15 01:31 -0700
          Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-15 10:50 +0200
            Re: Serial EEPROM or Serial Flash? Reinhardt Behm <rbehm@hushmail.com> - 2018-06-15 17:03 +0800
              Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-15 14:59 +0200
                Re: Serial EEPROM or Serial Flash? David Brown <david.brown@hesbynett.no> - 2018-06-15 15:51 +0200
        Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-15 10:47 +0200
          Re: Serial EEPROM or Serial Flash? Jack <jack4747@gmail.com> - 2018-06-15 02:20 -0700
            Re: Serial EEPROM or Serial Flash? Stef <stef33d@yahooI-N-V-A-L-I-D.com.invalid> - 2018-06-15 13:41 +0200
            Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-15 15:01 +0200
      Re: Serial EEPROM or Serial Flash? David Brown <david.brown@hesbynett.no> - 2018-06-15 11:25 +0200
        Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-15 15:10 +0200
          Re: Serial EEPROM or Serial Flash? Jack <jack4747@gmail.com> - 2018-06-15 06:51 -0700
          Re: Serial EEPROM or Serial Flash? David Brown <david.brown@hesbynett.no> - 2018-06-15 16:21 +0200
            Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-15 17:20 +0200
              Re: Serial EEPROM or Serial Flash? Richard Damon <Richard@Damon-Family.org> - 2018-06-15 13:11 -0400
                Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-15 22:54 +0200
                Re: Serial EEPROM or Serial Flash? Richard Damon <Richard@Damon-Family.org> - 2018-06-15 18:25 -0400
                Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-16 08:26 +0200
                Re: Serial EEPROM or Serial Flash? Richard Damon <Richard@Damon-Family.org> - 2018-06-16 09:20 -0400
                Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-16 19:38 +0200
                Re: Serial EEPROM or Serial Flash? Richard Damon <Richard@Damon-Family.org> - 2018-06-16 16:41 -0400
                Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-16 23:05 +0200
              Re: Serial EEPROM or Serial Flash? David Brown <david.brown@hesbynett.no> - 2018-06-17 18:41 +0200
            Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-16 20:12 +0200
              Re: Serial EEPROM or Serial Flash? David Brown <david.brown@hesbynett.no> - 2018-06-17 19:27 +0200
                Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-19 17:40 +0200
              Re: Serial EEPROM or Serial Flash? Richard Damon <Richard@Damon-Family.org> - 2018-06-17 17:05 -0400
                Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-19 17:44 +0200
                Re: Serial EEPROM or Serial Flash? Richard Damon <Richard@Damon-Family.org> - 2018-06-19 23:14 -0400
                Re: Serial EEPROM or Serial Flash? pozz <pozzugno@gmail.com> - 2018-06-20 11:03 +0200
                Re: Serial EEPROM or Serial Flash? Richard Damon <Richard@Damon-Family.org> - 2018-06-20 08:03 -0400
  Re: Serial EEPROM or Serial Flash? Michael Karas <mkaras@carousel-design.com> - 2018-06-23 09:43 -0700

csiph-web