Forum: Compiler & IDEs Mein Compiler spinnt mit 32- und 64-Bit-Werten.


von Dampfente (Gast)


Lesenswert?

Moinsen,

vor Kurzem bin ich von AVR Studio unter WIN zu Eclipse mit Plugin unter 
Linux migriert, ein Schritt, den ich mir schon länger vorgenommen habe. 
Ein weiterer solcher Schritt: Ich hab mir mal endlich eine UART-library 
geschrieben, die sich stressfrei benutzen lässt und gleichzeitig sehr 
flashsparsam ist.

Nun motzt aber der Compiler, sobald ich zB. Folgendes mache:
1
uint64_t ente = uart_receive64();
2
ente |= (uart_receive() << 16);
3
uart_transmit64(ente);

Der Code oben ist zwar mehr oder weniger sinnleer, aber reproduziert 
meinen Fehler: "Warnung: Links-Schiebe-Weite >= Breite des Typs". Nach 
dem Flashen und testen kommen auch tatsächlich nur die zwei least 
significant Bytes an.
Dasselbe Problem, ebenfalls gut reproduzierbar, habe ich mit dem 
uint32-Typ; Signeds habe ich nicht getestet, weil in der Implementation 
nicht verwendet.
<inttypes.h> ist eingebunden, <stdint.h> demnach auch.

Frage in die Runde: WTF?

Ich hatte das intuitiv auf die sehr kryptischen typedefs in der stdint.h 
geschoben (der else-Teil von dem Doxygen-Compilerswitch), aber weder das 
Definieren eines Doxygenflags, noch das komplette entfernen des 
Compilerswitch in der stdint hat was gebracht.

Aktuelle Struktur:
/uartlibtest
- defines.h
- main.c
- /uartlib
 - uartlib.c
 - uartlib.h
 - uartlib16.c
 - uartlib16.h
 - uartlib32.c
 - (...)
 - uartlibp.c (Pointerfunktionalität)
 - uartlibp.h

wobei die einzelnen Module "uartlibX.h" wieder die "uartlib.h" 
einbinden, die die inttypes included hat. In der aktuellen Main wird 
deshalb nur die "uartlib/uartlib64.h" included.

Es bringt auch irgendwie nix, die inttypes an beliebigen weiteren 
Stellen zu includen...

Wie auch immer. Könnte das noch weiter ausführen, aber ich merk gerade, 
dass ich besser pennen sollte. Ich schau morgen mal hier rein, 
vielleicht ist ja wer schlauer als ich :)

von Dampfente (Gast)


Lesenswert?

Noch ein Add:

- Ich habe im Moment nur einen Mega16 da. KANN der überhaupt uint32 und 
größer?

von Andreas B. (andreas_b77)


Lesenswert?

Dampfente schrieb:
> uint64_t ente = uart_receive64();
> ente |= (uart_receive() << 16);

Ein int ist beim avr-gcc 16 Bit breit und in C werden Ausdrücke als int 
berechnet wenn kein größerer Datentyp was anderes erzwingt. Deshalb wird 
das Ergebnis von uart_receive() als 16 Bit int um 16 Positionen nach 
links geschoben. Deshalb die Warnung und deshalb kommt nur 0 dabei raus.

Also richtig so:
1
ente |= (uint64_t)uart_receive() << 16;

Dampfente schrieb:
> - Ich habe im Moment nur einen Mega16 da. KANN der überhaupt uint32 und
> größer?

Nö, nicht als direkte Anweisung. Der Compiler muss halt dafür mehr Code 
erzeugen oder eine Bibliotheksfunktion aufrufen (dafür ist dann die 
libgcc zuständig).

von Peter D. (peda)


Lesenswert?

Dampfente schrieb:
> Ich hab mir mal endlich eine UART-library
> geschrieben, die sich stressfrei benutzen lässt und gleichzeitig sehr
> flashsparsam ist.

Naja, das bische UART-Zeugs braucht doch kaum Flash.
Was beim AVR etwas mühsam ist, das Namenschaos der verschiedenen AVRs zu 
ordnen, da hat sich Atmel nicht mit Ruhm bekleckert.
Geht aber:
Beitrag "AVR-GCC: UART mit FIFO"


Wenn Flash knapp ist, dann ist 64Bit keine gute Ideee, das ist beim 
AVR-GCC völlig unoptimiert und braucht sogar mehr Flash als float.
Da purzeln die kBytes nur so hinunter.

64Bit also nur, wenn Flash und CPU-Zeit keinerlei Rolle spielt.
Ansonsten nimm ein Array aus kleineren Datentypen.


Peter

von Peter D. (peda)


Lesenswert?

Dampfente schrieb:
> - uartlib16.c
>  - uartlib16.h
>  - uartlib32.c

Das läßt mich vermuten, Du versuchst multi-Byte-Werte direkt aus der 
UART zu lesen. Das geht schief!
Das Startbit zeigt nur an, wann ein Byte beginnt, aber nicht, wann ein 
Paket.
Du brauchst also erstmal ein Protokoll, um den Anfang bestimmter 
Binärdaten zu kennzeichnen.
Die UART schiebt nur eine Anzahl Bytes in ein Array.
Erst ein Parser, der das Protokoll kennt, kann daraus dann die einzelnen 
Werte extrahieren.


Peter

von Dampfente (Gast)


Lesenswert?

Super. Jetz hats mir beim "Vorschau" klicken den Text weggemacht. 
Kurzversion:

Andreas B. schrieb:
> Ein int ist beim avr-gcc 16 Bit breit und in C werden Ausdrücke als int
> berechnet wenn kein größerer Datentyp was anderes erzwingt. Deshalb wird
> das Ergebnis von uart_receive() als 16 Bit int um 16 Positionen nach
> links geschoben.
Natürlich! *Kopf->Tisch*. Aber uart_receive liefert einen uint8_t - 
wieso hab ich dann 16 Bit? Der Shift?

> Nö, nicht als direkte Anweisung. Der Compiler muss halt dafür mehr Code
> erzeugen oder eine Bibliotheksfunktion aufrufen (dafür ist dann die
> libgcc zuständig).
Jo, das meinte ich; dass ein 8b-Prozessor keine native 
64-Bit-Unterstützung hat, ist irgendwie logisch. Sonst wäre ja auch mein 
Rechner locker 8 mal so schnell ;)

Peter Dannegger schrieb:
> Was beim AVR etwas mühsam ist, das Namenschaos der verschiedenen AVRs zu
> ordnen, da hat sich Atmel nicht mit Ruhm bekleckert.
> Geht aber:
> Beitrag "AVR-GCC: UART mit FIFO"
Schicke Sache, aber im Moment noch ein zu großer Overkill - ich brauche 
vorerst keine Portabilität zu anderen AVRs, und ein Puffer ist schön, 
aber eben auch nicht immer absolut notwendig; Ich brauche den RAM 
vermutlich dringender für andere Sachen... aber ich werds mir noch 
genauer ansehen, wenn ich das uartlib-Modul restrukturiere.

Im jetzigen Zustand verhält es sich nämlich noch nicht ganz so, wie ich 
mir das vorstelle, aber das krieg ich schon noch hingebogen (und wenn 
ich mal wieder über meine Hirnfürze stolpere, melde ich mich wieder 
hier...) :)

Danke, Ente.

von Rolf Magnus (Gast)


Lesenswert?

Dampfente schrieb:
> Natürlich! *Kopf->Tisch*. Aber uart_receive liefert einen uint8_t -
> wieso hab ich dann 16 Bit?

"Integer Promotion". Alle Berechnungen werden mindestens mit int 
gemacht. Alles, was kleiner ist, wird entsprechend konvertiert.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Andreas B. schrieb:
> Also richtig so:ente |= (uint64_t)uart_receive() << 16;

Sicher?

Der Typ eines Schiebeausdrucks bestimmt sich durch den Typ des
Teilausdrucks, der die Schiebeweite festlegt.  Meiner Meinung
nach daher so:
1
ente |= uart_receive() << 16ull;

von Oliver (Gast)


Lesenswert?

Jörg Wunsch schrieb:
> Meiner Meinung
> nach daher so:
> ente |= uart_receive() << 16ull;

Dem gcc ist es anscheinend egal, der nimmt auch die andere Version.

Oliver

von Peter D. (peda)


Lesenswert?

Jörg Wunsch schrieb:
> Der Typ eines Schiebeausdrucks bestimmt sich durch den Typ des
> Teilausdrucks, der die Schiebeweite festlegt.  Meiner Meinung
> nach daher so:
> ente |= uart_receive() << 16ull;


Nö.

Alle Operationen richten sich nach dem Format des größten Operanden, mit 
einer Ausnahme: der Schiebeoperator.

Der richtet sich wirklich nur nach dem Format des ersten Operanden.
1
uint32_t i = 1 << 31UL;
ist falsch und ergibt 0.
Richtig ist:
1
uint32_t i = 1UL << 31;


Peter

von (prx) A. K. (prx)


Lesenswert?

Jörg Wunsch schrieb:

> Der Typ eines Schiebeausdrucks bestimmt sich durch den Typ des
> Teilausdrucks, der die Schiebeweite festlegt.

Wie kommst du jetzt ausgerechnet darauf? Grad der ist irrelevant.

Der Text im C99 Standard (draft) selbst ist etwas irritierend 
formuliert, weil nicht eindeutig macht, welche integer promotions er 
meint. Gemeint ist wohl nur, dass beide Seiten ggf. zu "int" erweitert 
werden: "The integer promotions are performed on each of the operands. 
The type of the result is that of the promoted left operand. If the 
value of the right operand is negative or is greater than or equal to 
the width of the promoted left operand, the behavior is undefined."

Dankenswerterweise sagt das Rationale 5.10 dazu: "The description of 
shift operators in K&R suggests that shifting by a long count should 
force the left operand to be widened to long before being shifted.  A 
more intuitive practice, endorsed by the C89 Committee, is that the type 
of the shift count has no bearing on the type of the result."

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

A. K. schrieb:
> Wie kommst du jetzt ausgerechnet darauf? Grad der ist irrelevant.

Dann hab' ich's mir falsch herum gemerkt :), hätte wohl besser
nachsehen sollen.  Man ist halt von den Compilerwarnungen
ausreichend verwöhnt, denn die sagen einem für gewöhnlich schon,
wenn man da schief liegt.

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.