Forum: Compiler & IDEs optimierung von sbi/cbi


von claudia (Gast)


Lesenswert?

hi,
gibt's eigentlich eine möglichkeit, dem compiler beizubringen, zb das 
folgende automatisch zu optimieren? da kann man ja so einiges 
zusammenfassen. von hand wäre das aber mühsam und fehleranfällig. 
normalerweise schreibe ich ja nicht "PORTB |= _BV(PB5);" sondern habe 
makros für sowas.
1
PORTB |= _BV(PB5);
2
    393c:  2d 9a         sbi  0x05, 5  ; 5
3
PORTB |= _BV(PB4);
4
    393e:  2c 9a         sbi  0x05, 4  ; 5
5
PORTB |= _BV(PB2);
6
    3940:  2a 9a         sbi  0x05, 2  ; 5
7
PORTB |= _BV(PB3);
8
    3942:  2b 9a         sbi  0x05, 3  ; 5
9
PORTB |= _BV(PB0);
10
    3944:  28 9a         sbi  0x05, 0  ; 5
11
PORTB |= _BV(PB1);
12
    3946:  29 9a         sbi  0x05, 1  ; 5

das sollte mir der gcc abnehmen:
1
PORTB |= _BV(PB5) | _BV(PB4) | _BV(PB2) | _BV(PB3) | _BV(PB0) | _BV(PB1);
2
    393c:  85 b1         in  r24, 0x05  ; 5
3
    393e:  8f 63         ori  r24, 0x3F  ; 63
4
    3940:  85 b9         out  0x05, r24  ; 5

ich nehme an, das liegt daran, daß PORTB usw. volatile sind. aber kann 
man keine ausnahmen definieren oder sowas?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

claudia schrieb:

> gibt's eigentlich eine möglichkeit, dem compiler beizubringen,
> zb das folgende automatisch zu optimieren?

Ja, du könntest ein Patch einreichen, das GCC dahingehend ändert, daß
der Compiler nicht mehr volatile-korrekt ist :-;

> da kann man ja so einiges zusammenfassen.

Nö, das darf ein korrekter C-Compiler nicht.

> ich nehme an, das liegt daran, daß PORTB usw. volatile sind.

Genau,

> kann man keine ausnahmen definieren oder sowas?

Du jannst die Definition von PORTB so machen, daß es nicht mehr volatile 
ist:
 
1
#include <avr/io.h>
2
3
#undef PORTB
4
#define PORTB (*((uint8_t*) 0x25))
5
6
void foo (void)
7
{
8
    PORTB |= 1 << 0;
9
    PORTB |= 1 << 1;
10
    PORTB |= 1 << 2;
11
    PORTB |= 1 << 3;
12
    PORTB |= 1 << 4;
13
    PORTB |= 1 << 5;
14
}

avr-gcc macht dann
 
1
foo:
2
  in r24,0x5
3
  ori r24,lo8(63)
4
  out 0x5,r24
5
  ret

Das ist nicht was du willst.

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


Lesenswert?

Man könnte auch auf die ganzen dummen VerODERungen verzichten an
den Stellen, wo sie gar nicht nötig sind.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wenn du
1
  PORTB |= _BV(PB5);
2
  PORTB |= _BV(PB4);
3
  PORTB |= _BV(PB2);
4
  PORTB |= _BV(PB3);
5
  PORTB |= _BV(PB0);
6
  PORTB |= _BV(PB1);

schreibst, möchtest du, dass die einzelnen Bits nacheinander in der
angegebenen Reihenfolge gesetzt werden.

Wenn du hingegen
1
  PORTB |= _BV(PB5) | _BV(PB4) | _BV(PB2) | _BV(PB3) | _BV(PB0) | _BV(PB1);

schreibst, möchtest du, dass alle Bits gleichzeitig gesetzt werden.

Würde der Compiler nach eigenem Gutdünken die eine Variante durch die
andere ersetzen, könnte das die korrekte Funktion des Programms beein-
trächtigen.

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


Lesenswert?

Yalu X. schrieb:
> Würde der Compiler nach eigenem Gutdünken die eine Variante durch die
> andere ersetzen, könnte das die korrekte Funktion des Programms beein-
> trächtigen.

Man stelle sich nur sowas vor:
1
  PORTB &= ~(1 << STROBEPIN);  // assert strobe signal
2
  PORTB |= (1 << STROBEPIN);

Der "intelligente" Compiler würde nun feststellen, dass der Gesamt-
effekt beider Zeilen sich ja aufhebt und man folglich gar keinen
Code dafür emittieren müsste. ;-)

von claudia (Gast)


Lesenswert?

hi jungs,

mensch, mir ist ja klar gewesen, woran es liegen könnte. es gibt doch 
aber anwendungsfälle, wo es einfach wichtiger ist, daß die veroderungen 
zusammengefasst werden, auch wenn ich sie einzeln schreiben muss. ich 
steuere zb ein lcd an. da benutze ich nicht _BV(PB2) und _PV(PB3) usw. 
sondern eben makros, damit der code lesbar bleibt und sich kein fehler 
einschleicht. und dann brauche ich einfach mehrere befehle 
hintereinander (die aber gerne gleichzeitig ausgeführt werden dürfen).

ich denke, der eine hinweis war schon gut. nur sollte ich das ganze dann 
nicht PORTB nennen sondern zb NVPORTB
1
#define NVPORTB (*((uint8_t*) 0x25))

ich werd's mal versuchen. :)

lg c

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


Lesenswert?

claudia schrieb:
> ich
> steuere zb ein lcd an. da benutze ich nicht _BV(PB2) und _PV(PB3) usw.
> sondern eben makros, damit der code lesbar bleibt und sich kein fehler
> einschleicht.

Und darin kommt's dann auf jeden Takt und jedes Byte Code an?

Wenn sowas einmalig während der Initialisierung der Fall ist, würde
ich mir da keine großen Gedanken drum machen und Lesbarkeit einfach
über Effizienz gehen lassen.

von Karl H. (kbuchegg)


Lesenswert?

claudia schrieb:
> hi jungs,
>
> mensch, mir ist ja klar gewesen, woran es liegen könnte. es gibt doch
> aber anwendungsfälle, wo es einfach wichtiger ist, daß die veroderungen
> zusammengefasst werden, auch wenn ich sie einzeln schreiben muss. ich
> steuere zb ein lcd an. da benutze ich nicht _BV(PB2) und _PV(PB3) usw.
> sondern eben makros, damit der code lesbar bleibt und sich kein fehler
> einschleicht. und dann brauche ich einfach mehrere befehle
> hintereinander (die aber gerne gleichzeitig ausgeführt werden dürfen).

Und was hindert dich daran, für diese Zusammenfassung wieder sinnvolle 
Makros zu benutzen? Dann hat man die Dinge an einer Stelle beisammen.

von Karl H. (kbuchegg)


Lesenswert?

Im übrigen ist da noch etwas zu bedenken

  PORTB |= (1<<PD2) | (1<<PD3);

(oder wie dann auch immer) ist ganz sicher KEINE atomare Operation. D.h. 
wenn da ein Interrupt dazwischenfunkt und in der ISR am PORTB gefummelt 
wird, dann gibt es Ärger.

Wenn der Compiler aber

  PORTB |= (1<<PD2);
  PORTB |= (1<<PD3);

zu jeweils einem sbi compiliert, dann sind das 2 atomare Operationen. 
Ganz abgesehen davon, dass letztere Sequenz dann sogar noch kürzer als 
die generelle Oder-Lösung ist.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

claudia schrieb:
> mensch, mir ist ja klar gewesen, woran es liegen könnte. es gibt doch
> aber anwendungsfälle, wo es einfach wichtiger ist, daß die veroderungen
> zusammengefasst werden, auch wenn ich sie einzeln schreiben muss.

Dann benutze eine lokale Variable (z.B. portb) zum 
Nacheinander-Verodern.

Du schreibst:
1
  uint8_t portb = 0;
2
3
  portb |= _BV(PB5);
4
  ...
5
  portb |= _BV(PB3);
6
7
  PORTB = portb;

Damit setzt Du dann den Wert von PORTB auf einen Schlag, wobei der 
Compiler die Volatilität von PORTB auch weiterhin korrekt behandelt.

von Simon K. (simon) Benutzerseite


Lesenswert?

claudia schrieb:
> mensch, mir ist ja klar gewesen, woran es liegen könnte. es gibt doch
> aber anwendungsfälle, wo es einfach wichtiger ist, daß die veroderungen
> zusammengefasst werden

Und wie soll der Compiler wissen, wo ein solcher Anwendungsfall 
vorliegt?

von -_- (Gast)


Lesenswert?

Simon K. schrieb:
> Und wie soll der Compiler wissen, wo ein solcher Anwendungsfall
> vorliegt?

Intuitiv, Frau eben... ;-)

von Peter D. (peda)


Lesenswert?

claudia schrieb:
> ich
> steuere zb ein lcd an. da benutze ich nicht _BV(PB2) und _PV(PB3) usw.
> sondern eben makros, damit der code lesbar bleibt und sich kein fehler
> einschleicht. und dann brauche ich einfach mehrere befehle
> hintereinander (die aber gerne gleichzeitig ausgeführt werden dürfen).

Du mußt eh warten, da kommt es nicht auf den Zyklus an.
Du gewinnst daher beim Zusammenfassen garnichts.

Fürs LCD nehme ich die völlig freie Pinzuordnung:

http://www.avrfreaks.net/index.php?name=PNphpBB2&file=viewtopic&t=102296

Dann werden Spezialfunktionen der Pins nicht behindert.


Peter

von claudia (Gast)


Lesenswert?

das problem ist längst gelöst.

freie pinzuordnung verwende ich auch, allerdings gewinne ich mit der 
neuen methode jetzt im init alleine 16 bytes, bei gleichem resultat und 
gleicher übersichtlichkeit.

von Cyblord -. (cyblord)


Lesenswert?

claudia schrieb:
> das problem ist längst gelöst.
>
> freie pinzuordnung verwende ich auch, allerdings gewinne ich mit der
> neuen methode jetzt im init alleine 16 bytes, bei gleichem resultat und
> gleicher übersichtlichkeit.

Na wenn das nicht nach einem Turing Award schreit. Wer übernimmt die 
Nominierung ;-)

von Peter D. (peda)


Lesenswert?

claudia schrieb:
> das problem ist längst gelöst.

Möchtest Du uns auch an der Lösung teilhaben?
Ist so üblich in Foren.

claudia schrieb:
> freie pinzuordnung verwende ich auch

Dann ist eine Zusammenfassung doch nicht möglich.
Es müssen alle 4 Datenpins auf dem selben Port sitzen.


Peter

von Uwe (Gast)


Lesenswert?

> Möchtest Du uns auch an der Lösung teilhaben?

Hat sie doch schon.

>> ich denke, der eine hinweis war schon gut. nur sollte ich das ganze dann
>> nicht PORTB nennen sondern zb NVPORTB

>> #define NVPORTB (*((uint8_t*) 0x25))

von Peter II (Gast)


Lesenswert?

Uwe schrieb:
> Hat sie doch schon.
>
>>> ich denke, der eine hinweis war schon gut. nur sollte ich das ganze dann
>>> nicht PORTB nennen sondern zb NVPORTB
>
>>> #define NVPORTB (*((uint8_t*) 0x25))

das diese lösung sauber funktionert bezweife ich, dann der compiler kann 
auf die Idee kommen den wert komplett in einem register zu behalten. 
Oder aus zwei zuweisungen eine machen.

von Peter D. (peda)


Lesenswert?

Peter II schrieb:
> Oder aus zwei zuweisungen eine machen.

Vermutlich das E=1 und E=0, d.h. es erfolgt kein Puls.
Oder die Reihenfolge umsortieren (erst der E-Puls und dann die Daten).

Wegen nur 8 Befehlen würde ich mir nicht solche faulen Eier ins Nest 
legen.


Peter

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter II schrieb:

> Oder aus zwei zuweisungen eine machen.

Das war doch das Zeil der Übung. Hauptsache der Code ist optimal.
Und wenn er nicht mehr funktioniert, kann man schön über den Compiler 
schimpfen :-)

von Karl H. (kbuchegg)


Lesenswert?

Peter Dannegger schrieb:
> Peter II schrieb:
>> Oder aus zwei zuweisungen eine machen.
>
> Vermutlich das E=1 und E=0, d.h. es erfolgt kein Puls.
> Oder die Reihenfolge umsortieren (erst der E-Puls und dann die Daten).

na ja, sie hat von der Initialisierung gesprochen. (faiererweise 
angemerkt).
Vermutlich so was in der Art

   DDR_D_0 |= (1 << DB0);
   DDR_D_1 |= (1 << DB1);
   DDR_D_2 |= (1 << DB2);
   DDR_D_3 |= (1 << DB3);
   DDR_D_4 |= (1 << DB4);
   DDR_D_5 |= (1 << DB5);
   DDR_D_6 |= (1 << DB6);
   DDR_D_7 |= (1 << DB7);

und wenn diese Einzelbits jetzt alle an ein und demselben Port liegen, 
dann kann der Compiler das zusammenfassen.

> Wegen nur 8 Befehlen würde ich mir nicht solche faulen Eier ins Nest
> legen.

Würd ich auch nicht. Es sei denn ich finde nirgends mehr sonst was, wo 
ich ein paar Bytes sparen kann um das Pgm doch noch gerade so ins Flash 
zu bekommen. Aber dann würde ich es auf C Ebene selbst umschreiben.

von Simon K. (simon) Benutzerseite


Lesenswert?

Um das noch mal zu betonen: Das volatile "wegzuoptimieren" ist mMn. grob 
fahrlässig.

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.