Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]
Groups > comp.arch.embedded > #27829
| From | David Brown <david.brown@hesbynett.no> |
|---|---|
| Newsgroups | comp.arch.embedded |
| Subject | Re: Serial EEPROM or Serial Flash? |
| Date | 2018-06-17 18:41 +0200 |
| Organization | A noiseless patient Spider |
| Message-ID | <pg62v7$hq5$1@dont-email.me> (permalink) |
| References | (2 earlier) <pfvqei$9t7$1@dont-email.me> <pg00mh$cmj$1@dont-email.me> <pg0ds2$sal$1@dont-email.me> <pg0i0n$nlr$1@dont-email.me> <pg0lfs$es1$1@dont-email.me> |
On 15/06/18 17:20, 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) );
>
No, you can't necessarily do that. C has rules about what pointers can
alias what data in what ways - so the compiler knows the data inside the
struct is independent of a pointer to an uint16_t (such as you might use
in the crc function), unless they are part of a union. In many cases,
the compiler can't actually make use of such information for
optimisation - so code messing about with pointers of different types
will work. But sometimes it won't, especially with higher optimisation
choices and link-time optimisation. It is much better to get in the
habit of doing things correctly - if you want to access the data as
16-bit or 32-bit blocks (for checksums, for copying data, for passing in
bulk to external memory, etc.) then use a union here. It might not be
necessary, depending on the rest of your code (and by C's alias rules,
the 8-bit raw is not needed), but I'm showing the principle here.
>
>> 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.
>
That depends on the application. I'm not doing /all/ your work for you :-)
> Or you declare a "unsigned char reserved_for_future[1024]" big array in
> the struct, so the CRC is calculated over the full area?
>
Keeping some extra space for the future is a good idea. How much you
use is a matter of taste.
>
>> 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.
Yes.
>
>>
>> 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
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