Hi,
ich bin nun von der 8051- in die ARM-Welt eingestiegen und verwende NxP
Cortex-M0/M3 in Verbindung mit der LPCxpresso IDE von CodeRed und GCC.
IDE läuft, Debugging geht, soweit alles im Grünen.
Ich versuche nun im Selbststudium mit den Controllern fit zu werden, was
auch klappt. Der UART läuft schon, der ADC wird grad zum Leben erweckt
:)
Wo ich nun eher Anlaufschwierigkeiten habe ist der GCC, beispielsweise
wie die Variablen bzgl. Speichermanagement gehandhabt werden, wie
Parameterübergabe stattfindet, etc.
Ich komm leider momentan mit der in der LPCxpresso-IDE mitgelieferten
GCC-Hilfe (noch) nicht zurecht, die Suchwörter spucken alles mögliche
aus, nur aus meiner Sicht absolut nicht das was ich wissen will :(
Ein konkretes Beispiel: Ich hab die 8051er-Programme u.a. mit dem Keil
C51 Compiler erstellt.
- Das Alignment spielte da keine Rolle, weil es ein 8-Bit-Controller
ist. Wie ist das beim GCC & ARM? Wenn ich vier 8-Bit-Variablen (char)
anlege, packt der GCC das automatisch in ein 32-Bit-Wort, oder verwendet
er vier Mal ein 32-Bit-Wort? Wie kann ich das selber prüfen? Einfach mal
ein paar Variablen anlegen und nach dem Build bei der Speicherangabe
prüfen wieviel Bytes verbraten wurden? Ist das "aussagekräftig"?
Was ich bis jetzt in der Hilfe zum GCC rausgefunden habe, ist dass es
beispielsweise beim Anlegen von structs nicht gepackt wird, d.h. im
Speicher sind Lücken, wenn die struct nicht ein Vielfaches von 32-Bit
verwendet. Das Packen kann durch ein Attribut bei der struct-Definition
aktiviert werden.
- Der Keil C51-Compiler bzw. der Linker kann funktionsinterne Variablen
überlagern, sodass Speicherplatz gespart wird. Allerdings geht das nur
auf Sourcedatei-Ebene, d.h. die optimale Speicherauslastung hat man nur
erreicht, wenn jede Funktion in einer eigenen Sourcedatei steht. Wie
funktioniert dieser Mechanismus beim GCC?
- Beim Keil C51-Compiler wurden aufgrund der 8051-Architektur die
Parameter in den Registern übergeben, auch hier war die Reihenfolge der
Parameter bei der Übergabe wichtig, um optimalen Code zu erzeugen, da
ansonsten zusätzlicher RAM für die Parameterübergabe verwendet wurde.
Ich vermute beim GCC werden die Parameter über einen Stack übergeben,
richtig? Wie lässt sich hier der Code optimieren? Wenn ich
beispielsweise für eine Funktion vier Parameter habe, für die der Typ
char ausreichend ist, werden dann vier 32-Bit-Wörter auf den Stack
geschoben oder wird's gepackt? Bzw. wenn's nicht gepackt wird, wäre doch
die Verwendung eines structs als Parameter geschickter, oder?
Ich versuche eben, meine Erfahrung mit dem 8051/Keil auf den ARM/GCC zu
"portieren", um die Lernkurve bzgl. des Compilers zu optimieren :)
Wenn also jemand nützliche Links hat, bitte hier posten. Ich werde das
gleiche tun, vielleicht ergibt sich ja eine ordentliche Linksammlung.
Ralf
Ralf schrieb:> ist. Wie ist das beim GCC & ARM? Wenn ich vier 8-Bit-Variablen (char)> anlege, packt der GCC das automatisch in ein 32-Bit-Wort, oder verwendet> er vier Mal ein 32-Bit-Wort?
Register: jede einzeln in ein Register. Deklaration lokaler Variablen
mit weniger als 32 Bits ist ineffizient.
Speicher: in 8 Bits gespeichert.
> Wie kann ich das selber prüfen?
Code ansehen, Adressen von Variablen im Dump suchen oder ggf.ausgeben.
> Was ich bis jetzt in der Hilfe zum GCC rausgefunden habe, ist dass es> beispielsweise beim Anlegen von structs nicht gepackt wird, d.h. im> Speicher sind Lücken,
Es wird nicht gepackt, es sei denn das wird ausdrücklich gewünscht.
Gepackt ist recht ineffizient.
> - Der Keil C51-Compiler bzw. der Linker kann funktionsinterne Variablen> überlagern, sodass Speicherplatz gespart wird.
51er speichern lokale Daten statisch im RAM, da sie mit einem Stack für
Daten schlecht umgehen können. ARMs verwenden einen Stack und damit löst
sich das Problem in Luft auf.
> Ich vermute beim GCC werden die Parameter über einen Stack übergeben,
Nein, Register. Nur wenns zu viele sind, was seltten ist.
A. K. schrieb:>> ist. Wie ist das beim GCC & ARM? Wenn ich vier 8-Bit-Variablen (char)>> anlege, packt der GCC das automatisch in ein 32-Bit-Wort, oder verwendet>> er vier Mal ein 32-Bit-Wort?>> Register: jede einzeln in ein Register. Deklaration lokaler Variablen> mit weniger als 32 Bits ist ineffizient.
das is so nicht richtig. Die Register sind immer 32 bit, es macht aber
keinen unterschied, da man die Registeroperationen auch auf Bytes
anwenden kann
Die erste Variable wir immer mit dword aligment angelegt.
1
struct{
2
uint8_ta[3],
3
uint32_tb[1]
4
}c;
in diesem Beispiel hat c eine größe von 8, weil eine Lücke zwischen a
und b ist.
packed sollte man nur machen wenn man es wirklich braucht, z.B. bei
Frames die über einen Bus kamen. Lieber beim erstellen selbst darauf
auchten das keine Lücken entstehen.
Hi zusammen,
Zum Attribut "packed" hätte ich ne Frage
struct{
uint8_t a;
uint8_t b;
uint8_t c;
uint8_t d;
} foo;
ist diese Struct ohne packed auch nur 4 Byte lang oder 16?
Also packt der GCC Variablen die in ein Dword passen zusammen rein und
fängt erst beim Nächsten an, wenn er eine Var nicht mehr in das erste
Dword bekommt?
MfG
Tec
@Prx & nicht Gast:
Danke für die Erklärungen.
>> Es wird nicht gepackt, es sei denn das wird ausdrücklich gewünscht.>> Gepackt ist recht ineffizient.> in diesem Beispiel hat c eine größe von 8, weil eine Lücke zwischen a> und b ist.
Passt jetzt aber nicht zur der Antwort auf tecnologics Frage:
>> ist diese Struct ohne packed auch nur 4 Byte lang oder 16?> 4 byte. Hier muss die CPU sowieso einen byte weisen zugriff machen.
Oder ist im Beispiel von nicht Gast die Lücke in der struct weil der
uint32_t Member "geschickterweise" auf ein Word-Boundary gelegt wird?
Ralf
HI Ralf,
so verstehe ich das auch, der uin32_t passt nicht mehr in das Dword mit
den 3 uint8_t deshalb beginnt der Compiler ein neues Dword, so erreicht
er ja 32bit aligned Zugriff, und muss sich die Var nicht aus 2 Blöcken
zusammen suchen. Darauf wollte ich ja auch hinnaus, weil ich mir da nie
sicher war.
hab das dann immer in der Form gemacht.
struct{
uint32_t bFlag:1;
uint32_t reseve:31;
} foo __attribute_((_packed_));
so ist der Compiler gezwungen. ich werde das aber noch mal ohne Packed
probieren.
MfG
Tec
nicht Gast schrieb:> das is so nicht richtig. Die Register sind immer 32 bit, es macht aber> keinen unterschied, da man die Registeroperationen auch auf Bytes> anwenden kann
Das ist so nicht richtig. Erstens kann man nur Lade/Speicheoperationen
auf 8/16 Bits anwenden, nicht aber ALU Operationen. Zweitens stehen
(deshalb) bestimmte Compiler-Konventionen im Weg.
A. K. schrieb:> 51er speichern lokale Daten statisch im RAM, da sie mit einem Stack für> Daten schlecht umgehen können.
Nö, der 8051 kann auch mit dem Stack arbeiten.
Allerdings ist es erheblich effizienter, direkt auf den SRAM
zuzugreifen. Es muß kein Stackframe angelegt werden und viele Befehle
können im SRAM gemacht werden (MOV, INC, DEC, DJNZ, ANL, XRL, ..).
Außerdem wird selten ein RTOS verwendet, daher lassen sich lokale
Variablen überlagern.
A. K. schrieb:> ARMs verwenden einen Stack und damit löst> sich das Problem in Luft auf.
Sie können nichts direkt im SRAM ausführen (RISC), daher ist es fast
egal, ob aus dem SRAM laden oder PUSH/POP.
Peter
Peter Dannegger schrieb:> Nö, der 8051 kann auch mit dem Stack arbeiten.> Allerdings ist es erheblich effizienter, direkt auf den SRAM> zuzugreifen.
Ich habe nichts anderes behauptet, mit schlecht=ineffizient.
Peter Dannegger schrieb:>> ARMs verwenden einen Stack und damit löst>> sich das Problem in Luft auf.>> Sie können nichts direkt im SRAM ausführen (RISC), daher ist es fast> egal, ob aus dem SRAM laden oder PUSH/POP.
Es ging hier um die Platzfrage.
Wenn lokale Variablen statisch angelegt werden, dann ist es aus
Platzgründen sinnvoll, dass der Compiler etwas Aufwand treibt, um solche
Variablen von sich nicht gegenseitig aufrufenden Funktionen überlagern
zu können.
Wenn lokale Variablen in Registern bzw. auf einem Stack angelegt werden,
dann löst sich diese Platzfrage ohne jede Optimierung ganz von allein,
da diese Überlagerung eine implizite Eigenschaft des Stacks ist.
Hier eine App Note von TI zu den Cortex M3 von TI:
Optimizing Code Performance and Size for Stellaris® Microcontrollers
Das dürfte auch für deine NXP gelten.
Gruss