mikrocontroller.net

Forum: Compiler & IDEs avr-gcc nutzt kein "Store Indirect and Post-Inc."


Autor: Tuxpilot (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo, ich verstehe gerade den avr-gcc nicht.

Ich habe einen Attiny841 und möchte folgendes ausführen:
// highest_byte_in_value wird berechnt, ist dann 0 bis 3.
switch (highest_byte_in_value) {
    case 3 : base64_buffer[current_buffer_element++] = value >> 24;
    case 2 : base64_buffer[current_buffer_element++] = value >> 16;
    case 1 : base64_buffer[current_buffer_element++] = value >> 8;
    default : base64_buffer[current_buffer_element] = value;
}

Ergebnis ist:
    switch (highest_byte_in_value) {
 342:  84 0f         add  r24, r20
 344:  91 1d         adc  r25, r1
 346:  43 e0         ldi  r20, 0x03  ; 3
 348:  95 95         asr  r25
 34a:  87 95         ror  r24
 34c:  4a 95         dec  r20
 34e:  e1 f7         brne  .-8        ; 0x348 <__stack+0x49>
 350:  82 30         cpi  r24, 0x02  ; 2
 352:  91 05         cpc  r25, r1
 354:  61 f0         breq  .+24       ; 0x36e <__stack+0x6f>
 356:  83 30         cpi  r24, 0x03  ; 3
 358:  91 05         cpc  r25, r1
 35a:  19 f0         breq  .+6        ; 0x362 <__stack+0x63>
 35c:  01 97         sbiw  r24, 0x01  ; 1
 35e:  99 f4         brne  .+38       ; 0x386 <__stack+0x87>
 360:  0c c0         rjmp  .+24       ; 0x37a <__stack+0x7b>
        case 3 : base64_buffer[current_buffer_element++] = value >> 24;
 362:  ae 2f         mov  r26, r30
 364:  b0 e0         ldi  r27, 0x00  ; 0
 366:  ac 5d         subi  r26, 0xDC  ; 220
 368:  be 4f         sbci  r27, 0xFE  ; 254
 36a:  3c 93         st  X, r19
 36c:  ef 5f         subi  r30, 0xFF  ; 255
        case 2 : base64_buffer[current_buffer_element++] = value >> 16;
 36e:  ae 2f         mov  r26, r30
 370:  b0 e0         ldi  r27, 0x00  ; 0
 372:  ac 5d         subi  r26, 0xDC  ; 220
 374:  be 4f         sbci  r27, 0xFE  ; 254
 376:  2c 93         st  X, r18
 378:  ef 5f         subi  r30, 0xFF  ; 255
        case 1 : base64_buffer[current_buffer_element++] = value >> 8;
 37a:  ae 2f         mov  r26, r30
 37c:  b0 e0         ldi  r27, 0x00  ; 0
 37e:  ac 5d         subi  r26, 0xDC  ; 220
 380:  be 4f         sbci  r27, 0xFE  ; 254
 382:  1c 93         st  X, r17
 384:  ef 5f         subi  r30, 0xFF  ; 255
        default : base64_buffer[current_buffer_element] = value;
 386:  f0 e0         ldi  r31, 0x00  ; 0
 388:  ec 5d         subi  r30, 0xDC  ; 220
 38a:  fe 4f         sbci  r31, 0xFE  ; 254
 38c:  00 83         st  Z, r16
    }

Das scheint mir viel zu umständlich, gerade weil die Funkion sehr oft 
ausgeführt werden soll. Besser wäre doch, den Quelltext 1:1 zu 
kompilieren:
...
st    Z+, r19
st    Z+, r18
st    Z+, r17
st    Z+, r16

Muss ich dieses Feature "Store Indirect and Post-Inc." erst irgendwie 
aktivieren oder so?

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wenn du so eine Frage stellst, solltest du auch angeben mit welchen 
Optionen du kompilierst.

Autor: Tuxpilot (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hmm, ich benutze einfach nur das WinAVR-Makefile.

Bei -O (Optimierung) habe ich alles ausprobiert (0, 1, 2, 3, s), das hat 
aber keinen Einfluss auf den SRAM-Zugriff, nur auf die Verzweigung oben 
im switch. Was ich hier gepostet habe ist -Os, der Rest benutzt im 
wesentlichen mehr Register.

Insgesamt habe ich diese Optionen gefunden, kenne aber die meisten 
Bedeutungen nicht (noch nicht benutzt...):

Im Makefile:
CSTANDARD = -std=gnu99

CFLAGS = -g$(DEBUG)
CFLAGS += $(CDEFS)
CFLAGS += -O$(OPT)
#CFLAGS += -mint8
#CFLAGS += -mshort-calls
CFLAGS += -funsigned-char
CFLAGS += -funsigned-bitfields
CFLAGS += -fpack-struct
CFLAGS += -fshort-enums
#CFLAGS += -fno-unit-at-a-time
CFLAGS += -Wall
CFLAGS += -Wstrict-prototypes
CFLAGS += -Wundef
#CFLAGS += -Wunreachable-code
#CFLAGS += -Wsign-compare
CFLAGS += -Wa,-adhlns=$(<:%.c=$(OBJDIR)/%.lst)
CFLAGS += $(patsubst %,-I%,$(EXTRAINCDIRS))
CFLAGS += $(CSTANDARD)

In der Konsole:
Compiling C: base64tx.c
avr-gcc -c -mmcu=attiny841 -I. -gdwarf-2 -DF_CPU=14745600UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -Wa,-adhlns=obj/base64tx.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/base64tx.o.d base64tx.c -o obj/base64tx.o

Ich glaube, das Makefile kam damals von hier:
https://www.mikrocontroller.net/articles/Beispiel_Makefile

Autor: Walter T. (nicolas)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tuxpilot schrieb:
> weil die Funkion sehr oft
> ausgeführt werden soll

Die ganze Funktion wäre auch sinnvoll, um den Scope der Variablen zu 
kennen.

Ich habe zwar eine grobe Ahnung, wie der Rückgabewert aussehen soll, 
aber wissen ist besser als raten.

: Bearbeitet durch User
Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Was ändert sich, wenn du den Index zu einem 16-Bit Typ machst?

Autor: mh (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Walter T. schrieb:
> Die ganze Funktion wäre auch sinnvoll, um den Scope der Variablen zu
> kennen.

Nicht nur der Scope ist interessant, sondern auch die genauen 
Datentypen.

Autor: Carl D. (jcw2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tuxpilot schrieb:
> Hmm, ich benutze einfach nur das WinAVR-Makefile.

WinAvr wieviel?   2010....?
Der ist auch steinalt und danach wurde das AVR-Backend deutlich 
verbessert.

Autor: A. K. (prx)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
avr-gcc 4.7.2 machts auch schon richtig.
extern char a[];

void f(unsigned char i, unsigned long v)
{
        a[i++] = v >> 24;
        a[i++] = v >> 16;
        a[i++] = v >> 8;
        a[i] = v;
}

        mov r26,r24
        ldi r27,0
        subi r26,lo8(-(a))
        sbci r27,hi8(-(a))
        st X,r23
        mov r26,r24
        subi r26,lo8(-(1))
        ldi r27,0
        subi r26,lo8(-(a))
        sbci r27,hi8(-(a))
        st X,r22
        mov r26,r24
        subi r26,lo8(-(2))
        ldi r27,0
        subi r26,lo8(-(a))
        sbci r27,hi8(-(a))
        st X,r21
        subi r24,lo8(-(3))
        mov r30,r24
        ldi r31,0
        subi r30,lo8(-(a))
        sbci r31,hi8(-(a))
        st Z,r20

Grund: Bei i=255 geht der zweite Store nach a[0]. Bei z++ oder z+1 ging 
er aber nach a[256].

Mit 16-Bit Index ist das kein Problem mehr:
void f(unsigned i, unsigned long v)
{
        a[i++] = v >> 24;
        a[i++] = v >> 16;
        a[i++] = v >> 8;
        a[i] = v;
}

        mov r30,r24
        mov r31,r25
        subi r30,lo8(-(a))
        sbci r31,hi8(-(a))
        st Z,r23
        std Z+1,r22
        std Z+2,r21
        std Z+3,r20

: Bearbeitet durch User
Autor: A. K. (prx)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Allgemein: Wenn der Typ vom Index kleiner ist als eine Adressrechnung, 
tritt der beschriebene Effekt auf. Nicht nur beim AVR, auch bei 64-Bit 
x86.

Wenn der Compiler die Bereichsgrenzen vom Index kennt, kann er das 
besser machen. Ich hatte den Code oben aber bewusst so formuliert, dass 
er das nicht kann.

Und da sollte sich schon mancher drüber gewundert haben, denn die 
üblicherweise als Index verwendeten int/unsigned sind 32 Bit breit, 
Adressrechnungen aber 64 Bit. Weshalb das ähnlich mies aussieht wie oben 
beim AVR:
        movq    %rsi, %rdx
        movl    %edi, %eax
        shrq    $24, %rdx
        movb    %dl, a(%rax)
        leal    1(%rdi), %eax
        movq    %rsi, %rdx
        shrq    $16, %rdx
        movb    %dl, a(%rax)
        leal    2(%rdi), %eax
        movq    %rsi, %rdx
        shrq    $8, %rdx
        addl    $3, %edi
        movb    %dl, a(%rax)
        movb    %sil, a(%rdi)

Macht man den Index 64 Bit breit, sieht es besser aus:
        movq    %rsi, %rax
        movb    %sil, a+3(%rdi)
        shrq    $24, %rax
        movb    %al, a(%rdi)
        movq    %rsi, %rax
        shrq    $16, %rax
        movb    %al, a+1(%rdi)
        movq    %rsi, %rax
        shrq    $8, %rax
        movb    %al, a+2(%rdi)

: Bearbeitet durch User
Autor: Tuxpilot (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Carl D. schrieb:
> Tuxpilot schrieb:
>> Hmm, ich benutze einfach nur das WinAVR-Makefile.
>
> WinAvr wieviel?   2010....?

Steht da nicht...

> Der ist auch steinalt und danach wurde das AVR-Backend deutlich
> verbessert.

Wo bekomme ich denn neuere Makefiles, ohne sie neu zu erfinden? Im Wiki 
scheinbar nicht:

Tuxpilot schrieb:
> Ich glaube, das Makefile kam damals von hier:
> https://www.mikrocontroller.net/articles/Beispiel_Makefile

A. K. schrieb:
> Was ändert sich, wenn du den Index zu einem 16-Bit Typ machst?

Der Index war uint8_t, ändert aber nichts.
Aber: Wenn der Index uint16_t ist, und der switch komplett fehlt, gibt 
es std Z+1, r17 usw. Auch gut, wenn ich den switch nicht bräuchte.
st Z+, r17 bekomme ich nur mit einer Schleife drumrum, aber eine 
Schleife brauche ich da nicht.

So, der restliche Code funktioniert jetzt, ich habe alles zum .zip 
gemacht und angehängt.

Walter T. schrieb:
> Ich habe zwar eine grobe Ahnung, wie der Rückgabewert aussehen soll,
> aber wissen ist besser als raten.

(Rückgabetyp ist void...)
Die Funktion dient dazu, Daten mit variabler Bitzahl in ein Array zu 
packen, um dieses als Base64 "geschützt" an den PC zu senden. Auf dessen 
AMD64 läuft dann die Umkehrfunktion.
(Wegen diesem schlecht passenden C-'<<' und -'>>' habe ich inzwischen 
Zweifel, dass dies schneller geht, als jedes Byte einzeln per UART zu 
senden, oder gar CSV draus zu machen.)

Autor: Tuxpilot (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich konnte mit switch und C allgemein nicht mehr viel rausholen, und 
habe die Funktion in Assembler neu geschrieben, mit "Store Indirect and 
Post-Inc." sehr sinnvoll eingesetzt.

Wird später noch wichtig, diese Funktion. Heute nicht mehr.

Hier eine gekürzte Version, bei der man noch geschätzt 5 bis 10 Takte 
sparen kann:
push_value:
    ; Calculate and store new bits_in_buffer:
    lds r18, bits_in_buffer
    mov r21, r18
    add r18, r20
    sts bits_in_buffer, r18
    mov r18, r21
    
    ; Calculate how many bits are free in the last buffer element:
    neg r21
    andi r21, 0x07
    ; Set X to first buffer element with free space
    ldi r26, lo8(base64_buffer)
    ldi r27, hi8(base64_buffer)
    lsr r18
    lsr r18
    lsr r18
    add r26, r18
    adc r27, r1
    
    ; Calculate how many bits our value must be shifted left
    ldi r19, 32
    sub r19, r20
    add r19, r21
    
    ; Calculate how many bits and bytes this are:
    mov r0, r19
    lsr r0
    lsr r0
    lsr r0
    andi r19, 0x07
    
    clr r18
    
    ; Shift value left by r0 bytes:
    rjmp 2f
    1:
    mov r18, r25
    mov r25, r24
    mov r24, r23
    mov r23, r22
    clr r22
    2:
    dec r0
    brpl 1b
    
    ; Shift value left by r19 bits:
    rjmp 7f
    6:
    lsl r22
    rol r23
    rol r24
    rol r25
    rol r18
    7:
    dec r19
    brpl 6b
    
    ; OR last buffer element with our highest bits:
    tst r21
    breq 9f
    ld  r19, X
    or  r19, r18
    st  X+, r19
    9:
    
    ; Store all bytes of value in next buffer elements:
    st  X+, r25
    st  X+, r24
    st  X+, r23
    st  X+, r22
    
    ret
Min. 49 Takte mit RCALL und RET, Max. 139 Takte.

Hier wird erstmal nichts mehr verbessert. ;)

Autor: Carl D. (jcw2)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tuxpilot schrieb:
> Carl D. schrieb:
>> Tuxpilot schrieb:
>>> Hmm, ich benutze einfach nur das WinAVR-Makefile.
>>
>> WinAvr wieviel?   2010....?
>
> Steht da nicht...
avr-gcc -v

>> Der ist auch steinalt und danach wurde das AVR-Backend deutlich
>> verbessert.
>
> Wo bekomme ich denn neuere Makefiles, ohne sie neu zu erfinden? Im Wiki
> scheinbar nicht:

Erst mal frischen Compiler besorgen:
http://blog.zakkemble.net/avr-gcc-builds/

Wer Angst vor fremden Binaries hat, hier die Anleitung für diy:
https://www.nongnu.org/avr-libc/user-manual/install_tools.html
(nur Linux, aber "Tuxpilot")

> Tuxpilot schrieb:
>> Ich glaube, das Makefile kam damals von hier:
>> https://www.mikrocontroller.net/articles/Beispiel_Makefile
Eventuell muß man Pfade, etc im makefile anpassen, das stimmt.

: Bearbeitet durch User
Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Und da sollte sich schon mancher drüber gewundert haben, denn die
> üblicherweise als Index verwendeten int/unsigned sind 32 Bit breit,
> Adressrechnungen aber 64 Bit.

Ist das tatsächlich üblich? Ich verwende auf dem PC als Index 
normalerweise immer size_t.
Aber ist interessant zu sehen, dass es auf einem 8-Bitter durchaus 
effizienter sein kann, den Index nicht 8, sondern 16 Bit breit zu 
machen.

Autor: A. K. (prx)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rolf M. schrieb:
> Ist das tatsächlich üblich?

In legacy code schon. Ob man heute konsequent xxx_t Typen statt 
Basistypen einsetzt, dafür fehlt mit der Überblick über genug aktuellen 
Code.

: Bearbeitet durch User
Autor: Rolf M. (rmagnus)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
A. K. schrieb:
> Rolf M. schrieb:
>> Ist das tatsächlich üblich?
>
> In legacy code schon. Ob man heute konsequent xxx_t Typen statt
> Basistypen einsetzt, dafür fehlt mit der Überblick über genug aktuellen
> Code.

Naja, size_t ist ja nicht gerade neu. Das gab's auch vor C99 schon.

Autor: Peter D. (peda)
Datum:

Bewertung
1 lesenswert
nicht lesenswert
Bevor man Micro-Optimization betreibt, sollte man erstmal prüfen, ob 
sich das im realen Programm auch wirklich auswirkt, d.h. ein 
Timingproblem oder zu hohe CPU-Last beseitigt wird.

Autor: Robert L. (lrlr)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
würde mich jetzt auch interessieren..
es sollen "Joystick" Daten (also von einem Menschen eingegeben)
direkt an eine PC übergeben werden

auch wenn man das  (übertrieben) 250 mal in der Sekunden macht, wäre die 
Datenmenge trotzdem noch sehr überschaubar.

und in 1/250 Sekunde kann man schon verdammt viel Code ausführen ..

dass man hier base64 braucht  wäre auch zu bezweifeln.. (oder schickt er 
a e-mail ;-) )

wüsste nicht wo da bit 8 verloren gehen sollte..

Autor: Tuxpilot (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Robert L. schrieb:
> auch wenn man das  (übertrieben) 250 mal in der Sekunden macht, wäre die
> Datenmenge trotzdem noch sehr überschaubar.

Hähä, stimmt.
Ich möchte allerdings später 10.000 Datensätze pro Sekunde übertragen, 
bestehend aus ein paar ADC-Werten (rund 10 Bit) und ein paar internen 
Variablen (rund 1 Bit).

Um sich beim Programmieren daran zu tasten, welche Daten man braucht, 
ist so eine Funktion push_value() sehr praktisch. Wenn es schneller 
werden soll, muss man natürlich den Puffer manuell befüllen, evtl. gar 
nicht linear.

Base64 ist auch nicht nötig, macht aber das überprüfen des Datenstroms 
im Terminal einfacher, da die Datensätze durch \n getrennt sind. Die 
Datenrate ist besser als Hexadezimal, ausserdem werde ich mein 
Plotprogramm noch auf Uuencoding umstellen.

Carl D. schrieb:
> Erst mal frischen Compiler besorgen:
> http://blog.zakkemble.net/avr-gcc-builds/

Gemacht, der Code wird tatsächlich etwas schlanker. Nur das "Store 
Indirect and Post-Inc." wird immer noch unelegant umgangen. Compiliert 
habe ich hiermit:
avr-gcc-8.2.0-x64-linux/bin/avr-gcc -c -mmcu=attiny841 -I. -gdwarf-2 -DF_CPU=14745600UL -Os -funsigned-char -funsigned-bitfields -fpack-struct -fshort-enums -Wall -Wstrict-prototypes -Wundef -Wa,-adhlns=obj/base64tx.lst  -std=gnu99 -Wundef -MD -MP -MF .dep/base64tx.o.d base64tx.c -o obj/base64tx.o

Autor: Oliver S. (oliverso)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit 16-Bit Index?

Oliver

Autor: Tuxpilot (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ja

Autor: Kaj (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Tuxpilot schrieb:
> Die Datenrate ist besser als Hexadezimal
Kann ich mir nur schwer vorstellen. Denn durch die Codierung musst du 
entweder genau so viele (Bytes/3 = 0) oder sogar mehr Bytes (Bytes/3 != 
0 -> Padding) uebertragen als bei Hex. Die Datenrate kann mit Base64 
also gar nicht besser werden.
Aber vielleicht kannst du mich ja erleuchten, und erklaeren wie das 
funktionieren soll.

Autor: Rentenzahler (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich vermute er meint HEX als ASCII übertragen.

Autor: Tuxpilot (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Rentenzahler schrieb:
> Ich vermute er meint HEX als ASCII übertragen.

Ja. Wie kann man es denn sonst noch übertragen? Jeweils zwei Hex-Werte 
in einem Byte, das wäre ja wieder binär, so kann man kein \n senden.

Padding in Form von = oder == braucht man nicht anhängen. Enthält keine 
weitere Information, denn das Format ist ja bekannt. Nur die 2 oder 4 
Bits im letzten Zeichen muss man anhängen.

Autor: Robert L. (lrlr)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
die \n und Base64 im Terminal anzeigen, macht welchen Sinn?

ist das so wie in dem Matrix Film, wo der grüne Code über den Bildschirm 
läuft?

Antwort schreiben

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

Wichtige Regeln - erst lesen, dann posten!

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

Formatierung (mehr Informationen...)

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




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.

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