Forum: Compiler & IDEs gcc und Optimierung


von Sam .. (sam1994)


Lesenswert?

Hallo,

ich frage mich gerade echt, was gcc eigentlich optimiert. Ich schreibe 
ein Programm für einen Prozessor (attiny), der nicht so viel Flash hat 
und der Speicher geht mir einfach flöten. Das Schlimme daran, es ist 
nicht meine falsche Prozessorwahl (höchstens knapp gewählt), sondern der 
Compiler! 10 Zeilen inline Assembler verkürzen den Flashbedarf um 
100Byte (10%), weil der Compiler nicht erkennt, dass man Variablen am 
Anfang der Funktion laden sollte und nicht zwischendurch bei jedem 
Schreibzugriff erst laden und danach sofort wieder in den SRAM 
schreiben.

Im Internet liest man immer wieder von der tollen Optmierung der 
mordernen Compiler - auf dem Avr muss man alles selber machen.


Gibts irgendwelche Tricks um das Programm klein zu halten, damit gcc 
nicht auf blöde Ideen kommt? Bzw. Ausdrücke, die man aufjedenfall 
vermeiden sollte? Ich möchte nicht mein ganzes Programm in inline 
Assembler schreiben.

von Floh (Gast)


Lesenswert?

Zeig doch mal dein Programm her. Dann wird das kein Kaffeesatzlesen.

von Bernd (Gast)


Lesenswert?

Floh schrieb:
> Zeig doch mal dein Programm her.

Genau!
Sind die Variablen vielleicht volatile?

Ich finde die Optimierung schon recht gut

von Sam .. (sam1994)


Angehängte Dateien:

Lesenswert?

Bernd schrieb:
> Sind die Variablen vielleicht volatile?

Es sind Daten im Ram. "lcd" im Code ist ein uint8_t lcd[8];

von Floh (Gast)


Lesenswert?

Erlich, das Programm kannste auch gleich in Assembler schreiben, soviel 
wie da schon an Inline-Assembler einsetzt.

Was soll denn das ganzee mit dem T-Flag?

von Sam .. (sam1994)


Lesenswert?

Floh schrieb:
> Erlich, das Programm kannste auch gleich in Assembler schreiben, soviel
> wie da schon an Inline-Assembler einsetzt.

Ich weiß, aber hier ist nichts anderes sinnvoll. Gcc hat keine 
Bitverschiebungsfunktion.

Manchmal sind kleine Inlineasm-Makros auch sehr praktisch. Z.b. 
mem_clear passt in 5 Words. Gcc macht gern das doppelte daraus, wenn man 
es in c schreibt.

> Was soll denn das ganzee mit dem T-Flag?
Das ganze ist ein Teil einer Lcd_Ansteuerungsroutine. Aber auf einer 
einseitigen Platine kann man den LcdTreiber und die Lcd-Segmente nicht 
"richtig" verbinden. Deswegen muss ich die ganzen Bits tauschen.

von Floh (Gast)


Lesenswert?

Samuel K. schrieb:
> Das ganze ist ein Teil einer Lcd_Ansteuerungsroutine. Aber auf einer
> einseitigen Platine kann man den LcdTreiber und die Lcd-Segmente nicht
> "richtig" verbinden. Deswegen muss ich die ganzen Bits tauschen.

Wie ist das ganze denn verdrahtet?

Wenn nur die Segmente vertauscht sind, lässt sich das ganze mit einer 
Tabelle mit 4 (digits) x 10 (Möglichkeiten) = 40 Einträgen erschlagen.

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


Lesenswert?

Samuel K. schrieb:
> Ich möchte nicht mein ganzes Programm in inline
> Assembler schreiben.

Hast du aber gemacht.  Wenn du jedoch ohnehin praktisch den
gesamten Assemblercode bereits vorgibst, was soll denn der
Compiler dann überhaupt noch optimieren können?

Vielleicht beschreibst du ja mal die Funktion verbal, die du
erreichen willst, und lässt dir von jemandem hier eine
Implementierung dafür vorschlagen.

von Rolf Magnus (Gast)


Lesenswert?

Wo ist in diesem Codefragment jetzt der Teil, den gcc nicht deinen 
Wünschen entsprechend optimiert und mit welcher Kommandozeile hast du 
das übersetzt?

Samuel K. schrieb:
> weil der Compiler nicht erkennt, dass man Variablen am
> Anfang der Funktion laden sollte und nicht zwischendurch bei jedem
> Schreibzugriff erst laden und danach sofort wieder in den SRAM
> schreiben.

Das habe ich bisher nur erlebt, wenn die Variable volatile ist oder der 
Optimizer gar nicht eingeschaltet war.

von Sam .. (sam1994)


Lesenswert?

Floh schrieb:
> Wenn nur die Segmente vertauscht sind, lässt sich das ganze mit einer
> Tabelle mit 4 (digits) x 10 (Möglichkeiten) = 40 Einträgen erschlagen.

Wenn du dir mal die Pinanordnungnen von Lcds anschaust (=40Pins), dann 
siehst du dass das schon extrem schwer ist.

Rolf Magnus schrieb:
> Wo ist in diesem Codefragment jetzt der Teil, den gcc nicht deinen
> Wünschen entsprechend optimiert und mit welcher Kommandozeile hast du
> das übersetzt?
Hier der Ouput von case1 (39 Words):
1
    case 1:
2
      bst(value, 2);
3
 216:  62 fb         bst  r22, 2
4
      bld(lcd[2+(digit&1)], 7);
5
 218:  e8 2f         mov  r30, r24
6
 21a:  f0 e0         ldi  r31, 0x00  ; 0
7
 21c:  e1 70         andi  r30, 0x01  ; 1
8
 21e:  f0 70         andi  r31, 0x00  ; 0
9
 220:  34 96         adiw  r30, 0x04  ; 4
10
 222:  9f 01         movw  r18, r30
11
 224:  25 59         subi  r18, 0x95  ; 149
12
 226:  3f 4f         sbci  r19, 0xFF  ; 255
13
 228:  d9 01         movw  r26, r18
14
 22a:  8c 91         ld  r24, X
15
 22c:  87 f9         bld  r24, 7
16
 22e:  8c 93         st  X, r24
17
      bst(value, 3);
18
 230:  63 fb         bst  r22, 3
19
      bld(lcd[4+(digit&1)], 0);
20
 232:  32 96         adiw  r30, 0x02  ; 2
21
 234:  df 01         movw  r26, r30
22
 236:  a5 59         subi  r26, 0x95  ; 149
23
 238:  bf 4f         sbci  r27, 0xFF  ; 255
24
 23a:  8c 91         ld  r24, X
25
 23c:  80 f9         bld  r24, 0
26
      bst(value, 4);
27
 23e:  64 fb         bst  r22, 4
28
      bld(lcd[4+(digit&1)], 1);
29
 240:  81 f9         bld  r24, 1
30
 242:  8c 93         st  X, r24
31
      bst(value, 5);
32
 244:  65 fb         bst  r22, 5
33
      bld(lcd[digit&1], 7);
34
 246:  34 97         sbiw  r30, 0x04  ; 4
35
 248:  e5 59         subi  r30, 0x95  ; 149
36
 24a:  ff 4f         sbci  r31, 0xFF  ; 255
37
 24c:  80 81         ld  r24, Z
38
 24e:  87 f9         bld  r24, 7
39
      bst(value, 6);
40
 250:  66 fb         bst  r22, 6
41
      bld(lcd[digit&1], 6);
42
 252:  86 f9         bld  r24, 6
43
 254:  80 83         st  Z, r24
44
      bcpf(value, lcd[2+(digit&1)], 0x03);
45
 256:  f9 01         movw  r30, r18
46
 258:  80 81         ld  r24, Z
47
 25a:  63 70         andi  r22, 0x03  ; 3
48
 25c:  8c 7f         andi  r24, 0xFC  ; 252
49
 25e:  86 2b         or  r24, r22
50
 260:  d9 01         movw  r26, r18
51
 262:  8c 93         st  X, r24
52
 264:  08 95         ret
53
      break;
Zum vergleich der Ouput mit dem inlineasm Code, der die Variablen vorher 
in Register lädt und danach sichert (27 Words):
1
 1e6:  ed e6         ldi  r30, 0x6D  ; 109
2
 1e8:  f0 e0         ldi  r31, 0x00  ; 0
3
 1ea:  86 95         lsr  r24
4
 1ec:  e1 1d         adc  r30, r1
5
 1ee:  f1 1d         adc  r31, r1
6
 1f0:  90 81         ld  r25, Z
7
 1f2:  32 81         ldd  r19, Z+2  ; 0x02
8
 1f4:  24 81         ldd  r18, Z+4  ; 0x04
9
 1f6:  46 81         ldd  r20, Z+6  ; 0x06
10
11
[...]
12
13
    case 1:
14
      bst(value, 2);
15
 212:  62 fb         bst  r22, 2
16
      bld(d2, 7);
17
 214:  37 f9         bld  r19, 7
18
      bst(value, 3);
19
 216:  63 fb         bst  r22, 3
20
      bld(d3, 0);
21
 218:  20 f9         bld  r18, 0
22
      bst(value, 4);
23
 21a:  64 fb         bst  r22, 4
24
      bld(d3, 1);
25
 21c:  21 f9         bld  r18, 1
26
      bst(value, 5);
27
 21e:  65 fb         bst  r22, 5
28
      bld(d1, 7);
29
 220:  97 f9         bld  r25, 7
30
      bst(value, 6);
31
 222:  66 fb         bst  r22, 6
32
      bld(d1, 6);
33
 224:  96 f9         bld  r25, 6
34
      bcpf(value, d2, 0x03);
35
 226:  63 70         andi  r22, 0x03  ; 3
36
 228:  3c 7f         andi  r19, 0xFC  ; 252
37
 22a:  36 2b         or  r19, r22
38
 22c:  19 c0         rjmp  .+50       ; 0x260 <SetDigit+0x7a>
39
      break;
40
41
[...]
42
43
 260:  90 83         st  Z, r25
44
 262:  32 83         std  Z+2, r19  ; 0x02
45
 264:  24 83         std  Z+4, r18  ; 0x04
46
 266:  46 83         std  Z+6, r20  ; 0x06

von Floh (Gast)


Lesenswert?

Samuel K. schrieb:
> Wenn du dir mal die Pinanordnungnen von Lcds anschaust (=40Pins), dann
> siehst du dass das schon extrem schwer ist.

Zeig sie her, am besten als Schaltplan.
Durch deine Bitkopiererei denk ich mich jetzt nicht durch.

von (prx) A. K. (prx)


Lesenswert?

Wie ist das Verhältnis beider Versionen, wenn du in C das gleiche 
Verfahren verwendest wie in der durch Assembler aufgeputschten Version? 
Wenn du also vorweg die 4 LCD-Bytes in d1,...d4 ziehst und hinterher 
wieder zurückschreibst - aber eben nicht in Assembler, sondern in C.

Register-Optimierung von globalen Array-Zugriffen mit berechnetem Index 
ist ein deutlich heisseres Eisen als Optimierung von lokalen skalaren 
Variablen. Da solltest du nicht erwarten, dass er von sich aus das 
Verfahren völlig umstellt, wie du es in der handoptimierten Version 
getan hast.

von Sam .. (sam1994)


Lesenswert?

Floh schrieb:
> Zeig sie her, am besten als Schaltplan.
> Durch deine Bitkopiererei denk ich mich jetzt nicht durch.

Geht grad schlecht, da ich 2 Versionen der Platine hab. Eine geätzte und 
eine auf Streifenraster, leider mit unterschiedlicher Pinbelegung und 
den Schaltplan hab ich nur vom geätzten, mit der anderen experimentiere 
ich gerade.
Aber ich kann dir die Pinbelegungen zeigen:
Treiber:
http://pdf1.alldatasheet.com/datasheet-pdf/view/18217/PHILIPS/PCF8577C/+07328UL.hKpRudTHl.DGhM+/datasheet.pdf
Lcd:
http://www.reichelt.de/index.html?;ACTION=7;LA=3;OPEN=0;INDEX=0;FILENAME=A500%252FDE113-RS-20_635.pdf;SID=26RDQypKwQARoAAFzTKDQ7e665c550ff3a600c39e5b820a0e3faa

A. K. schrieb:
> Wie ist das Verhältnis beider Versionen, wenn du in C das gleiche
> Verfahren verwendest wie in der durch Assembler aufgeputschten Version?
> Wenn du also vorweg die 4 LCD-Bytes in d1,...d4 ziehst und hinterher
> wieder zurückschreibst - aber eben nicht in Assembler, sondern in C.

gute Idee:

Von Os kann man hier trotzdem nicht sprechen. Den Code ist total 
umständlich und auf keinem Fall besser.
Aus
1
  if(digit & 0x01) //Wenn bit 0 von digit gesetzt ist, wird das 2. Lcd angesprochen
2
    lcd_addr++;
3
  digit >>=1;  
4
  d1 = lcd_addr[0];
5
  d2 = lcd_addr[2];
6
  d3 = lcd_addr[4];
7
  d4 = lcd_addr[6];
8
9
        [...]
10
11
        lcd_addr[0] = d1;
12
        lcd_addr[2] = d2;
13
        lcd_addr[4] = d3;
14
        lcd_addr[6] = d4;
wurde
1
  if(digit & 0x01)
2
 1ee:  80 fd         sbrc  r24, 0
3
 1f0:  05 c0         rjmp  .+10       ; 0x1fc <SetDigit+0x16>
4
 1f2:  90 91 6d 00   lds  r25, 0x006D
5
 1f6:  ed e6         ldi  r30, 0x6D  ; 109
6
 1f8:  f0 e0         ldi  r31, 0x00  ; 0
7
 1fa:  04 c0         rjmp  .+8        ; 0x204 <SetDigit+0x1e>
8
 1fc:  90 91 6e 00   lds  r25, 0x006E
9
 200:  ee e6         ldi  r30, 0x6E  ; 110
10
 202:  f0 e0         ldi  r31, 0x00  ; 0
11
    lcd_addr++;
12
  digit >>=1;  
13
  d1 = lcd_addr[0];
14
  d2 = lcd_addr[2];
15
 204:  8f 01         movw  r16, r30
16
 206:  0e 5f         subi  r16, 0xFE  ; 254
17
 208:  1f 4f         sbci  r17, 0xFF  ; 255
18
 20a:  32 81         ldd  r19, Z+2  ; 0x02
19
  d3 = lcd_addr[4];
20
 20c:  ef 01         movw  r28, r30
21
 20e:  24 96         adiw  r28, 0x04  ; 4
22
 210:  24 81         ldd  r18, Z+4  ; 0x04
23
  d4 = lcd_addr[6];
24
 212:  df 01         movw  r26, r30
25
 214:  16 96         adiw  r26, 0x06  ; 6
26
 216:  46 81         ldd  r20, Z+6  ; 0x06
27
28
  switch(digit)
29
 218:  86 95         lsr  r24
30
31
[...]
32
33
lcd_addr[0] = d1;
34
 282:  90 83         st  Z, r25
35
lcd_addr[2] = d2;
36
 284:  f8 01         movw  r30, r16
37
 286:  30 83         st  Z, r19
38
lcd_addr[4] = d3;
39
 288:  28 83         st  Y, r18
40
lcd_addr[6] = d4;
41
 28a:  4c 93         st  X, r20
Zum Vergleich nochmal den Asm Code:
1
asm volatile(  "lsr %[digit]        \n\t" 
2
          "adc %A[addr], __zero_reg__  \n\t" //Wenn bit 0 von digit 1 war, addr um eins erhöhen
3
          "adc %B[addr], __zero_reg__  \n\t"
4
          "ldd %[d1],%a[addr]+0    \n\t"
5
          "ldd %[d2],%a[addr]+2    \n\t"
6
          "ldd %[d3],%a[addr]+4    \n\t"
7
          "ldd %[d4],%a[addr]+6    \n\t"
8
          : [d1] "=r"(d1), [d2] "=r"(d2), [d3] "=r"(d3), [d4] "=r"(d4),
9
            [addr] "+b"(lcd_addr), [digit] "+r"(digit));
10
11
[...]
12
13
  asm volatile(  "std %a[addr]+0, %[d1]  \n\t"
14
          "std %a[addr]+2, %[d2]  \n\t"
15
          "std %a[addr]+4, %[d3]  \n\t"
16
          "std %a[addr]+6, %[d4]  \n\t"
17
          : : [d1] "r"(d1), [d2] "r"(d2), [d3] "r"(d3), [d4] "r"(d4),
18
            [addr] "b"(lcd_addr) : "memory");

Da ich inziwschen ein paar andere Funktionen umgeschrieben habe, ist der 
Gesamtcode ein bisschen kleiner. Ich habe jedoch keine Funktion, die mit 
der obrigen in Verbindung steht verändert. Die geänderten waren beim 
Compilieren auskommentiert, da sie noch nicht fertig sind.

Inline Assembler:  644 Bytes
C:                 688 Bytes

Der Unterschied ist nur noch klein, auch wenn es viel ist, dafür das es 
nur die Lade und Speicherroutinen sind.

von Volker U. (volkeru)


Lesenswert?

Samuel K. schrieb:
> Ich weiß, aber hier ist nichts anderes sinnvoll. Gcc hat keine
> Bitverschiebungsfunktion.

Bitte?!? Schonmal "<<" und ">>" probiert?

M.e. gibt es keinen Grund, Assembler zu verwenden, außer es kommt 
wirklich auf allerhöchste Optimierung bei der Geschwindigkeit an. gcc 
optimiert so hervorragend, dass Assembler im Grunde überflüssig ist.

von (prx) A. K. (prx)


Lesenswert?

Samuel K. schrieb:

> Der Unterschied ist nur noch klein, auch wenn es viel ist, dafür das es
> nur die Lade und Speicherroutinen sind.

Yep, dieser Code ist wirklich etwas schräg. Aber der Unterschied ist nun 
m.E. so klein, dass man es als Ergebnis eines Compilers akzeptieren 
kann. Falls dich das immer noch nervt, dann solltest du auf die 
Verwendung eines Compilers gleich ganz verzichten, denn Du wirst immer 
über sowas stolpern.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Um welchen C-Code geht's hier eigentlich?

Ich seh nur Code, der zu 90% aus Inline-Assembler besteht...

von (prx) A. K. (prx)


Lesenswert?

Im obigen Attachment sind beide Versionen der Funktion drin, die erste 
mit Assembler, die zweite ohne. Übersetzbar ist es nicht, da fehlt zu 
viel: http://www.mikrocontroller.net/attachment/124421/SetDigit.c

Die bld/bst Horden sind dort natürlich trotzdem drin. Er verwendet C 
eher als Gerüst für seine Assembler-Inlines, könnte man eine 
strukturierte Assembler-Programmierung nennen.

von Volker U. (volkeru)


Lesenswert?

Samuel K. schrieb:
> Gibts irgendwelche Tricks um das Programm klein zu halten, damit gcc
> nicht auf blöde Ideen kommt? Bzw. Ausdrücke, die man aufjedenfall
> vermeiden sollte? Ich möchte nicht mein ganzes Programm in inline
> Assembler schreiben.

Auch wenn das im vorliegenden Fall keine Rolle spielt, gibt es zwei ganz 
entscheidende Dinge, auf die man bei Microcontrollern ohne FPU 
verzichten sollte: Jegliche Fließkommaoperationen! Diese blähen den Code 
gewaltig auf und sind eigentlich fast immer überflüssig. Außerdem 
sollten bei entsprechenden Controllern (z.B. AVR) String-Konstanten mit 
"PROGMEM" oder PSTR() erzeugt werden, damit sie während der Laufzeit 
nicht ins RAM kopiert werden. Beides ist extrem speicherschonend.

von Sam .. (sam1994)


Angehängte Dateien:

Lesenswert?

Volker U. schrieb:
> Bitte?!? Schonmal "<<" und ">>" probiert?
>
> M.e. gibt es keinen Grund, Assembler zu verwenden, außer es kommt
> wirklich auf allerhöchste Optimierung bei der Geschwindigkeit an. gcc
> optimiert so hervorragend, dass Assembler im Grunde überflüssig ist.

Wenn man bits kopieren möchte (verschieben war ein schlechter Ausdruck), 
sind die HW-Instructions bst und bld am geeignesten. Ich glaube nicht 
dass gcc sie je einsetzt.

A. K. schrieb:
> Im obigen Attachment sind beide Versionen der Funktion drin, die erste
> mit Assembler, die zweite ohne. Übersetzbar ist es nicht, da fehlt zu
> viel: http://www.mikrocontroller.net/attachment/124421/SetDigit.c

Stimmt, ich hatte vergessen ein Makro hinzuzufügen. Hier mit 
Projektdatei im Avrstudio4, im default Ordner ist auch das Makefile 
erhalten.

> Die bld/bst sind dort natürlich trotzdem drin. Er verwendet C eher als
> Gerüst für seine Assembler-Inlines.

Ich hab nicht so viel Flash, aber ich muss noch ein bisschen 
kompliziertere Logik rein, deswegen optimiere ich lieber die einfacheren 
Sachen von Hand - sonst könnte ich gleich in Assembler schreiben.



Volker U. schrieb:
> Jegliche Fließkommaoperationen!
Keine Sorge, auf solche Ideen komme ich nicht. Dafür wollte ich vor 2 
Jahren mal einen 64bit Integer verwenden, das kostet KB-weise Code.

Volker U. schrieb:
> damit sie während der Laufzeit
> nicht ins RAM kopiert werden.

Kann  man avrgcc dazu zwingen entweder allgemein oder speziell verbieten 
Variablen zu initialisieren?


PS: Ich verwende inline Assembler hier nicht um auf Geschwindigkeit zu 
optimieren, es geht mir allein um die Größe. Im Moment habe ich noch 1KB 
Flash frei, sollte es nicht reichen, bekomme ich noch 300B durch den 
Bootloader.

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


Lesenswert?

Samuel K. schrieb:

> Wenn man bits kopieren möchte (verschieben war ein schlechter Ausdruck),
> sind die HW-Instructions bst und bld am geeignesten. Ich glaube nicht
> dass gcc sie je einsetzt.

Das mit dem Glauben solltest du besser den Kirchen überlassen, die
sind darauf spezialisiert. ;-)
1
$ egrep -i 'bst|bld' *
2
avr-protos.h:extern void avr_output_bld (rtx operands[], int bit_nr);
3
avr.c:#include "obstack.h"
4
avr.c:                  avr_output_bld (operands, bit_nr);
5
avr.c:                  avr_output_bld (operands, bit_nr);
6
avr.c:                  avr_output_bld (operands, bit_nr);
7
avr.c:                    AS2 (bld,%3,%2-1)));
8
avr.c:                AS2 (bld,r1,5)   CR_TAB
9
avr.c:    return (AS2 (bst,%0,6)  CR_TAB
10
avr.c:            AS2 (bld,%0,0));
11
avr.c:                AS2 (bld,r1,3)   CR_TAB
12
avr.c:             The scratch register is substituted for real move.  */
13
avr.c:  int hadbst = 0, hadlsl = 0;
14
avr.c:      /* To avoid using bst and bld if the source and
15
avr.c:          sprintf (buf, "bst r%d, 7", rsource + tlen[1] - 1);
16
avr.c:          hadbst = 1;
17
avr.c:      if (hadbst)
18
avr.c:          sprintf (buf, "bld r%d, 7", rdest + tlen[0] - 1);
19
avr.c:avr_output_bld (rtx operands[], int bit_nr)
20
avr.c:  static char s[] = "bld %A0,0";
21
avr.h:#define LIBSTDCXX "-lgcc"
22
avr.h:/* No libstdc++ for now.  Empty string doesn't work.  */
23
avr.md: clt\;bld %D0,7"
24
avr.md: bst %D0,7\;com %D0\;bld %D0,7\;com %D0"
25
libgcc-fixed.S:        bst     r_res, 7
26
libgcc-fixed.S:        bst     r_resL, 7
27
libgcc-fixed.S:        bst     r_resL, 7
28
libgcc-fixed.S:        bst     r_resL, 7
29
libgcc.S:        bst     r_arg1,7       ; store sign of dividend
30
libgcc.S:        bst     r_arg1H,7      ; store sign of dividend
31
libgcc.S:        bst     r_arg1HH,7     ; store sign of dividend

> Ich hab nicht so viel Flash, aber ich muss noch ein bisschen
> kompliziertere Logik rein, deswegen optimiere ich lieber die einfacheren
> Sachen von Hand

Falsche Voraussetzungen.  Für die Entwicklungsarbeit beginnt man
einfach mit einem größeren Controller.  Optimiert wird am Ende,
und dann kann man auch sehen, ob es sich lohnt, hie oder da mit
ein wenig inline asm so weit nachzuhelfen, dass man auf diese Weise
auf den nächst kleineren Controller gehen kann.  Am Anfang sollte
man einfach erstmal mit dem Projekt so weit zu Potte kommen, dass
es die gewünschten Ergebnisse bringt, ohne dass man an allen Ecken
und Enden mit dem Ressourcenverbrauch geizen muss.  Zum Debuggen
braucht man schnell auch mal paar Strings mehr, weil sie einem das
Entwicklerleben einfacher machen.

von (prx) A. K. (prx)


Lesenswert?

Samuel K. schrieb:

> Kann  man avrgcc dazu zwingen entweder allgemein oder speziell verbieten
> Variablen zu initialisieren?

Speziell: ja, es gibt ein Attribut um sie in eine Sektion für nicht zu 
initialisierende Variablen zu bringen. Steht wohl irgendwo in der Doku 
der avr-libc.

Das Spielt beim Platz aber keine Rolle, denn ob eine Variable implizit 
mit 0 initialisiert wird, oder überhaupt nicht, ist neutral. Nur 
explizite Initialisierungen kosten Flash.

von Sam .. (sam1994)


Lesenswert?

Jörg Wunsch schrieb:
> Falsche Voraussetzungen.  Für die Entwicklungsarbeit beginnt man
> einfach mit einem größeren Controller.  Optimiert wird am Ende,
> und dann kann man auch sehen, ob es sich lohnt, hie oder da mit
> ein wenig inline asm so weit nachzuhelfen, dass man auf diese Weise
> auf den nächst kleineren Controller gehen kann.  Am Anfang sollte
> man einfach erstmal mit dem Projekt so weit zu Potte kommen, dass
> es die gewünschten Ergebnisse bringt, ohne dass man an allen Ecken
> und Enden mit dem Ressourcenverbrauch geizen muss.  Zum Debuggen
> braucht man schnell auch mal paar Strings mehr, weil sie einem das
> Entwicklerleben einfacher machen.

Ich weiß, allersdings, seit ich Assembler programmiere merkte ich 
wieviel mit effizienter Programmierung möglich ist. Außerdem: Ein Inline 
Asm Schnipsel kostet mich 2 Minuten, ein größerer Avr Geld (und als 
Schüler hat man vom ersten mehr als genug).

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

A. K. schrieb:
> Im obigen Attachment sind beide Versionen der Funktion drin, die erste
> mit Assembler, die zweite ohne. Übersetzbar ist es nicht, da fehlt zu
> viel: http://www.mikrocontroller.net/attachment/124421/SetDigit.c

@Samuel: Ist es möglich, das als übersetzbaren C-Code zu bekommen?

Wenn es um Compiler-Optimierungen geht, dann ist Inline-Assembler als 
Beispiel-Code nicht zielführend.

Wenn du dich über schlechten Code vom Compiler beschwerst, dann bitte 
ein Beispiel in dem der Compiler schlechten Code macht und nicht eines, 
wo das — wie auch immer — umgangen wird.

> Die bld/bst Horden sind dort natürlich trotzdem drin. Er verwendet C
> eher als Gerüst für seine Assembler-Inlines, könnte man eine
> strukturierte Assembler-Programmierung nennen.

Ja, sieht so aus...

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


Lesenswert?

Samuel K. schrieb:

> Ich weiß, allersdings, seit ich Assembler programmiere merkte ich
> wieviel mit effizienter Programmierung möglich ist.

Wenn dem so wäre, hätte niemand Compiler erfunden.  Kann sein, dass
du damit geringfügig kleineren Code erzeugen kannst (das bestreitet
ohnehin keiner), aber er ist am Ende schwerer wartbar.

> Außerdem: Ein Inline
> Asm Schnipsel kostet mich 2 Minuten, ein größerer Avr Geld (und als
> Schüler hat man vom ersten mehr als genug).

Die 3 Euro für den nächstgrößeren Controller schick ich dir noch im
Brief zu, wenn du sonst keinen Sponsor dafür findest. ;-)  Wir
reden hier ja nicht über den Ersatz durch einen Controller mit
256 KiB Flash und was weiß ich, sondern darüber, dass man für den
Anfang eben besser einen ATmega168 oder sowas nimmt, damit man nicht
gleich gegen die Gummiwand "Flash-Größe" rennt beim ersten Anlauf.

Eine alte Programmiererweisheit besagt: "Never start optimizing before
you have profiled it.", oder "Premature optimization is the root of
all evil."

Wenn deine Projekte noch so übersichtlich sind, dass dies alles in
Assembler klaglos machbar ist, dann lass den inline-Asembler
beiseite (der hat andere Aufgaben), pack' die entsprechenden paar
Routinen in separate, handgefeilte Assemblerdateien, und schreib
das Framework ringsrum in C.  Die Parameterkonventionen für den
Aufruf von Assemblerroutinen aus C heraus sind ja nun nicht gerade
das best gehütete Geheimnis der Welt.

von Floh (Gast)


Lesenswert?

Noch so als Beispiel für Bitstauschen, das relativ gut übersetzt werden 
dürfte:
1
//bit reverse als beispiel
2
3
uint8_t source = 34; //Quelle
4
uint8_t des = 0; //Ziel
5
6
if(source & (1<<0))
7
  des |= (1<<7);
8
if(source & (1<<1))
9
  des |= (1<<6);
10
if(source & (1<<2))
11
  des |= (1<<5);
12
if(source & (1<<3))
13
  des |= (1<<4);
14
if(source & (1<<4))
15
  des |= (1<<3);
16
if(source & (1<<5))
17
  des |= (1<<2);
18
if(source & (1<<6))
19
  des |= (1<<1);
20
if(source & (1<<7))
21
  des |= (1<<0);
Dürfte recht kompakt übersetzt werden, da die Einzelbitzugriffe als 
Maschinenbefehle existieren (sbrs, sbr) und sie vom Compiler (avr gcc) 
auch verwendet werden.

Allerdings wärs trotzdem mal sinvoll, die Pinbelegung herzuzeigen.
Denn mit angepasster Tabelle wird das ganze nochmal viel kompakter.
:-)

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Floh schrieb:

> Dürfte recht kompakt übersetzt werden, [...]

Ja. Folgender Code (Funktion wird gebraucht, damit es compilierbar ist)
1
uint8_t swap_bits_34 (void)
2
{
3
    uint8_t source = 34;
4
    uint8_t des = 0; //Ziel
5
6
    if (source & (1<<0))
7
        des |= (1<<7);
8
    if (source & (1<<1))
9
        des |= (1<<6);
10
    if (source & (1<<2))
11
        des |= (1<<5);
12
    if (source & (1<<3))
13
        des |= (1<<4);
14
    if (source & (1<<4))
15
        des |= (1<<3);
16
    if (source & (1<<5))
17
        des |= (1<<2);
18
    if (source & (1<<6))
19
        des |= (1<<1);
20
    if (source & (1<<7))
21
        des |= (1<<0);
22
23
    return des;
24
}
wird zu mit -Os zu
1
swap_bits_34:
2
  ldi r24,lo8(68)
3
  ret
Hier ist wohl nicht mehr viel zu holen...

Wenn source als uint8_t Parameter in die Funktion kommt wird es zu
1
swap_bits:
2
  mov r25,r24
3
  sbrs r24,0
4
  rjmp .L10
5
  ldi r24,lo8(-128)
6
  rjmp .L2
7
.L10:
8
  ldi r24,lo8(0)
9
.L2:
10
  sbrc r25,1
11
  ori r24,lo8(64)
12
.L3:
13
  sbrc r25,2
14
  ori r24,lo8(32)
15
.L4:
16
  sbrc r25,3
17
  ori r24,lo8(16)
18
.L5:
19
  sbrc r25,4
20
  ori r24,lo8(8)
21
.L6:
22
  sbrc r25,5
23
  ori r24,lo8(4)
24
.L7:
25
  sbrc r25,6
26
  ori r24,lo8(2)
27
.L8:
28
  sbrc r25,7
29
  ori r24,lo8(1)
30
.L9:
31
  ret
Am Anfang könnten 2 Befehle gespart werden, aber wirklich schlecht ist 
der Code doch nicht?

ALSO UM WELCHEN CODE GEHT ES?

von Floh (Gast)


Lesenswert?

Johann L. schrieb:
> Hier ist wohl nicht mehr viel zu holen...

Klar. War eigentlich gedacht, den Sourcewert variabel zu machen :-)

Aber der Code, der dabei rauskommt ist optimal im Bezug auf Laufzeit und 
Codegröße für beliebiges tauschen.
Hier ist der Compiler sehr gut angepasst.
Und das ganze ist sogar lesbar und in C.
:-)

von Sam .. (sam1994)


Lesenswert?

Ich hab den Code oben in der zip datei gepostet.

Bei meinem Problem kommt halt hinzu, dass es 4 Bytes sind in denen Bits 
getauscht werden müssen. Zudem maximal 8 gleichzeitig. Also schnell mal 
den Buffer löschen geht nicht. Bei Flohs Routine müsste man erstmal alle 
relevanten Bits löschen.

von Floh (Gast)


Lesenswert?

Samuel K. schrieb:
> Bei Flohs Routine müsste man erstmal alle
> relevanten Bits löschen.
Quark. Bits im Register oder direkt auf dem Port machen beim AVR keinen 
Unterschied.

Einfach so was:

uint8_t digit = 45; //deine Information
PORTD = 0; //Alles löschen

if(digit & (1<<2))
  PORTD |= (1<<7);

...
Sind für 4 Byte auch nur 32 solche Konstrukte, die 64 Assemblerbefehle 
ergeben. Pinbelegung ist vollständig frei wählbar.

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


Lesenswert?

Samuel K. schrieb:
> Ich hab den Code oben in der zip datei gepostet.

Das ist doch aber nach wie vor das verwürgte inline-asm-Geraffel.

Wir wollten den C-Code sehen, der deiner Meinung nach zu unpraktikabel
großen Code generieren würde, denn durch dieses inline-asm-Zeug steigt
kein Schwein durch, und du kannst von keinem Compiler der Welt
erwarten, dass er bei bereits zu 90 % hart vorgegebenem Assemblercode
dann auch noch was dran optimieren könnte.

Floh schrieb:
> PORTD = 0; //Alles löschen
>
> if(digit & (1<<2))
>   PORTD |= (1<<7);

Hier tauchen die Bits aber der Reihe nach einzeln auf PORTD auf.  Kann
für den vorliegenden Fall egal sein, wird aber nicht immer das sein,
was gewünscht ist.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Samuel K. schrieb:
> Ich hab den Code oben in der zip datei gepostet.

Johann L. schrieb:

> Wenn es um Compiler-Optimierungen geht, dann ist Inline-Assembler als
> Beispiel-Code nicht zielführend.
>
> Wenn du dich über schlechten Code vom Compiler beschwerst, dann bitte
> ein Beispiel in dem der Compiler schlechten Code macht und nicht eines,
> wo das — wie auch immer — umgangen wird.

Und wo ist da jetzt schlechter Code für Bitmanipulation???

Das ist doch alles Inline-Assembler!

Manno, dauern über Compiler mosern und nörgeln aber noch nichma ein 
gscheites Beispielprogramm hinschreiben können oder erklären worum es 
geht.

NEIN, DER COMPILER WIRD KEINEN INLINE-ASSEMBLER-CODE OPTIMIEREN

von Sam .. (sam1994)


Lesenswert?

Er soll keinen Inline-Assembler Code optimieren. Aber: In meinem Code, 
egal ob ihr in versteht oder nicht, werden je nach Fall Bits getauscht. 
Das Tauschen ist inline Assembler, das soll er auch nicht ändern, aber 
wenn man nicht explizit die Variablen aus dem Ram in die Register holt, 
holt er sie immer vor dem Schreibvorgang raus und schreibt sie danach 
wieder rein.

Der Compiler macht aus:

Kopiere in SRAM Bit
Kopiere in SRAM Bit

das:

Lade Byte
Kopiere Bit
Speichere Byte

Lade Byte
Kopiere Bit
Speichere Byte

wobei die Bytes dieselben sind. Natürlich macht er das nicht direkt 
hintereinander, es kommt jedoch vor.



Floh schrieb:
> Samuel K. schrieb:
>> Bei Flohs Routine müsste man erstmal alle
>> relevanten Bits löschen.
> Quark. Bits im Register oder direkt auf dem Port machen beim AVR keinen
> Unterschied.
Nichts Quark:
> Einfach so was:
>
> uint8_t digit = 45; //deine Information
> PORTD = 0; //Alles löschen
Damit löscht du mir alle Bits
> if(digit & (1<<2))
>   PORTD |= (1<<7);
Bit 7 wird gesetzt, Bit 0:6 sind verloren gegangen.
> ...
> Sind für 4 Byte auch nur 32 solche Konstrukte, die 64 Assemblerbefehle
> ergeben. Pinbelegung ist vollständig frei wählbar.
+ 4 mal Löschen per andi nicht durch Zuweisung.

bst und bld brauchen 64 Assembler-Befehle.

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


Lesenswert?

Samuel K. schrieb:
> Aber: In meinem Code,
> egal ob ihr in versteht oder nicht, werden je nach Fall Bits getauscht.

Dann schreib' diesen vermaledeiten Code endlich mal als C auf.
Danach diskutieren wir über die Optimierung des Compilers.

Niemand wird sich hinstellen und den Compiler auf genau deinen
pathologischen Fall optimieren, dass 80 ... 90 % der Arbeit durch
den Inline-Assembler vorgegeben werden, er aber bitteschön den
Rest jetzt noch mit zwei Befehlen weniger hinbekommen soll, weil
du das im Assembler ja auch geschafft hättest.

Siehe oben: wenn du Assembler programmieren willst, dann schreib
eine Assembler-Quelldatei und lass den Compiler aus dem Spiel.  Wenn
du C programmieren willst, dann schreib eine C-Quelldatei.  Nein,
"asm" ist kein Schlüsselwort der Programmiersprache C, das ist
erst einmal komplett tabu.

von Sam .. (sam1994)


Lesenswert?

Gerne würde ich es extern auslagern, nur finde ich kein gutes Tutorial 
das sagt wie man auf externe Variablen zugreift, wie man Funktionen und 
deren Argumente bzw. Rückgabewerte definiert...

Allerdings hat man dann den Nachteil, dass die Register fest definiert 
sind und nicht vom Compiler passend gewählt werden können.

Ich finde wenn man C mit inline Assembler schreibt, kann man nebenbei 
optimieren und gleichzeitig übersichtlich schreiben. In Assembler reicht 
eine kleiner Fehler und man sucht sich tot.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Du meinst den Fall mit digitfunc == 2?
Oder digitfunc == 1?
Oder den else-Fall?
Oder alle?

von Sam .. (sam1994)


Lesenswert?

Ja ich meine digitfunc == 2.

Bei digitfunc == 1 wurde es durch inline asm behoben. Bei digitfunc > 2 
durch das gleiche in C.

von Rolf Magnus (Gast)


Lesenswert?

In deinen inline-Assembler-Teilen sind übrigens die Constraints falsch. 
Das könnte den Compiler auch bei der Optimierung behindern.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Samuel K. schrieb:
> Ja ich meine digitfunc == 2.

Aber da stehen doch Steicherstellen wie
1
bld (lcd[2+(digit&1)], 7);
 in einem volatile inline assembler und die Constraint ist "+r". Das 
muss doch gelesen und geschrieben werden.

bcpf ist übrigens falsch. Es muss heissen:
1
#define bcpf(source, dest, mask)                    \
2
    asm ("andi %[src], %[msk]      \n\t"            \
3
         "andi %[dst], ~%[msk]     \n\t"            \
4
         "or   %[dst], %[src]"                      \
5
         : [dst] "+d" (dest), [src] "+d" (source)   \
6
         : [msk] "M" (mask))
Inline-Assembler ist noch fehleranfälliger als Assembler, da man die 
Fehler von Assembler und die des Inline-Assembler Interfaces machen 
kann.

> Bei digitfunc > 2 durch das gleiche in C.

Nein. Für digitfunc > 2 steht auch alles in Inline-Assembler.

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


Lesenswert?

Samuel K. schrieb:
> Gerne würde ich es extern auslagern, nur finde ich kein gutes Tutorial
> das sagt wie man auf externe Variablen zugreift, wie man Funktionen und
> deren Argumente bzw. Rückgabewerte definiert...

Braucht man denn heutzutage für alles 'n Tutorial?

Die Konventionen für die Parameterübergabe stehen in der avr-libc-
FAQ, außerdem gibt's bei der avr-libc ein Beispielprojekt, bei dem
eine ISR in Assembler realisiert worden ist, der Rest in C.

Globale Variablen kann man, wenn man freundlich sein will, seinem
Assembler als .extern kund tun.  Muss man aber nicht, denn alle
Symbolnamen, die der Assembler nicht kennt, trägt er in der Symbol-
tabelle automatisch als `global undefined' ein, um sie durch den
Linker auflösen zu lassen.

Exportierte Symbole muss man als .global deklarieren, alle anderen
Symbole sind lokal in der Assemblerdatei und werden nicht in die
Symboltabelle des Objektmoduls übernommen.  In der Regel wird man
nur die Funktionseintrittspunkte in der Assemblerdatei exportieren
wollen.

> Ich finde wenn man C mit inline Assembler schreibt, kann man nebenbei
> optimieren und gleichzeitig übersichtlich schreiben.

Ich finde nicht, dass da noch irgendwas wirklich übersichtlich ist.
Ich würde das C-Programm komplett anders aufziehen.  Da du den von
dir gewünschten Algorithmus aber nicht formal (oder durch ein
einfaches C-Programmstück) beschreiben kannst oder willst, ist dir
in dieser Hinsicht auch nicht zu helfen.  Nicht nur ich bin immer
noch der Meinung, dass deine fest vorgefasste Idee, man müsse diese
Bitwurschtelei partout über BST und BLD implementieren, keineswegs
notwendigerweise das Optimum darstellt.

> In Assembler reicht
> eine kleiner Fehler und man sucht sich tot.

Das ist bei deinem Inline-Assemblercode nicht anders.  Schon allein,
die Constraints ordentlich auf die Reihe zu bringen, ist nicht immer
ganz einfach, und oftmals merkst du die Fehler nicht sofort, weil
der Compiler halt je nach Randbedingungen u. U. völlig anders agiert.

von Sam .. (sam1994)


Lesenswert?

Johann L. schrieb:
> Aber da stehen doch Steicherstellen wiebld (lcd[2+(digit&1)], 7); in einem 
volatile inline assembler und die Constraint ist "+r". Das
> muss doch gelesen und geschrieben werden.

Naja, +r heißt doch nur, dass es sowohl input als auch output ist. Der 
Compiler hat doch die Freiheit sich ein paar Register zu schnappen und 
die Variablen zu puffern.

Danke für die Korrektur, ich dachte wenn das Register nicht mehr 
weiterverwendet wird, darf man es als nicht output angeben.

Jörg Wunsch schrieb:
> Nicht nur ich bin immer
> noch der Meinung, dass deine fest vorgefasste Idee, man müsse diese
> Bitwurschtelei partout über BST und BLD implementieren, keineswegs
> notwendigerweise das Optimum darstellt.

Ja ich bin festüberzeugt, dass es nicht schneller als 2 Takte/bit geht.

Ich habe ein 3-1/2 Lcd. Der Treiber hat 8Byte Ram für 64 Segmente. Jetzt 
müssen (da die Leitungen nicht "richtig" geroutet sind, fast alle Bits 
getauscht werden um die einzelnen Segmente anzusteuern.

Wenn du folgendes (4bit) auf 32bit skalierst siehst du was umsortiert 
werden muss.
cbda -> abcd

Ich habe mir auch schon überlegt, das ganze mit irgendeinem kleinen 
hin-und herschieben aufzulösen, aber man hat keine Chance. Selbst eine 
Tabelle welche die Zuordnungen beschreibt, wäre viel zu groß: 10bit/Byte 
+ Code (der Code wird sicher größer als 6bit*21 Segmente=16Byte=8Words).

Ich hoffe man hat das verstanden. Jetzt bin ich gespannt. Ich bin immer 
offen für kleiner Lösungen. Das EEprom ist schon reserviert.

Jörg Wunsch schrieb:
>> Ich finde wenn man C mit inline Assembler schreibt, kann man nebenbei
>> optimieren und gleichzeitig übersichtlich schreiben.
>
> Ich finde nicht, dass da noch irgendwas wirklich übersichtlich ist.

Ich finde es ist übersichtilicher als Assembler, besonders dann wenn man 
die Makros per #define in eine Headerdatei steckt.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Samuel K. schrieb:
> Johann L. schrieb:
>> Aber da stehen doch Steicherstellen wiebld (lcd[2+(digit&1)], 7);
>> in einem volatile inline assembler und die Constraint ist "+r".
>> Das muss doch gelesen und geschrieben werden.
>
> Naja, +r heißt doch nur, dass es sowohl input als auch output ist. Der
> Compiler hat doch die Freiheit sich ein paar Register zu schnappen und
> die Variablen zu puffern.

Nein. Es ist volatile.

von Floh (Gast)


Lesenswert?

Samuel K. schrieb:
> Ich habe ein 3-1/2 Lcd. Der Treiber hat 8Byte Ram für 64 Segmente. Jetzt
> müssen (da die Leitungen nicht "richtig" geroutet sind, fast alle Bits
> getauscht werden um die einzelnen Segmente anzusteuern.
>
> Wenn du folgendes (4bit) auf 32bit skalierst siehst du was umsortiert
> werden muss.
> cbda -> abcd

Ich wage noch einen Versuch:
Beschreib doch bitte mal genau die Position der Segmente in den Bytes, 
also vielleicht in der Form:

Byte 1:
1a 1c 1b 1d 1e ...
Byte 2:
2a 2d 2f ...

Dann lässt sich abschätzen, obs eine Tabelle bringt. :-)
PS.: Und am besten mal einen Kauflink auf den TreiberIC, alldatasheets 
ist anscheinend offline.

von Rolf Magnus (Gast)


Lesenswert?

Samuel K. schrieb:
> Johann L. schrieb:
>> Aber da stehen doch Steicherstellen wiebld (lcd[2+(digit&1)], 7); in einem
> volatile inline assembler und die Constraint ist "+r". Das
>> muss doch gelesen und geschrieben werden.
>
> Naja, +r heißt doch nur, dass es sowohl input als auch output ist.

Es ging darum, daß die asm-Anweisung volatile ist und das den Zugriff 
erzwingt. Warum ist die überhaupt volatile? Wenn die Constraints richtig 
definiert sind, ist das unnötig.

Ein input-Operand muß übrigens laut Doku auch zwingend in der 
Input-Liste stehen, was er bei dir gar nicht tut.

von Volker U. (volkeru)


Lesenswert?

Samuel K. schrieb:
> Kann  man avrgcc dazu zwingen entweder allgemein oder speziell verbieten
> Variablen zu initialisieren?

Deklarierte Variablen werden NIEMALS intialisiert! Eine Variable hat 
nach der Deklaration einen undefinierten Wert (nämlich den, der an der 
Speicherstelle zufällig gerade stand). Erst nach der Initialisierung hat 
sie einen definierten Wert.

Beispiel:
1
unsigned char c;
c enthält einen undefinierten Wert zwischen 0 und 255.
1
unsigned char c = 0;
c enthält den Wert 0.

Gruß, Volker

von Sam .. (sam1994)


Lesenswert?

Floh schrieb:
> Dann lässt sich abschätzen, obs eine Tabelle bringt. :-)
> PS.: Und am besten mal einen Kauflink auf den TreiberIC, alldatasheets
> ist anscheinend offline.

Ich hab ihn bei reichelt gekauft, doch Angelika hat ihn aus dem Programm 
genommen (leider). Bei mir sieht der Link gut aus. Hier nochmal die 
Links: Beitrag "Re: gcc und Optimierung"

Byte 1: + ,BAT, 2f, 2a, 2b, 3g, 1g, 1f
Byte 2: a1, b1, : , 2g, 2d, 2e,dp2, 1c
Byte 3: 1d, 1e,dp0,   , 3a, 3f, K , -
Byte 4: 3b, 3c, 3d, 3e,   ,   ,dp1, 2c

+,-,:,BAT,K sind andere Zeichen. K steht für die führende 1 aus dem Lcd.

Rolf Magnus schrieb:
> Ein input-Operand muß übrigens laut Doku auch zwingend in der
> Input-Liste stehen, was er bei dir gar nicht tut.

Sie stehen immer in der Input-Liste, außer sie sind sowohl input als 
auch output (+).

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:

> Warum ist die überhaupt volatile? Wenn die Constraints richtig
> definiert sind, ist das unnötig.

Nö, nicht so wie es momentan dasteht: T wird gesetzt und gelesen.

Aber auch mit volatile ist das falsch, denn obwohl die asms volatile 
sind, kann der Compiler T verändern. Das T-Flag wird genauso verwendet 
wie __tmp_reg__ aka. R0.

Die einzig korrekte Weise, mit T umzugehen, ist die beiden Makros zu 
einem zu kombinieren.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Volker U. schrieb:
> Deklarierte Variablen werden *NIEMALS* intialisiert! Eine Variable hat
> nach der Deklaration einen undefinierten Wert (nämlich den, der an der
> Speicherstelle zufällig gerade stand).

Das gilt nur für lokale auto-Variablen.

Variablen im static Storage (egal ob lokal oder global) werden zu 0 
initialisiert.

von Floh (Gast)


Lesenswert?

Samuel K. schrieb:
> Byte 1: + ,BAT, 2f, 2a, 2b, 3g, 1g, 1f
> Byte 2: a1, b1, : , 2g, 2d, 2e,dp2, 1c
> Byte 3: 1d, 1e,dp0,   , 3a, 3f, K , -
> Byte 4: 3b, 3c, 3d, 3e,   ,   ,dp1, 2c

Das nenn ich mal Anarchie :-)
Tabelle scheidet also klar aus.
Allerdings wär da noch mein Vorschlag mit dem Setzen der Bits abhängig 
von einem anderen Bit.

Als Input bekommst du 4 8-Bit-Werte (deine Digitstellen).
Du richtest dir 4 leere Byte ein (temporär) und setzt dann je nach Bit 
im Input die Bits im dem Zwischenspeichere wieder.

Also in der Richtung:

if(digit0 & (1<<0)) //a1 gesetzt?
  byte2 |= (1<<a1);
if(digit0 & (1<<1)) //b1 gesetzt?
  byte2 |= (1<<b1);
if(digit0 & (1<<2)) //c1 gesetzt?
  byte2 |= (1<<b1);
...

Eine if-Abfrage pro möglichem Bit, ergibt also bei 32 Bit insgesamt 64 
Befehle (128 Byte Flash) + ein paar Bytes Initialisierung.
Schneller geht es meiner Meinung nach nicht.
Dann musst du byte1 - byte4 nur noch über i2c übertragen. Voila.
:-)

von Volker U. (volkeru)


Lesenswert?

Johann L. schrieb:
> wird zu mit -Os zu
>
1
> swap_bits_34:
2
>   ldi r24,lo8(68)
3
>   ret
> Hier ist wohl nicht mehr viel zu holen...

Och, du bist aber jetzt gemein. Mit einer Variablen statt 34 sieht es 
nicht mehr ganz so wunderschön aus, wie du schon selbst schreibst ;).

Dein Beispiel ist aber sehr schön um zu zeigen, wie gut gcc optimiert. 
Genauso arbeitet gcc übrigens mit Fließkommaoperationen: Der Precompiler 
optimiert die wunderbarsten Berechnungen weg, sofern dabei nur 
Konstanten benutzt werden.

Und mal ganz allgemein zum alten Disput Assembler vs. Compiler:

Die Sprache C wurde extra so maschinennah gestaltet, damit sie perfekt 
optimiert werden kann. Anfang der 70er Jahre war jedes Byte im Computer 
noch so wertvoll wie heute im Mikrocontroller. Die MC-Programmierung 
führt uns heute deshalb wieder zurück zu den Ursprüngen, was ich sehr 
schön finde. Es gibt wohl keine andere Sprache, die so übersichtlich und 
gleichzeitig so hoch optimierbar ist, wie C. Deshalb liebe ich sie auch 
so. Und um mit Boltzmann zu sprechen: "Wüssten wir nicht, dass sie von 
Dennis Ritchie erfunden wurde, könnten man denken, ein Gott hätte sie 
geschaffen" lol. Naja, Boltzmann hatte das zwar mehr in Bezug auf die 
Maxwellschen Gleichungen gemeint, aber es passt trotzdem schön ;-).

Wie schon erwähnt wurde, sollte ein Gerüst immer aus hochsprachlichem 
Code bestehen und nur wenige, zeitkritische Routinen sollten dann als 
separate, externe Assembler-Files eingebunden werden. Sonst findet man 
meist schon nach wenigen Wochen durch seinen eigenen Code nicht mehr 
durch.

Man sollte sich guten Programmierstil von Anfang an angewöhnen.

Gruß, Volker

von Volker U. (volkeru)


Lesenswert?

Johann L. schrieb:
> Volker U. schrieb:
>> Deklarierte Variablen werden *NIEMALS* intialisiert! Eine Variable hat
>> nach der Deklaration einen undefinierten Wert (nämlich den, der an der
>> Speicherstelle zufällig gerade stand).
>
> Das gilt nur für lokale auto-Variablen.
>
> Variablen im static Storage (egal ob lokal oder global) werden zu 0
> initialisiert.

Aha!? Ist das eine Spezialität von gcc oder bestimmter Controller? Laut 
Sprach-Definition ist eine statische Variable anfänglich ebenso 
undefiniert, wie eine dynamische.

von (prx) A. K. (prx)


Lesenswert?

Volker U. schrieb:

> Aha!? Ist das eine Spezialität von gcc oder bestimmter Controller?

Das ist eine Spezialität der Sprache C und hat alle Versionen überlebt.

> Laut Sprach-Definition ist eine statische Variable anfänglich ebenso
> undefiniert, wie eine dynamische.

Definitiv falsch, was statische/globale angeht. Korrekt nur für "auto", 
wie Johann schon schrieb.

von Martin (Gast)


Lesenswert?

Volker U. schrieb:
> Die Sprache C wurde extra so maschinennah gestaltet, damit sie perfekt
> optimiert werden kann.

Haha.

Pointeraliasing führt schonmal dazu, daß nix optimiert wird.

Und maschinennah? Du kannst in portablem C nicht mal das Speicherlayout 
einer Struktur oder eines Bitfeldes beschreiben.

C ist relativ ausdrucksschwach, stellt also dem Compiler diverse 
Informationen nicht bereit, die er für Optimierungen nutzen könnte.

Die Sprache wurde so gestaltet wie sie ursprünglich war, um einfach 
einen für damalige Verhältnisse recht effizienten Compiler 
zusammenstricken zu können.

Schau dir doch mal an, was Ada-Compiler so optimieren können. Oder wie 
Ocaml performancemäßig an C rankommt, obwohl die Sprache viel 
high-leveliger ist und der komplette Ocaml-Compiler kleiner ist als der 
SSA-Optimierer im gcc alleine.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Volker U. schrieb:
> Johann L. schrieb:
>>
>> Variablen im static Storage (egal ob lokal oder global) werden zu 0
>> initialisiert.
>
> Aha!? Ist das eine Spezialität von gcc oder bestimmter Controller? Laut
> Sprach-Definition ist eine statische Variable anfänglich ebenso
> undefiniert, wie eine dynamische.
1
Buch:    ISO/IEC 9899:1999
2
Kapitel: 6.7.8
3
Vers:    10
4
5
If an object that has automatic storage duration is not initialized
6
explicitly, its value is indeterminate. If an object that has static
7
storage duration is not initialized explicitly, then:
8
9
— if it has pointer type, it is initialized to a null pointer;
10
— if it has arithmetic type, it is initialized to (positive or unsigned) zero;
11
— if it is an aggregate, every member is initialized (recursively)
12
  according to these rules;
13
— if it is a union, the first named member is initialized (recursively)
14
  according to these rules.

von Volker U. (volkeru)


Lesenswert?

A. K. schrieb:
> Das ist eine Spezialität der Sprache C und hat alle Versionen überlebt.

Hmm, da hast du wohl Recht. Ich habs grade mal getestet, statische 
Variablen bzw. Pointer werden sogar schon bei MSC 1.0 zu 0 bzw. NULL 
initialisiert. Das war mir irgendwie nicht (mehr?) bewusst.

Wenn eine Variable initialisiert werden muss, macht man dies ja immer 
explizit, allein schon der Übersichtlichkeit halber. Deshalb geraten 
solche Automatismen irgendwann in Vergessenheit. Wie gut, wenn es 
Menschen gibt, die sogar solche Kleinigkeiten noch im Kopf haben... Aber 
gibt es eigentlich irgendeinen plausiblen Grund, warum statische 
Variablen initialisiert werden und dynamische nicht?

Dann dürfte die Information auf dieser URL auch falsch sein: 
http://www.eckart-winkler.de/computer/c/statvar.htm

Gruß, Volker

von Rolf Magnus (Gast)


Lesenswert?

xJohann L. schrieb:
> Rolf Magnus schrieb:
>
>> Warum ist die überhaupt volatile? Wenn die Constraints richtig
>> definiert sind, ist das unnötig.
>
> Nö, nicht so wie es momentan dasteht: T wird gesetzt und gelesen.

Und was hat das mit volatile zu tun?

Martin schrieb:
> Pointeraliasing führt schonmal dazu, daß nix optimiert wird.

Dafür gibt's doch das Schlüsselwort restrict.

von (prx) A. K. (prx)


Lesenswert?

Martin schrieb:

> Die Sprache wurde so gestaltet wie sie ursprünglich war, um einfach
> einen für damalige Verhältnisse recht effizienten Compiler
> zusammenstricken zu können.

Und insbesondere, damit man auf den damaligen 16-Bit Maschinen überhaupt 
in der Lage war, einen Compiler zusammenzustricken, der in erträglicher 
Zeit compilieren konnte. So arg viel Platz hatte man da nicht.

Dieser Aspekt trug auch dazu bei, das aus Ada nie wirklich etwas wurde. 
Bis die Compiler und die Maschinen so weit waren, dass sie sich 
gegenseitig vertrugen, war der Zug längst abgefahren. Die ersten 
Compiler waren zu unhandliche Klöpse - oder die Maschinen noch nicht 
weit genug, sie zu ertragen. Nicht der beste Weg, an Popularität zu 
gewinnen.

von Peter II (Gast)


Lesenswert?

Volker U. schrieb:
> gibt es eigentlich irgendeinen plausiblen Grund, warum statische
> Variablen initialisiert werden und dynamische nicht?

C soll ja schnell sein, dann das würde ja auch hiesen das speicher der 
mit malloc angefordert wird initalisiert wird.
Global und Static müssen nur einmal initalisiert werden das ändert dann 
nichts an der Laufzeit.

von Volker U. (volkeru)


Lesenswert?

Martin schrieb:
> Volker U. schrieb:
>> Die Sprache C wurde extra so maschinennah gestaltet, damit sie perfekt
>> optimiert werden kann.

> C ist relativ ausdrucksschwach, stellt also dem Compiler diverse
> Informationen nicht bereit, die er für Optimierungen nutzen könnte.

Wir vergleichen es wohl mal lieber mit anderen Sprachen, die zu der Zeit 
(1973) verfügbar waren: Fortran, Basic, Cobol. Davon ist nur C noch 
weitgehend unverändert übrig geblieben.

Hinzu kommt, dass C auf allen Plattformen verfügbar ist und optimale 
Portabilität bietet, was man von Ada und Ocaml nicht unbedingt behaupten 
kann.

Du vergleichst ja auch keinen Audi A6 mit einer Postkutsche ;)

von (prx) A. K. (prx)


Lesenswert?

Volker U. schrieb:

> Wenn eine Variable initialisiert werden muss, macht man dies ja immer
> explizit

Bei Controllern ist das eher kontraproduktiv, denn eine explizite 
Initialisierung benötigt Platz im Flash, eine implizite nicht.

> gibt es eigentlich irgendeinen plausiblen Grund, warum statische
> Variablen initialisiert werden und dynamische nicht?

Statische werden vom Loader oder vom Startup-Code initialisiert. Das 
darf man als gratis ansehen.

von (prx) A. K. (prx)


Lesenswert?

Volker U. schrieb:

> Hinzu kommt, dass C auf allen Plattformen verfügbar ist

Das ja...

> und optimale Portabilität bietet

...aber das?

Portabilität in dem Sinn, dass man auf so ziemlich jeder Zielplattform 
einen C Compiler findet, egal ob sie sich dafür eignet oder nicht, das 
ja. Aber in dem Sinn, dass eine Migration auf eine Plattform mit anderen 
Randbedingungen bei von diesbezüglich Ungeübten geschriebenen Programme 
ohne grössere Revision hinhaut, das nein.

von Rolf Magnus (Gast)


Lesenswert?

A. K. schrieb:
>> Wenn eine Variable initialisiert werden muss, macht man dies ja immer
>> explizit
>
> Bei Controllern ist das eher kontraproduktiv, denn eine explizite
> Initialisierung benötigt Platz im Flash, eine implizite nicht.

Nicht bei einem halbwegs ernstzunehmenden Compiler. So schwer ist es für 
den ja nun nicht gerade, explizite Null-Iinitialisierungen als solche zu 
erkennen und mit den  impliziten zusammenzuwerfen.

von Volker U. (volkeru)


Lesenswert?

Peter II schrieb:
> Volker U. schrieb:
>> gibt es eigentlich irgendeinen plausiblen Grund, warum statische
>> Variablen initialisiert werden und dynamische nicht?
>
> Global und Static müssen nur einmal initalisiert werden das ändert dann
> nichts an der Laufzeit.

Trotzdem benötigt die Initialisierung auch im statischen Fall Zeit, wenn 
auch nur einmal. Meinst du wirklich, dass man bei dynamischen Variablen 
nur wegen der Zeit auf eine Initialisierung verzichtet hat? Okay, das 
ist zumindest ein halbwegs nachvollziehbarer Grund. Trotzdem irgendwie 
inkonsistent.

von Peter II (Gast)


Lesenswert?

Rolf Magnus schrieb:
> Nicht bei einem halbwegs ernstzunehmenden Compiler.

klar doch.

[c]
void foo() {
  int i;
  ...
  i = x + y;
}
[\c]

hier müsste dann i immer wieder mit 0 initalisiert werden, das kostet 
jedesmal zeit und ist auch noch unnötig. Man kann es auch nicht nur 
einmal machen, weil es immer auf einer anderne stelle auf dem Stack 
liegen kann.

von Volker U. (volkeru)


Lesenswert?

A. K. schrieb:
> Volker U. schrieb:
>
>> Wenn eine Variable initialisiert werden muss, macht man dies ja immer
>> explizit
>
> Bei Controllern ist das eher kontraproduktiv, denn eine explizite
> Initialisierung benötigt Platz im Flash, eine implizite nicht.

Wenn der Loader das miterledigt, hast du Recht. Dann macht dies wohl 
Sinn, weil es keinen zusätzlichen Maschinencode bzw. Platz benötigt. 
Darüber hatte ich noch gar nicht nachgedacht. Wieder was dazu gelernt 
:-)

von (prx) A. K. (prx)


Lesenswert?

Volker U. schrieb:

> Trotzdem benötigt die Initialisierung auch im statischen Fall Zeit, wenn
> auch nur einmal.

Wenn wir mal den Rahmen der Microcontroller verlassen und uns in 
Bereiche ernsthafter Betriebssysteme begeben: Ein Kernel kann einem 
Prozess keinen uninitialisierten Speicher übergeben, da könnten ja die 
Passwörter vom Vorgänger drin sein.

Daher muss eine Initialisierung des Speichers stattfinden. Und was 
liegt da näher, als 0 zu verwenden. Das gilt natürlich auch für den 
Stack, aber eben nur bei der ersten Benutzung davon, nützt daher nichts.

> Meinst du wirklich, dass man bei dynamischen Variablen
> nur wegen der Zeit auf eine Initialisierung verzichtet hat?

Mit Sicherheit. Die C Compiler der ersten ca. 2 Jahrzehnte Jahre waren 
was Optimierung angeht nicht sonderlich weit entwickelt. Viele hätten 
bei
  int i;
  i = 1;
seelenruhig nacheinander erst 0 dann 1 reingeschrieben.

> Trotzdem irgendwie inkonsistent.

Ist es. Aber C erweckt im Vergleich zu den Wirth'schen Sprachen sowieso 
nicht grad den Eindruck, als ob da jemand vorher gross nachgedacht 
hätte. Da stand m.E. eher das Prinzip "legen wir mal los und sehen was 
am Ende rauskommt" Pate.

von Volker U. (volkeru)


Lesenswert?

Rolf Magnus schrieb:
> A. K. schrieb:
>> Bei Controllern ist das eher kontraproduktiv, denn eine explizite
>> Initialisierung benötigt Platz im Flash, eine implizite nicht.
>
> Nicht bei einem halbwegs ernstzunehmenden Compiler. So schwer ist es für
> den ja nun nicht gerade, explizite Null-Iinitialisierungen als solche zu
> erkennen und mit den  impliziten zusammenzuwerfen.

Hihi, ein Expertenstreit beginnt :-). Erkennt der gcc-Compiler nun die 
unnötige, explizite Initialisierung und optimiert sie weg? Dann könnte 
ich sie der Übersichtlichkeit halber ja beruhigt stehen lassen :)

von (prx) A. K. (prx)


Lesenswert?

Grad ausprobiert. Er erkennt es.

von Peter II (Gast)


Lesenswert?

A. K. schrieb:
> Daher muss eine Initialisierung des Speichers stattfinden. Und was
> liegt da näher, als 0 zu verwenden. Das gilt natürlich auch für den
> Stack, aber eben nur bei der ersten Benutzung davon, nützt daher nichts.
aber das gilt nicht für den Programmablauf. Dann für Variabel auf dem 
Heap wird im normalfall kein Speicher von BS angefordern. Selbst bei 
einem malloc kann die Runtime noch speicher auf vorrat haben bevor er 
von BS angeordert wird.

von (prx) A. K. (prx)


Lesenswert?

Andere Baustelle. Heap ist ja nicht static. Dieses Verhalten ist also 
konsistent, weder auto noch malloc initialisieren, weil beides zur 
Laufzeit Kosten verursachen würde. Ausserdem gibts immer noch calloc.

von Sam .. (sam1994)


Lesenswert?

sry - ich muss euch mal kurz unterbrechen.

Ich hab noch eine Frage zum externen Assemblercode: Woher weiß der 
Compiler welche Argumente einer Funktion übergeben werden und welche den 
Rückgabewert bilden?

von (prx) A. K. (prx)


Lesenswert?

Samuel K. schrieb:

> Ich hab noch eine Frage zum externen Assemblercode: Woher weiß der
> Compiler welche Argumente einer Funktion übergeben werden und welche den
> Rückgabewert bilden?

Weil du es ihm sagst:
  extern double f (long x, int y);

von Volker U. (volkeru)


Lesenswert?

A. K. schrieb:
> Ist es. Aber C erweckt im Vergleich zu den Wirth'schen Sprachen sowieso
> nicht grad den Eindruck, als ob da jemand vorher gross nachgedacht
> hätte. Da stand m.E. eher das Prinzip "legen wir mal los und sehen was
> am Ende rauskommt" Pate.

Klar, man muss es in die damalige Zeit einordnen. Es ging darum, schnell 
neue Betriebssysteme für neue Plattformen zu schreiben (z.B. Unix), die 
sich damals sprunghaft entwickelten. Dafür benötigte man eine schnelle 
und zuverlässige, system- und vor allem praxisnahe Sprache. Das war C. 
Ich erinnere mich noch gut an die ersten 68000er (16 Bit) Systeme Mitte 
der 80er Jahre. Man war begeistert von der neuen Geschwindigkeit, die 
diese ersten, kleinen Multiuser/Multitasking Systeme für den 
Hausgebrauch erreichten. Schönheit war da nicht gefragt, sondern vor 
allem Schnelligkeit. Man denke daran, dass bei der Datenübertragung noch 
300-2400 bit/sek üblich waren.

Vor dem Hintergrund ist es doch mehr als respektabel, dass sich die 
Sprache bis heute weitgehend unverändert erhalten hat.

von Sam .. (sam1994)


Lesenswert?

A. K. schrieb:
> Samuel K. schrieb:
>
>> Ich hab noch eine Frage zum externen Assemblercode: Woher weiß der
>> Compiler welche Argumente einer Funktion übergeben werden und welche den
>> Rückgabewert bilden?
>
> Weil du es ihm sagst:
>   extern double f (long x, int y);

Das schreibt man ja in den Header. Im Assemblercode steht kann ja jedes 
Register für x oder y stehen.

von Volker U. (volkeru)


Lesenswert?

A. K. schrieb:
> Samuel K. schrieb:
>
>> Ich hab noch eine Frage zum externen Assemblercode: Woher weiß der
>> Compiler welche Argumente einer Funktion übergeben werden und welche den
>> Rückgabewert bilden?
>
> Weil du es ihm sagst:
>   extern double f (long x, int y);

Ich glaube, Samuel meinte mehr die Assembler-Seite, oder?

von (prx) A. K. (prx)


Lesenswert?

Volker U. schrieb:

> Ich glaube, Samuel meinte mehr die Assembler-Seite, oder?

Er sprach aber vom Compiler. Aber wir sind uns offenbar darin einig, 
dass wir nicht wirklich wissen, was er meint. ;-)

von (prx) A. K. (prx)


Lesenswert?

Samuel K. schrieb:

> Im Assemblercode steht kann ja jedes
> Register für x oder y stehen.

Nein. Die Art der Parameterübergabe ist vom Compiler oder vom ABI 
festgelegt. Mich dünkt, dass man das beim AVR (GCC) irgendwo in der Doku 
der avr-libc finden kann. Also dass der ersten Paramater in Register 
soundso steht, der zweite in ... etc.

von Sam .. (sam1994)


Lesenswert?

Ich, ok, ihr habt ja recht. Wie weiß der Compiler/Linker/Assembler, wenn 
die Funktion Bin3BCD aufgerufen wird, welche Argumente übergeben werden 
sollen und am Ende in welchen Registern stehen.

Ich brauche diese Funktion: http://avr-asm.tripod.com/math32x.html 
(ziemlich in die Mitte scrollen)

von (prx) A. K. (prx)


Lesenswert?

Wie der Compiler das weiss hatte ich oben bereits geschrieben. Und in 
welchen Registern sie stehen weiss er, weil er das vorgibt und die 
externe Assembler-Funktion sich gefälligst daran zu halten hat.

von Volker U. (volkeru)


Lesenswert?

Also ich bin kein Experte für Assembler, kenne mich da eher nur 
rudimentär aus meiner 8086-Zeit aus und habe bisher nur ein bisschen 
Assembler für AVRs programmiert.

/* Calling conventions on gcc:
 * First parameter passed in r24/r25, second in r22/23 and so on.
 * Callee must preserve r1-r17, r28/r29
 * Result is passed in r24/r25
*/

LSB ist das jeweils niederigere Register, MSB das höhere.

von Sam .. (sam1994)


Lesenswert?

Danke euch, ich hab jetzt ein paar Seiten gefunden. Ich werde es wohl 
trotzdem in Inline Assembler schreiben - wer hat sich den Schrott 
ausgedacht Input und Output Register übereinander zulegen?

von (prx) A. K. (prx)


Lesenswert?

Samuel K. schrieb:

> Danke euch, ich hab jetzt ein paar Seiten gefunden. Ich werde es wohl
> trotzdem in Inline Assembler schreiben - wer hat sich den Schrott
> ausgedacht Input und Output Register übereinander zulegen?

Wieso Schrott? Wäre andernfalls Verschwendung.

von Volker U. (volkeru)


Lesenswert?

Samuel K. schrieb:
> Danke euch, ich hab jetzt ein paar Seiten gefunden. Ich werde es wohl
> trotzdem in Inline Assembler schreiben - wer hat sich den Schrott
> ausgedacht Input und Output Register übereinander zulegen?

Das habe ich mich auch schon gefragt, aber AVR-Register sind eben knapp. 
Hier ein kurzer Beispielcode für eine Warteroutine, die als externe 
Assembler-Datei eingebunden werden kann.

Die Deklaration in C lautet:
1
extern void wait_x_075u(uint16_t count);

Die Assembler-Datei ist:
1
#include <avr/io.h>
2
3
; AVR @ 4MHz
4
5
loop_cnt_1l  = 24    ; loop counter LSB
6
loop_cnt_1m = 25    ; loop counter MSB
7
loop_cnt_2l  = 22    ; help
8
loop_cnt_2m = 23    ; help
9
10
;***************************************************************************
11
;*
12
;*  func:  wait_x_100u
13
;*
14
;*  vars:  loop_cnt_1l, loop_cnt_1m
15
;*      loop_cnt_2l, loop_cnt_2m
16
;*
17
;***************************************************************************
18
; Bei Parameter n=0 dauert die Schleife 7,5 us
19
; Bei Parameter n>0 dauert die Schleife (n*2,75+5,5) us
20
.global wait_x_075u
21
.func wait_x_075u
22
wait_x_075u:
23
    push  loop_cnt_1l
24
    push  loop_cnt_1m
25
    push  loop_cnt_2l
26
    push  loop_cnt_2m
27
    mov  loop_cnt_2l,loop_cnt_1l    ;Anzahl der Durchläufe: loop_cnt_1
28
    mov  loop_cnt_2m,loop_cnt_1m
29
l2loop:  ldi   loop_cnt_1l,1      ;1: 0.75 µs pro Schleifendurchlauf bei 4 MHz (3 Takte)
30
lcloop:  dec  loop_cnt_1l
31
    ;or  loop_cnt_1l,loop_cnt_1l
32
    brne  lcloop
33
    ;or  loop_cnt_2l,loop_cnt_2l
34
    cpi    loop_cnt_2m,0
35
    brne  sbcntm
36
    cpi    loop_cnt_2l,0
37
    breq  wend
38
sbcnt:  subi  loop_cnt_2l,1
39
    sbc    loop_cnt_2m,loop_cnt_1l  ;Register hier immer 0
40
    rjmp  l2loop
41
sbcntm:  nop
42
    subi  loop_cnt_2l,1
43
    sbc    loop_cnt_2m,loop_cnt_1l  ;Register hier immer 0
44
    rjmp  l2loop
45
wend:  pop  loop_cnt_2m
46
    pop loop_cnt_2l
47
    pop  loop_cnt_1m
48
    pop loop_cnt_1l
49
    ret
50
.endfunc
51
.end

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


Lesenswert?

A. K. schrieb:
> Grad ausprobiert. Er erkennt es.

Erst seit Version 3.x.

Samuel K. schrieb:
> Danke euch, ich hab jetzt ein paar Seiten gefunden. Ich werde es wohl
> trotzdem in Inline Assembler schreiben - wer hat sich den Schrott
> ausgedacht Input und Output Register übereinander zulegen?

Mit deiner Arroganz wirst du es noch weit bringen im Leben.

Das mach viele ABIs so, denn Register sind immer knapp, und zu
viele davon will man nicht schon für die Parameterübergabe reservieren.
Bei manchen CPUs (bspw. ARM) wird das ABI übrigens vom
Prozessorhersteller sogar vorgegeben, damit sind unterschiedliche
Compiler dann ABI-kompatibel.

von Volker U. (volkeru)


Lesenswert?

Jörg Wunsch schrieb:
> Mit deiner Arroganz wirst du es noch weit bringen im Leben.

Ach, nicht gleich so böse, er hat sich doch nur unreflektiert geärgert 
;-)

> Das mach viele ABIs so, denn Register sind immer knapp, und zu
> viele davon will man nicht schon für die Parameterübergabe reservieren.

Zumal die "Doppelnutzung" ja auch völlig unproblematisch ist, da die 
Übergabe "by value" und nicht "by reference" stattfindet, die 
Eingangs-Parameter also unverändert bleiben. Für einen passionierten 
Basic-Programmierer ist das auf den ersten Blick vielleicht "Schrott". 
;)

By the way: Bist du eigentlich für das avrdude Paket zuständig? Falls 
ja, kannst du bei Interesse meinen Schnipsel für den ATtiny167 
aufnehmen: Beitrag "ATtiny167: Erweiterung der avrdude.conf". ATtiny87 kommt 
auch noch... (sorry für OT, fiel mir grade so ein, als ich deinen Namen 
sah)

Gruß, Volker

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


Lesenswert?

Volker U. schrieb:
> By the way: Bist du eigentlich für das avrdude Paket zuständig?

Jaja, alles zu seiner Zeit. ;-)

Nachdem ich kürzlich endlich das leidige Überspringen von Bytes (bzw.
Pages), die nicht in der Eingabedatei enthalten sind, implementiert
habe, ist der nächste Schritt nun erst einmal, dass sich avrdude.conf-
Einträge auf andere beziehen können und diese nur noch partiell
abändern.  Damit kann man sich für all die Controller, die sich nur
in der signature unterscheiden, diesen Wust an expliziten Einträgen
sparen.

Wenn das getan ist, werde ich mal sehen, das ganze avrdude.conf zu
verschlanken, danach kommen die neuen Einträge hinzu.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Samuel K. schrieb:
> wer hat sich den Schrott
> ausgedacht Input und Output Register übereinander zulegen?

Es zwingt dich niemand, diesen Schrott zu verwenden.

Und wie sonst willst du mitteilen, daß der Inline-Schnippel einen Wert 
ändert und welche Registerklassen zulässigf sind?

Ein MULSU Ra, Rb hat andere Nebenbedingungen als ein LDD Ra, Rb+const

Wie Register verwendet und Parameter übergeben werden steht zB in
http://www.rn-wissen.de/index.php/Avr-gcc/Interna#Registerverwendung

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


Lesenswert?

Samuel K. schrieb:
> Bei mir sieht der Link gut aus.

Naja, aber eben nur bei dir, vermutlich hat deine Brause das was
gecachet.  Aber warum jemanden wie alldatasheet befragen, wenn man
die Info auch komplett authentisch von NXP bekommt?

http://www.nxp.com/documents/data_sheet/PCF8577C.pdf

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Rolf Magnus schrieb:
> Johann L. schrieb:
>> Rolf Magnus schrieb:
>>
>>> Warum ist die überhaupt volatile? Wenn die Constraints richtig
>>> definiert sind, ist das unnötig.
>>
>> Nö, nicht so wie es momentan dasteht: T wird gesetzt und gelesen.
>
> Und was hat das mit volatile zu tun?

Wenn das bld-asm nur eine input-Constraint hat (der Input wird nicht 
verändert, das "+r" ist also überflüssig und "r" reicht) und das asm 
nicht volatile ist, kann es komplett entfernt werden. Das asm überlebt 
nur deshalb und setzt T, weil ev volateil ist bzw. wegen der Constraint 
"+r" und weil der Output später nich verwendet wird.

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


Lesenswert?

Samuel K. schrieb:
> Byte 1: + ,BAT, 2f, 2a, 2b, 3g, 1g, 1f
> Byte 2: a1, b1, : , 2g, 2d, 2e,dp2, 1c
> Byte 3: 1d, 1e,dp0,   , 3a, 3f, K , -
> Byte 4: 3b, 3c, 3d, 3e,   ,   ,dp1, 2c

OK, das ist ja mal ein Anfang. ;-)

Jetzt müsstest du nur noch erklären, wie die Eingangsdaten dafür
aussehen.  Das wird aus deiner SetDigit-Routine nicht wirklich
ersichtlich, zumal du von einem 8-Byte-Array lcd[] schreibst.
Was darin die 8 Byte sind, und ob das lcd[] nun die Eingangs- oder
Ausgangsdaten sind, ist mir nicht klar.  Ich vermute letzteres,
aber wie passt das dann zu den nur 4 Byte, die ich hier oben
zitiert habe?

Btw.:

Samuel K. schrieb:
>> Nicht nur ich bin immer
>> noch der Meinung, dass deine fest vorgefasste Idee, man müsse diese
>> Bitwurschtelei partout über BST und BLD implementieren, keineswegs
>> notwendigerweise das Optimum darstellt.
>
> Ja ich bin festüberzeugt, dass es nicht schneller als 2 Takte/bit geht.

Zuvor ging es dir aber immer um die Codegröße, nicht um die
Geschwindigkeit.

von Peter D. (peda)


Lesenswert?

Floh schrieb:
>
1
> //bit reverse als beispiel
2
> 
3
> uint8_t source = 34; //Quelle
4
> uint8_t des = 0; //Ziel
5
> 
6
> if(source & (1<<0))
7
>   des |= (1<<7);
8
> if(source & (1<<1))
9
>   des |= (1<<6);
10
> if(source & (1<<2))
11
>   des |= (1<<5);
12
> if(source & (1<<3))
13
>   des |= (1<<4);
14
> if(source & (1<<4))
15
>   des |= (1<<3);
16
> if(source & (1<<5))
17
>   des |= (1<<2);
18
> if(source & (1<<6))
19
>   des |= (1<<1);
20
> if(source & (1<<7))
21
>   des |= (1<<0);
22
> 
23
>
> Dürfte recht kompakt übersetzt werden, da die Einzelbitzugriffe als
> Maschinenbefehle existieren (sbrs, sbr) und sie vom Compiler (avr gcc)
> auch verwendet werden.


Genauso muß man es machen, das ganze Assembler-Rumgewurstel ist voll für 
die Katz!

Und da es hier um 32 Pins des LCD geht, die 32 Bits in 4 Registern 
zugeordnet sind, nimmt man ganz einfach source und dest als uint32_t.
Das ergibt dann bis zu 32 Zuordnungen, also rund 68 Words Code (136 
Byte).

Noch weniger Code könnte man mit einer Schleife und einer Tabelle mit 32 
Einträgen (Bit-Index) erreichen.


Peter

von Sam .. (sam1994)


Lesenswert?

Jörg Wunsch schrieb:
> Jetzt müsstest du nur noch erklären, wie die Eingangsdaten dafür
> aussehen.

Die Eingangsdaten sind sortiert, so dass für die Zeichen eine Tabelle 
genutzt werden kann: a,b,c,d,e,f,g. Diese werden dann bitweise in das 
Ausgangsarray gesetzt (lcd). Das Array ist so aufgebaut, dass es einfach 
an den Slave gesendet werden kann, der die Daten dann übernimmt.

> Das wird aus deiner SetDigit-Routine nicht wirklich
> ersichtlich, zumal du von einem 8-Byte-Array lcd[] schreibst.
> Was darin die 8 Byte sind, und ob das lcd[] nun die Eingangs- oder
> Ausgangsdaten sind, ist mir nicht klar.  Ich vermute letzteres,
> aber wie passt das dann zu den nur 4 Byte, die ich hier oben
> zitiert habe?

8Bytes weil es 2 Lcds sind, die vom Treiber mulitplext werden.

> Btw.:
>
> Samuel K. schrieb:
>>> Nicht nur ich bin immer
>>> noch der Meinung, dass deine fest vorgefasste Idee, man müsse diese
>>> Bitwurschtelei partout über BST und BLD implementieren, keineswegs
>>> notwendigerweise das Optimum darstellt.
>>
>> Ja ich bin festüberzeugt, dass es nicht schneller als 2 Takte/bit geht.
>
> Zuvor ging es dir aber immer um die Codegröße, nicht um die
> Geschwindigkeit.

2 Words / bit ^= (meistens) 2 Takte / bit

Peter Dannegger schrieb:
> Genauso muß man es machen, das ganze Assembler-Rumgewurstel ist voll für
> die Katz!

Warum? Es ist (meiner Meinung nach) nicht übersichtlicher und weder 
schneller noch kleiner.

> Und da es hier um 32 Pins des LCD geht, die 32 Bits in 4 Registern
> zugeordnet sind, nimmt man ganz einfach source und dest als uint32_t.
> Das ergibt dann bis zu 32 Zuordnungen, also rund 68 Words Code (136
> Byte).
bst/bld sind doch genau für diese Aufgabe gedacht -> 64Words = 128Byte

> Noch weniger Code könnte man mit einer Schleife und einer Tabelle mit 32
> Einträgen (Bit-Index) erreichen.

Das stimmt, man spart vielleicht 40Byte hat dafür 10fache Laufzeit.

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


Lesenswert?

Samuel K. schrieb:

> Die Eingangsdaten sind sortiert, so dass für die Zeichen eine Tabelle
> genutzt werden kann: a,b,c,d,e,f,g. Diese werden dann bitweise in das
> Ausgangsarray gesetzt (lcd).

OK.

> 8Bytes weil es 2 Lcds sind, die vom Treiber mulitplext werden.

Ah, dieses Detail fehlte bislang.

>> Genauso muß man es machen, das ganze Assembler-Rumgewurstel ist voll für
>> die Katz!
>
> Warum? Es ist (meiner Meinung nach) nicht übersichtlicher und weder
> schneller noch kleiner.

Kann man möglicherweise mit paar Präprozessor-Tricks noch recht
übersichtlich hingeschrieben bekommen.

>> Noch weniger Code könnte man mit einer Schleife und einer Tabelle mit 32
>> Einträgen (Bit-Index) erreichen.
>
> Das stimmt, man spart vielleicht 40Byte hat dafür 10fache Laufzeit.

Nun widersprichst du dir aber komplett selbst.  Oben schriebst du
noch, dass du das alles nur wegen der Codegröße machst, jetzt
lehnst du eine in dieser Hinsicht bessere Version jedoch vehement
ab.

Wenn du gar nichts anderes willst, als genau deine BLD/BST-Idee auf
Teufel komm raus durchzuboxen, dann tu's, aber belästige uns nicht,
dass dir der Compiler deinen vorgeworfenen Fraß nicht mehr optimieren
kann.  Du hast ihm zu wenige Brocken als "Jongliermaterial" dafür
übrig gelassen, noch was zu optimieren.

Ich habe eine ähnliche Aufgabe mal für ein 20stelliges VFD
implementiert, angesteuert mit 3 x MAX6921 (SPI-angesteuertes
20-bit-Schieberegister mit VFD-Treibern).  Die Verdrahtung war dabei
auch komplett durch das Layout vorgegeben, es musste also alles in
Software gemappt werden.  Der Code dafür sieht dann so aus:
1
/*
2
 * MAX6921 chain setup:
3
 *
4
 *       bit#7   6   5   4   3   2   1   0
5
 * byte 0    x   x   x   x g20 g19 g18 g17
6
 *      1  g16 g15 g14 g13 g12 g11 g10  g9
7
 *      2   g8  g7  g6  g5  g4  g3  g2  g1 [IC4]
8
 *      3    x   x  a1  a3  a5  a7  a9 a11
9
 *      4  a13 a15 a17 a19 a21 a23 a25 a27
10
 *      5  a29 a31 a33 a35                 [IC3]
11
 *     [5]                   x   x   x  a2
12
 *      6  a34 a32 a30 a28 a26 a24 a22 a20
13
 *      7  a18 a16 a14 a12 a10  a8  a6  a4 [IC2]
14
 *                                       ^-- MOSI
15
 */
16
17
static const digit_bits dbits[NDIGITS] PROGMEM =
18
{
19
  { 0b0000, 0b00000000, 0b00000001 },   /* g1 */
20
  { 0b0000, 0b00000000, 0b00000010 },
21
  { 0b0000, 0b00000000, 0b00000100 },
22
  { 0b0000, 0b00000000, 0b00001000 },
23
  { 0b0000, 0b00000000, 0b00010000 },
24
  { 0b0000, 0b00000000, 0b00100000 },
25
  { 0b0000, 0b00000000, 0b01000000 },
26
  { 0b0000, 0b00000000, 0b10000000 },
27
  { 0b0000, 0b00000001, 0b00000000 },
28
  { 0b0000, 0b00000010, 0b00000000 },
29
  { 0b0000, 0b00000100, 0b00000000 },
30
  { 0b0000, 0b00001000, 0b00000000 },
31
  { 0b0000, 0b00010000, 0b00000000 },
32
  { 0b0000, 0b00100000, 0b00000000 },
33
  { 0b0000, 0b01000000, 0b00000000 },
34
  { 0b0000, 0b10000000, 0b00000000 },
35
  { 0b0001, 0b00000000, 0b00000000 },
36
  { 0b0010, 0b00000000, 0b00000000 },
37
  { 0b0100, 0b00000000, 0b00000000 },
38
  { 0b1000, 0b00000000, 0b00000000 }, /* g20 */
39
};
40
41
/*
42
 * Diagnostic patterns.
43
 */
44
static const segment_bits sbits[] PROGMEM =
45
{
46
  //         a    aaaaaaaa    aaaa        aaaaaaaa    aaaaa
47
  //    aaaaa1    11112222    2333   a    33322222    11111aaa
48
  //  xx135791    35791357    9135xxx2    42086420    86420864
49
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 }, /* all off */
50
  { 0b00111111, 0b11111111, 0b11110001, 0b11111111, 0b11111111 }, /* all on */
51
  { 0b00000000, 0b00000000, 0b00000001, 0b11111111, 0b11111111 }, /* checker */
52
  { 0b00111111, 0b11111111, 0b11110000, 0b00000000, 0b00000000 }, /* checker */
53
54
  { 0b00100000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 }, /* 1 */
55
  { 0b00000000, 0b00000000, 0b00000001, 0b00000000, 0b00000000 },
56
  { 0b00010000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 },
57
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000001 },
58
  { 0b00001000, 0b00000000, 0b00000000, 0b00000000, 0b00000000 },
59
60
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000010 }, /* 6 */
61
  { 0b00000100, 0b00000000, 0b00000000, 0b00000000, 0b00000000 },
62
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00000100 },
63
  { 0b00000010, 0b00000000, 0b00000000, 0b00000000, 0b00000000 },
64
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00001000 },
65
66
  { 0b00000001, 0b00000000, 0b00000000, 0b00000000, 0b00000000 }, /* 11 */
67
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00010000 },
68
  { 0b00000000, 0b10000000, 0b00000000, 0b00000000, 0b00000000 },
69
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b00100000 },
70
  { 0b00000000, 0b01000000, 0b00000000, 0b00000000, 0b00000000 },
71
72
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b01000000 }, /* 16 */
73
  { 0b00000000, 0b00100000, 0b00000000, 0b00000000, 0b00000000 },
74
  { 0b00000000, 0b00000000, 0b00000000, 0b00000000, 0b10000000 },
75
  { 0b00000000, 0b00010000, 0b00000000, 0b00000000, 0b00000000 },
76
  { 0b00000000, 0b00000000, 0b00000000, 0b00000001, 0b00000000 },
77
78
  { 0b00000000, 0b00001000, 0b00000000, 0b00000000, 0b00000000 }, /* 21 */
79
  { 0b00000000, 0b00000000, 0b00000000, 0b00000010, 0b00000000 },
80
  { 0b00000000, 0b00000100, 0b00000000, 0b00000000, 0b00000000 },
81
  { 0b00000000, 0b00000000, 0b00000000, 0b00000100, 0b00000000 },
82
  { 0b00000000, 0b00000010, 0b00000000, 0b00000000, 0b00000000 },
83
84
  { 0b00000000, 0b00000000, 0b00000000, 0b00001000, 0b00000000 }, /* 26 */
85
  { 0b00000000, 0b00000001, 0b00000000, 0b00000000, 0b00000000 },
86
  { 0b00000000, 0b00000000, 0b00000000, 0b00010000, 0b00000000 },
87
  { 0b00000000, 0b00000000, 0b10000000, 0b00000000, 0b00000000 },
88
  { 0b00000000, 0b00000000, 0b00000000, 0b00100000, 0b00000000 },
89
90
  { 0b00000000, 0b00000000, 0b01000000, 0b00000000, 0b00000000 }, /* 31 */
91
  { 0b00000000, 0b00000000, 0b00000000, 0b01000000, 0b00000000 },
92
  { 0b00000000, 0b00000000, 0b00100000, 0b00000000, 0b00000000 },
93
  { 0b00000000, 0b00000000, 0b00000000, 0b10000000, 0b00000000 },
94
  { 0b00000000, 0b00000000, 0b00010000, 0b00000000, 0b00000000 },
95
};
96
97
static void
98
putseg(uint8_t digit, char c)
99
{
100
  digit_bits db;
101
  segment_bits sb;
102
103
  memcpy_P(&db, dbits + (unsigned)(uint8_t)digit, sizeof(db));
104
  if (patternmode)
105
      memcpy_P(&sb, sbits + (unsigned)(uint8_t)c, sizeof(sb));
106
  else
107
      memcpy_P(&sb, font + (unsigned)(uint8_t)c, sizeof(sb));
108
109
  for (uint8_t i = 0; i < sizeof(db) / sizeof(db[0]); i++)
110
    {
111
      SPDR = db[i];
112
      while ((SPSR & _BV(SPIF)) == 0) /* wait */;
113
    }
114
  for (uint8_t i = 0; i < sizeof(sb) / sizeof(sb[0]); i++)
115
    {
116
      SPDR = sb[i];
117
      while ((SPSR & _BV(SPIF)) == 0) /* wait */;
118
    }
119
  PORTB |= _BV(2);
120
  asm volatile("nop");
121
  PORTB &= ~_BV(2);
122
}

Allerdings habe ich mir einen ATmega48 gegönnt dafür, der empfängt die
Daten seriell und stellt sie multiplex auf dem VFD dar.  Dabei werden
noch ein paar Steuerzeichen (Cursorbewegung, Löschen und dergleichen)
implementiert, das alles passt zusammen mit dem 5x7-Font dann in 2,7
KiB.  Dafür kann ich das da oben auch heute noch lesen. :-)

von Sam .. (sam1994)


Lesenswert?

Jörg Wunsch schrieb:
>>> Noch weniger Code könnte man mit einer Schleife und einer Tabelle mit 32
>>> Einträgen (Bit-Index) erreichen.
>>
>> Das stimmt, man spart vielleicht 40Byte hat dafür 10fache Laufzeit.
>
> Nun widersprichst du dir aber komplett selbst.  Oben schriebst du
> noch, dass du das alles nur wegen der Codegröße machst, jetzt
> lehnst du eine in dieser Hinsicht bessere Version jedoch vehement
> ab.

Sollte ich am Ende die letzten freien Bytes suchen, dann würde ich die 
Tabelle wählen. Es ist so, dass je effizienter das Programm ist, desto 
langsamer kann ich den Takt wählen, desto weniger Strom verbraucht das 
Gerät. 500 Taktzyklen mehr bei 32kHz sind viel.

Jörg Wunsch schrieb:
> Wenn du gar nichts anderes willst, als genau deine BLD/BST-Idee auf
> Teufel komm raus durchzuboxen, dann tu's, aber belästige uns nicht,
> dass dir der Compiler deinen vorgeworfenen Fraß nicht mehr optimieren
> kann.  Du hast ihm zu wenige Brocken als "Jongliermaterial" dafür
> übrig gelassen, noch was zu optimieren.

Ich lasse mich gern überzeugen. Doch ich glaube dass meine 40Byte 
Progrose sehr optimistisch war.

128Byte (bld/bst) - 10bit*32 Tabelle = 68 Byte = 34 Words.

Diese Bitfummelei in 34 Befehle zu bekommen wird schwer sein.

von Volker U. (volkeru)


Lesenswert?

Jörg Wunsch schrieb:
> ist der nächste Schritt nun erst einmal, dass sich avrdude.conf-
> Einträge auf andere beziehen können und diese nur noch partiell
> abändern.  Damit kann man sich für all die Controller, die sich nur
> in der signature unterscheiden, diesen Wust an expliziten Einträgen
> sparen.

Na, das hört dich doch sehr vielversprechend an. Frohes Schaffen!

Beim tiny167 hätte es mir aber nicht so viel geholfen, da Speichermodell 
und Timing hier deutlich anders sind, als bei anderen, ähnlichen AVRs 
(z.B. tiny84). Aber ich habe es genossen, den Eintrag zu erstellen, da 
ich so endlich gezwungen war, mich genauer mit der Struktur der 
avrdude.conf auseinanderzusetzen, die ich zuvor eher als kryptisch 
empfand. ;)

Tschö, Volker

von Volker U. (volkeru)


Lesenswert?

Samuel K. schrieb:
> Es ist so, dass je effizienter das Programm ist, desto
> langsamer kann ich den Takt wählen, desto weniger Strom verbraucht das
> Gerät. 500 Taktzyklen mehr bei 32kHz sind viel.

32 kHz? ;-)))

Wieviel verbraucht der Prozessor denn bei 128 kHz (50 uA?) oder bei 1 
MHz (400 uA)? Selbst bei Batteriebetrieb sollte das eigentlich kaum ins 
Gewicht fallen, da die anderen Komponenten vermutlich deutlich höhere 
Verluste haben.

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


Angehängte Dateien:

Lesenswert?

Samuel K. schrieb:

> Sollte ich am Ende die letzten freien Bytes suchen, dann würde ich die
> Tabelle wählen. Es ist so, dass je effizienter das Programm ist, desto
> langsamer kann ich den Takt wählen, desto weniger Strom verbraucht das
> Gerät. 500 Taktzyklen mehr bei 32kHz sind viel.

Du solltest natürlich alle Optimierungskriterien schon vorab nennen.

Allerdings ist es meist sinnvoller, den Prozessor mit einem höheren
Takt laufen zu lassen und dann nach getaner Arbeit schlafen zu
legen.  Während dieser Zeit kann man ja auch die CPU runtertakten.

Logischerweise ist sorgfältig handgefeilter Assemblercode dann das
Beste, wenn man:

. sowohl nach Codegröße als auch Ausführungszeit optimieren muss,
  weil die anderen Randbedingungen das verlangen,
. ausreichend Zeit hat, den Kram zu pflegen (auch bei Änderungen
  in der Verdrahtung zum Beispiel)

Dann aber, wie schon genannt, am besten als separate Assemblerdatei.
Innerhalb dieser bist du dein eigener Herr, und du kannst trotzdem
noch den C-Compiler außen herum für den Rest benutzen, bei dem du
irgendwelche komplexere Logik umsetzen willst.

Ansonsten hier ein Vorschlag, wie man es in C schreiben könnte.  Ja,
es ist größer als dein handgefeilter Code (das war zu erwarten), aber
man kann es zumindest lesen ;-), und es lässt sich bei Bedarf schnell
anpassen.  Vielleicht fällt ja noch jemandem was ein, wie man es
kürzer bekommt (das Array ist ja ziemlich `sparse').

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


Angehängte Dateien:

Lesenswert?

Jörg Wunsch schrieb:
> ielleicht fällt ja noch jemandem was ein, wie man es
> kürzer bekommt (das Array ist ja ziemlich `sparse').

Ach, klar, man kann natürlich statt der fertigen Bitwerte die
Bitnummern ins Array schreiben.

von Sam .. (sam1994)


Lesenswert?

Ich grab den Thread nochmal aus:
1
void synth_load(uint8_t* data)
2
{
3
  //Initialize Vars
4
  synth.read = synth.write = synth.buffer;
5
  synth.file = data;
6
  synth.timer1 = TIMER1_PRESCALER;
7
  synth.timer2 = TIMER2_PRESCALER;
8
}
Diese Funktion wird nur mit sts übersetzt:
1
void synth_load(uint8_t* data)
2
{
3
  //Initialize Vars
4
  synth.read = synth.write = synth.buffer;
5
  aa:  28 e9         ldi  r18, 0x98  ; 152
6
  ac:  31 e0         ldi  r19, 0x01  ; 1
7
  ae:  30 93 03 01   sts  0x0103, r19
8
  b2:  20 93 02 01   sts  0x0102, r18
9
  b6:  30 93 01 01   sts  0x0101, r19
10
  ba:  20 93 00 01   sts  0x0100, r18
11
  synth.file = data;
12
  be:  90 93 05 01   sts  0x0105, r25
13
  c2:  80 93 04 01   sts  0x0104, r24
14
  synth.timer1 = TIMER1_PRESCALER;
15
  c6:  82 e0         ldi  r24, 0x02  ; 2
16
  c8:  80 93 06 01   sts  0x0106, r24
17
  synth.timer2 = TIMER2_PRESCALER;
18
  cc:  85 e0         ldi  r24, 0x05  ; 5
19
  ce:  80 93 07 01   sts  0x0107, r24
20
}
21
  d2:  08 95         ret

Die Daten liegen schon in einer gepackten struct? Warum greift gcc nicht 
mit einem Pointer darauf zu? Das würde einen Haufen Flash sparen. Gcc 
verwendet im Code auch dauernd jmp, obwohl rjmp reichen würde? Kann man 
das abschalten?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Peter Dannegger schrieb:
> Floh schrieb:
>>
1
>> //bit reverse als beispiel
2
>>
3
>> uint8_t source = 34; //Quelle
4
>> uint8_t des = 0; //Ziel
5
>>
6
>> if(source & (1<<0))
7
>>   des |= (1<<7);
8
>> if(source & (1<<1))
9
>>   des |= (1<<6);
10
>> if(source & (1<<2))
11
>>   des |= (1<<5);
12
>> if(source & (1<<3))
13
>>   des |= (1<<4);
14
>> if(source & (1<<4))
15
>>   des |= (1<<3);
16
>> if(source & (1<<5))
17
>>   des |= (1<<2);
18
>> if(source & (1<<6))
19
>>   des |= (1<<1);
20
>> if(source & (1<<7))
21
>>   des |= (1<<0);
22
>>
23
>>
>> Dürfte recht kompakt übersetzt werden, da die Einzelbitzugriffe als
>> Maschinenbefehle existieren (sbrs, sbr) und sie vom Compiler (avr gcc)
>> auch verwendet werden.
>
> Genauso muß man es machen, das ganze Assembler-Rumgewurstel ist voll für
> die Katz!

Für Bitgewurschdl ist vielleicht auch folgendes interessant:

http://gcc.gnu.org/onlinedocs/gcc/AVR-Built_002din-Functions.html

Damit geht dann
1
#idfef __BUILTIN_AVR_MAP16
2
    int des = __builtin_avr_map16 (0xfe3d21457b68ac09LL, source);
3
#else
4
    // Bitgewurschdel für nicht-avr-gcc
5
#endif

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.