Irgendwelche Tipps zum Umgang mit einem sehr kleinen Stack?

8

Ich habe mich gefragt, ob Entwickler im Embedded-Bereich von irgendwelchen interessanten Tricks wissen, um den Aufwand für die Entwicklung von Mikrocontrollern mit sehr begrenztem Stack-Platz zu verringern. Ich habe vor kurzem einige Firmware für 8-Bit-uCs (Microchip PIC18F-Familie, 31-Byte-Stack) geschrieben und als Konsequenz musste ich meine Programme reduzieren und die Anzahl der an Funktionen übergebenen Parameter reduzieren. Ich habe auch versucht, meine Abhängigkeit von größeren lokalen Variablen zu minimieren. Die Abflachung wurde entwickelt, um weniger Dinge auf den Stapel zu legen, und durch das Reduzieren von lokalen Variablen wird Speicherplatz im Programmabschnitt "automatische Variable" (psect) im RAM gespart. Harvard-Architektur macht keinen Spaß, ich weiß, aber es ist, womit ich es zu tun habe. Ich habe Probleme mit dem Aufruf von mehr als ein paar Funktionen aus einem ISR bemerkt, was wahrscheinlich darauf zurückzuführen ist, dass mein Stack-Fenster vom IRQ-Kontext-Speichern betroffen ist. Ich weiß, dass ich mit einer begrenzenden Architektur arbeite, aber ich frage mich, ob jemand irgendwelche Tipps hat, um Kopfschmerzen zu reduzieren. Ich benutze Zeiger und Grenzen, wann immer es möglich ist, aber ich bin sicher, dass es Nuggets der Weisheit gibt, die ich selbst nicht entdeckt habe. Als Haftungsausschluss verwende ich derzeit Funktionszeiger, um eine Zustandsmaschine zu erleichtern. Ich fühle mich wie ein Drahtseilakt zwischen 90 Linienhohlraumfunktionen und Code, der tatsächlich Funktionen verwendet, wie sie beabsichtigt sind.

    
Nate 02.09.2010, 03:07
quelle

7 Antworten

7

Verwenden Sie register Variablen für Parameter und Locals. Abhängig von der Anzahl der im Prozessor verfügbaren Register und der Qualität des Codes, den der Compiler erzeugt, ist dies natürlich kein Vorteil. Deklarieren Sie Locals als static , wenn möglich. Dies verhindert, dass sie auf dem Stapel zugewiesen werden.

    
BillP3rd 02.09.2010, 03:47
quelle
2

Für nicht-rekursive Funktionen können Sie Ihre lokalen Variablen statisch machen.

Übrigens bezieht sich der Begriff "Harvard-Architektur" einfach auf die Tatsache, dass es separate Adressräume für Anweisungen und Daten gibt, im Gegensatz zu einer "Von-Neumann-Architektur", die Befehle aus dem gleichen Adressraum lädt, aus dem die Daten stammen. Dies ist für Ihr Problem irrelevant, da es nur bedeutet, dass Sie selbst modifizierenden Code nicht schreiben können.

    
Gabe 02.09.2010 04:18
quelle
1
  1. Verwenden Sie einen Compiler, der ganze Programme optimiert, der den "Stack" effektiv statisch zuordnen kann - ich glaube, die Hi-Tech PICC-Compiler können dies tun. Siehe Abschnitt 5.9 im PICC18-Handbuch

  2. Verwenden Sie Protothreads

Doug Currie 02.09.2010 03:49
quelle
1

Verwenden Sie nicht-lokales Goto ( setjmp() und longjmp() , wobei jmp_buf nicht im Stack gespeichert ist), um Funktionsaufrufe zu vermeiden.

    
Matt Curtis 02.09.2010 04:04
quelle
1

BEARBEITEN

Die PIC-Familie ist keine kompilerfreundliche Befehlssatzarchitektur. Der erste Trick für den Umgang mit dem kleinen Stack und allgemein begrenzten Ressourcen auf einem PIC besteht darin, in Assembler zu programmieren. Sie können mehr Aufgaben im selben Programmbereich und mit derselben Ausführungszeit oder mit derselben Aufgabe in kürzerer Zeit ausführen als bei der Programmierung in C.

Die zweite ist nur nicht den Stapel verwenden. Machen Sie Ihre Variablen globale oder lokale globale Variablen (lokale Variablen mit dem Wort static vor), wie auch immer Sie es nennen, oder wie auch immer Sie die Compiler-Ausgabe deklarieren, die Variable hat einen statischen Speicherort und verwendet den Stack nicht.

Für andere Prozessoren als die PIC-Familie können Sie einen Optimierer verwenden oder eine Deklaration von Variablen registrieren, um die Verwendung des Stapels zu verhindern. Der PIC hat nur zwei Register und um irgendetwas Nützliches zu tun, müssen Sie den Inhalt ständig rammen, so dass dies eine minimale Auswirkung auf den PIC hat. Bei Nicht-PIC-Prozessoren können Sie zusätzlich dazu, den Compiler zu ermutigen, die Daten in Registern so gut wie möglich zu speichern, dazu beitragen, indem Sie die Anzahl der an die Funktion übergebenen Variablen sowie die Anzahl der zusätzlichen lokalen Variablen begrenzen Funktion. In Fällen, in denen es nicht offensichtlich ist, wie viele Register verwendet werden, disassemblieren und untersuchen Sie die Compilerausgabe. Wenn Sie Ihre Codeausführung neu anordnen, können Sie die Registerzuweisung und -vertreibung ändern. Wenn das nicht funktioniert, erwägen Sie, die Funktion in zwei Teile aufzuteilen und dann nacheinander die andere Funktion aufzurufen, so dass jede Funktion in der Lage ist, innerhalb der verfügbaren Register ohne Verwendung des Stacks zu arbeiten.

Wenn dieses Projekt für eine lehrreiche Erfahrung ist, dann ist die Programmierung für den PIC an und für sich lehrreich, und die Programmierung des PIC in C ist auch eine lehrreiche Erfahrung. Es lohnt sich, es zu lernen, es lohnt sich, in Fallen zu verfallen und sich zurückzukämpfen.

Wenn Sie dies für die Arbeit tun (Karriere und / oder Lebensunterhalt hängen in der Balance) und nicht in Assembler programmieren wollen, empfehle ich, das Projekt auf eine andere Plattform zu stellen. msp430, avr oder einer der ARM-basierten (stellaris vielleicht). Das Wissen um ARM ist sehr hilfreich für Ihre Karriere in Embedded, aber die ARM-Teile werden im Vergleich zu einem PIC aus Power- und Kostengründen etwas sperriger. Die avr und msp430 Teile sind mehr auf Augenhöhe. Alle drei dieser alternativen Architekturen sind viel besser für die C-Programmierung geeignet und das gleiche Programm wird weniger Speicher / Stapel verbrauchen als das Äquivalent auf dem PIC. Das heißt, Sie können vielleicht ein 8K PIC durch ein 8K etwas anderes ersetzen, Sie brauchen nicht notwendigerweise mehr Speicher auf der alternativen Architektur, um mehr zu tun. Sie müssen sich immer noch Sorgen um Ihren Stack machen und sollten (nicht globale) lokale Variablen vermeiden, um Stack-Wachstum zu verhindern. Diese Architekturen variieren auch von dem PIC, indem sie keinen separaten Stapel- und Allzweck-RAM aufweisen. Sie können global zugewiesene Variablen und stack-basierte Variablen ausbalancieren und nicht von der Architektur erzwungen werden. Wenn stack-basierte Variablen verwendet werden, überprüfen Sie in einer Arbeitsumgebung den Code, um den Worst-Case-Pfad verschachtelter Funktionen zu ermitteln und zu zählen, wie viele nicht-globale Variablen sich in diesem Pfad befinden und der Stack mit den global zugewiesenen Variablen kollidiert.

Disassembly ist dein bester Freund in embedded, du musst nicht unbedingt in Assembler programmieren, solltest es aber trotzdem lesen können. Die Überprüfung der Assembly ist der beste Trick, um alle Formen von Embedded Pain zu reduzieren: Stack-Probleme, Speicherauslastung, Performance, Boot-Probleme, etc.

    
old_timer 02.09.2010 05:38
quelle
0

Ich habe einmal für 8051 programmiert und dafür den Keil C Compiler benutzt. Dieser Compiler hat Funktionen nicht aufgerufen, indem er den Stack verwendet hat, sondern Parameter über globale Funktionen übertragen hat (Sie könnten dieses Verhalten immer noch erhalten, wenn Sie die Funktion "reentrant" markieren). Ich denke, es hat auch die Stack-Nutzung reduziert, indem ich noch mehr Sachen gemacht habe.

Was ich sagen möchte, ist, dass es Compiler gibt, die weniger Stapelspeicher als andere verwenden. Wenn solche Compiler für PIC verfügbar sind, kann ich es nicht sagen.

    
ziggystar 03.09.2010 07:57
quelle
0

Einige allgemeine plattformübergreifende Tipps (wie bereits erwähnt):

  • Verwenden Sie statische lokale Variablen. Dies ist überall anwendbar, wenn die Funktion nicht reentrant ist (entweder für die Rekursion oder für den Aufruf von mehreren asynchronen Threads / Interrupts). Sie müssen nur daran denken, keine Initialisierer für sie zu verwenden (da sie ihren Wert nur beim Start und nicht bei jedem Eintrag in der Funktion festlegen). Um den Unterschied zwischen den beabsichtigten statischen Variablen zu verdeutlichen, können Sie für Ihre Locals etwas wie "#define fn_local static" verwenden.

  • Reduzieren Sie die Parameterübergabe an Funktionen. In 8-Bit-Mikros, die ein char-Derivat anstelle eines int-Derivats übergeben, sparen Sie in der Regel ein Byte auf dem Stack (wie es normalerweise 16 Bits sind). Wenn Sie viele Parameter haben, sollten Sie sie in einer Struktur gruppieren und die Funktion einen Zeiger auf diese Struktur akzeptieren lassen.

  • Seien Sie sehr vorsichtig mit Interrupts. Vermeiden Sie Funktionsaufrufe innerhalb. Versuchen Sie, Ihre Softwarearchitektur so zu entwerfen, dass Sie keine verschachtelten Interrupts benötigen. Abhängig vom Compiler und davon, wie / was es beim Interrupt-Eintrag speichert, kann dies eine erhebliche Menge an Stack-Nutzung sein oder auch nicht.

MPLAB / PIC18 spezifische Tipps:

  • In MPLAB-Funktion können Parameter auch statisch sein. Der Effekt ist derselbe wie bei statischen Locals: Sie benötigen keinen Stack, und ihre Verwendung macht die Funktion nicht reentrant. Beachten Sie, dass die Verwendung von statisch auf PIC18 auch ein großer Leistungsvorteil sein kann, wenn es vorsichtig ausgeführt wird, da die relative Adressierung des Stackpointers ziemlich komplex ist.

  • Der PIC18 hat einen Hardwarestapel für Funktionsaufrufe. Die 31 Ebenen, die es unterstützt, sind typischerweise mehr als genug für die komplexeste Software. Der Effekt davon ist, dass Aufrufe selbst nicht zu dem Software-Stack beitragen, der Platz von Ihrem wertvollen RAM nimmt: Sie können wirklich PIC-Software schreiben, die unter Verwendung der oben präsentierten Methoden möglicherweise keinen Software-Stack benötigt.

  • Was der Compiler beim Interrupt-Eintrag speichert, kann gesteuert werden, was nicht nur die Stack-Nutzung, sondern auch die Interrupt-Latenz beeinflusst. Siehe dieses anständige Papier zu diesem Thema. Vor allem externe Funktionsaufrufe von Interrupt-Code machen den Compiler nicht in der Lage zu bestimmen, welche Ressourcen benötigt werden, so dass es langwierige Interrupt-Eingabe / Exit-Code generieren wird viele Dinge auf Stapel speichern.

Jubatian 07.07.2013 20:09
quelle