[C]Geschwindigkeit von Funktionsaufrufen

Herakles

Profifragensteller
Moin!

Ich entwickle gerade eine Steuerung, die beim Abschalten in einen Flash-Speicher schreiben soll, damit "alte" genutzte Daten weiterverwandt werden können, wenn die Steuerung wieder eingeschaltet wird. Um das zu machen, muss erkannt werden, dass ein Spannungsausfall auftrat (per Schalter oder weil eben Stromausfall ist) und über einen Kondensator kurz eine Spannung aufrecht erhalten werden, damit der Speicher weggeschrieben werden kann.

Das funktioniert prächtig, über eine ISR fange ich diesen Fall ab und reagiere dann entsprechend: Ich setze eine Semaphore, die im Hauptprogramm (eine endlose Schleife) abgefragt wird und sollte sie gesetzt sein, schreibe ich den Speicher weg und beim Einschalten ist dieser Speicher dann wieder auslesbar. Wichtige Informationen werden so über einen Spannungsausfall hinaus behalten.

Realisiert habe ich das, indem ich nach einem bemerkten "raisen" der Semaphore eine eigene Funktion aufrufe, die den Speicher wegschreibt. Drumherum werden aber auch noch Tasks gekillt, insgesamt sind das 5 Aufrufe:

Code:
vTaskDelete( globals.pcb_com_task_handle ); \
vTaskDelete( globals.lwip_task_handle ); \
vTaskDelete( globals.i2c_task_handle ); \
write_remanent_RAM_to_FLASH_on_shutdown( remanent_memory_RAM ); \
vTaskDelete( NULL );

Der Fachmann erkennt: FreeRTOS. :D

Wichtig zu erwähnen: Der genutzte Kondensator hält die Spannung nur für den Bruchteil einer Sekunde (~10ms), das "Speicherwegschreiben" ist also extrem zeitkritisch.

Naja, wie auch immer. So funktioniert das alles sehr schön. Nun will ich das aber nicht nur im Fall des Spannungsausfalls machen, sondern auch, wenn zum Beispiel die Steuerung in einen "Stop" geht oder wenn sie einen Fehler bemerkt. Um also Codedopplungen zu vermeiden, habe ich den oben genannten Code-Teil in eine Funktion gegossen und muss nun nur noch die Funktion aufrufen.

Effekt: Funktioniert nicht. NUR, wenn ich die obigen Aufrufe an die jeweiligen Stellen schreibe, habe ich eine gesicherte Funktion. Wenn ich das über einen Funktionsaufruf mache (wie gesagt: ich will keine Codedopplungen), klappt das nicht beim Spannungsausfall.

Mein aktueller Workaround ist ein #define:

Code:
#define TURN_OFF_ALL_TASKS_AND_WRITE_TO_REMANENT \
     vTaskDelete( globals.pcb_com_task_handle ); \
     vTaskDelete( globals.lwip_task_handle ); \
     vTaskDelete( globals.i2c_task_handle ); \
     write_remanent_RAM_to_FLASH_on_shutdown( remanent_memory_RAM ); \
     vTaskDelete( NULL );

Wenn ich das an den entsprechenden Stelle aufrufe, klappt's!

Sooo, lange erklärt und nun meine eigentliche Frage: Was ist der Grund dafür, dass das, was ich hier beschrieben habe, mit einem Funktionsaufruf nicht klappt, mit einem "#define" oder direkt an Ort und Stelle geschriebenem Code (was meinem Verständnis zufolge ja dasselbe ist wie mit einem #define) aber sehr wohl funktioniert?

Viele Grüße vom berufsbedingten Kopfqualmer
Herakles
 
Einfach die Funktion inline definieren.

Je nach Plattform ist ein Funktionsaufruf ziemlich teuer. Du machst nicht nur ein LCALL sondern musst noch alle möglichen Register auf den Stack schmeißen.

Je nach Plattform kann man da einiges dran optimieren (durch den geschickenten Einsatz von Registerbänken z.B.) aber so Spiele kann man mit einem RTOS im Rücken nicht einfach so treiben.
 
Verstehe ich das richtig, dass Dein Code im "Panic"-Fall, d.h. bei durch die Software erkannten Fehlern, korrekt funktioniert (auch mit Funktion statt Makro) -- aber der Pfad Interrupthandler -> Semaphore -> Funktion nicht mehr funktioniert, wenn Du statt einem Makro eine Funktion verwendest?

Wenn Du zwischen Power Failure und Brown-out Reset tatsächlich 10ms hast, dann glaube ich fast, dass das Problem nicht Makro vs. Funktion ist. Evtl. verlierst Du einige Zeit zwischen Posten und Behandeln der Semaphore. Wie ist denn Dein Timer Tick eingestellt? Da Du eh schon FreeRTOS verwendest -- warum verwendest Du überhaupt eine Semaphore ins "Hauptprogramm" (was immer das bei Dir ist)? Könntest Du nicht auch den "write to Flash" Teil direkt im Interrupthandler durchführen? Außerdem: Warum machst Du Dir noch die Mühe, die Tasks zu löschen? Schließlich schreibst Du einen Notfall-Handler -- keinen Literatur-Beitrag zum Thema "sauberes Aufräumen von Resourcen".

Ohne Deinen Code weiter zu kennen denke ich, dass es lohnt über o.g. Fragen nachzudenken.
 
Auf CPUs, die schnell genug sind um ein RTOS zu benutzen, sind einzelne Funktionsaufrufe kaum signifikant im Vergleich zu 10ms. Auf nem Mikrocontroller dieser Größenordnung kannst du oft noch im Datenblatt die Latenz der einzelnen Instruktionen finden. Solltest du es so genau wissen wollen guck in den Assembleroutput deines C Compilers und addiere die Takte. Was mir eher sorgen machen würde ist die Brown-out Schaltung. Bei welcher Spannung löst sie wie schnell aus? Bis zu welcher Spannung funktioniert noch dein Mikrocontroller inklusive non-volatile Memory? Was ist der worst-case Stromverbrauch der Schaltung? Sprich wieviele Takte hast du wirklich bevor die relevanten Teile der Schaltung unzuverlässig werden. Leider hört ein Prozessor nicht einfach sauber auf, wenn die Spannung sinkt. Erst treten hässliche Fehler auf z.B. bedingte Sprünge werden unbedingt, bei Multiplikationen stimmen die oberen Bits nicht mehr, Schreibvorgänge im Flashspeicher kippen Bits nicht mehr zuverlässig. In diesem Randfall kannst du deinen Prozessor nicht mehr als getaktete digitale Schaltung betrachten. Er wird ein Haufen analoger Wahnsinn. Guck also mal nach ob deine Brown-out Logik auch nen sauberen Stopp bei noch sicherer Spannung bieten kann.

Viel Spaß bei der Suche nach den Heisenbugs.
 
Auf CPUs, die schnell genug sind um ein RTOS zu benutzen, sind einzelne Funktionsaufrufe kaum signifikant im Vergleich zu 10ms.
Es gibt auch RTOS für 8051, was die tun ist der totale Wahnsinn. Die darf man einfach nicht benutzen, aber es gibt sie. OK, 10ms sind mehr als genug Zeit um etwas in den Flash zu schreiben (ich brauche im worst case ca. 2ms), aber Sprünge sind hier ein durchaus signifikanter Faktor in den ISRs.

In Flash Speicher schreiben ist ziemlich fitzelig. Da hast Du für bestimmte Dinge enge Zeitfenster von ein paar µs. Da musst Du hier und da schon gründlich durchrechnen damit das klappt.
 
Ich bitte das als konstruktive Einlassung zu verstehen ...

Mir scheint, da ist einiges verquer. Zum Beispiel landen da zwei unterschiedliche, wenn auch zusammenhängende Probleme in einem Topf. Nämlich das Thema "Störfall Spannungsversorgung" und das Thema "(Zustands?) Daten (speichern).
Auch scheinst du immerhin Eingriffsmöglichkeiten in die Hardware zu haben, entscheidest dich aber für eine (Pardon, fragwürdige) "Minimal Überbrückungs Versorgung" statt für eine saubere Zustandssicherung (z.B. battery backed store). Dabei unterstellst du ein Stromaufnahme Verhalten beim Prozessor, da ziemlich wahrscheinlich nur eher zufällig ungefähr der Realität entspricht.
Dann verlässt du dich darauf, dass dein Compiler, bzw. der Präprozessor mit deinem Macro macht, was du meinst, was er machen sollte und dass der Compiler das auch genau so inline positioniert wie du meinst.

Du beschreibst, das Hauptprogramm bestünde im wesentlichen aus einer Schleife, in der die ISR irgendwo eine Semaphore setzt, die dann wiederum irgendwo bemerkt/ausgewertet wird. Und das alles in einer unterstellten Situation mit Ausfall oder signifikanter Störung der Stromversorgung!
Wie fett ist diese Schleife? Wie fett sind die Brocken, die darin laufen, durchschnittlich und worst-case? Wie sicher bist du, dass die Semaphore gesetzt wird? Und wo? on-die Speicher oder off-die Speicher (und wie verhält der sich in so einer Situation mit bestenfalls einem winzig Tantal davor?)?

Deine "Daten wieder verwenden" Logik ist entweder ein Pipifax-Gadget und dann keinen Aufwand wert oder aber wichtig und dann auch eine solide Lösung wert.

Was immer du tust, du solltest Notfall Handling aufs Allernötigste beschränkt halten, frühestmöglich (also in der ISR) und lokal (also in der ISR) erledigen. Das Speichern der Daten kann, wenn wichtig, in der Schleife liegen (bei jeder Veränderung oder 1 mal pro Sekunde oder ...).

Ehe man etwas zu deiner eigentlichen Frage sagen kann, müsste man erst mal einiges mehr über die Hardware und den Compiler wissen. Was sich aber vermutlich eh erledigt, wenn du dein Design anders strukturierst. Eins jedenfalls ist klar, da haben Crest und Kamikaze völlig Recht, Spielereien, die sich auf timing und deterministisches Funktionieren verlassen, sind in so einer Situation (Spannungsausfall) sowieso Lotto.
 
Zurück
Oben