Forum: Compiler & IDEs Bitfelder und Register - geht das?


von Bit Bastler (Gast)


Lesenswert?

Hallo ihr GCC-Profis,
ich will folgendes machen:
Um das Programmieren mit einem ARM7-Mikrocontroller ein bisschen zu 
vereinfachen, dachte ich mir folgendes:
Ich defineire für jedes Register, das der Controller hat, ein Bitfeld. 
Also bekommt z.B. das Register "PLLSTAT" ein Bitfeld, das wie folgt 
aussieht:

struct stPLLStatus
{
   int MSEL : 15;
   int NSEL : 8;
   int PLLE : 1;
   int PLLC : 1;
   int PLOCK : 1;
} tstPLLStatus, *tpstPLLStatus;

Dann definiere ich eine Variable vom Typ "tpstPLLStatus" (also ein 
Zeiger), der auf das entsprechende Register zeigt, ungefähr so:

tpstPLLStatus test = (tpstPLLStatus)0xE01FC088;

jetzt kann ich ja wunderbar auf die Bits zugreifen:

test->MSEL

oder

test->PLOCK

geht. ABER: Bei einigen Registern gibt es ja mitten drin ein oder zwei 
Bits, die nicht benötigt werden. Wie 'richte' ich jetzt die Bits in 
einem solchen Bitfeld aus, sodass alle an der richtigen Position sind?
Weil, beim Beispiel oben wäre das Bit PLLE an der Position 23, in 
wirklichkeit ist es aber an Position 24. Zwischen MSEL und PLLE ist ein 
'unused' Bit. Diese Bitfelder funktionieren also bis jetzt nur mit 
Registern, die keine 'unused' Bits haben.... Kann mir einer Verraten, ob 
mein Gedanke mit den Bitfeldern grundsätzlich funktioniert mit dem GCC 
und ob/wie ich das Bitfeld umschreiben muss, dass auch die nicht 
genutzten Bits mitgezählt werden?
Der Vorteil von solchen Bitfeldern wäre natürlich, dass man nicht 
umständlich über Maskierungen und solchen Kram Bits bearbeiten muss, 
sondern schön auf die einzelnen Felder zugreifen kann. Das macht dann 
auch den Code besser lesbar ;-)

Das ganze möchte ich mit GCC machen, und es soll auf einem ARM7 LPC2470 
laufen.

Vielen Dank,

Schöne Grüsse

von Nico (Gast)


Lesenswert?

Für doch einfach Dummydaten ein.

struct stPLLStatus
{
   unsigned MSEL : 15;
   unsigned NSEL : 8;
   unsigned unused : 1; <----
   unsigned PLLE : 1;
   unsigned PLLC : 1;
   unsigned PLOCK : 1;
} tstPLLStatus, *tpstPLLStatus;


Ich weiss jetzt auch nicht wie der gcc auf deine "int" in den bitfeldern 
reagiert, normal kenne ich das nur mit signed/unsigned

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Das könnte so aussehen:
1
typedef union
2
{
3
    struct
4
    {
5
        unsigned MSEL :15; // 0..14
6
        unsigned NSEL :8;  // 15..23
7
        unsigned :1;
8
        unsigned PLLE :1;  // 24
9
        unsigned PLLC :1;  // 25
10
        unsigned PLOCK :1; // 26
11
        unsigned :6;       // 27..31
12
    } bits;
13
    unsigned int reg;
14
} __attribute__((packed)) PLLStatus_t;
15
16
#define PLL_STATUS (*((PLL_Status_t volatile *) 0x12345678))

Über .reg kann auf's ganze SFR gegriffen werden, über .bits auf einzelne 
Bit(felder). In GNU-C gehen auch anonyme Strukts, macht's etwas bequemer 
hinzuschreiben beim Codieren später (.bits kann entfallen), würd ich 
aber nicht empfehlen.

Das volatile hab ich nicht in den Typ reingemacht, weil man manchmal 
mehrere Felder zu setzen hat, aber nur einen Zugriff haben will/darf. 
Dann kann man eine lokale Variable von dem Typ machen, ohne daß diese 
volatile ist; die Felder setzen, und schließlich alles zusammen per 
Zuweisung schreiben.

Allerdings setzt das ganze eine gewisse Unterstützung durch den Compiler 
voraus, wenn du direkt Zugriffe codierst wie
1
   PLL_STATUS.PLLE = 1;

(für avr-gcc zB gäbe ähnliche Konstrukte grottenüblen Code, weil er was 
Bits angeht noch hinzulernen muss. Da arm7-Entwickler nicht so rar sind 
wie für avr, wurde das bestimmt schon komplett beackert)

Johann

von Bit Bastler (Gast)


Lesenswert?

Hi Nico,
danke soweit. Gibts noch ne 'schönere' Möglichkeit?

von mthomas (Gast)


Lesenswert?

Antwort auf Frage gab es ja bereits aber in dem Zusammenhang noch einen 
Nachtrag. Da das Thema in mal in anderen Forum aufkam, habe ich das mit 
den Regisiter-zu-Bitfeldern-Mapping vor einer ganzen Weile (wohl noch zu 
gcc 3.x-Zeiten) ausprobiert. Dabei wurden die Zugriffe über 
Bitfelderelemente zwar korrekt in ARM-Assemblercode umgesetzt, jedoch 
auch so optimiert, dass bei Bedarf auch nur Bytezugriffe erzeugt wurden. 
Sehr viele Hardwareregister beim seinerzeit verwendeten Controller 
(LPC2k, genauen Typ vergessen) dürfen aber nur als ganzes (32-bit) 
geschrieben/gelesen werden, folglich gab es eine Exception. Es gibt dazu 
auch entspr. Einträge im gcc Bugzilla. Es ist aber mglw. gar kein 
Fehler, da der Compiler den Quellcode m.M. schon korrekt umgesetzt hat. 
Kann ja nicht wissen, das die Hardware dies einschränkt.

Der Ansatz mit den Bitfeldern mag zwar Tipparbeit sparen und etwas 
Übersicht schaffen, kann aber Verdruss bereiten. Portablen Code erzeugt 
man damit auf jeden Fall nicht. Falls die Idee aus Beispielquellcode für 
EWARM kommt: es gibt im IAR Compiler eine herstellerspezifische 
Erweiterung dafür. Wie anfangs geschrieben: Test sind schon eine Weile 
her, mglw. wurde in der zwischenzeigt etwas an impliziten Regeln für 
volatile (fehlt oben übrigens) verändert und/oder ein spezielles 
Attribut eingeführt.

von Bit Bastler (Gast)


Lesenswert?

@Johann L.:
Genial! Vielen Dank :-) das sieht doch super aus! THX!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Bit Bastler wrote:
> @Johann L.:
> Genial! Vielen Dank :-) das sieht doch super aus! THX!

Aber beachte das, was mthomas gesagt hat und ich mit "ob das der 
Compiler unterstützt" gemeint habe!

Ich kann dir hier Tipps auf C-Ebene geben, aber nicht auf foo-gcc-Ebene. 
Vielleicht sind die Regionen, wo spezielle Zugriffsregeln herrschen, in 
gcc hinterlegt, und er weiß wie Zugriff auf SFRs sein soll. Auch 
vorstellbar, daß es über ne Section geht, hab ich auch mal gesehen: Die 
Section dient dann nur als Tag für den Zugriff; Objekte werden ja keine 
in der Section angelegt. Dadurch kann das in Headern codiert werden und 
muss nicht in gcc rein, was bei den 1000 arm-Derivaten bestimmt übel 
ist.

Johann

von Marcus H. (mharnisch) Benutzerseite


Lesenswert?

Ha, eines meiner Lieblingsthemen!

mthomas wrote:
> Dabei wurden die Zugriffe über Bitfelderelemente zwar korrekt in
> ARM-Assemblercode umgesetzt, jedoch auch so optimiert, dass bei
> Bedarf auch nur Bytezugriffe erzeugt wurden.

Ein ABI compliant Compiler darf das nicht. Der Zugriff muss auf den
Container erfolgen. Bei Registern ist das üblicherweise ein 32-Bit
Typ. Kompliziert wird es erst, wenn man Bitfeldstrukturen mit
mehreren, ggf überlappenden, Containern definiert.

> Der Ansatz mit den Bitfeldern mag zwar Tipparbeit sparen und etwas
> Übersicht schaffen, kann aber Verdruss bereiten.

Ja. Vor allem wenn man sich verzählt und damit versehentlich ein neuer
Container aufgemacht wird :-)

Ich bevorzuge Bitfelder trotzdem. Shift und Mask hat seine eigenen
Probleme und sieht furchtbar aus.

> Portablen Code erzeugt man damit auf jeden Fall nicht.

Doch, solange man sich an ABI compliant Compiler hält. Was anderes
sollte man für ARM ohnehin nicht verwenden.

> Test sind schon eine Weile her, mglw. wurde in der zwischenzeigt
> etwas an impliziten Regeln für volatile (fehlt oben übrigens)
> verändert und/oder ein spezielles Attribut eingeführt.

Das ist ebenfalls alles im AAPCS (Teil des ABI) detailliert
ausgeführt.

Gruß
Marcus
http://www.doulos.com/arm/

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.