PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : C-Compiler mit negativer Optimierungsstufe ?



Siro
09.02.2010, 11:05
Hallo zusammen, ich bin noch recht neu auf der "ARM" Schiene mit dem IAR-C-Compiler.
Habe bisher nur PICs in Assembler programmiert.
Nun bin ich auf folgende Merkwürdigkeit gestoßen:

Dazu ersteinmal der Der C-Code:

#define IOSET (*((volatile unsigned long*) 0xE0000004))
IO0SET = (1 << 25);

Der erzeugte Assembler-Code sieht dann so aus:

MOV R0,#0xE0000004 ; lade eine Teil der Adresse ins R0 Register
ORR R0,R0,#0x28000 ; und nun den "vergessenen Rest" oben drauf ???
MOV R1,#0x2000000 ; das hat der Preprocessor richtig gemacht 1<<25
STR R1,[R0,#+0] ; und wieder Speichern

Wie kommt denn dieser Code zustande ???
oder warum wird nur ein Teil der Adresse geladen E0000004
und der fehlende Teil 0x28000 aufoderiert.
Das funktioniert natürlich und ist Programmtechnisch völlig korrekt,
aber das sieht aus, als würde hier bewust Speicher verschwendet werden.
Macht das der C-Compiler evtl. absichtlich, weil es keine Vollversion ist ??????
Oder gibt es doch einen Sinn, welchen ich bisher nicht verstanden habe.

Das find ich generell sehr aufwändig um ein einzelnes Bit zu setzen.
Bei den PIC's, welche ich bisher programmiert habe, ging sowas in einer einzelnen Zeile
mit einem Bit-Set-Befehl "BSF"

für jegliche Info bin ich dankbar.

mfg. Siro

Siro
09.02.2010, 14:19
Die Ursache muss wohl völlig woanders liegen. Das hat vermutlich nichts mit dem Compiler zu tun.
Ich habe mal folgendes versucht:

unsigned long temp;
temp = 0xE0000010;

auch hier wird ein (für mich) merkwürdiger Code erzeugt:

MOV R1,#0x10
ORR R1,R1,#0xE0000000;

hätte er die 0x10 nicht gleich mitladen können ?????


während dessen bei temp = 0xE1000000;
MOV R1,#0xE1000000


diese Zahl ist sogar noch größer und die läd er direkt ?????

mal wird der Wert in einem Befehl geladen, dann wieder in 2 Befehlen.
Der Zusammenhang ist mir bisher nicht klar geworden.

mfg. Siro

Siro
09.02.2010, 16:43
Oh Gott, ich habe aber eben diesen Artikel gefunden:

The op2 field in the ARM instructions allows only 8-bit constants that may be rotated by an even number (0, 2, 4, 6, .., 30). Valid constants are: #0x12, #0xFF, #0x2FC0, #0xC000003F. Invalid constants are: #0x1FF, #0x1FE, #0x1234. These numbers cannot be represented in an 8-bit value that is rotated as described above. Such constants must be loaded via a so-called memory pool and the LDR Rd,[PC, offset] instruction.
The ARM tools offer automated ways to create memory pool entries. The assembly instruction LDR Rd,=value generates the instruction MOV Rd,#value for constants that can be represented by 8-bit rotated values. For other values it is translated to LDR Rd,[PC, offset] with a corresponding memory pool entry.

Wenn mir das jetzt jemand erklären könnte, wär ich echt dankbar.
Ich hab ja schon alle möglichen Assembler durch, aber was ist das denn ????
Ich kann nur 8 Bit Konstanten angeben, welche um eine grade Anzahl geschoben werden können.
Wohin denn schieben nach links oder rechts ??? und wo steht denn um wieviel die geschoben werden sollen, können, müssen ???
Und was hat denn der Programmcounter damit zu tun ???

Uffffff........

Besserwessi
09.02.2010, 17:21
So wie es aussieht, kann man halt kein 32 Bit Konstanten Direkt angeben, sondern nur so eine "Fleißkomma" aus 8 Bit + einer Verschiebung.
Macht ja auch Sinn, denn so vermeidet man ASM Befehle mit mehr als 32 Bit Länge.

Wenn man dann eine Konstante braucht die nicht in das Muster paßt wird des etwas umständlich:
1. Alternative: Stückweise per OR zusammensetzen aus bis zu 4 Teilen

2. Alternative: Über den Umweg Speicherzugriff die 32 Bit aus dem COde Speicher laden. Damit die Adresse nicht wieder 32 Bit lang ist, wird hier wohl relativ zum PC geladen.

Welcher Weg schneller ist, hängt vom Wert ab. Das entscheidet dann der Compiler / (Macro-)"Assembler" was besser ist.

Der 2 te Weg ist für PIC gewohnte natürlich neu, denn da ist es ja sehr schwer (unmöglich ?) Konstanten aus dem Flash zu laden. Beim PIC hat man dsa Problem ja nicht, weil die OP-Codes alle 12...16 breit sind, also mehr als die Bitbreite der Register.

Siro
09.02.2010, 22:11
Hallo Besserwessi,
da hast Du ja anscheinend auch schon einige Erfahrungen gesammelt.
Stimmt mit dem PIC mal eben was aus dem Speicher laden ist echt katastrophal. Bankselect, Pageselect usw.

Ich hab mir das mit dem ARM nochmal genauer angeschaut.
2 Stunden lang, die S-Bahn in Berlin hatte mal wieder Probleme :-)
Ich hoffe, daß ich etwas mehr verstanden habe, hat schon einige Zeit gekostet, den Artikel auseinander zu pflücken.
Ich hab es aber nochmals völlig ohne Vorurteil analysiert.
Schon teilweise "krank" die ARM Architektur, aber den Perfekten
Prozessor gibt es nunmal nicht.
Man macht dann halt seine Vergleiche. mal eben ein Bit setzen kann doch nicht das Problem sein, sollte man denken.
Die Arm-Architektur mag sicher seine Vorteile haben, aber bisher muss ich feststellen, daß ist auch nicht die Eierlegende Wollmilchsau.
72 MHz wobei ich 4 Assemblerzeilen brauche um 1 Bit zu setzen, bei
einer Pipeline Struktur, die 3 Cyclen braucht und dann" 4 * 4 Bytes" Programmcode benötigt um ein einzelnes Bit zu setzen. Also 16 Bytes
KK, Kein Kommentar.
Ich danke Dir aber sehr, für Deine Rückmeldung.
Ich werd mich da weiter reinarbeiten müssen. Man lernt ja nicht aus.
Was waren das für schöne Zeiten mit dem 6502,6805,68000,8085,8086,
Da kannte ich jedes Bit mit Vornamen...
Siro

PICture
10.02.2010, 09:43
Hallo!

@ Siro

Ich habe schon vor langer Zeit festgestellt, dass Analisieren von Hochsprachen mit ASM keinen Sinn macht und deswegen bleibe ich unabhängig vom Prozessor (6502, 80286, 80386, 80486, Z80, 8051, PIC) beim reinem ASM oder Hochsprache und beschränke mich nur auf Analise richtiger Bearbeitung der Daten. Mann muss nur die Programmiersprache verstehen und nicht den Compiler, der nur ein Werkzeug ist... :)

MfG

Besserwessi
10.02.2010, 10:51
Die ARM sind nunmal ersteinmal als CPU für normale Computer entwickelt worden, und nicht als µC. Entsprechend ist der Zugriff auf einzelne Bits nicht so komfortabel. Dafür hat man es leichter mit 32 BIt Additionen, Multiplikationen usw. .

Wenn ich das richtig verstanden habe war beim PIC12xx eine 16 Bit Addition schon nicht ganz einfach - mich hat das unter anderem davon abgehalten mit näher mit den PICs zu beschäftigen. Es halt fast jede CPU so ihre Schwächen und Speziallitäten.

Das mit den Konstanten ist etwas verwirrend, aber die meisten Konstanten die man im Code braucht, sind nunmal eher kleine Zahlen, vor allem 0, 1 und 2. Da macht die Begrenzung schon Sinn. Wenn man dafür sorgt, dass alle OP-Codes gleich lang sind, vereinfacht das die Sache vor allem mit der Pipeline sehr. Als klassische Risk-CPU ist beim ARM halt vorgesehen das sich ein Compiler oder intelligente Assembler um solche Kleinigkeiten zu kümmern.

Siro
10.02.2010, 12:03
Ich möchte meinen Beitrag nochmal erweitern, da ich es jetzt verstanden habe. Hoffe ich zumindest.

Der ARM Kern arbeitet mit 32 Bit. Jeder Befehl belegt damit 4 Bytes.
Um eine 32 Bit Konstante in dem Befehl unterzubringen gibt es nicht genug Platz.
Der eigentliche Befehl (Mnemonic) benötigt ja schon einige Bits. Dann kommen noch Bits
für die Auswahl des Registers dazu usw. Also hat man hier einen besonderen Weg beschritten.
Die 32 Bit Konstante wird irgendwo im Speicher abgelegt und über einen Zeiger wird
darauf zugegriffen. Sie wird aber nicht direkt hinter dem momentanen Befehl abgelegt, daß
würde den Programmablauf stören, denn dann müste man über diesen Wert ja rüberspringen.
Also irgendwo dort, wo sie nicht stört. Zum Beispiel am Ende einer Unterfunktion.
Allzuweit jedoch darf der Wert auch wieder nicht stehen, da auf ihn mit einem Offset zum
momentanen Programmcounter zugegriffen wird.
Dieser Offset hat nur 12 Bit. Damit muss die Konstante innerhalb des momentanen 4-KByte Blockes stehen.
Also Plus Minus 2KByte vom aktuellen Programmcounter. Wo genau, legt der C-Compiler fest, da braucht
man sich nicht drum zu kümmern. Das sieht dann nur etwas verwirrend aus, wenn man sich das erste mal
den Assemblercode ansieht.

Beispiel:

unsigned long temp;
temp = 0x1FF;

Der resultierende Assembler-Code:

LDR R0,[PC,#+1632]
MOV R1,#0xFF
ORR R1,R1,0x0100
STR R1,[R0,#+0]

Erklärung:
Lade R0 Register mit der Adresse von Temp. Diese findest du an der momentanen Programmcounter Adresse + einem positiven Offset von 1632. Dort

wurde nämlich der 32 Bit Wert der RAM Adresse von Temp eingetragen. Dafür hat der C-Compiler gesorgt. Zum Beispiel 0x400005C4
Nun lade R1 Register mit dem Wert 0xFF. Es kann nicht direkt 0x1FF geladen werden. Erklärung folgt.
in R1 haben wir also vorerst den Wert 0xFF. Nun wird mit einem logischem OR der Rest von 0x1FF (also der vordere Teil 0x100) aufaddiert

(dazugepackt, oderiert). Wir haben nun also den Wer 0x1FF im R1 Register.
Jetzt wird der Wert welcher sich im R1 Register befindet an die Speicherstelle geschrieben, worauf
das Register R0 zeigt. Als Offset wird hier 0 angegeben, da R0 schon direkt auf die Speicheradresse von temp zeigt.

Soweit so gut, doch warum kann der Prozessor nicht gleich den Wert 0x1FF in sein Register R1 laden ?
Dazu muss man sich die Architectur bzw. den Adressmodus für den Befehl genauer ansehen.
Im Bit 0..7 des Befehls "MOV" ist Platz für eine 8 Bit Konstante. Also Werte von 0x00 bis 0xFF.
In Bit 8 bis 11 kann ein Wert zum Rotieren angegeben werden. Also 4 Bits für die Rotation.
Wobei hier beachtet werden muss. Man kann nur gradzahlig rotieren. Das bedeutet, die Rotation erfolgt um das doppelte,
als dieser 4 Bit Wert wiederspiegelt. Der höchste Wert, der in diese 4 Bits passt ist 15 also 0x0F. Das bedeutet
die 8 Bit Konstante in Bit 0..7 kann maximal um 30 Bits rotiert werden. Rotiert wird generell nach rechts.
Wichtig hierbei ist, daß "rotiert" wird, daß bedeutet die rechts "rausfallenden Bits" kommen links am oberen Ende
wieder an. Wenn also Bit 0 nach rechts rausgeschoben wird, kommt es an der Bitposition 31 wieder an.
Also kein Shift sondern Rotate. Bedingt durch diese Vorgegehensweise kann natürlich nicht jeder beliebige Wert als Konstante
dargestellt werden.

Genauere Information findet man im
ARM Achitecture Reference Manual , Juli 2005
ARM Adressing Modes
A5.1.3 Data-processing operands - Immediate

das war ja eine schwierige Geburt, aber man lernt nie aus.
Zumindest macht der Compiler keinen zusätzlichen Code, was meine erste Vermutung war.
Er nimmt einem die Prozessorbedingten Probleme (Eigenheiten) ab.
Damit kann ich jetzt beruhigt diesen Thread schließen.

mfg. Siro