Hallo!
Ich programmiere einen LEON 3 SPARC 32bit-Microproc obwohl ich keine
Ahnung davon habe. Ich will eine Adresse bitweise ansprechen koennen und
habe mir diesen Source Code einfallen lassen:
typedef struct {
unsigned int divider : 12; clk_divider
unsigned int test : 4;
unsigned int rest : 16;
} config ;
volatile config * ConfigReg = (config *) 0x80000b00; // 80000b00 ist
//die Adresse in die ich schreiben moechte
main()
{
ConfigReg->divider = 0xeae;
ConfigReg->test = 0xF;
ConfigReg->rest = 0x1234;
}
Wenn ich das laufen lasse steht nachher das an der Adresse:
0x80000b00 I/O data register 0x12341234
Rest wird zweimal reingeschrieben, das andere is weg.
Wenn ich den Struct aber so deklariere:
typedef struct {
unsigned int divider : 12;
unsigned int test : 20;
} config ;
volatile config * ConfigReg = (config *) 0x80000b00;
main()
{
ConfigReg->divider = 0xeae;
ConfigReg->test = 0xF1234;
}
kommt korrekterweise dies raus:
0x80000b00 I/O data register 0xeaef1234
Kann mir jemand vielleicht den Unterschied erklaeren? Muss/ kann ich
mein struct so deklarieren, dass der Compiler 32-bit dafuer reserviert?
Was mache ich falsch? Compiler is sparc-elf-gcc.
Fuer Hinweise bin ich sehr dankbar!
Obwohl mir beide Beispiele eigenlich richtig erscheinen, und das selbe
Ergebnis liefern sollten, würde ich generell davon abraten ein
Cofigurationsregister so anzusprechen, wie Du es gemacht hast.
Das Problem ist, daß Du keine richtige Kontrolle darüber hast, welchen
Kode der Compiler generiert.
Im Prinzip könnte der Compiler die 3 Anweisungen
ConfigReg->divider = 0xeae;
ConfigReg->test = 0xF;
ConfigReg->rest = 0x1234;
zu einer zusammenfassen und mit einer Schreiboperation 0xeaef1234
schreiben.
Der Compiler kann aber nicht wissen, ob das gewünscht ist, schließlich
hast Du das Register sogar als volatile deklariert.
Der Compiler wird also möglicherweise zuerst immer das Register lesen,
den Wert hineinmaskieren und das Register schreiben.
Es ergeben sich somit wahrscheinlich 3 Leseoperationen und 3
Schreiboperation, wo eine Schreiboperation wahrscheinlich genügt hätte.
Wenn die Leseoperationen des Config-Registers dann in Hardware sogar
irgendwelche Aktionen auslösen, um so schlimmer.
Du sollest deshalb das Config-Register als unsigned int deklarieren und
deine Bits selbst zusammenstellen.
Grüße
Klaus
Klaus Falser wrote:
> Du sollest deshalb das Config-Register als unsigned int deklarieren und> deine Bits selbst zusammenstellen.
Es genügt vollkommen, die einzelnen Bitfelder selbst als volatile
zu deklarieren.
Was allerdings in der Tat compilerabhängig ist, ist die Zählrichtung
der Bits innerhalb des Bitfeldes.
Man sollte Bitfelder nicht verwenden, das ist meine Meinung.
Bei der systemnahen Programmierung braucht es die Kontrolle über jeden
Registerzugriff, bei der Verwendung von hat man diese Kontrolle nicht
mehr.
Klaus Falser wrote:
> Man sollte Bitfelder nicht verwenden, das ist meine Meinung.
Die Meinung sei dir unbenommen, aber...
> Bei der systemnahen Programmierung braucht es die Kontrolle über jeden> Registerzugriff, bei der Verwendung von hat man diese Kontrolle nicht> mehr.
...das ist einfach Quatsch bzw. ein Vorurteil. Die Kontrolle hast du
dort ganz genauso. Die Wahlfreiheit des Compilers beschränkt sich
auf die Sortierrichtung der Bits, sonst nichts.
> Die Kontrolle hast du dort ganz genauso.
Das dürfte nicht stimmen.
Nimm das Beispiel aus dem Quellcode oben:
ConfigReg->divider = 0xeae;
ConfigReg->test = 0xF;
ConfigReg->rest = 0x1234;
Diese ändern alle das selbe Register. Wie kann der Compiler wissen, ob 3
Schreibzugriffe gewünscht und notwendig sind, oder ob ein einziger
Zugriff genügt, der alle Bits gleichzeitig ändern?
Wenn hinter dem ConfigReg Register Hardware steckt (was anzunehmen ist),
dann kann dies einen großen Unterschied machen.
Oder du möchtest nur die Bits von test ändern :
ConfigReg->test = 0xA;
Wie soll wiederum der Compiler wissen, welche Bits ins restliche Word
geschrieben werden sollen? Nachdem er wahrscheinlich anmimmt, daß diese
gleich bleiben sollen, wird er das Register vorher lesen, die Bits von
"test" ändern und das Word wieder zurückschreiben.
Diese Lesezugriff muß nicht notwendig sein, weil der Programmierer
meistens weiss, wie die restlichen Bits gesetzt werden sollen.
Im schlimmsten Fall kann der Lesezugriff irgendeine Hardware-Aktion
auslösen, die gar nicht gewüscht ist.
Die Verwendung von volatile verschlimmert die Situation meiner Meinung
nach sogar, weil sie dem Compiler mitteilt, daß er keine Annahme über
den Inhalt des Registers treffen darf. Der Compiler muß also Code
erzeugen, der das Register ausliest, dem Wert ändert und wieder
zurückschreibt.
Grüße
Klaus
Klaus Falser wrote:
> Diese ändern alle das selbe Register. Wie kann der Compiler wissen, ob 3> Schreibzugriffe gewünscht und notwendig sind, oder ob ein einziger> Zugriff genügt, der alle Bits gleichzeitig ändern?
Schrieb ich doch: indem man die einzelnen Subregister "volatile"
deklariert, statt des kompletten Registers.
Andere Sprachen (Ada) haben sowas auch, und es funktioniert dort.
Ich würdes es auch nicht so machen, aber das ist ne andere Frage. Es
ging um deine Behauptung, dass man nicht die Kontrolle darüber hätte.
Übrigens gibt's auch für den AVR Leute, die die Registerdefinitionen
als Bitfelder umgebogen haben. All die Optimierungen, die sonst bereits
funktionieren, funktionieren dort genauso, also bis zur Generierung
von SBI- und CBI-Befehlen. Es gibt wirklich keinen Unterschied im
erzeugten Code zur manuellen Manipulation der Bits.