Hallo Ich habe die Aufgabe bekommen eine 10 Jahre alte Software auf eine neue Architektur zu portieren, da der verwendete Motorola Controller abgekündigt wurde. Der neue Controller ist ein Freescale Kinetis M0+ MKE02Z. Compiler/IDE: Keil MDK ARM 5.10 In der alten Software wurde eine uminitialisierte Variable im RAM verwendet um einem bestimmten Betriebszustand nach einem allfälligen Reset wieder herzustellen. D. h. die Variable wurde zu Laufzeit mit einem entsprechenden Wert beschrieben. Wurde der Controller dann durch den internen/externen Watchdog geresettet, so konnte beim Wiederaufstarten überprüft werden, ob ein plausibler Wert in dieser Variable steht und der Betriebszustand wurde entsprechende wiederhergestellt. Wie kann ich dieses Verhalten wieder hinbekommen? Globale und statische Variablen werden von C Compiler ja automatisch im 0 initialisiert und eine lokale Variable wird im Stack abgelegt. Meiner Meinung nach könnte ich einfach das EEPROM des Kinetis verwenden, dafür ist es ja da. Jedoch habe ich ausdrücklich die Restriktion des Kunden, dass ich möglichst alles so belassen soll wie es damals implementiert wurde! Weiss da jemand weiter? Gruss Steff
Schau in dein Compiler manual. Dort in der linker Sektion sollte so was wie uninit / nicht initialisiert zu finden sein. Dann kannst Du Deine Linkerdatei angeben, dass ein Bereich im RAM nicht initialisiert wird. Andere Möglichkeit: Manche uC haben auch extra Register, die bei einem Reset erhalten bleiben.
Stefan K. schrieb: > In der alten Software wurde eine uminitialisierte Variable im RAM > verwendet um einem bestimmten Betriebszustand nach einem allfälligen > Reset wieder herzustellen. Normalerweise setzt der Watchdog eines µCs ein Register-Bit als Flag, dass er zugeschlagen hat. Dieses kann man dann beim Boot auswerten. Mich würde es wundern, wenn Dein µC es anders macht.
>Normalerweise setzt der Watchdog eines µCs ein Register-Bit als Flag, >dass er zugeschlagen hat. Dieses kann man dann beim Boot auswerten. Mich >würde es wundern, wenn Dein µC es anders macht. Ich denke, es geht ihm darum zu erkennen, wann der Reset kam, nicht ob Resettet wurde.
Ist nicht ganz einfach. Dein Problem wird nämlich sein Grün von Grün zu unterscheiden. Jeder Prozessor/Fertigungsprozess hat sein eigenes Verhalten. Du kannst zwar dem Compiler sagen: "Finger weg von Variable XY" - was aber nicht geht ist: "Prozessor X verhalte Dich wie Prozessor Y". Oder noch anders ausgedrückt: In welchem Zustand ist eine beliebige Speicherstelle nach dem unbeeinflussten Reset und in welchem nach dem Einschalten. Null, nach dem Einschalten, klingt zwar Plausibel – stimmt das aber? Aus diesem Grunde würde ich den Weg über eine EEPROM-Variable wählen.
Wenn dein Chip einen internen EEProm merkt der Kunde doch nichtmal, dass nicht der RAM genutzt wird. Ausserdem: Sollte der Kunde so versiert sein, dass er den Unterschied EEprom <-> RAM in deinem Programm erkennt, dann ist er auch versiert genug um zu verstehen warum der RAM eine dumme Idee ist.
Sebastian Hepp schrieb: > Ich denke, es geht ihm darum zu erkennen, wann der Reset kam, nicht ob > Resettet wurde. Ja ich muss erkennen im welchen Zustand der Controller war als der Watchdog kam. foo schrieb: > Schau in dein Compiler manual. > Dort in der linker Sektion sollte so was wie uninit / nicht > initialisiert zu finden sein. Dann kannst Du Deine Linkerdatei angeben, > dass ein Bereich im RAM nicht initialisiert wird. Die entsprechende Sektion habe ich gefunden, danke. :) http://www.keil.com/support/man/docs/armcc/armcc_chr1359124243221.htm Jedoch scheint es nicht zu funktionieren, nach einem Reset ist die Variable wieder 0. Mal schauen ich hoffe ich kann den Kunden vom EEPROM überzeugen, das wäre sicher der bessere weg. Danke
>Jedoch scheint es nicht zu funktionieren, nach einem Reset ist die >Variable wieder 0. Schau mal in den Initialisierungscode Deines Compilers. Da steht drinnen, was der Compiler "vor" dem Programmstart (z. B. vor main) macht.
Stefan K. schrieb: > Die entsprechende Sektion habe ich gefunden, danke. :) > http://www.keil.com/support/man/docs/armcc/armcc_chr1359124243221.htm > > Jedoch scheint es nicht zu funktionieren, nach einem Reset ist die > Variable wieder 0. Hast Du Deine Variable auch mit den pragamas umklammert? Hast Du im map file überprüft ob die Variable im richtigen Bereich liegt?
Stefan K. schrieb: > In der alten Software wurde eine uminitialisierte Variable im RAM > verwendet um einem bestimmten Betriebszustand nach einem allfälligen > Reset wieder herzustellen. Was wurde damit bezweckt? Was sollte von was unterschieden werden? Der Watchdog-Reset vom Powerup-Reset?
>Was wurde damit bezweckt? Was sollte von was unterschieden werden? Der >Watchdog-Reset vom Powerup-Reset? Die Software hat zwischendurch ganz bestimmte Zustände. Diese werden in der Variablen gesichert. Sollte ein Reset stattfinden, dann kann man in diesem Zustand weiter machen, statt komplett von vorne zu beginnen.
Lothar Miller schrieb: > Stefan K. schrieb: >> In der alten Software wurde eine uminitialisierte Variable im RAM >> verwendet um einem bestimmten Betriebszustand nach einem allfälligen >> Reset wieder herzustellen. > Was wurde damit bezweckt? Was sollte von was unterschieden werden? Der > Watchdog-Reset vom Powerup-Reset? So wie sich der Rest dann liest, rate ich mal ins Blaue, dass das Programm je nach Programmzweig sich einfach Kennungen in dieser Variablen hinterlassen hat, um sich die Information für den Neustart zu retten, woran es zuletzt gearbeitet hat. Das kann jetzt natürlich mit einem EEPROM insofern problematisch werden, als ein EEPROM oft (meist, immer?) über eine relativ kleine begrenzte Anzahl an garantierten Schreibzyklen verfügt. Insofern sollte man mal klären, wie oft die Variable in einem Programmlauf tatsächlich verändert wird.
> Mal schauen ich hoffe ich kann den Kunden vom EEPROM überzeugen, das > wäre sicher der bessere weg. Wenn deine Merkervariable zur Laufzeit evtl. mehrfach geändert wird, musst du Glück haben, dass ein unerwarteter Reset nicht genau das Eeprom-Schreiben unterbricht. Und das: ... > Ja ich muss erkennen im welchen Zustand der Controller war als der > Watchdog kam. ...klingt doch sehr danach. Wenn du also kein sauberes Schreiben ins Eeprom garantieren kannst, ist das Verwenden von Eeprom Pfusch. Übrigens, es ist gängig, dass man NonInit-RAM zum Speichern über Reset hinweg verwendet. Dabei muss natürlich klar sein, dass die Versorgungsspannung nicht ausfallen darf (bzw. nicht unter die DataRetention Voltage fallen darf). Dann ist das alles kein Problem. Gegen zufälliges Kippen auf genau einen speziellen Wert, hilft es, wenn man nicht nur ein Bit setzt, sondern ein bestimmtes Muster (z.B. 32Bit) abspeichert. Dass genau dies durch Spannungsschwankungen entsteht ist sehr unwahrscheinlich (muss man halt abwägen).
Wenn man nicht nur ein byte mit 1,2,3 benutzt, sondern z.B. zwei uint32 die invertiert abgelegt sind (0xDEADBEEF - 0x21524110) sollte es auch hinreichend unwahrscheinlich sein, dass diese Muster zufällig auftritt.
>hinreichend unwahrscheinlich sein, dass diese Muster zufällig auftritt.
Jede beliebige Zahl, kann mit gleicher Wahrscheinlichkeit, auftreten.
Ob 1, 2, 3 und 4 oder 222, 173, 190 und 239 (DEADBEEF) ist dabei
unerheblich.
Amateur schrieb: > Jede beliebige Zahl, kann mit gleicher Wahrscheinlichkeit, auftreten. Nein, ob ich ein Byte oder 8 Bytes überprüfe macht schon einen Unterschied.
Karl Heinz schrieb: > So wie sich der Rest dann liest, rate ich mal ins Blaue, dass das > Programm je nach Programmzweig sich einfach Kennungen in dieser > Variablen hinterlassen hat, um sich die Information für den Neustart zu > retten, woran es zuletzt gearbeitet hat. Genau so ist es, vom Controller werden drei Schrittmotoren angesteuert, nach einem Reset muss ich wissen welche Motor gerade angesteuert wurde und in welche Richtung. Dafür wird eine entsprechende Bitmuster in die Variable geschrieben. Karl Heinz schrieb: > einem EEPROM insofern problematisch werden, Das stimmt allerdings, soweit habe ich gar noch nicht studiert. Dieter schrieb: > Dabei muss natürlich klar sein, dass die > Versorgungsspannung nicht ausfallen darf Das ist klar, und auch so mit dem Kunde besprochen. foo schrieb: > Hast Du Deine Variable auch mit den pragamas umklammert? > Hast Du im map file überprüft ob die Variable im richtigen Bereich > liegt? Ja habe ich, die Variable wird allerdings immer am Anfang des RAMs abgelegt. Egal ob mit #pragma.. oder ohne. Hier mal ein Auszug meines Testcodes:
1 | /******************************************************************************
|
2 | * Global variables
|
3 | ******************************************************************************/
|
4 | #pragma arm section zidata = "non_initialized"
|
5 | uint8 wdog_var; // uninitialized data in non_initialized section (without the pragma, would be in .bss section by default) |
6 | #pragma arm section zidata // back to default (.bss section)
|
1 | int main (void) |
2 | {
|
3 | |
4 | wdog_enable(); |
5 | wdog_unlock(); |
6 | FGPIOB_PDDR = (1<<7); // PTH1 as output |
7 | GPIOConfig(); |
8 | SystickConfig(4000); |
9 | if(wdog_var) |
10 | {
|
11 | FGPIOB_PCOR = (1<<7); |
12 | }
|
13 | delayMs(3000); |
14 | wdog_var = 1; |
15 | while(1) |
16 | {
|
17 | //DisableInterrupts; // disable global interrupt
|
18 | //wdog_unlock(); //Watchdog reset
|
19 | //EnableInterrupts;
|
20 | }
|
21 | }
|
Der Code sollte folgendes bewecken, beim ersten durchlauf leuchtet die Diode nicht (PTH1). Die uninitialisierte Varaiable wird gesetzt und dann ein Reset durch den Watchdog ausfegührt, dann sollte die LED sogleich zu leuchten beginnen. Irgendwas mach ich wohl falsch.
Stefan K. schrieb: > Ja habe ich, die Variable wird allerdings immer am Anfang des RAMs > abgelegt. Egal ob mit #pragma.. oder ohne. Hast Du eine Warnung im compiler / linker log file? Kenn mich mit Keil nicht aus - aber hast Du das auch berücksichtigt? The non_initialized section is placed into its own UNINIT execution region, as follows: LOAD_1 0x0 { EXEC_1 +0 { * (+RO) * (+RW) * (+ZI) ; ZI data gets initialized to zero } EXEC_2 +0 UNINIT { * (non_init) ; ZI data does not get initialized to zero } }
Stefan K. schrieb: > Wie kann ich dieses Verhalten wieder hinbekommen? Globale und statische > Variablen werden von C Compiler ja automatisch im 0 initialisiert und > eine lokale Variable wird im Stack abgelegt. > > Meiner Meinung nach könnte ich einfach das EEPROM des Kinetis verwenden, > dafür ist es ja da. Jedoch habe ich ausdrücklich die Restriktion des > Kunden, dass ich möglichst alles so belassen soll wie es damals > implementiert wurde! Ich kenn den kleinen Kinetis nicht, aber auf dem K60/K70 hast Du ein VBAT Register File, das sind 32Byte, die Resets ueberleben (ausser die, bei denen die Versorgungsspannung weggeht). Damit hab ich schon mal Crash-Zusammenfassungen ueber einen Watchdogreset hinweg gerettet. Vielleicht hat der kleine Kinetis das ja auch?
foo schrieb: > Hast Du eine Warnung im compiler / linker log file? > > Kenn mich mit Keil nicht aus - aber hast Du das auch berücksichtigt? Das habe ich auch vorhin gemerkt, daran muss es wohl liegen. Das ist das Scatter File welches vom Linker verarbeitet wird. Ein Erklärung auf der Hilfeseite wäre schön, teilweise finde ich den Keil Compiler ziemlich schlecht erläutert, oder ich bin einfach zu blöd! Das vom Kinetis sieht jetzt aber doch ein wenig anders aus wie im Beispiel, bin gerade am probieren.
In dem Beispiel ist glaub ich ein Fehler, die Sektion muss gleich heißen. Also beim pragma "non_init" statt "non_initialized": #pragma arm section zidata = "non_init" int i; #pragma arm section zidata EXEC_2 +0 UNINIT { * (non_init) ; }
**Freu** ich habs endlich hingekriegt, eine Ablenkung durch die Mittagspause ist manchmal doch nicht schlecht. Falls jemand mal vor den gleichen Problem steht, so hat es bei mir jetzt funktioniert: Deklaration der Variable:
1 | uint8 wdog_var __attribute__( ( section( "NoInit"),zero_init) ) ; |
Memory Mapping im Scatter File "xxx.sct"
1 | ; ************************************************************* |
2 | ; *** Scatter-Loading Description File generated by uVision *** |
3 | ; ************************************************************* |
4 | |
5 | LR_IROM1 0x00000000 0x00010000 { ; load region size_region |
6 | ER_IROM1 0x00000000 0x00010000 { ; load address = execution address |
7 | *.o (RESET, +First) |
8 | *(InRoot$$Sections) |
9 | .ANY (+RO) |
10 | } |
11 | RW_IRAM1 0x1FFFFC00 0x00000400 { |
12 | .ANY (+RW +ZI) |
13 | } |
14 | RW_IRAM2 0x20000000 0x00000A00 { |
15 | .ANY (+RW +ZI) |
16 | } |
17 | ; add for noinit variable, 4 byte boundary |
18 | RW_IRAM3 0x20000BFC UNINIT 0x00000004 { ; RW data |
19 | * (NoInit) ; |
20 | } |
21 | } |
Im Keil Linker Dialog muss die Option "[]Use Memory Layout form Target Dialog" noch deaktiviert werden, ansonsten wird das Scatter File immer wieder überschrieben. Insbesondere ist dieser Link hilfreich: http://www.keil.com/support/docs/3480.htm Danke allen für die Hilfe Gruss Steff
Den Vorschlag von Konrad mit den Backupregistern ist der wohl sinnvollste im ganzen Fred hier. Genau fuer sowas sind die naemlich da...
Stefan K. schrieb: > In der alten Software wurde eine uminitialisierte Variable im RAM > verwendet um einem bestimmten Betriebszustand nach einem allfälligen > Reset wieder herzustellen. ??? Wieso ist ein Reset "allfällig"? Meine Konstrukte resetten üblicherweise nur dann, wenn die Betriebsspannung unter einen zulässigen Wert gefallen ist oder wenn jemand den Resetknopf drückt. In beiden Szenarien ist ein Fortsetzung der letzten Operation eher unsinnig, ja sogar u.U. gefährlich und im Falle des Resetknopfes obendrein explizit unerwünscht. Das Ganze hört sich für mich verdammt nach einem Workaround um lausige und deswegen schwer absturz/deadlock-gefährdete Software an. Also der typische, von unfähigen "Programmierern" fix zusammengeklaute und dann notdürftig zusammengeleimte C-Schrott. Ich würde also damit beginnen, die Fehler in der Software zu suchen, die solch krude Workarounds überhaupt erst nötig machen. Und eine sowieso anstehende Portierung auf ein anderes Zielsystem wäre definitiv ein überaus geeigneter Zeitpunkt dafür...
Stefan K. schrieb: > Sebastian Hepp schrieb: >> Ich denke, es geht ihm darum zu erkennen, wann der Reset kam, nicht ob >> Resettet wurde. > > Ja ich muss erkennen im welchen Zustand der Controller war als der > Watchdog kam. > > foo schrieb: >> Schau in dein Compiler manual. >> Dort in der linker Sektion sollte so was wie uninit / nicht >> initialisiert zu finden sein. Dann kannst Du Deine Linkerdatei angeben, >> dass ein Bereich im RAM nicht initialisiert wird. > > Die entsprechende Sektion habe ich gefunden, danke. :) > http://www.keil.com/support/man/docs/armcc/armcc_chr1359124243221.htm > > Jedoch scheint es nicht zu funktionieren, nach einem Reset ist die > Variable wieder 0. > > Mal schauen ich hoffe ich kann den Kunden vom EEPROM überzeugen, das > wäre sicher der bessere weg. Nein, so einfach ist das nicht, denn der Wert steht ja auch nach dem Power-On-Reset noch im EEPROM. Da kann durchaus zu falschem Verhalten führen. Prinzipiell muss man im µC-Bereich die Möglichkeit haben, nach einem Watchdog-Reset mit den vorhandenen Variablen-Werten weiter zu arbeiten. Das kenne ich so von anderen Compilern und Plattformen, un die Möglichkeit ist auch in der verlinkten Doku beschrieben. Wenn es nicht zu funktionieren scheint, solltest du zuerst mal die Möglichkeit in Betracht ziehen, dass du selbst was falsch gemacht hast. Sicher liegt es nicht daran, dass jemand was "zum Spaß" in die Doku geschrieben hat, was so gar nicht funktioniert, zumal bei solch einem essentiellen Problem. Leider wissen wir nicht, was du gemacht so wirklich hast. Deshalb kann dir auch niemand sagen, woran das Problem sein könnte. ;-)
eine seriöse Anwendung: wenn wegen Not-Aus o.ä. abgeschaltet wurde, passiert die Verriegelung per Hardware, aber die Software merkt sich den genauen Grund und letzten Betriebszustand. Das sollte auch über einen Reset hinweg funktionieren -- allerdings auch bei Spannungsausfall.
Dieter schrieb: > Übrigens, es ist gängig, dass man NonInit-RAM zum Speichern über Reset > hinweg verwendet. Dabei muss natürlich klar sein, dass die > Versorgungsspannung nicht ausfallen darf (bzw. nicht unter die > DataRetention Voltage fallen darf). Dann ist das alles kein Problem. > Gegen zufälliges Kippen auf genau einen speziellen Wert, hilft es, wenn > man nicht nur ein Bit setzt, sondern ein bestimmtes Muster (z.B. 32Bit) > abspeichert. Dass genau dies durch Spannungsschwankungen entsteht ist > sehr unwahrscheinlich (muss man halt abwägen). Diese Bitmuster sind normalerweise unnötig, es sei denn man hat einen µC ohne Brown-Out-Erkennung. Aber das sollte bei aktuellen Controllern eigentlich drin sein. Nach dem Reset ist zuerst zu untersuchen, ob es ein Power-On-, Brown-Out- oder Watchdog-Reset war, und dementsprechend ist der Speicher ganz oder nur teilweise zu initialisieren. Genau so und nicht anderes wird ein Schuh daraus.
c-hater schrieb: > Stefan K. schrieb: > >> In der alten Software wurde eine uminitialisierte Variable im RAM >> verwendet um einem bestimmten Betriebszustand nach einem allfälligen >> Reset wieder herzustellen. > > ??? > > Wieso ist ein Reset "allfällig"? Meine Konstrukte resetten üblicherweise > nur dann, wenn die Betriebsspannung unter einen zulässigen Wert gefallen > ist oder wenn jemand den Resetknopf drückt. Du scheinst "allfällig" mit "allgegenwärtig, andauernd, ständig" zu übersetzen. Tatsächlich bedeutet es jedeoch "eventuell, möglicherweise", und widerspricht keineswegs der Maßgabe, dass ein Reset üblicherweise durch Power-On oder Brown-Out ausgelöst wird. > In beiden Szenarien ist ein Fortsetzung der letzten Operation eher > unsinnig, ja sogar u.U. gefährlich und im Falle des Resetknopfes > obendrein explizit unerwünscht. Ja, das gilt bei Power-On oder Brown-Out, aber eben nicht für einen Watchdog-Reset. Da kann es durchaus sinnvoll sein, wohlüberlegt mit den gemerkten Zuständen fortzufahren. > Das Ganze hört sich für mich verdammt nach einem Workaround um lausige > und deswegen schwer absturz/deadlock-gefährdete Software an. Also der > typische, von unfähigen "Programmierern" fix zusammengeklaute und dann > notdürftig zusammengeleimte C-Schrott. Das hört sich für mich verdammt nach deinem allgegenwärtigen, andauernden, ständigen Beisreflex an. > Ich würde also damit beginnen, die Fehler in der Software zu suchen, die > solch krude Workarounds überhaupt erst nötig machen. Und eine sowieso > anstehende Portierung auf ein anderes Zielsystem wäre definitiv ein > überaus geeigneter Zeitpunkt dafür... Du solltest eher damit beginnen, nach einem Therapeuten für dich zu suchen, der dir deine kruden Denkmuster wegprogrammiert. Jetzt damit anzufangen wäre definitiv ein überaus geeigneter Zeitpunkt dafür...
Nach den letzten Antworten muss ich wohl doch noch ein wenig weiter ausholen. Die geforderte Funktion hat nichts mit irgendwelcher schlampig zusammengebastelter Software o. ä. zu tun. Es ist ein Redesign einer Firmware welche nach EN62304 entwickelt wurde, da wir vom Kunden explizit verlangt das alle möglichen und unmöglichen Zustände abgefangen und nicht zu einem unkontrollierten Verhalten führen. Unter anderem eben auch dann, wenn der Watchdog anspricht. Wenn ein Motor zu dieser Zeit in eine Richtung fährt so darf er nach den Reset eben nicht in diese Richtung weiter drehen. Darum das ganze mit dieser Variable überhaupt. Über die Sinnhaftigkeit brauchen wir hier nicht zu diskutieren, da einfach so vom Kunden gefordert. ./. schrieb: > Den Vorschlag von Konrad mit den Backupregistern ist der wohl > sinnvollste im ganzen Fred hier. Wenn der kleine MC dieses Register hätte würde ich es natürlich einsetzen. ;) Michael L. schrieb: > Leider wissen wir nicht, was du gemacht so wirklich hast. Deshalb kann > dir auch niemand sagen, woran das Problem sein könnte. Das Problem wurde gelöst, siehe mein letzter Post. :)
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.