optimierung von sbi/cbi

Gast #2684013
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?
Persönliche Seite #2684098
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.
Moderator #2684229
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.
Moderator Persönliche Seite #2684231
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. ;-)
Gast #2684287
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
Moderator Persönliche Seite #2684295
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.
#2684348
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.
#2684377
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.
Moderator Persönliche Seite #2684507
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.
#2685943
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
Gast #2686568
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.
Gast #2687409
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))
Gast #2687417
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.
#2687576
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.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren