www.mikrocontroller.net

Forum: Compiler & IDEs 32-Bit Arithmetik mit dem AVR


Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi!

Kann es sein, dass der GCC es noch nicht so ganz drauf hat mit 32-Bit 
Arithmetik im AVR?

cplex_data ist eine 32-Bit Variable, seconds, minutes und hours sind 
8-Bit Variablen. Das ganze ist für eine Binär-Uhr und soll das 
Ausgaberegister für den Charlieplexer zusammenbauen.
cplex_data = ((uint32_t) hours << 12) | (minutes << 6) | seconds;

Diese Zeile jedoch wird vom Compiler mit 300(!!) Bytes an Code 
übersetzt.

Eigentlich hat der AVR da kaum Arbeit. cplex_data besteht aus 4 
einzelnen 8-bit speicherstellen. Seconds kann man einfach mit der ersten 
OR-verknüpfen. Minutes muss in zwei Stücke zerteilt werden, wovon das 
erste mit der ersten Speicherstelle OR-verknüpft und das zweite einfach 
in die zweite Speicherstelle kopiert wird. Bei Hours ist es nicht 
wesentlich komplizierter. Genauergesagt würden 24bit auch ausreichen 
aber es gibt standardmäßig keinen 24-bit Datentyp.

Kann man irgendwie auf die einzelnen Speicherstellen zugreifen und das 
ganze somit per Hand etwas optimieren?

lg PoWl

Autor: Peter (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Welchen Optimierungs-Level hast Du eingestellt? Ich empfehle zumindest 
-O1 zu verwenden. Die anderen Stufen -O2/3/s bringen kaum noch was.

Verwendest Du die für den AVR optimierte MathLib? (Linker Option -lm) 
Defaultmässig wird nämlich die generische GCC MathLib gelinkt, die ist 
natürlich gross und langsam.

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
-O1 bringt nochmal knapp 190 Bytes obendrauf und die Zeile wird mit 326 
Bytes übersetzt.

-lm beim Linker reinzuschreiben hatte keinerlei Auswirkungen.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Paul Hamacher wrote:

>
cplex_data = ((uint32_t) hours << 12) | (minutes << 6) | seconds;
>
> Diese Zeile jedoch wird vom Compiler mit 300(!!) Bytes an Code
> übersetzt.

Sicher?
Bei mir sind es ganz ohne Optimierungen 112 Bytes und mit "-Os" 86 
Bytes.

> Eigentlich hat der AVR da kaum Arbeit.

Der AVR hat keine Asm-Befehle für mehrfaches Schieben, also muss "<< 6" 
und "<< 12" über Schleifen realisiert werden und das auch noch mit 
32-Bit Werten. Da kommt halt einiges zusammen.

Du kannst den Code etwas reduzieren, indem du den Compiler nicht dazu 
zwingst, alles in 32-Bit zu tun:
cplex_data = ((uint16_t)minutes << 6) | seconds;
cplex_data |= ((uint32_t) hours << 12);

Mit "-Os" sind es dann bei mir 50 Bytes.

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
vielleicht liegts auch daran dass alle variablen volatile sind.
Thx ich versuche mal das aufzuteilen.

Kann man die einzelnen Speicherstellen der 32-Bit Variable irgendwie 
einzeln erreichen?
  cplex_data = ((uint16_t) minutes << 6) | seconds;
  cplex_data |= ((uint32_t) hours << 12);

Das wird bei mir bei -Os leider sogar noch 32 Bytes länger als vorher.

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
oh tut mir leid.. hab mich wohl irgendwie verguckt?! Bei mir sinds auch 
86 Bytes. Der Zweizeiler bringt es allerdings auf 90.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Kann man die einzelnen Speicherstellen der 32-Bit Variable irgendwie
> einzeln erreichen?

Klar, aber was soll dir das hier konkret bringen?

> oh tut mir leid.. hab mich wohl irgendwie verguckt?! Bei mir sinds auch
> 86 Bytes. Der Zweizeiler bringt es allerdings auf 90.

Wenn cplex_data volatile ist, wird es beim Zweizeiler ja zwischendurch 
gespeichert und neu geladen. Das sind immerhin 32 Byte. Du könntest das 
mit einer temporären nicht volatile Variable umgehen. Ich frage mich 
allerdings, ob cplex_data überhaupt volatile sein muss.

Autor: ... (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Must du unbedingt so krumme Werte für die Bit-Schieberei nehmen? Wenn du 
die Möglichkeit hast, die Daten so abzuspeichern wie du willst, dann 
kannst du jeweils ein ganzes Byte für einen Wert nehmen. Das ist 
bedeutend effizienter.

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Paul Hamacher wrote:
> vielleicht liegts auch daran dass alle variablen volatile sind.

Aua!

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Jörg Wunsch (dl8dtl) (Moderator)

>> vielleicht liegts auch daran dass alle variablen volatile sind.

>Aua!

Sicher ist sicher, damit die nicht weglaufen . . . ;-)

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sie müssen volatile sein weil mein programm noch etwas mehr als nur 
diese paar zeilen umfasst

Autor: Chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> sie müssen volatile sein weil mein programm noch etwas mehr als nur
> diese paar zeilen umfasst

Was spricht dagegen, die volatile-Variablen im ersten Schritt in 
temporäre (sprich, lokale) nicht-volatile-Variablen zu kopieren und die 
shifts dann mit denen zu machen?

Denn es ist ja eh klar, dass 32-Bit-Variablen auch mit volatile nicht 
atomar bearbeitet werden können, also immer noch andere 
Synchronisationsmechanismen vorhanden sein müssen.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Chris wrote:

> Was spricht dagegen, die volatile-Variablen im ersten Schritt in
> temporäre (sprich, lokale) nicht-volatile-Variablen zu kopieren und die
> shifts dann mit denen zu machen?

Ist gar nicht nötig (bzw vergrößert den Code nur), da nur lesend auf die 
Variablen zugegriffen wird. Nur beim cplex_data stört das volatile, 
insbesondere in der Version mit den zwei Zeilen. Lässt sich aber leicht 
"entschärfen":
uint32_t temp;
temp = ((uint16_t) minutes << 6) | seconds;
temp |= ((uint32_t) hours << 12);
cplex_data = temp;


Paul Hamacher wrote:

> sie müssen volatile sein weil mein programm noch etwas mehr als nur
> diese paar zeilen umfasst

Sorry, aber ich vertraue deinen C-Kenntnissen nicht weit genug, um das 
einfach so zu glauben. Wenn du nämlich cplex_data in einem Interrupt 
rausschiftest und im "normalen" Code zusammenstellst (oder einem anderen 
Interrupt), sehe ich nicht, warum das zwingend volatile sein muss.
"Zu viel" volatile ist meist der Anschlussfehler zu "gar kein" volatile. 
Und warst du nicht erst kürzlich beim Status "gar kein", oder verwechsel 
ich dich da?

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Das wird sowohl im Interrupt gelesen als auch geschrieben und muss im 
gesamten Programm verfügbar sein.

Ja, ich gebe zu, ich würde mich auch eher zu den Anfängern schätzen, 
aber Übung macht den Meister.

Danke jedenfalls für die Hilfe!

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Paul Hamacher wrote:
> Das wird sowohl im Interrupt gelesen als auch geschrieben und muss im
> gesamten Programm verfügbar sein.

Wie gesagt, das alleine macht ein volatile nicht zwingend nötig.

Autor: Paul H. (powl)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn ich das volatile weglasse erzeugt der Compiler noch 2 Bytes mehr 
code.. gibt sich wohl nicht viel.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Paul Hamacher wrote:

> Eigentlich hat der AVR da kaum Arbeit. cplex_data besteht aus 4
> einzelnen 8-bit speicherstellen.

Was spricht dann dagegen, es als Array aus 4 Bytes zu definieren?

32Bit mag der AVR-GCC nicht, er macht dann immer 4 Byte-Zugriffe.

8Bit kann er wesentlich besser optimieren.


Peter

Autor: bitfield benutzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
sollte man normalerweise ja aus Portabilitätsgründen meiden, aber hier 
würde mal Sinn machen: bitfield benutzen á la:
struct cplex {
        uint32_t :16;
        uint32_t hours:4;
        uint32_t minutes:6;
        uint32_t seconds:6;
};
typedef struct cplex cplex_t;

extern cplex_t cplex_data;
cplex_t cplex_data;


void bla(uint8_t hours, uint8_t minutes, uint8_t seconds)
{
        cplex_data.hours = hours;
        cplex_data.minutes = minutes;
        cplex_data.seconds = seconds;
}

reicht für eine 12h Uhr. Daraus macht der gcc dann:
  54:   96 2f           mov     r25, r22
  56:   92 95           swap    r25
  58:   90 7f           andi    r25, 0xF0       ; 240
  5a:   8f 70           andi    r24, 0x0F       ; 15
  5c:   89 2b           or      r24, r25
  5e:   80 93 62 00     sts     0x0062, r24
  62:   62 95           swap    r22
  64:   6f 70           andi    r22, 0x0F       ; 15
  66:   44 0f           add     r20, r20
  68:   44 0f           add     r20, r20
  6a:   63 70           andi    r22, 0x03       ; 3
  6c:   64 2b           or      r22, r20
  6e:   60 93 63 00     sts     0x0063, r22

Macht 30 byte. Mit einer 24h Uhr wird es für den Compiler etwas 
komplexer und er kommt auf 52 bytes.

Autor: Simon K. (simon) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
bitfield benutzer wrote:
> sollte man normalerweise ja aus Portabilitätsgründen meiden

Das ist hochgradiger Quatsch! Bitfelder sind C-definiert.

Portabilitätsbedenken gibts nur, wenn man versucht solche Bitfields 
(beispielsweise) über Netzwerk überträgt und die eine Maschine 
Big-Endian ist, und die andere Little-Endian.
Oder wenn man ein Bitfield benutzt und das dann um-castet in eine 
integrale Variable (int, long, char). Die Position der Bits ist nämlich 
nicht bei jeder Maschine gleich.

Wenn man das Bitfeld aber nur zum Speichern von Daten verwendet, die auf 
ein und der selben Maschine bleiben und auf die auch nur über die 
Elemente des Bitfields zugegriffen wird, stimmt alles.

Also dein Beispiel wäre in Ordnung.

Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
bitfield benutzer wrote:

> Macht 30 byte. Mit einer 24h Uhr wird es für den Compiler etwas
> komplexer und er kommt auf 52 bytes.

Also keinerlei Vorteil gegenüber der Lösung ohne Bitfelder.

Autor: bitfield benutzer (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Simon K. wrote:
> Das ist hochgradiger Quatsch! Bitfelder sind C-definiert.

naja, nur bedingt, wie Du ja so richtig im nächsten Absatz beschrieben 
hast. Wenn man Daten mit anderen Systemen austauschen muß, sollte man 
Bitfelder meiden, da abhängig von der Kompilerimplementierungen 
(schriebst Du ja schon). Hab viel mit solchen Systemen zu tun, deswegen 
meine Bemerkung. War vielleicht etwas unvollständig, sorry.

@Stefan Ernst:
Wird schon so sein, sieht aber IMHO mit Bitfeldern etwas übersichtlicher 
aus. Und der OP hat ja explizit gefragt, ob es eine Möglichkeit gibt, 
direkt auf die Speicherstellen zuzugreifen. Klang für mich nach 
Bitfeldern.

Autor: Michael Appelt (micha54)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Paul Hamacher wrote:
> Wenn ich das volatile weglasse erzeugt der Compiler noch 2 Bytes mehr
> code.. gibt sich wohl nicht viel.

Hallo,

das volatile nervt mich auch immer ein wenig. Es soll verhindern, daß 
der Wert in registern gehalten wird und im Speicher immer der alte Wert 
zu lesen ist.

Es schützt aber meines Wissens nicht wirklich dagegen, daß die Änderung 
nur teilweise durchgeführt wurde, bis der Interrupt dazwischen sprang.

Wie ist das mit nem einfachen volatile integer x, kann da der Interrupt 
bei einem x -= 100 im Hauptprogramm zwischen die Befehle 
dazwischespringen ? D.h. benötige ich nicht eigentlich ein cli() und 
sei() drumherum ?

Gruss,
Michael

Autor: Chris (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> Wie ist das mit nem einfachen volatile integer x, kann da der Interrupt
> bei einem x -= 100 im Hauptprogramm zwischen die Befehle
> dazwischespringen ?

Ja, kann er, weil diese Zuweisung nicht mit einem Maschinenbefehl zu 
schaffen ist.

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@ Michael Appelt (micha54)

>Es schützt aber meines Wissens nicht wirklich dagegen, daß die Änderung
>nur teilweise durchgeführt wurde, bis der Interrupt dazwischen sprang.

Richtig.

>dazwischespringen ? D.h. benötige ich nicht eigentlich ein cli() und
>sei() drumherum ?

Ja, siehe Interrupt.

MFG
Falk

Autor: Daniel R. (zerrome)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
hallo,
wenn ich auf die stellen einer 32 bit variabel schreiben möchte, nehme 
ich immer einen gecasteten void pointer. so kann man z.b. bequem in 8 
bit häppchen schreiben.
hab mal die hier geschriebenen sachen ausprobiert und komme mit meiner 
methode auf 28 byte an code...
unsigned long int cplex_data;
unsigned char hours;
unsigned char minutes;
unsigned char seconds;



void set_Time(void){

   //28 bytes
   void *vcplex_data=&cplex_data;
   void *vhours=&hours;
   void *vminutes=&minutes;
   void *vseconds=&seconds;

   *(unsigned char*)vcplex_data++=*(unsigned char*)vseconds;
   *(unsigned char*)vcplex_data++=*(unsigned char*)vminutes;
   *(unsigned char*)vcplex_data=*(unsigned char*)vhours;


   /*// 90 bytes
   uint32_t temp;
   temp = ((uint16_t) minutes << 6) | seconds;
   temp |= ((uint32_t) hours << 12);
   cplex_data = temp;
   */


   //118 bytes
   //cplex_data = ((uint32_t) hours << 12) | (minutes << 6) | seconds;

}



void main(void){
   set_Time();
}


Autor: Stefan Ernst (sternst)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Daniel Platte wrote:

> hab mal die hier geschriebenen sachen ausprobiert und komme mit meiner
> methode auf 28 byte an code...

Schön. Nur dumm, dass dein Code nicht das macht, was der ursprüngliche 
Code tut und der OP braucht.

Autor: Daniel R. (zerrome)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ja war schon spät...
hab nur gelesen 8 bit variablen ... bla bla und gesehen das geshiftet 
wird...

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.