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.
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
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
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.
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?
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
> 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?
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.
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:
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.
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:
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...
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 ?
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.)
... 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.
>// __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.
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.
> >>>> 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.
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.
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)
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?
Ü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
voidWriteBufToFlash(MyAddressTypeflashStartAdr){
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
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.
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.
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.
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...
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