Go Down

Topic: EEPROM Wear Leveling (Read 8653 times) previous topic - next topic

MrAl

Hi there,

Has it ever been established whether or not EEPROM wear leveling in any Arduino does any good?
Mostly for Uno and Nano, but others would be interesting too.

So say we had a way to use successive addresses in EEPROM rather than the same 8 bit location all the time.  That would give us 1024 times the write endurance for an EEPROM that can store 1024 values if we only have to write one 8 bit value for an application.
For example, normally we write maybe 0x34 to location 0x001 and then later write 0x8A to location 0x001, but with wear leveling we would write 0x34 to location 0x001 and then later write 0x8A to location 0x002, leaving location 0x001 alone for now.  Once we got to the last location we would wrap around back to the location 0x000 or whatever.

There are a couple different ways to implement this but if it does not do any good then it is a waste of time and energy.

cattledog

Quote
Has it ever been established whether or not EEPROM wear leveling in any Arduino does any good?
What? You don't believe in math? :)

Of course, wear leveling will reduce the number of writes to any given location so it will increase the overall life of the eeprom. Most data indicates that the 100,000 write specified life of the eeprom registers is very conservative.

I think your question is really does it make any practical difference, and that really depends on the specific sketch and the number of cycles you are going to put any register through.

Another question is that if wear leveling is required to preserve the life of the eeprom, is the eeprom the optimal storage device for the application?

CrossRoads

Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

MrAl

#3
Mar 12, 2016, 03:58 pm Last Edit: Mar 12, 2016, 04:19 pm by MrAl
Hello,


Thanks for the replies.

I looked into the FRAM's and might use one in the future, but let me clarify the question and the reason for it in the first place.

The question is really about a low write frequency application, where the writes might only be 16 times in one day, then not written to for another 7 days.  On the other hand, if wear leveling does any good then i might increase the write frequency which would help improve the program somewhat.  So it's partly what i can get away with, not what else i can do about it.

The reason for the question itself is because not all EEPROM's are equal.  Some write to blocks of bits rather than just one 8 bit section.  For example, some write to 64 bits all at once even if we only tell it to write to one 8 bit section.  That means that for say the first 8 bytes if we write to location 0x02, it will write to 0x02 but will also over write 0x00 to 0x07, and that means that using wear leveling on a byte by byte basis is the wrong way to do it.  At best, we would be able to do it on an 8 byte basis which would also improve the endurance but only 1/8 as effective unless we store 7 values and then write on the 8th value occurance.  That would still help though, unless it worked on a 16 byte block and then we'd have to resort to only a 16 byte block algorithm in one way or another.

I tend to think the EEPROM in the Uno or Nano probably works on an 8 bit block, so one byte per block, but i have no data to back this up.  If this is true, i would not need any other type of memory although i know there are better ones out there.  It could even be different on those such as the Due, which has more EEPROM.  The larger the EEPROM probably the more likely it is to use multi byte block processing rather than single byte in order to speed the write process up for the user.

Thanks again :-)

cattledog

Quote
I tend to think the EEPROM in the Uno or Nano probably works on an 8 bit block, so one byte per block, but i have no data to back this up
From section 5.4 "EEPROM Data Memory" in the ATmega328 data sheet ( a useful reference to have on your computer)
Quote
The ATmega48P/88P/168P/328P contains 256/512/512/1K bytes of data EEPROM memory. It
is organized as a separate data space, in which single bytes can be read and written.

shawnlg

FRAM eh?  That's those little Ferris cores that you thread copper wire through to store memory in magnets, right?  The kind they used on the moon lander and early shuttle missions?  It's supposed to be impervious to high radiation you would find in space, unlike most chips.  Maybe I'll get me one. :)
Shawn Gordhamer

MrAl

Hi again,


Cattledog:
That quote may or may not mean that one byte is written to, but i tend to think it does mean that.  I can write one byte to my flash drive too, but it might actually write 64k bits just for that one byte.


Shawnlg:
Those little ferrite cores are ferromagnetic, a FRAM is ferroelectric.  Slightly different operating principles.
I used to have a ferromagnetic core that held about 32 bytes or so.  The core was about 5 inches by 5 inches and the PC board with the drive electronics was about 12 inches long by maybe 8 inches wide.  That's pretty big for 32 bytes :-)
Yes the little 1/8 inch ferro cores had two very thin copper wires running through the center.  When both wires passed current the core would flip it's state.  Sense amplifiers were used to detect any pulse that would indicate that the tiny core flipped it's magnetic state.  Pretty cool, but kinda big for what it did.



shawnlg

Shawnlg:
Those little ferrite cores are ferromagnetic, a FRAM is ferroelectric.  Slightly different operating principles.
I used to have a ferromagnetic core that held about 32 bytes or so.  The core was about 5 inches by 5 inches and the PC board with the drive electronics was about 12 inches long by maybe 8 inches wide.  That's pretty big for 32 bytes :-)


So, I wonder how you could connect one of those to an arduino :-)
Shawn Gordhamer

MrAl

So, I wonder how you could connect one of those to an arduino :-)
Hi,

Now that you mention it, it would be fun to try i think.

Going back in memory to early to mid 70's, the cores are arranged on a grid say N x M.
If you want to write a '1' to one bit, you have to energize the Nth column and Mth row with half the current it takes to set the state of the core.  That individual core then gets the full current sum and so the state changes to a '1' unless it was already a '1' and then it would not change.  To write a '0' use negative currents.

To read, do the same thing, but after the current pulses have ended read the sense amplifiers and see if any of them sensed a pulse after the energizing pulses have ended.  If they sensed a pulse then the core changed state so it was a '0', and then the '0' has to be written back to that core, but if no sensed pulse then no extra write is needed and it must have been a '1'.

This is going back many years now, so it may be that the sensed pulse comes on top of the current pulse, i cant remember for sure.  Have to look it up :-)

To do this with an Arduino would mean addressing it with i/o lines where the half current is set somehow, maybe with resistors.  For a 4x4 core matrix we'd need 8 i/o lines.  We'd also have to see if we can sense the pulse though, which may be very fast and of low level so we'd need sense amplifiers too and if the pulse was too fast we might need an external latch to catch the pulse too, then reset when done.
So it would take a little doing but is most likely possible.
I dont have that core anymore though but i would imagine we could find tiny cores somewhere on the web and make our own matrix with very thin magnetic wire (like 32 gauge perhaps).

For proof of concept, all we would have to do is learn how to energize one single core and be able to read back the stored state.  So we'd only need one tiny toroid core to start with.  I am not sure however if a little 'bead' core used for noise suppression would work or not.  I think the core has to have a square hysteresis loop and i am not sure how those bead cores are.  We'd have to look at the data sheet or something.

This would be a very interesting project though, because often we just have to store a tiny amount of data so that when the application starts up from power up it can remember one little thing like one byte.
If we could find an easy enough way to do this, it would be a good thing to have and i am sure other people would like to do it too for their own projects.

Oh yeah another thing would be the level of current required.  If the current had to be higher than maybe 20ma then we might have to use transistors or a transistor array to drive the cores.  That would require 8 transistors for a 4x4 array which is only 2 bytes of data.

This is something i might look into just for the fun of it even if it does not turn out to be practical.



BigBobby

#9
Mar 19, 2016, 12:54 am Last Edit: Mar 19, 2016, 12:54 am by BigBobby
From section 5.4 "EEPROM Data Memory" in the ATmega328 data sheet ( a useful reference to have on your computer)
Ok, but section 28.7.5 says
Quote
The EEPROM is organized in pages, see Table 28-12 on page 285. When programming the EEPROM, the program data is latched into a page buffer. This allows one page of data to be programmed simultaneously.
Table 28-12 says that the EEPROM has a 4 byte page size.  When I first used my Arduino EEPROM the datasheet seemed contradictory, so I proved to myself that I could write to individual EEPROM bytes.  I don't know if behind-the-scenes somehow the AVR was read-modify-writing 4 physical bytes when I wrote one byte, but if I ever get an Arduino I don't care about I might write a sketch to purposely beat down one EEPROM location to see if it has any effect on the 3 bytes around it.

MrAl

Hello again,

Oh ok so they are using something other than 8 bit internal organization.  Apparently it is 32 bit.
So writing  8 bits would not be any more beneficial than writing 32 bits, which is 4 bytes to us.

So the next question then is, are these contiguous bytes?
That is, are location 0x000, 0x001, 0x002, and ox003 one of those groups of 32 bits?
I would hope so, but have no way to prove this, yet.  Maybe it is written down somewhere.

If it is not contiguous, then it might be:
 0x000, 0x002, 0x004, 0x006

 as the first group of 32 bits and:
0x001, 0x003, 0x005, 0x007

as the next group of 32 bits, etc.

If you decide to burn one out for the sake of knowing for sure,you might want to check the whole EEPROM if possible just to make sure you can see all the changed bits,if any.  You might want to use test bit patterns like the binary 01010101 and 10101010  for the testing, or something like that.
Would be interesting.

I have one Nano with the USB connector broken off, so if i decide to burn that one maybe i'll try it too. I would think it would still program using another Arduino as programmer and the typical 4 wire connection to the pins of the Nano.  I'll have to look that up again though as i have not done that for probably two years now, using the USB interface for almost everything.


BigBobby

True.

Best thing would be to write/read one memory location until it no longer reads back what was written.

Then, try to write/read all of the other memory locations in the EEPROM.

Track which 3 (if any) EEPROM locations can no longer be written after the 1st location was burnt out.

CrossRoads


Figure 2-1. Block Diagram
shows the EEPROM on the 8-bit bus side of the AVR CPU, not the wider side where the FLASH and SRAM sit.
Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

BigBobby

#13
Mar 22, 2016, 01:43 pm Last Edit: Mar 24, 2016, 12:32 am by BigBobby
Figure 2-1. Block Diagram
shows the EEPROM on the 8-bit bus side of the AVR CPU, not the wider side where the FLASH and SRAM sit.
Do you understand what the datasheet means, when it says the EEPROM has a 4 byte page size in Table 28-12?

I still don't understand the significance of the 4 byte page size.  I actually found an Arduino that I could destroy (well...one EEPROM location anyway) with this code:

Code: [Select]

#include "Arduino.h"
#define EEPROM_SIZE                 1024
#define EEPROM_LOCATION_TO_RUIN     666

#define PRINT_INTERVAL              1000
#define PRINT_INTERVAL2             128

void setup()
{
  Serial.begin(9600);
  EEPROM_Erase();
}

void loop()
{
  uint8_t toggle = 0;
  uint8_t read_value;
  uint8_t write_value;
  uint32_t num_writes = 0;

  // Write new values to the EEPROM location to ruin and read back until failure.
  do
  {
    write_value = toggle ? 0xAA : 0x55;
    toggle = 1 - toggle;
    eeprom_write_block(&write_value, (void*)EEPROM_LOCATION_TO_RUIN, sizeof(write_value));
    eeprom_read_block(&read_value, (void*)EEPROM_LOCATION_TO_RUIN, sizeof(read_value));
    num_writes++;
    if((num_writes % PRINT_INTERVAL) == 0)
    {
      Serial.print(num_writes);
      Serial.print(" writes performed to address 0x");
      Serial.println(EEPROM_LOCATION_TO_RUIN, HEX);
    }
  } while (read_value == write_value);

  // Report how many writes were needed for failure.
  Serial.println(F("====================================================="));
  Serial.print(F("EEPROM write failed after "));
  Serial.print(num_writes);
  Serial.println(F(" writes."));
  Serial.print(F("Written = "));
  Serial.print(write_value);
  Serial.print(F(", Read = "));
  Serial.print(read_value);
  Serial.println(".");
  Serial.println(F("====================================================="));

  // Attempt to write to every byte in the EEPROM and report if any fail.

  for(uint16_t address = 0; address < EEPROM_SIZE; address++)
  {
    for(toggle = 0; toggle <= 1; toggle++)
    {
      write_value = toggle ? 0xAA : 0x55;
      eeprom_write_block(&write_value, (void*)address, sizeof(write_value));
      eeprom_read_block(&read_value, (void*)address, sizeof(read_value));
      if(read_value != write_value)
      {
        Serial.print("Could not read from address 0x");
        Serial.println(address, HEX);
        Serial.println(".");
        Serial.print(F("Written = "));
        Serial.print(write_value);
        Serial.print(F(", Read = "));
        Serial.print(read_value);
        Serial.println(".");
      }
    }

    if(((address % PRINT_INTERVAL2) == 0) || (address == EEPROM_LOCATION_TO_RUIN) || (address == EEPROM_SIZE - 1))
    {
      Serial.print("0x");
      Serial.print(address, HEX);
      Serial.println(" written/read.");
    }
  }

 while(1);
}

void EEPROM_Erase(void)
{
  uint16_t address = 0;
  uint8_t erase_row[16];

  while (address < EEPROM_SIZE)
  {
// Enable to clear the entire EEPROM.
    memset(erase_row, 0xFF, sizeof(erase_row));
    eeprom_write_block(erase_row, (void*)address, sizeof(erase_row));
    address += sizeof(erase_row);
  }
}


These were the results:
Quote
...
9582000
9583000
9584000
=====================================================
EEPROM write failed after 9584038 writes.
Written = 170, Read = 162.
=====================================================
0x0 written/read.
0x80 written/read.
0x100 written/read.
0x180 written/read.
0x200 written/read.
0x280 written/read.
Could not read from address 0x29A
.
Written = 170, Read = 162.
0x29A written/read.
0x300 written/read.
0x380 written/read.
0x3FF written/read.
This seems to prove that you can burn out one location, and it has no effect on any other 3 locations.  I don't know why Atmel put that note about the 4 byte page size in the datasheet, however.

Also something to note:  I expected to get more than 100k writes, but I didn't expect to get almost 10M!

BTW - We're neighbors-ish (I'm in Lowell).  Don't mean to be creepy, but you did put your location in your profile.

CrossRoads

"Do you understand what the datasheet means, when it says the EEPROM has a 4 byte page size in Table 28-12?"
I think that you can write 4 bytes at a time during one 3.3mS write operation.

"BTW - We're neighbors-ish (I'm in Lowell).  Don't mean to be creepy, but you did your location in your profile."
Not a problem. We have Arduino-ites all over the state.

Designing & building electrical circuits for over 25 years.  Screw Shield for Mega/Due/Uno,  Bobuino with ATMega1284P, & other '328P & '1284P creations & offerings at  my website.

Go Up