Forum: Mikrocontroller und Digitale Elektronik SPM@AtMega168: FLASH-Write nicht zuverlässig?!


von Alexander I. (daedalus)


Lesenswert?

Hallo,

ich muss die Firmware von einem AtTiny88 auf einen AtMega168 portieren. 
Der Funktionsumfang wird dabei abwärtskompatibel erweitert.

Als Entwicklungsumgebung dient IAR EWB.

Die Firmware hat einen reservierten FLASH-Speicherbereich (0x3000 - 
0x33FF) der als persistenter Konfigurationsspeicher für ein anderes 
Gerät dient. Das EEPROM wird für andere Dinge gebraucht, deshalb muss 
ich hier den FLASH-Speicher verwenden.

Das andere Gerät wird durch eine PC-Software simuliert, die mit dem 
Device per RS232 kommuniziert. Hier kann ich auch reproduzierbare 
Testbenches laufen lassen, z.B. "Fülle den Konfigurationsspeicher mit 
1,2,3,4,5 und lese die Daten zurück".

Bei der Portierung gibt es hier unerwartet Probleme bei der Funktion
FlashWrite(). Sie funktioniert einfach nicht zuverlässig! Mal geht sie, 
mal nicht, manchmal kippen nur ein paar Bits, manchmal steht nur 0xFF an 
den Adressen, manchmal crasht der ganze Debugger, irgendwie etwas 
mysteriös.

Ich vermute, dass irgendein Programmiertiming o.Ä. nicht eingehalten 
wurde. Auch die AN109 und AN106 habe ich schon durchgelesen und mir ein 
paar Bootloader-Beispiele angeschaut, ich kann keinen Fehler finden.

Bei der Portierung musste ich ein paar Dinge anpassen:
- Definition einer Bootlader-Sektion für die SPM-Funktionen
- Platzierung der SPM-Funktionen in dieser BL-Sektion (z.B. 
FlashWrite())
- BOOTSZ-Fuses setzen (=1 KByte)
- Reservierten FLASH-Konfigurationsspeicher im Linker ausblenden, damit 
dort kein Code liegt.
- RWW-Bereich muss reaktiviert werden
- Registernamen im Source anpassen (SPMCSR statt SPMCR, etc.)
- Pagesize=128 Byte statt 64 Byte

Habt ihr eine Idee, wo der Fehler liegt? Hab ich noch was vergessen?
Vielen Dank vorab...
1
#define SPMCR                     SPMCSR
2
3
/*! \brief Daten bis zur Groesse sizeof(SizeBuffer) in FLASH schreiben
4
 *
5
 * \param  Address    FLASH-Adresse an die geschrieben werden soll
6
 * \param  DataBuffer Adresse von der die zu schreibenden Daten gelesen werden
7
 * \param  SizeBuffer Groesse des DataBuffer (in Bytes)
8
 * \param  Partition  PartitionTable_t-Struktur mit Informationen über PageSize, StartAdresse, etc.
9
 * \return Erfolg des Lesevorgangs
10
 *
11
 * Es werden die Daten aus DataBuffer ins FLASH an Adresse geschrieben, sofern diese
12
 * im gueltigen Bereich liegt. Das Data Alignment, sowie der ggf. notwendige
13
 * "Read-Modify-Write"-Zyklus wird automatisch durchgefuehrt. Fuer jede bearbeitete
14
 * FLASH-Page werden ca. 4ms für den Speichervorgang benoetigt.
15
 */
16
uint32_t FlashWrite(uint32_t Address, uint8_t *DataBuffer, uint32_t SizeBuffer, const PartitionTable_t* Partition) @"BOOT_SEGMENT"
17
{
18
  uint8_t  OldInterrupt = __save_interrupt();                                    // Interrupt-Flags sichern
19
  __disable_interrupt(); 
20
  uint8_t WriteCount = 1, BackupEECR;
21
  BackupEECR = EECR;                                                            // Zustand von EECR speichern
22
  EECR &= ~(1<<EERIE);                                                          // EEPROM-Interrupt temporaer deaktivieren
23
  while(EECR & (1<<EEPE))
24
  {
25
    ;
26
  }                                                                             // Warte ggf. ab bis EEPROM-Operation abgeschlossen ist
27
  uint8_t DirtyFlag = 0;
28
  while(WriteCount)
29
  {
30
    DirtyFlag = 0;
31
    WriteCount = PrepareTempBuffer(Address, DataBuffer, SizeBuffer, Partition, &DirtyFlag); // Temporaeren Puffer vorbereiten (Read-Modify-Write)
32
    if(DirtyFlag)
33
    { // Aktualieren der FLASH-Page notwendig, da Dirty-Flag gesetzt wurde.
34
      _SPM_ERASE((Partition->AddressStart + Address) & ~(uint8_t)(Partition->PageSize-1));
35
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geloescht wurde
36
      _SPM_PAGEWRITE((Partition->AddressStart + Address) & ~(uint8_t)(Partition->PageSize-1));
37
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geschrieben wurde
38
      __DataToR0ByteToSPMCR_SPM( 0, (unsigned char)(1<<RWWSRE)|(1<<SELFPRGEN));         // RWW-Bereich reaktivieren
39
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geschrieben wurde
40
    }
41
    else
42
    { // Alter Code vom AtTiny88
43
      /*SPMCR |= (1<<CTPB) | (1<<SPMEN);                                          // SPM-Puffer leeren um Stackoverflow zu vermeiden
44
      __asm("SPM");                                                             // SPM-Enstellungen uebernehmen
45
      while( SPMCR & (1<<SPMEN) ){;}                                            // Warten bis SPM-Puffer geleert wurde*/
46
    }
47
    // Warten bis FLASH-Page geschrieben wurde
48
    Address     += WriteCount;                                                  // Position im FLASH aktualisieren
49
    DataBuffer  += WriteCount;                                                  // Position im Datenpuffer aktualisieren
50
    SizeBuffer  -= WriteCount;                                                  // Anzahl verbleibender Zeichen berechnen
51
  }
52
  
53
  EECR |= BackupEECR;                                                           // EECR wiederherstellen
54
  __restore_interrupt(OldInterrupt);                                            // Interrupt-Flags widerherstellen
55
  return TRUE;
56
}
57
58
/*! \brief Daten bis zur Groesse sizeof(SizeBuffer) in FLASH schreiben
59
 *
60
 * \param  Address    FLASH-Adresse an die geschrieben werden soll
61
 * \param  DataBuffer Adresse von der die zu schreibenden Daten gelesen werden
62
 * \param  SizeBuffer Groesse des DataBuffer (in Bytes)
63
 * \return Erfolg des Lesevorgangs
64
 *
65
 * Bereitet eine FLASH-Page vor in dem vorhandene FLASH-Daten sowie die neu zu speichernden
66
 * Daten 'gemischt' werden, damit die betroffene Page vor dem Schreibvorgang ohne
67
 * Datenverlust geloescht werden kann. Da das FLASH in 16-Bit Zellen organisiert
68
 * ist, muss immer mit einem geraden und ungeraden Byte gearbeitet werden. Es werden
69
 * maximal FLASH_PAGESIZE Bytes oder weniger geschrieben, je nach Startadresse.
70
 * Sofern die Datenmenge groesser FLASH_PAGESIZE ist, muss die bearbeitete Page
71
 * zunaechst gespeichert werden und dann die nachfolgende Page durch einen erneuten
72
 * Aufruf dieser Funktion (mit geaenderten Uebergabeparametern) zur Speicherung
73
 * vorbereitet werden.
74
 */
75
uint32_t PrepareTempBuffer(uint32_t Address, uint8_t *DataBuffer, uint32_t SizeBuffer, const PartitionTable_t* Partition, uint8_t *DirtyFlag) @ "BOOT_SEGMENT"
76
{
77
  uint8_t odd, even;
78
  uint16_t BufIdx, StartAddr;
79
  StartAddr = (Partition->AddressStart + Address) & (Partition->PageSize-1);    // Startadresse innerhalb Page erzeugen
80
  for(BufIdx = 0; BufIdx < Partition->PageSize; BufIdx++)                       // Page wird byteweise durchlaufen
81
  {
82
    if((BufIdx >= StartAddr) && (BufIdx < StartAddr + SizeBuffer))              // Es werden neue Daten gepuffert
83
    {
84
      if(BufIdx & 1)
85
      {
86
        odd = DataBuffer[BufIdx-StartAddr];
87
        if(!*DirtyFlag)                                                          // Speichervergleich nur wenn DirtyFlag noch 0 (Performance ...)
88
        {
89
          if(odd != (uint8_t)*((FlashCharPtr)(((Partition->AddressStart + Address) & ~(uint8_t)(Partition->PageSize-1)) + BufIdx)))
90
          {
91
            *DirtyFlag = 1;                                                      // Datum unterscheidet sich von FLASH, DirtyFlag setzen!
92
          }
93
        }
94
      }
95
      else
96
      {
97
        even = DataBuffer[BufIdx-StartAddr];
98
        if(!*DirtyFlag)                                                          // Speichervergleich nur wenn DirtyFlag noch 0 (Performance ...)
99
        {
100
          if(even != (uint8_t)*((FlashCharPtr)(((Partition->AddressStart + Address) & ~(uint8_t)(Partition->PageSize-1)) + BufIdx)))
101
          {
102
            *DirtyFlag = 1;                                                      // Datum unterscheidet sich von FLASH, DirtyFlag setzen!
103
          }
104
        }
105
      }
106
    }
107
    else                                                                        // Es werden die Daten aus dem FLASH gepuffert
108
    {
109
      if(BufIdx & 0x01)
110
      {
111
        odd = (uint8_t)*((FlashCharPtr)(((Partition->AddressStart + Address) & ~(uint8_t)(Partition->PageSize-1)) + BufIdx));
112
      }
113
      else
114
      {
115
        even = (uint8_t)*((FlashCharPtr)(((Partition->AddressStart + Address) & ~(uint8_t)(Partition->PageSize-1)) + BufIdx));
116
      }
117
    }
118
    if(BufIdx & 0x01)
119
    {
120
      _SPM_FILLTEMP(BufIdx, (unsigned short)((((uint16_t)odd)<<8) | even ));    // 16-Bit Schreibvorgang um Page Buffer zu fuellen
121
    }
122
  }
123
  if(StartAddr + SizeBuffer > Partition->PageSize)                              // Anzahl tatsaechlich geschriebener Bytes zurueckgeben
124
  {
125
    return BufIdx - StartAddr;
126
  }
127
  else
128
  {
129
    return SizeBuffer;
130
  }
131
}

von Stephan B. (matrixstorm)


Lesenswert?

SPM darf nur im Bootloader (NRWW Section) ausgefuehrt werden.
Hast du das bereits beachtet? (Ich bin nicht 100% sicher, obwohl du ja 
meintest es wuerde manchmal funktionieren...)

MfG

von Alexander I. (daedalus)


Lesenswert?

Ja, manchmal klappt es, machmal nicht. Wäre das falsch, dürfte es ja 
generell nie funktionierten.

Die beiden Funktionen sind im "BOOT_SEGMENT" platziert (siehe @ 
"BOOT_SEGMENT" im Sourcecode).

Hier noch der passende Linker-Ausdruck:

-D_..X_BOOTSEC_SIZE=400
-Z(CODE)BOOT_SEGMENT=(_..X_FLASH_END-_..X_BOOTSEC_SIZE+1)-_..X_FLASH_END

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Stephan B. schrieb:
> SPM darf nur im Bootloader (NRWW Section) ausgefuehrt werden.
> Hast du das bereits beachtet? (Ich bin nicht 100% sicher, obwohl du ja
> meintest es wuerde manchmal funktionieren...)

 Ich bin aber 100% sicher, dass das so nicht gemacht werden soll.
 Alles andere mag sonstwo sein, aber SPM nur im Bootloader.

: Bearbeitet durch User
von Alexander I. (daedalus)


Lesenswert?

Gerade beobachtet:
Setze ich einen Breakpoint auf Funktionsanfang und Ende, klappt es 
nahezu immer. Lass' ichs einfach laufen, dann gibts die o.g. Probleme.

Sind die while()-Dinger wirklich alle korrekt?

von Stephan B. (matrixstorm)


Lesenswert?

Alexander I. schrieb:
> Gerade beobachtet:
> Setze ich einen Breakpoint auf Funktionsanfang und Ende, klappt es
> nahezu immer. Lass' ichs einfach laufen, dann gibts die o.g. Probleme.
>
> Sind die while()-Dinger wirklich alle korrekt?

Mach mal nen _delay_ms(5) rein und teste

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alexander I. schrieb:
1
    if(DirtyFlag)
2
    { // Aktualieren der FLASH-Page notwendig, da Dirty-Flag gesetzt wurde.
3
      _SPM_ERASE((Partition->AddressStart + Address) & ~(uint8_t)(Partition->PageSize-1));
4
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geloescht wurde
5
6
<<<<< Und gleich hier RWW reaktivieren, nicht erst unten !!!  <<<<<<<<<
7
      __DataToR0ByteToSPMCR_SPM( 0, (unsigned char)(1<<RWWSRE)|(1<<SELFPRGEN));         // RWW-Bereich reaktivieren
8
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten
9
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
10
11
      _SPM_PAGEWRITE((Partition->AddressStart + Address) & ~(uint8_t)(Partition->PageSize-1));
12
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geschrieben wurde

 Probieren.

: Bearbeitet durch User
von Max D. (max_d)


Lesenswert?

Schau halt mal wie es ein bootloader macht (z.B. Der vom arduino).
Da funktioniert es ja auch.

von Alexander I. (daedalus)


Lesenswert?

>  Probieren.

Das scheint es nicht zu sein. Ich habe gerade noch einen anderen Ansatz:
Der Parameter "Partition" ist "const" und liegt damit doch im FLASH oder 
??

Könnte es sein, dass hier auf RWW-FLASH in einer NRWW-Section 
zugegriffen wird?

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alexander I. schrieb:
> Das scheint es nicht zu sein. Ich habe gerade noch einen anderen Ansatz:

 a) SPMCR = PageErase
 b) SPM
 c) Warten auf SELFPRGEN
 d) SPMCR = RWW Enable
 e) SPM

 Neuen Pageinhalt einschreiben.

: Bearbeitet durch User
von Alexander I. (daedalus)


Lesenswert?

Das RWW-Enable zwischen ERASE und WRITE führt zu einem 
CSTACK-Overflow... danach hilft nur noch RESET.

Am ehesten (also manchmal) funktioniert es noch so:
1
  while(WriteCount)
2
  {
3
    DirtyFlag = 0;
4
    WriteCount = PrepareTempBuffer(Address, DataBuffer, SizeBuffer, Partition, &DirtyFlag); // Temporaeren Puffer vorbereiten (Read-Modify-Write)
5
    if(DirtyFlag)
6
    { // Aktualieren der FLASH-Page notwendig, da Dirty-Flag gesetzt wurde.
7
      _SPM_ERASE((Partition->AddressStart + Address) & ~(uint8_t)(Partition->PageSize-1));
8
      __asm("SPM");                                                             // SPM-Enstellungen uebernehmen
9
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geloescht wurde
10
11
      _SPM_PAGEWRITE((Partition->AddressStart + Address) & ~(uint8_t)(Partition->PageSize-1));
12
      __asm("SPM");                                                             // SPM-Enstellungen uebernehmen
13
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geschrieben wurde
14
      
15
      __DataToR0ByteToSPMCR_SPM( 0, (unsigned char)(1<<RWWSRE)|(1<<SELFPRGEN));         // RWW-Bereich reaktivieren
16
      __asm("SPM");                                                             // SPM-Enstellungen uebernehmen
17
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geschrieben wurde
18
19
    }
20
    else
21
    {
22
      /*SPMCR |= (1<<CTPB) | (1<<SPMEN);                                          // SPM-Puffer leeren um Stackoverflow zu vermeiden
23
      __asm("SPM");                                                             // SPM-Enstellungen uebernehmen
24
      while( SPMCR & (1<<SPMEN) ){;}                                            // Warten bis SPM-Puffer geleert wurde*/
25
    }
26
    // Warten bis FLASH-Page geschrieben wurde
27
    Address     += WriteCount;                                                  // Position im FLASH aktualisieren
28
    DataBuffer  += WriteCount;                                                  // Position im Datenpuffer aktualisieren
29
    SizeBuffer  -= WriteCount;                                                  // Anzahl verbleibender Zeichen berechnen
30
  }

von Alexander I. (daedalus)


Lesenswert?

Achso, das _SPM_ERASE und _SPM_PAGEWRITE generiert automatisch ein 
__asm("SPM") direkt nach SPMCSR-Zugriff laut Disassembly... muss ich 
also nicht nochmal als Kommando hinschreiben.

von Alexander I. (daedalus)


Lesenswert?

Lösung gefunden!! Vielen Dank für eure Hilfe!
Es lag an dem extralangen Parameter für die 2 SPM-Intrinsics ...

Wenn man die Adressberechnung vorverlagert funktioniert es prima:
1
  while(WriteCount)
2
  {
3
    DirtyFlag = 0;
4
    WriteCount = PrepareTempBuffer(Address, DataBuffer, SizeBuffer, Partition, &DirtyFlag); // Temporaeren Puffer vorbereiten (Read-Modify-Write)
5
    if(DirtyFlag)
6
    { // Aktualieren der FLASH-Page notwendig, da Dirty-Flag gesetzt wurde.
7
      uint32_t addr = (Partition->AddressStart + Address) & ~(uint8_t)(Partition->PageSize-1);
8
      _SPM_ERASE(addr);
9
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geloescht wurde
10
11
      _SPM_PAGEWRITE(addr);
12
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geschrieben wurde
13
      
14
      __DataToR0ByteToSPMCR_SPM( 0, (unsigned char)(1<<RWWSRE)|(1<<SELFPRGEN));         // RWW-Bereich reaktivieren
15
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geschrieben wurde
16
17
    }
18
    else
19
    {
20
      /*SPMCR |= (1<<CTPB) | (1<<SPMEN);                                          // SPM-Puffer leeren um Stackoverflow zu vermeiden
21
      __asm("SPM");                                                             // SPM-Enstellungen uebernehmen
22
      while( SPMCR & (1<<SPMEN) ){;}                                            // Warten bis SPM-Puffer geleert wurde*/
23
    }
24
    // Warten bis FLASH-Page geschrieben wurde
25
    Address     += WriteCount;                                                  // Position im FLASH aktualisieren
26
    DataBuffer  += WriteCount;                                                  // Position im Datenpuffer aktualisieren
27
    SizeBuffer  -= WriteCount;                                                  // Anzahl verbleibender Zeichen berechnen
28
  }

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alexander I. schrieb:
> Das RWW-Enable zwischen ERASE und WRITE führt zu einem
> CSTACK-Overflow... danach hilft nur noch RESET.
1
 SPMCR = (1<<PGERS) | (1<<SPMEN)
2
 __asm("SPM");
3
 while( SPMCR & (1<<SELFPRGEN) ){;}
4
5
 SPMCR = (1<<RWWSRE) | (1<<SPMEN));
6
 __asm("SPM");

  Neuen Pageinhalt einschreiben.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alexander I. schrieb:
> Lösung gefunden!! Vielen Dank für eure Hilfe!
> Es lag an dem extralangen Parameter für die 2 SPM-Intrinsics ...
>
> Wenn man die Adressberechnung vorverlagert funktioniert es prima:

 Es lag bestimmt nicht an der Adressberechnung, aber OK...

von Alexander I. (daedalus)


Lesenswert?

Marc Vesely schrieb:
>  Es lag bestimmt nicht an der Adressberechnung, aber OK...

Stimmt leider ... so ein Mist.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alexander I. schrieb:
> Achso, das _SPM_ERASE und _SPM_PAGEWRITE generiert automatisch ein
> __asm("SPM") direkt nach SPMCSR-Zugriff laut Disassembly... muss ich
> also nicht nochmal als Kommando hinschreiben.

 Was für eine Frechheit vom Compiler.
 Sollte zwar quasi-Atomic sein, aber trotzdem...
 Ohne so etwas wie Atomic=True, Unwissend=True, KeineAhnung=On o.ä.
 ist ein solches Verhalten unerhört, aber so ist C eben.
 Langsam habe ich den Verdacht, dass die Leute die C-Compiler
 geschrieben haben, denken, dass die Leute die C nutzen, dumm sind.
 Kann das sein ?

von Ralf G. (ralg)


Lesenswert?

Marc Vesely schrieb:
> Was für eine Frechheit vom Compiler.

Ich würde den Flash-Schreibkram in ein extra ASM-Modul packen. Da kann 
der Compiler nicht so schnell dazwischenfunken. (Gibt's, glaube ich, ein 
Beispiel in der Assembler-Referenz. Braucht man nur abmalen.)

von Alexander I. (daedalus)


Lesenswert?

... was mein seltsames Problem aber leider immer noch nicht löst :-/

Beim AtTiny hatte ich seinerzeit keine solchen Probleme, das Ding hat 
nach wenigen Versuchen geflutscht.

Ich werde morgen mal versuchen die AN106 bzw 109 (?) auf meinem Prozzi 
zum laufen zu bringen ... wenn das mal zuverlässig und IMMER 
funktioniert, kann man weitersuchen, was da bei meiner Firmware schief 
läuft.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alexander I. schrieb:
> ... was mein seltsames Problem aber leider immer noch nicht löst :-/

 Weil du nicht hören willst...
1
 SPMCR = (1<<PGERS) | (1<<SPMEN)
2
// __asm("SPM");   // Falls Compiler hier von alleine SPM einsetzt
3
 while( SPMCR & (1<<SELFPRGEN) ){;}
4
5
 SPMCR = (1<<RWWSRE) | (1<<SPMEN));
6
// __asm("SPM");   // Falls Compiler hier von alleine SPM einsetzt

  Neuen Pageinhalt einschreiben.

 >>>>  Zum dritten (und letzten) Mal  <<<<

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

Marc Vesely schrieb:

>
1
>  SPMCR = (1<<PGERS) | (1<<SPMEN)
2
> // __asm("SPM");   // Falls Compiler hier von alleine SPM einsetzt
3
>  while( SPMCR & (1<<SELFPRGEN) ){;}
4
> 
5
>  SPMCR = (1<<RWWSRE) | (1<<SPMEN));
6
> // __asm("SPM");   // Falls Compiler hier von alleine SPM einsetzt
7
>

Das verstehe ich nicht. Kann es sein, daß in den Kommentaren das 
Wörtchen "nicht" fehlt?


Übrigens: Das Zeug ist nicht atomar, egal ob nun das SPM vom Compiler 
kommt oder manuell eingefügt wird.

Wenn also im Bootloader irgendwelcher interruptbasierter Code läuft, ist 
die Wahrscheinlichkeit sehr hoch, daß die vom TO beschriebenen Probleme 
genau darauf zurückgehen, denn selbst eine völlig leere ISR genügt, um 
das 4Takte-Limit zwischen dem Schreibzugriff auf SPMCR und dem SPM zu 
sprengen, wenn der Interrupt eben beim Schreiben auf SPMCR auftritt.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

c-hater schrieb:
> Das verstehe ich nicht. Kann es sein, daß in den Kommentaren das
> Wörtchen "nicht" fehlt?

 Dachte, das versteht sich von selbst.

c-hater schrieb:
> Übrigens: Das Zeug ist nicht atomar, egal ob nun das SPM vom Compiler
> kommt oder manuell eingefügt wird.

 Das habe ich auch nirgendwo behauptet. Aber jeder, der sich seinen
 eigenen Bootloader schreiben will, sollte wissen, dass SPM unmittelbar
 nach dem Schreibzugriff auf SPMCR folgen muss.

c-hater schrieb:
> Wenn also im Bootloader irgendwelcher interruptbasierter Code läuft, ist
> die Wahrscheinlichkeit sehr hoch, daß die vom TO beschriebenen Probleme
> genau darauf zurückgehen, denn selbst eine völlig leere ISR genügt, um
> das 4Takte-Limit zwischen dem Schreibzugriff auf SPMCR und dem SPM zu
> sprengen, wenn der Interrupt eben beim Schreiben auf SPMCR auftritt.

 Wenn da interruptbasierter Code laufen sollte, kann es sich höchstens
 um Kommunikation mit PC handeln.
 Und auch da dachte ich, dass es selbstverständlich ist, Interrupts zu
 sperren, bevor man mit SPMCR und SPM rumfummelt.
 Ob ich mich da geirrt habe, kann nur der TO beantworten.

: Bearbeitet durch User
von Alexander I. (daedalus)


Lesenswert?

>  >>>>  Zum dritten (und letzten) Mal  <<<<

Siehe Post von mir um 15:13 Uhr weiter oben:
RWWSRE nach ERASE führt zum CSTACK-Overflow und damit zum Crash.

Nicht falsch verstehen, ich bin ja dankbar für jede Hilfe, aber ein 
ganzes Stück Code anstatt immer nur kleine Schnippsel wäre wohl 
zielführender.

von Alexander I. (daedalus)


Lesenswert?

Die Interrupts sind natürlich deaktiviert, ist ja auch in meinem 
Codeausschnitt so. Das mit den 4 Cycles weiß ich, das war ja beim AtTiny 
glaub ich auch schon so. Laut Disassembly kommt "SPM" auch unmittelbar 
nach der SPMCR-Geschichte.

Es funktioniert ja auch prinzipiell, z.B. wenn ich an Start und Ende von 
WriteFlash() einen Break rein mache, aber wenn es ohne Breaks laufen 
soll, geht's halt nur manchmal.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alexander I. schrieb:
> Siehe Post von mir um 15:13 Uhr weiter oben:
> RWWSRE nach ERASE führt zum CSTACK-Overflow und damit zum Crash.

 NICHT:
1
__DataToR0ByteToSPMCR_SPM( 0, (unsigned char)(1<<RWWSRE)|(1<<SELFPRGEN));         // RWW-Bereich reaktivieren
2
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geschrieben wurde

 SONDERN:
1
 SPMCR = (1<<RWWSRE) | (1<<SPMEN));

 Klarer ?

von Alexander I. (daedalus)


Lesenswert?

Ich werds morgen probieren ... das Zeug liegt im Büro.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alexander I. schrieb:
> Ich werds morgen probieren ... das Zeug liegt im Büro.

 Aber bitte in richtiger Reihenfolge:
 Erase
 Warte auf SELFPRGEN
 Enable
 Schreibe

 ;-D)

: Bearbeitet durch User
von Alexander I. (daedalus)


Lesenswert?

Guten Morgen

... kleine Frage:
Und wie lade ich jetzt den Z-Pointer mit der lokalen C-Variable, damit 
der ERASE und WRITE an der richtigen Stelle wirkt, wenn ich "_SPM_ERASE" 
etc. nicht verwenden soll?

von Alexander I. (daedalus)


Lesenswert?

Mannmannmann ... so ein wiederspenstiges Miststück ...

funktioniert NICHT: C-Stack/R-Stack Overflow
1
    WriteCount = PrepareTempBuffer(Address, DataBuffer, SizeBuffer, Partition, &DirtyFlag); // Temporaeren Puffer vorbereiten (Read-Modify-Write)
2
    if(DirtyFlag)
3
    { // Aktualieren der FLASH-Page notwendig, da Dirty-Flag gesetzt wurde.
4
5
      _SPM_ERASE(addr);
6
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geloescht wurde
7
8
       SPMCR = (1<<RWWSRE) | (1<<SPMEN);
9
       __asm("SPM");   // Falls Compiler hier von alleine SPM einsetzt
10
      
11
      _SPM_PAGEWRITE(addr);
12
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geschrieben wurde
13
      
14
//       SPMCR = (1<<RWWSRE) | (1<<SPMEN);
15
//       __asm("SPM");   // Falls Compiler hier von alleine SPM einsetzt
16
17
18
//      __DataToR0ByteToSPMCR_SPM( 0, (unsigned char)(1<<RWWSRE)|(1<<SELFPRGEN));         // RWW-Bereich reaktivieren
19
    }


funktioniert auch NICHT: Kein Overflow aber auch keine Daten im FLASH
1
    if(DirtyFlag)
2
    { // Aktualieren der FLASH-Page notwendig, da Dirty-Flag gesetzt wurde.
3
4
      _SPM_ERASE(addr);
5
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geloescht wurde
6
7
       SPMCR = (1<<RWWSRE) | (1<<SPMEN);
8
       __asm("SPM");   // Falls Compiler hier von alleine SPM einsetzt
9
      
10
      _SPM_PAGEWRITE(addr);
11
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geschrieben wurde
12
      
13
      SPMCR = (1<<RWWSRE) | (1<<SPMEN);
14
      __asm("SPM");   // Falls Compiler hier von alleine SPM einsetzt
15
16
17
//      __DataToR0ByteToSPMCR_SPM( 0, (unsigned char)(1<<RWWSRE)|(1<<SELFPRGEN));         // RWW-Bereich reaktivieren
18
    }

funktioniert genau einmal, danach erst nach Neustart wieder:
1
    if(DirtyFlag)
2
    { // Aktualieren der FLASH-Page notwendig, da Dirty-Flag gesetzt wurde.
3
4
      _SPM_ERASE(addr);
5
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geloescht wurde
6
7
      _SPM_PAGEWRITE(addr);
8
      while( SPMCR & (1<<SELFPRGEN) ){;}                                            // Warten bis FLASH-Page geschrieben wurde
9
      
10
      SPMCR = (1<<RWWSRE) | (1<<SPMEN);
11
      __asm("SPM");   // Falls Compiler hier von alleine SPM einsetzt
12
13
14
//      __DataToR0ByteToSPMCR_SPM( 0, (unsigned char)(1<<RWWSRE)|(1<<SELFPRGEN));         // RWW-Bereich reaktivieren
15
    }


Übrigens, das hier ist der Code von der AN106 "C functions for reading 
and writing to flash memory." von Atmel für IAR:
Dieser ist nahezu identisch mit meiner Version ... und läuft ebenfalls 
genau einmal, danach erst wieder nach dem Neustart:
1
/*!
2
* The function writes Flash temporary buffer to Flash page address given by
3
* input argument.
4
**/
5
void WriteBufToFlash(MyAddressType flashStartAdr){
6
#pragma diag_suppress=Pe1053 // Suppress warning for conversion from long-type address to flash ptr.
7
  _SPM_ERASE(flashStartAdr);
8
  while( SPMControllRegister & (1<<SPMEN) ); // Wait until Flash write completed
9
  _SPM_PAGEWRITE(flashStartAdr);
10
  while( SPMControllRegister & (1<<SPMEN) ); // Wait until Flash write completed
11
  __DataToR0ByteToSPMCR_SPM( 0, (unsigned char)(1<<RWWSRE)|(1<<SPMEN)); // Enable RWW
12
#pragma diag_default=Pe1053 // Back to default.
13
}

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alexander I. schrieb:
> Dieser ist nahezu identisch mit meiner Version ... und läuft ebenfalls
> genau einmal, danach erst wieder nach dem Neustart:
 Nicht alles was bei Atmel steht, funktioniert auch.

> funktioniert NICHT: C-Stack/R-Stack Overflow
 Es funktioniert doch.
 Warum du diesen Fehler kriegst, ist eine andere Frage.
 Eine an sich einfache Sache mittels einer Funktion mit Argumenten
 zu machen ist unnötig und führt zu obigen Fehlern.

> SPMCR = (1<<RWWSRE) | (1<<SPMEN);
>        __asm("SPM");   // Falls Compiler hier von alleine SPM einsetzt
 Du hast doch geschrieben, dass dein Compiler hier von alleine SPM
 einsetzt, warum dann noch einmal ?

> ... kleine Frage:
> Und wie lade ich jetzt den Z-Pointer mit der lokalen C-Variable, damit
> der ERASE und WRITE an der richtigen Stelle wirkt, wenn ich "_SPM_ERASE"
> etc. nicht verwenden soll?
 Was hindert dich daran, eine globale Variable, wie z.B. _Z_Pointer
 zu deklarieren ?

 Und auch für dich:
 Das ist ein Bootloader, also NICHT an 20 Stellen im Program mit
 Bits und Flags jonglieren, sondern Const benutzen, also etwa so:

const uint8_t   c_StoreData = 0x01;
const uint8_t   c_PageErase = 0x03;
const uint8_t   c_PageWrite = 0x05;
const uint8_t   c_RWW_Ena = 0x11;

Ralf G. schrieb:
> Ich würde den Flash-Schreibkram in ein extra ASM-Modul packen. Da kann
> der Compiler nicht so schnell dazwischenfunken.
 Ich auch.

: Bearbeitet durch User
von Alexander I. (daedalus)


Lesenswert?

Marc Vesely schrieb:
>  Nicht alles was bei Atmel steht, funktioniert auch.

Was hiermit wohl bewiesen wäre. Oder es funktioniert in einem anderen 
Kontext, als dem meinem.

>> funktioniert NICHT: C-Stack/R-Stack Overflow
>  Es funktioniert doch.
>  Warum du diesen Fehler kriegst, ist eine andere Frage.
>  Eine an sich einfache Sache mittels einer Funktion mit Argumenten
>  zu machen ist unnötig und führt zu obigen Fehlern.

Es spielt keine Rolle wie groß der Stack ist. Und wenn ich ihn auch 3x 
so groß mache ... er läuft auch dann noch über.

>> SPMCR = (1<<RWWSRE) | (1<<SPMEN);
>>        __asm("SPM");   // Falls Compiler hier von alleine SPM einsetzt
>  Du hast doch geschrieben, dass dein Compiler hier von alleine SPM
>  einsetzt, warum dann noch einmal ?

Aus reinem Misstrauen. Es spielt keine Rolle ob mit oder ohne 
zusätzlicher SPM-Anweisung. Funktioniert beides nicht richtig. Bitte 
verzeih' mir wenn ich nicht jeden meine Sourcecode-Versuche hier poste. 
Es wäre mindestens der 137.

>
>> ... kleine Frage:
>> Und wie lade ich jetzt den Z-Pointer mit der lokalen C-Variable, damit
>> der ERASE und WRITE an der richtigen Stelle wirkt, wenn ich "_SPM_ERASE"
>> etc. nicht verwenden soll?
>  Was hindert dich daran, eine globale Variable, wie z.B. _Z_Pointer
>  zu deklarieren ?
>
>  Und auch für dich:
>  Das ist ein Bootloader, also NICHT an 20 Stellen im Program mit
>  Bits und Flags jonglieren, sondern Const benutzen, also etwa so:
>
> const uint8_t   c_StoreData = 0x01;
> const uint8_t   c_PageErase = 0x03;
> const uint8_t   c_PageWrite = 0x05;
> const uint8_t   c_RWW_Ena = 0x11;

Nein. Es ist KEIN Bootloader. Es ist nur eine "Speicher was im 
Flash"-Routine in einem ganz normalen Programm. Den ganzen Handstand mit 
Boot-Section usw. gab's beim AtTiny nicht. Da ich nunmal dessen Firmware 
als Grundlage nehmen muss, kann ich auch nicht alles komplett umbiegen.

>> Ich würde den Flash-Schreibkram in ein extra ASM-Modul packen. Da kann
>> der Compiler nicht so schnell dazwischenfunken.
>  Ich auch.

Kann man schon machen, klar ... aber dann frage ich mich, wofür die 
schönen Intrinsics denn da sind, wenn man dann doch wieder alles mit 
ASM-Schnippseln zusammenfrickeln muss.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Alexander I. schrieb:
> Flash"-Routine in einem ganz normalen Programm. Den ganzen Handstand mit
> Boot-Section usw. gab's beim AtTiny nicht. Da ich nunmal dessen Firmware
> als Grundlage nehmen muss, kann ich auch nicht alles komplett umbiegen.

 LOL.
 Mein Bootloader hat ganze 78 Words. Inklusive Senden und Empfangen
 vom PC.

 EDIT:
 78hex, aber 4 Befehle und FLASH und EEPROM mit Verify.

: Bearbeitet durch User
von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Marc Vesely schrieb:
>  LOL.
>  Mein Bootloader hat ganze 78 Words.

Du hast meine absolute Bewunderung!

>  EDIT:
>  78hex

Hm, das relativiert meine anfängliche Begeisterung allerdings wieder.

von Marc V. (Firma: Vescomp) (logarithmus)


Lesenswert?

Frank M. schrieb:
> Hm, das relativiert meine anfängliche Begeisterung allerdings wieder.

 Ja.
 Ich wollte nur unter 80h bleiben, vielleicht kann das ganze noch ein
 bisschen gequetscht werden, aber wozu...

: Bearbeitet durch User
von Alexander I. (daedalus)


Lesenswert?

Heute habe ich mich nach ein paar Tagen Pause mal wieder an das Problem 
gesetzt und die Fehlerursache doch noch gefunden.

Es hatte nichts mit den Intrinsics und der hier diskutierten 
Schreibweise zu tun, sondern mit der Variable DirtyFlag:

Die FLASH-Page wird nur aktualisiert, wenn die Daten auch wirklich 
unterschiedlich sind. Ist dies nicht der Fall, wurde vor dem nächsten 
Schleifendurchlauf kein RWW-Enable ausgeführt.

Falsch war:
1
    if(DirtyFlag)
2
    { // Aktualieren der FLASH-Page notwendig, da Dirty-Flag gesetzt wurde.
3
      _SPM_ERASE(addr);
4
      while( SPMCR & (1<<SELFPRGEN) ){;}                                        // Warten bis FLASH-Page geloescht wurde
5
6
      _SPM_PAGEWRITE(addr);
7
      while( SPMCR & (1<<SELFPRGEN) ){;}                                        // Warten bis FLASH-Page geschrieben wurde
8
9
      __DataToR0ByteToSPMCR_SPM( 0, (unsigned char)(1<<RWWSRE)|(1<<SELFPRGEN));   // RWW-Bereich reaktivieren
10
      while( SPMCR & (1<<SELFPRGEN) ){;}   
11
    }

Richtig ist:
1
    if(DirtyFlag)
2
    { // Aktualieren der FLASH-Page notwendig, da Dirty-Flag gesetzt wurde.
3
      _SPM_ERASE(addr);
4
      while( SPMCR & (1<<SELFPRGEN) ){;}                                        // Warten bis FLASH-Page geloescht wurde
5
6
      _SPM_PAGEWRITE(addr);
7
      while( SPMCR & (1<<SELFPRGEN) ){;}                                        // Warten bis FLASH-Page geschrieben wurde
8
    }
9
    __DataToR0ByteToSPMCR_SPM( 0, (unsigned char)(1<<RWWSRE)|(1<<SELFPRGEN));   // RWW-Bereich reaktivieren
10
    while( SPMCR & (1<<SELFPRGEN) ){;}

So long now...

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.