Forum: Mikrocontroller und Digitale Elektronik STM8S103: EEPROM-Write nach Watchdog-Reset verursacht CPU-Lockup ?


von TU Student 1. (student0)


Angehängte Dateien:

Lesenswert?

Kann sich jemand den Code ansehen ?   Sind meine EEPROM-Routinen 
grundsätzlich korrekt?  Ich habe vorsichtshalber auch Flags getestet, 
die eigentlich egal sein müssten.

Ich habe das Problem, dass nach einem(absichtlichen) Watchdog-Reset der 
nächste EEPROM - Schreibzugriff nach dem Reset zu einem kompletten Core 
- Lockup führt.

Manchmal passiert das nicht, wenn etwas über die UART herausgesendet 
wird.

Normale Schreibzugriffe (vor dem Watchdog-Reset) auf das EEPROM 
funktionieren problemlos.

Was ich bereits probiert habe:
- sim()/rim() damit während der Schreibvorgangs kein Interrupt auftritt
- Ca. 10 NOPs nach jedem geschriebenen Byte
- Prüfen des HV-Flags, was bei STM8 Low-Density nicht nötig sein sollte
- Prüfen, ob das EEPROM nicht schreibgeschützt ist, bevor ich die 
MASS-Keys schreibe  (was auch nicht notwendig sein sollte)
- Die CPU-Taktfrequenz vor dem Reset heruntersetzen
(es gibt ein Erratum im Bezug auf Halt-State und Flash-Zugriffe)


Mein Code macht folgendes:
(UART-RX und Timer-Interrupts Aktiv)
1. Flag-Byte im EEPROM setzen.  Das funktioniert
2. Absichtlichen WDT-Reset erzeugen
3. Nach Reboot das Flag prüfen
4. Das Flag resetten -> Hier passiert es.

Auch ohne sim() gibt es einen kompletten CPU-Lockup, denn auch auf 
externe Interrupts wird nicht reagiert (für jedes UART-Byte wird eine 
Board-LED angeschalten und nach 1s durch den Timer, der auch für 
Timeouts zuständig ist, abgeschalten).
1
unsigned char eeprom_peek(unsigned char offset)
2
{
3
  unsigned char *eeprom = 0x4000 + (unsigned char *)offset;
4
  return *eeprom;
5
}
6
7
void eeprom_poke(unsigned char data, unsigned char offset)
8
{
9
  volatile unsigned char *eeprom = 0x4000 + (unsigned char *)offset;
10
  sim();  // Is this needed?
11
  // Unlock EEPROM
12
  if (!(FLASH_IAPSR & (1 << 3))) // Check should not be needed?
13
  {
14
    FLASH_DUKR = 0xAE; // First MASS key
15
    FLASH_DUKR = 0x56; // Second MASS key
16
    while (!(FLASH_IAPSR & (1 << 3))); // Wait for EEPROM to unlock    
17
  }
18
  *eeprom = data;
19
  while (!(FLASH_IAPSR & (1 << 6))); // Wait for EEPROM to finish, is it needed?    
20
  // Is this needed on Low-Density STM8  ?!  
21
  
22
  FLASH_IAPSR &= ~(1 << 3); // Relock EEPROM
23
  rim();  // Is this needed?
24
}
25
26
void eeprom_write(unsigned char * data, unsigned char offset, unsigned char size)
27
{
28
    volatile unsigned char *eeprom = 0x4000 + (unsigned char *)offset;
29
    sim();  // Is this needed?
30
    // Unlock EEPROM
31
    if (!(FLASH_IAPSR & (1 << 3))) // Check should not be needed?
32
    {
33
      FLASH_DUKR = 0xAE; // First MASS key
34
      FLASH_DUKR = 0x56; // Second MASS key
35
      while (!(FLASH_IAPSR & (1 << 3))); // Wait for EEPROM to unlock    
36
    }
37
  
38
  while (size--)
39
  { 
40
  *eeprom++ = *data++;
41
    while (!(FLASH_IAPSR & (1 << 6))); // Wait for EEPROM to finish, is it needed?    
42
  }
43
  
44
  FLASH_IAPSR &= ~(1 << 3); // Relock EEPROM
45
  rim();  // Is this needed?
46
}
47
48
void eeprom_read(unsigned char * data, unsigned char offset, unsigned char size)
49
{
50
  volatile unsigned char * eeprom = 0x4000 + (unsigned char * ) offset;
51
  while (size--)
52
    *data++ = *eeprom++;
53
}
54
55
unsigned char eeprom_compare(unsigned char * data, unsigned char offset, unsigned char size)
56
{
57
  volatile unsigned char *eeprom = 0x4000 + (unsigned char *)offset;
58
  
59
  while (size--)
60
    if ( *data++ != *eeprom++)
61
      return 0;
62
  
63
  return 1;
64
}


All das hat nichts gebracht, ich habe es zurückgeführt auf den 
Watchdog-Reset - nur dann tritt dieses Problem auf.

Wenn ich statt dem absichtlichen Watchdog-Softreset:
1
CLK_CKDIVR = 0x06;
2
IWDG_KR = 0xCC;         //  Start the independent watchdog.
3
IWDG_KR = 0x55;         //  Allow the IWDG registers to be programmed.
4
IWDG_PR = 0x06;         //  Prescaler is 2 => each count is 250uS
5
IWDG_KR = 0xAA;         //  Reset the counter.
6
while(1);

einen Sprung an den Reset-Vektor mache:
1
__asm__("jpf 0x8000");

tritt das Problem nicht mehr auf, der Code funktioniert perfekt in der 
Applikation.

Nun habe ich einen Testbench-Code (eeprom.c) geschrieben um das 
Verhalten zu replizieren, und siehe da: Es tritt nicht mehr auf!

Meine Vermutung ist zum derzeitigen Stand:   Es hat irgendwas mit 
Interrupts zu tun oder es ist ein Silicon-Bug, denn der Testbench-Code 
verwendet keine Interrupts, meine Applikation jedoch UART-RX und 
Timer-Interrupts.   Ich schalte jedoch explizit auch die Interrupts ab 
um eventuellen Problemen vorzubeugen.

Weiss einer der STM8/SDCC Gurus (SDCC Maintainer) weiter?

Hab ich etwas nicht beachtet?

von TU Student 1. (student0)


Lesenswert?

Der Lockup passiert auch in der Applikation, wenn der Reboot über den 
Window-Watchdog, wie im STM8-CPU-Manual empfohlen, ausgelöst wird:
1
  WWDG_CR |= (1 << 7);    //  Enable the window watchdog
2
  WWDG_CR &= ~(1 << 6);   //  Bit 6 cleared triggers
3
  while(1);

von TU Student 1. (student0)


Lesenswert?

Ich bin der Sache näher gekommen - je länger das Delay zwischen 
EEPROM-Unlock und EEPROM-Write ist, desto weniger wahrscheinlich 
passiert der Lockup.

Nach dem Lockup passiert ist reagiert die CPU auch nicht mehr auf 
Interrupts!
(bei jedem UART Byte leuchtet eine LED auf, der Timer macht sie nach 1s 
aus)

Mit diesem Code funktioniert es:
1
static void delay(unsigned int t)
2
{
3
    while (t--)
4
        ;
5
}
6
7
void eeprom_poke(unsigned char data, unsigned char offset)
8
{
9
    unsigned char *eeprom = 0x4000 + (unsigned char *)offset;
10
    // Unlock EEPROM
11
  //sim();
12
  FLASH_DUKR = 0xAE;  // First MASS key
13
  FLASH_DUKR = 0x56;  // Second MASS key
14
  delay(1700);
15
  *eeprom = data;
16
  FLASH_IAPSR &= ~(1 << 3); // Lock EEPROM
17
  //rim();
18
  return;  
19
}

Offenbar ist das EEPROM-Unlock-Flag (Bit 3 im FLASH_IAPSR) fehlerhaft, 
weil man trotzdem nicht sofort nachdem es 1 wird draufschreiben kann.

Das erklärt auch, warum meine eeprom_write Funktion meistens 
funktionierte, die eeprom_poke Funktion jedoch nicht, da die 
while-Schleife offenbar genug CPU-Takte an  Overhead erzeugt.

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.