SpenceKonde / megaTinyCore

Arduino core for the tinyAVR 0/1/2-series - Ones's digit 2,4,5,7 (pincount, 8,14,20,24), tens digit 0, 1, or 2 (featureset), preceded by flash in kb. Library maintainers: porting help available!
Other
558 stars 146 forks source link

Manual powercycle EEPROM corruption ATtiny814 #270

Closed eabinayan closed 3 years ago

eabinayan commented 3 years ago

Hi,

I'm having an issue with EEPROM. It seems that when I manually powercycle the ATtiny814 some parts of the EEPROM value changes.

I am using a. Arduino IDE 1.8.13 b. megaTinyCore v2.1.5 c. Board settings Board Settings I have bootloaded the ATtiny814 before uploading the test code below. `#include

void setup() { pinMode(7, OUTPUT); Serial.begin(9600); delay(1000);

for (unsigned int i = 0; i < EEPROM.length(); i++) { Serial.print(EEPROM.read(i)); Serial.write(','); delay(1); } Serial.println();

}

void loop() { unsigned int sigrow_index;

sigrow_index = EEPROM.read(0);

if (sigrow_index == 0) { for (unsigned int i = 1; i < 50; i++) { if (SIGROW_SERNUM0 != EEPROM.read(i)) { Serial.print(i); Serial.write('\t'); Serial.print(SIGROW_SERNUM0); Serial.write('\t'); Serial.print(EEPROM.read(i)); Serial.write('\t');
Serial.println("EEPROM fail.");

    EEPROM.write(0, 0);
    for (unsigned int i = 1; i < 50; i++)
    {
      EEPROM.write(i, SIGROW_SERNUM0);
      delay(1);
    }

    while (1)
    {
      digitalWrite(7, HIGH);
      delay(50);
      digitalWrite(7, LOW);
      delay(50);
    }
  }
}
EEPROM.write(0, 1);
for (unsigned int i = 1; i < 50; i++)
{
  EEPROM.write(i, SIGROW_SERNUM1);
  delay(1);
}

} else if (sigrow_index == 1) { for (unsigned int i = 1; i < 50; i++) { if (SIGROW_SERNUM1 != EEPROM.read(i)) { Serial.print(i); Serial.write('\t'); Serial.print(SIGROW_SERNUM1); Serial.write('\t'); Serial.print(EEPROM.read(i)); Serial.write('\t');
Serial.println("EEPROM fail.");

    EEPROM.write(0, 0);
    for (unsigned int i = 1; i < 50; i++)
    {
      EEPROM.write(i, SIGROW_SERNUM0);
      delay(1);
    }

    while (1)
    {
      digitalWrite(7, HIGH);
      delay(50);
      digitalWrite(7, LOW);
      delay(50);
    }
  }
}
EEPROM.write(0, 2);
for (unsigned int i = 1; i < 50; i++)
{
  EEPROM.write(i, SIGROW_SERNUM2);
  delay(1);
}

} else if (sigrow_index == 2) { for (unsigned int i = 1; i < 50; i++) { if (SIGROW_SERNUM2 != EEPROM.read(i)) { Serial.print(i); Serial.write('\t'); Serial.print(SIGROW_SERNUM2); Serial.write('\t'); Serial.print(EEPROM.read(i)); Serial.write('\t');
Serial.println("EEPROM fail.");

    EEPROM.write(0, 0);
    for (unsigned int i = 1; i < 50; i++)
    {
      EEPROM.write(i, SIGROW_SERNUM0);
      delay(1);
    }

    while (1)
    {
      digitalWrite(7, HIGH);
      delay(50);
      digitalWrite(7, LOW);
      delay(50);
    }
  }
}
EEPROM.write(0, 3);
for (unsigned int i = 1; i < 50; i++)
{
  EEPROM.write(i, SIGROW_SERNUM3);
  delay(1);
}

} else if (sigrow_index == 3) { for (unsigned int i = 1; i < 50; i++) { if (SIGROW_SERNUM3 != EEPROM.read(i)) { Serial.print(i); Serial.write('\t'); Serial.print(SIGROW_SERNUM3); Serial.write('\t'); Serial.print(EEPROM.read(i)); Serial.write('\t');
Serial.println("EEPROM fail.");

    EEPROM.write(0, 0);
    for (unsigned int i = 1; i < 50; i++)
    {
      EEPROM.write(i, SIGROW_SERNUM0);
      delay(1);
    }

    while (1)
    {
      digitalWrite(7, HIGH);
      delay(50);
      digitalWrite(7, LOW);
      delay(50);
    }
  }
}
EEPROM.write(0, 4);
for (unsigned int i = 1; i < 50; i++)
{
  EEPROM.write(i, SIGROW_SERNUM4);
  delay(1);
}

} else if (sigrow_index == 4) { for (unsigned int i = 1; i < 50; i++) { if (SIGROW_SERNUM4 != EEPROM.read(i)) { Serial.print(i); Serial.write('\t'); Serial.print(SIGROW_SERNUM4); Serial.write('\t'); Serial.print(EEPROM.read(i)); Serial.write('\t');
Serial.println("EEPROM fail.");

    EEPROM.write(0, 0);
    for (unsigned int i = 1; i < 50; i++)
    {
      EEPROM.write(i, SIGROW_SERNUM0);
      delay(1);
    }

    while (1)
    {
      digitalWrite(7, HIGH);
      delay(50);
      digitalWrite(7, LOW);
      delay(50);
    }
  }
}
EEPROM.write(0, 5);
for (unsigned int i = 1; i < 50; i++)
{
  EEPROM.write(i, SIGROW_SERNUM5);
  delay(1);
}

} else if (sigrow_index == 5) { for (unsigned int i = 1; i < 50; i++) { if (SIGROW_SERNUM5 != EEPROM.read(i)) { Serial.print(i); Serial.write('\t'); Serial.print(SIGROW_SERNUM5); Serial.write('\t'); Serial.print(EEPROM.read(i)); Serial.write('\t');
Serial.println("EEPROM fail.");

    EEPROM.write(0, 0);
    for (unsigned int i = 1; i < 50; i++)
    {
      EEPROM.write(i, SIGROW_SERNUM0);
      delay(1);
    }

    while (1)
    {
      digitalWrite(7, HIGH);
      delay(50);
      digitalWrite(7, LOW);
      delay(50);
    }
  }
}
EEPROM.write(0, 6);
for (unsigned int i = 1; i < 50; i++)
{
  EEPROM.write(i, SIGROW_SERNUM6);
  delay(1);
}

} else if (sigrow_index == 6) { for (unsigned int i = 1; i < 50; i++) { if (SIGROW_SERNUM6 != EEPROM.read(i)) { Serial.print(i); Serial.write('\t'); Serial.print(SIGROW_SERNUM6); Serial.write('\t'); Serial.print(EEPROM.read(i)); Serial.write('\t');
Serial.println("EEPROM fail.");

    EEPROM.write(0, 0);
    for (unsigned int i = 1; i < 50; i++)
    {
      EEPROM.write(i, SIGROW_SERNUM0);
      delay(1);
    }

    while (1)
    {
      digitalWrite(7, HIGH);
      delay(50);
      digitalWrite(7, LOW);
      delay(50);
    }
  }
}
EEPROM.write(0, 7);
for (unsigned int i = 1; i < 50; i++)
{
  EEPROM.write(i, SIGROW_SERNUM7);
  delay(1);
}

} else if (sigrow_index == 7) { for (unsigned int i = 1; i < 50; i++) { if (SIGROW_SERNUM7 != EEPROM.read(i)) { Serial.print(i); Serial.write('\t'); Serial.print(SIGROW_SERNUM7); Serial.write('\t'); Serial.print(EEPROM.read(i)); Serial.write('\t');
Serial.println("EEPROM fail.");

    EEPROM.write(0, 0);
    for (unsigned int i = 1; i < 50; i++)
    {
      EEPROM.write(i, SIGROW_SERNUM0);
      delay(1);
    }

    while (1)
    {
      digitalWrite(7, HIGH);
      delay(50);
      digitalWrite(7, LOW);
      delay(50);
    }
  }
}
EEPROM.write(0, 8);
for (unsigned int i = 1; i < 50; i++)
{
  EEPROM.write(i, SIGROW_SERNUM8);
  delay(1);
}

} else if (sigrow_index == 8) { for (unsigned int i = 1; i < 50; i++) { if (SIGROW_SERNUM8 != EEPROM.read(i)) { Serial.print(i); Serial.write('\t'); Serial.print(SIGROW_SERNUM8); Serial.write('\t'); Serial.print(EEPROM.read(i)); Serial.write('\t');
Serial.println("EEPROM fail.");

    EEPROM.write(0, 0);
    for (unsigned int i = 1; i < 50; i++)
    {
      EEPROM.write(i, SIGROW_SERNUM0);
      delay(1);
    }

    while (1)
    {
      digitalWrite(7, HIGH);
      delay(50);
      digitalWrite(7, LOW);
      delay(50);
    }
  }
}
EEPROM.write(0, 9);
for (unsigned int i = 1; i < 50; i++)
{
  EEPROM.write(i, SIGROW_SERNUM9);
  delay(1);
}

} else { for (unsigned int i = 1; i < 50; i++) { if (SIGROW_SERNUM9 != EEPROM.read(i)) { Serial.print(i); Serial.write('\t'); Serial.print(SIGROW_SERNUM9); Serial.write('\t'); Serial.print(EEPROM.read(i)); Serial.write('\t');
Serial.println("EEPROM fail.");

    EEPROM.write(0, 0);
    for (unsigned int i = 1; i < 50; i++)
    {
      EEPROM.write(i, SIGROW_SERNUM0);
      delay(1);
    }

    while (1)
    {
      digitalWrite(7, HIGH);
      delay(50);
      digitalWrite(7, LOW);
      delay(50);
    }
  }
}
EEPROM.write(0, 0);
for (unsigned int i = 1; i < 50; i++)
{
  EEPROM.write(i, SIGROW_SERNUM0);
  delay(1);
}

}

Serial.println("EEPROM ok.");

delay(1000); Serial.println("New EEPROM.");

for (unsigned int i = 0; i < EEPROM.length(); i++) { Serial.print(EEPROM.read(i)); Serial.write(','); delay(1); } Serial.println();

while (1) { digitalWrite(7, HIGH); delay(1000); digitalWrite(7, LOW); delay(1000); } } `

I have also attached a compiled binary of the code. test_eeprom.zip

Below is the Arduino IDE compile log compiling log.txt

Test results viewed using Termite 3.4 The test code prints EEPROM content, but checks if it is valid only up to EEPROM address 49.

  1. Test01.txt - The EEPROM content above 49 changed. This is more observed at the last line where 0, 64, 64, 176 seems to have replaced 255. test01.txt

  2. Test02.txt - EEPROM address 0 content changed from 0 to 160 test02.txt

  3. Test03.txt - EEPROM address 0 content changed from 6 to 2 test03.txt

  4. Test03.txt - EEPROM address 0 content changed from 7 to 1 test04.txt

EEPROM change

The supplied voltage to the ATtiny814 is 5v. I manually disconnect and reconnect VCC and GND.

SpenceKonde commented 3 years ago

I see that you have BOD (Brown Out Detect disabled). Per datasheet

9.3.3 Preventing Flash/EEPROM Corruption
During periods of low VDD, the Flash program or EEPROM data can be corrupted if the supply voltage is too low for
the CPU and the Flash/EEPROM to operate properly. These issues are the same as for board level systems using
Flash/EEPROM, and the same design solutions should be applied.
A Flash/EEPROM corruption can be caused by two situations when the voltage is too low: First, a regular write
sequence to the Flash requires a minimum voltage to operate correctly. Also, the CPU itself can execute instructions
incorrectly when the supply voltage is too low. See the Electrical Characteristics chapter for Maximum Frequency vs.
VDD.
Flash/EEPROM corruption can be avoided by these measures:
Keep the device in reset during periods of insufficient power supply voltage. This can be done by enabling the
internal Brown-Out Detector (BOD).
The voltage level monitor in the BOD can be used to prevent starting a write to the EEPROM close to the BOD level.

Issues with EEPROM corruption in the event of losing power during a write have always happened. With brown-out detection disabled, there is no other possible result. Consider that the process of writing to EEPROM takes a non-zero length of time, and consumes non-zero power during that time. Without BOD enabled, in the moments after power is removed, it will continue trying to write to new bytes of flash right up until the voltage on the capacitors on the board has dropped to VPOR - at which point the chip will enter power-on reset - or the chip executes instructions wrongly enough that it stops trying to write to the EEPROM (note that the instructions that initiate EEPROM writes may not be what malfunctions - the ones preparing the value or address to write may misbehave first - I'd venture a guess that this is the cause when multiple addresses come out corrupted... The write process will keep trying as long as it can in he hopes that it will be written successfully, but you have minimized the amount of runway that you have between when the chip reached a point where it stopped trying to write to new locations, and when the NVM controller stopped being able to complete the write. If your sketch is writing to EEPROM and you have the BOD enabled, the EEPROM will be written correctly as long as the time between when the voltage (being supplied by on-board capacitors) falls below the BOD voltage (causing the chip to stop trying to write to new locations, while the pending attempt continues), and when it gets to whatever voltage the NVM controller actually falls over at. Consensus seems to be "just use BOD and you'll be fine", though if the board lacked a prudent amount of board-level decoupling, I wouldn't put money on it (I always put at least 4.7uF on a board - but I've seen people who are getting boards manufactured who don't know that at least one 0.1uF cap isn't optional... \o/ ) . I suppose if you do not wish to use BOD for some reason (likely the fact that it cannot be turned on and off from within the application code) you could measure supply voltage with the ADC before you decide to write to EEPROM.

But - my point is, the behavior you are observing is expected behavior, and section 9.3.3 of the datasheet explains how to prevent it. One could argue that I ought to mention that in the library - but cutting the power while writing to the EEPROM with BOD disabled will result in EEPROM disruption on all parts, as EEPROM has no mitigation against this anywhere, and essentially the same library is everywhere, and never mentions this.