Wo werden konstante Variablen im Mikrocontroller gespeichert?

Gemäß dem Speicherlayout des C-Programms werden konstante Variablen im initialisierten Datensegment des RAM gespeichert. Aber wie bei einigen Mikrocontroller-Speicherlayouts werden konstante Variablen im FLASH-Speicher gespeichert. Ich habe auch gesehen, dass die Größe der BIN-Datei meines Codes zunimmt, wenn ich konstante Variablen in Makrodefinitionen ändere? Helfen Sie mir, diese Zweideutigkeit zu beseitigen. Ich habe Nuc2240 Cortex M0 verwendet .

Der RAM in den meisten Mikros ist flüchtig, daher werden Konstanten im Flash gespeichert und können während der Initialisierung in den RAM kopiert werden (oder auch nicht) (es hängt vom Mikro ab). Das "Speicherlayout des C-Programms", auf das Sie verweisen, ist kein Standard als solches, viele dieser Details sind implementierungsabhängig. C selbst ist nur die Hochsprache, die Speichernutzung wird vom Compiler und Linker bestimmt, mit einigen Kenntnissen der Zielplattform. Ich sehe darin keine "Mehrdeutigkeit", es sei denn, ich habe Ihre Bedenken falsch verstanden?
In den Antworten hier werden verschiedene Schemata besprochen (Laden Sie die Konstante aus dem Flash oder RAM oder codieren Sie sie in der Anweisung). Sie können sehen, was Ihre Toolchain für verschiedene Fälle tut, indem Sie eine Disassemblierungsliste des Programms anzeigen und den Assembler-Anweisungen folgen. Viele IDEs ermöglichen das Generieren von Disassemblierungslisten, oder Sie können ein eigenständiges Programm verwenden.

Antworten (5)

Wenn Sie Assembler schreiben würden, würden Sie durch org oder ähnliche Direktiven entscheiden, welche Speicherorte verwendet werden, um welche Konstanten oder Variablen zu speichern.

Wenn Sie C schreiben, speichert die von Ihnen verwendete C-Toolkette Dinge dort, wo sie programmiert wurden. Variablen neigen offensichtlich dazu, in RAM zu gehen, Konstanten können entweder in RAM oder Flash gehen, Programme neigen dazu, in Flash zu gehen.

Es hängt alles davon ab, wie das Tool für diesen bestimmten Zielprozessor entworfen wurde, woher es seine Anweisungen erhält, wo es Dinge ablegen soll. Es könnte alles in den Code eingebrannt sein, in diesem Fall haben Sie kein Mitspracherecht, es geht dorthin, wo die Ersteller der Toolkette es ausgewählt haben. Es kann Anweisungen von irgendwo hernehmen (Quellcode, Befehlszeile, Make-Datei, Umgebungsvariable), damit Sie Dinge rebasen können.

Lesen Sie Ihre spezifische Dokumentation. Es ist keine Physik, es hängt alles von der Laune des Menschen ab.

Es ist nicht die Aufgabe des Compilers, sondern des Codegenerators und des Linkers, Segmente mit Konstanten an einer Stelle innerhalb von EPROM, FLASH oder batteriegepuffertem RAM zu platzieren. Konstanten können aber auch in den flüchtigen RAM gestellt werden, wenn sie beim Programmstart durch einen vom Compiler generierten speziellen Code initialisiert werden.
ja, Compiler bin ich faul, die Werkzeugkette.

Die Antwort auf Ihre Frage ist spezifisch für die von Ihnen verwendete Architektur. Da Sie ARM erwähnt haben, werde ich Ihnen darauf basierend eine Antwort geben.

Der ARM Cortex ist ein Register-basiertes Design, daher müssen grundsätzlich alle Operanden (konstant oder nicht) irgendwie in eines der internen Register gelangen, bevor sie von Programmanweisungen verwendet werden können. Dies ist für die Beantwortung Ihrer Frage von zentraler Bedeutung, da die Architektur dies auf verschiedene Arten zulässt.

Bei kleinen Konstanten (normalerweise weniger als 8 oder 10 Bit) kann der Wert direkt in bestimmte Anweisungen codiert werden. Beispielsweise gibt es einen ADD #imm-Befehl, der einen 12-Bit-Wert, der in das Befehlswort codiert ist, zu einem Register hinzufügt. Dies kann verwendet werden, um direkt eine Konstante in Ihrem Programm anzugeben. Wenn Sie dieselbe Konstante an verschiedenen Stellen in Ihrem Code hinzufügen und der Wert 12 Bit oder weniger beträgt, verwendet der Compiler einfach eine ADD #imm-Anweisung überall dort, wo Ihre Konstante erforderlich ist. Mit anderen Worten, die tatsächliche Konstante wird an mehreren Stellen im gesamten Code, der in FLASH gespeichert ist, repliziert.

Wenn die Konstante groß ist, funktioniert dies nicht, sodass der Wert an einem eigenen dedizierten FLASH-Speicherort gespeichert werden muss. Der Chip hat eine andere Anweisung namens LDR, die er verwendet, um einen Wert von einer Speicherstelle in die internen Register zu verschieben, wo er verwendet werden kann. Dieser Befehl kümmert sich nicht darum, ob der Wert physisch im FLASH oder RAM gespeichert ist, er zieht den Wert einfach von einer bestimmten Adresse im Speicher und legt ihn in ein Register. Beachten Sie, dass dies im Vergleich zu kleinen Konstanten einen zusätzlichen Befehlszyklus erfordert, aber wenn die Konstante wiederholt in einem Codeblock verwendet wird, speichert der Compiler sie zwischen, indem er sie in ein Ersatzregister einfügt, falls eines verfügbar ist.

Jetzt stoßen wir jedoch auf ein potenzielles Problem. Auf den meisten Cortex-Chips läuft der FLASH-Speicher mit der gleichen Geschwindigkeit oder langsamer als der CPU-Kern. Wenn wir versuchen, einen LDR-Befehl von einer FLASH-Stelle auszuführen, muss diese Leseoperation des physikalischen Speichers mit dem Lesen des nächsten Befehls konkurrieren. Dadurch entsteht ein Flaschenhals, der dazu führt, dass die CPU anhalten muss, bis die Daten gelesen wurden. Noch schlimmer ist, dass FLASH bei vielen Implementierungen sehr langsam läuft und ein Pipeline-Cache verwendet wird, um Anweisungen schneller an die CPU zu liefern. In diesen Geräten kann beispielsweise das Lesen einer Tabelle mit konstanten Werten den grundlegenden FLASH-Zugriffsengpass aufdecken, der zu schwerwiegenden Leistungsverlusten führt.

Aus diesem Grund werden viele Compiler versuchen, alle konstanten Daten nach Möglichkeit in freie RAM-Speicherplätze zu kopieren. Dies verhindert solche Probleme, und da auf RAM normalerweise schneller zugegriffen werden kann als auf FLASH, können viele Leistungsengpässe verhindert werden. Wenn in Ihrem Programm ohnehin ungenutztes RAM vorhanden ist, dann ist es praktisch kein Overhead für den Compiler, dies zu tun.

Es gibt also definitiv eine Logik dafür, wie der Compiler konstante Daten dem physischen Speicher zuweist, aber zum Glück haben die Chip- und Compiler-Designer in den meisten Situationen all diese harte Arbeit für Sie erledigt, und Sie können einfach darauf vertrauen, dass er das tut, was am besten ist Ihren speziellen Code und Ihre Optimierungsstufe.

Der von Ihnen bereitgestellte Link erklärt, was auf normalen Computern passiert, die sowieso alles (einschließlich Code) in den Arbeitsspeicher laden (weil dies das einzige verfügbare ist, aber es gibt viel davon). Es gilt nicht für die eingebettete Programmierung, bei der Sie auch Flash zur Verfügung haben. Wenn Sie also ein Linker-Skript verwenden, das für die eingebettete Programmierung geeignet ist (was die Standardeinstellung für eingebettete IDEs sein sollte), landen konstante Daten tatsächlich im Flash, wie es sein sollte.

Wenn Sie nun Makrodefinitionen verwenden, ist es völlig anders. Makros werden innerhalb des Quellcodes durch den Präprozessor ersetzt , was bedeutet, dass die Konstante jedes Mal, wenn Sie sie in Ihrem Code verwenden, unabhängig neu kompiliert wird. Und wenn Sie es mehrmals verwenden, wird der Code tatsächlich größer. Dies sollte natürlich bei einfachen ganzzahligen Konstanten nicht der Fall sein. Bei Zeichenfolgenkonstanten versuchen gute Compiler, mehrere identische Werte zusammenzuführen, um diesen Effekt zu reduzieren und die Größe klein zu halten, aber dies ist nicht garantiert und kann davon abhängen, ob die Zeichenfolgen nur innerhalb derselben Kompilierungseinheit (.c-Quelldatei) oder zwischen ihnen verwendet werden mehrere Kompilierungseinheiten. Bei konstanten Strukturen/Objekten führen die Compiler normalerweise keine große Optimierung durch.

Die Antworten wären für Ihren speziellen Fall relevanter, wenn Sie Auszüge Ihres Quellcodes bereitstellen und angeben, welchen Compiler/IDE Sie verwenden.

Die Antwort hängt von Ihrer Definition von "Konstante Variable" und dem verwendeten Compiler ab.

Wenn es sich tatsächlich um eine "konstante Variable" handelt, speichern die meisten Compiler sie wie in Flash.

Wenn es über ein Makro deklariert wird, kann es unmöglich eine konstante Variable sein - es ist eine Konstante. Es wird im Allgemeinen im Flash gespeichert und in den RAM kopiert.

Sieht so aus, als ob Sie Konstanten mit Variablen verwechseln . Dies ist besonders wichtig, wenn Sie C und nicht C++ verwenden.

In C wird das, was Sie eine konstante Variable nennen (wie a const int), tatsächlich in den RAM geworfen. So geht der Compiler mit der Notwendigkeit um, manchmal auf die Adresse der Variablen (Zeiger) zu verweisen.

Wenn es sich um eine globale Variable handelt, fügt der Compiler diese in den initialisierten Datenabschnitt ein, was bedeutet, dass die Werte im Abschnitt blinken und vom Startskript in Abschnitt (Teil von ) .etextkopiert werden . Wenn es sich um eine lokale Variable handelt, wird der Startcode der Funktion dies wie jede andere Variable tun..sdata.data

Wenn Sie jedoch ein Makro verwenden, erhält die "Variable" keinen Adressraum. Das bedeutet, dass es als unmittelbar geladen wird, entweder von genau der Anweisung, die Sie verwenden (wenn der Wert klein genug ist und die Anweisung unmittelbar unterstützt) oder direkt vor der Anweisung, die den Wert verwendet, in ein Register geladen wird. Dies wird nur Flash-Speicherplatz verwenden, vorausgesetzt, Sie führen Code aus dem Flash aus.

Wenn Sie dagegen C++ verwenden, verwendet der Compiler bei Optimierungen den Wert direkt aus dem Flash. In C ist dies nicht vorgesehen. Weitere Informationen finden Sie in dieser Antwort .

Übrigens macht der Cortex-M3, den ich verwende, die Dinge in GCC tatsächlich so.