Forum: Compiler & IDEs Verstehe GCC nicht


von Hagen R. (hagen)


Lesenswert?

Sorry, aber ein besser Titel ist mir nicht eingefallen.

Nachfolgender im WinAVR GCC
1
SIGNAL(SIG_OUTPUT_COMPARE1B) {
2
3
    uint8_t i = DisplayRow;
4
    if (++i >= 15) i = 0;  // 100Hz
5
    DisplayRow = i;
6
// 1.)
7
    PORTC = (PORTC & ~ROW_MASK) | (i << 3);
8
// 2.)
9
    uint8_t* p = (uint8_t*)&DisplayRAM[i];
10
    uint8_t v;
11
    SPDR = p[1];
12
    v = p[0];
13
    while (!(SPSR & (1 << SPIF)));
14
    SPDR = v;
15
    v = p[3];
16
    while (!(SPSR & (1 << SPIF)));
17
    SPDR = v;
18
    v = p[2];
19
    while (!(SPSR & (1 << SPIF)));
20
    SPDR = v;
21
    v = p[5];
22
    while (!(SPSR & (1 << SPIF)));
23
    SPDR = v;
24
    v = p[4];
25
    while (!(SPSR & (1 << SPIF)));
26
    SPDR = v;
27
    v = p[7];
28
    while (!(SPSR & (1 << SPIF)));
29
    SPDR = v;
30
    v = p[6];
31
    while (!(SPSR & (1 << SPIF)));
32
    SPDR = v;
33
    v = p[9];
34
    while (!(SPSR & (1 << SPIF)));
35
    SPDR = v;
36
    v = p[8];
37
    while (!(SPSR & (1 << SPIF)));
38
    SPDR = v;
39
    while (!(SPSR & (1 << SPIF)));

bei 1.) wird ein Spaltentreiber am PortC angesprochen
bei 2.) wird per ISP Shiftregister gefüttert

Der indizierte Zugriff auf das array p[] -> Displayram ist drinnen
damit der GCC mit ld reg,z+c also Speicherzugriff mit konstantem Offset
auf den Displayspeicher zugreifen kann.

Oh freude der erzeugte ASM dafür sieht auch so aus:
1
 479                 .L79:
2
 480 039e 779B          sbis 46-0x20,7
3
 481 03a0 FECF          rjmp .L79
4
 482 03a2 8FB9          out 47-0x20,r24
5
 483 03a4 8581          ldd r24,Z+5

super.

Aber beim Zugriff 1.) das (i << 3) baut GCC per int und demzufolge
unsinnigerweise
1
 447 0362 25B3          in r18,53-0x20
2
 448 0364 2778          andi r18,lo8(-121)
3
 449 0366 A32F          mov r26,r19
4
 450 0368 BB27          clr r27
5
 451 036a CD01          movw r24,r26
6
 452 036c 880F          lsl r24
7
 453 036e 991F          rol r25
8
 454 0370 880F          lsl r24
9
 455 0372 991F          rol r25
10
 456 0374 880F          lsl r24
11
 457 0376 991F          rol r25
12
 458 0378 282B          or r18,r24
13
 459 037a 25BB          out 53-0x20,r18

viel zu ineffizient.

So dachte ich mir, sagste es dem GCC explizit was du von ihm willst und
änderte 1.) um in
1
    PORTC = (PORTC & ~ROW_MASK) | (uint8_t)(i << 3);

also mit explizitem Typcast. Daraufhin verändert sich im ASM aber die
Registerauslastung und der ASM für 2.) sind danach so aus:
1
 480                 .L79:
2
 481 03a0 779B          sbis 46-0x20,7
3
 482 03a2 FECF          rjmp .L79
4
 483 03a4 8FB9          out 47-0x20,r24
5
 484 03a6 1596          adiw r26,5
6
 485 03a8 8C91          ld r24,X
7
 486 03aa 1597          sbiw r26,5

Das sind immerhin schon 4 Bytes und 2 Takte mehr als vorher !

So nun meine Frage: gibt es in der Art & Weise wie GCC einen C Source
in einen ASM übersetzt irgendeine Logik, eine nachvollziehbare Logik ?

Wie kann ich GCC sagen das er p[] in Register ZH:ZL speichern soll
damit er auch explizit per "LD reg,Z+constant" arbeitet.

Normalerweise hätte ich vermutet das der Compiler so smart genug ist
das selber zu erkenen. Macht er ja auch, aber nicht immer und nicht mit
nachvollziehbarer Logik.

Gruß Hagen

von Hagen R. (hagen)


Lesenswert?

Ok, habs selber gefunden

  register uint8_t* p asm("r30") = (uint8_t*)&DisplayRAM[i];

hilft.

Gruß Hagen

von A.K. (Gast)


Lesenswert?

Einfache Compiler => einfache Regeln.
Komplexe Compiler => komplexe Regeln.

Wenn du einen einfach nachvollziehbaren Compiler willst, nimm nicht
GCC, sondern z.B. SDCC. Am GCC wird seit 2 Jahrzehnten herumgefeilt mit
Optimierungen aller Art. Mit Architekturen wie PowerPC, Itanium usw. im
Auge, AVR ist da nur ein kleines Nebenprodukt.

von Hagen R. (hagen)


Lesenswert?

Naja halb so wild, soooo schlecht ist der GCC nun auch wieder nicht.
Wahrscheinlich ist es einfach so das mir noch die "Erfahrungen" mit
dessen Eigenheiten fehlen ;) Es ist halt so das mir beim Anblick so
mancher LST Datei des GCCs sofort einige Optimierungen auffallen die im
Grunde selbstverständlich sein sollten. Nun möchte man aber den Vorteil
eines C Sources nicht zerstören indem man dann doch wieder alles in
Assembler codet oder aber im C Source lauter Typcast, register,
volatile Deklarationen reinbaut so das er nicht mehr lesbar ist.


Gruß Hagen

von Wolfram (Gast)


Lesenswert?

Ich verstehe nicht wo dein Problem liegt, du machst dir sorgen um ein
paar Takte aber wartest ständig auf die SPI schnittstelle. Was dauert
wohl länger???
und das ganze machst du auch noch in einem Timerinterrupt?
EIN INTERRUPT IST KEIN THREAD!!!!!
Du bist ganz offensichtlich im Timer OCR Modus. Wenn der Interrupt
ausgelöst wird setzt dein Timer automatisch seinen Counter auf 0 und
zählt weiter noch während du im Interrupt bist. Sollte er wieder OCRB
erreichen wird eine Interruptbedingung erfüllt. Je nachdem ob du noch
im Interrupt bist und das Interruptflag gesetzt ist wirst du den
folgenden verpassen. Solltest du noch von irgendwo anders auf die
SPI-schnittstelle zugreifen wird es richtig interessant.
Setze ein Flag in der Interruptroutine und polle dieses im
Hauptprogramm, wenn es gesetzt ist greife auf die SPI-schnittstelle
zu.
(Flag mit volatile deklarieren)

von Stefan K. (_sk_)


Lesenswert?

@Wolfram:
ganz so schlimm ist das nicht. Der SPI kann bis zu Quarzfrequenz/2
konfiguriert werden. Dann braucht 1 * Senden 16 Takte. So schnell
schaffst Du es nicht, einen IR zu beenden und einen neuen zu starten -
jedenfalls nicht in C. Und Hagen macht es ja auch ganz richtig: nach
dem Starten per SPDR wird v schon wieder mit dem nächsten Wert geladen,
bevor auf SPIF gewartet wird. Die Wartezeit wird also möglichst immer
ausgenützt.

@Hagen:
Du kannst Dir das ganze Geshifte sparen, wenn Du DisplayRow statt um 1
um 8 erhöhst:

  uint8_t i = DisplayRow;
  i += 8;
  i &= ROW_MASK;            // nur zur Sicherheit, damit untere
                            // 3 Bits immer NULL sind (kann entfallen)
  if (i >= (15*8)){
    i = 0;
  }
  DisplayRow = i;

  PORTC = (PORTC & ~ROW_MASK) | i;

Gruß, Stefan

von Hagen R. (hagen)


Lesenswert?

@Wolfram: du kennst doch garnicht meinen gesammten Source und weist
somit auch nicht wie ich was konfiguriert habe.
Ja du hast Recht das ich dies in der ISR mache und ich tue dies aus
gutem Grunde. SPI läuft mit 8 Mhz, AVR mit 16 MHz, ergo warten die
Schleifen exakt 16 Takte. Der Timer läuft zwar mit Prescaler 1, aber
als TOP das OCR1A Register mit 10696 was dann ein Interval von 5ms
ergibt. Die Kommuniktation per SPI darf durch keine andere Sache in der
MCU unterbrochen werden, muß im zeitlichen Interval kontinuierlich
erfolgen. Fazit: alle 5 Millisekunden sendet der AVR per SPI 10 Bytes
was dann 625ns dauert und nicht durch andere ISRs wie die vom UART/ADC
unterbrochen werden darf. Die Kommmunikation über das SPI hätte auch
ausgelagert werden können, aber dann müsste sie denoch per cli/sei vor
Interrupts geschützt werden, zusätzlich synchronisiert werden über
Flags, und auf Grund der unterschiedlichen Priotitätenverteilung in der
Mainloop wäre denoch nicht sichergestellt das das Timing dann stimmt.
Also kann ich sie gleich in die ISR reinschreiben, das sparrt Resourcen
und ist strikter im Timing. Bei dieser Kommunikation geht es um die
multiplexte Ansteuerung eines LED Displays. Ein Ausfallen der
SPI-Kommunikation würde die Darstellung enorm beinflussen, da die
ON/OFF Steuerung sowie die Shiftregister-Latch-Steuerung des Displays
am OCR1B Pin hängt. Das Toggeln dieser Steuerleitungen hängt also
direkt mit dem Timer zusammen und die SPI Datenübertragung zwangsläufig
auch. Und ja, das SPI ist exklusiv nur für die Displaysteuerung
verantwortlich.

@Stefan: Das könnte ich machen, aber ;)
Erstens ist dieser Source ein vorläufiger auf einem Testboard. Auf dem
fertigen Board liegt der Spaltentreiber garnicht an PORTC sondern PORTA
und dort auf  PIN0 bis PIN3. Ich habe das also schon eingeplant.
Zweitens wird mit diesem Display-Spalten-Treiber auch ein analoger
Switch -> HC4051 angesteuert der über den ADC kapazitive Sensortasten
auswertet. Nun gleich nach dem obigen Sourcecode-Ausschnitt wird diese
Sensorauswertung mit dem ADC gestartet. Ergo: ich benötige i =
DisplayRow in obiger Form noch für andere Zwecke.

Es geht mir hier nicht um den Source im speziellen sondern nur um den
sich ergebenden Effekt im WinAVR GCC, und dieser kann in jedem Source
auftreten ;) Den Source sollte man eher als Beispiel für die
Begutachtung der Fähigkeiten des GCCs betrachten. Übrigens aus diesem
Source
1
SIGNAL(SIG_OVERFLOW1) {
2
3
    uint8_t i = DisplayRow;
4
    uint8_t v = i +1;
5
    if (v >= 15) v = 0;
6
// 1.)
7
    register uint8_t* p asm("r30") = (uint8_t*)&DisplayRAM[v];
8
    v = p[1];
9
    SPDR = v;
10
    v = p[0];
11
    while (!(SPSR & (1 << SPIF)));
12
    SPDR = v;
13
    v = p[3];
14
    while (!(SPSR & (1 << SPIF)));
15
    SPDR = v;
16
    v = p[2];
17
    while (!(SPSR & (1 << SPIF)));
18
    SPDR = v;
19
20
... hier gehts noich weiter !

erzeugt der GCC folgendes bei 1.)

[/code]
 483                 .L67:
 484 0398 8AE0          ldi r24,lo8(10)
 485 039a 989F          mul r25,r24
 486 039c D001          movw r26,r0
 487 039e 1124          clr r1
 488 03a0 A050          subi r26,lo8(-(DisplayRAM))
 489 03a2 B040          sbci r27,hi8(-(DisplayRAM))
// X -> Z
 490 03a4 FD01          movw r30,r26
// laden über X und mit 2 unnötigen Additionen !
 491 03a6 1196          adiw r26,1
 492 03a8 9C91          ld r25,X
 493 03aa 1197          sbiw r26,1

 494 03ac 9FB9          out 47-0x20,r25
// laden über X, obwohl im Source p[] -> register asm("r30")
explizit
 495 03ae 9C91          ld r25,X

 496                 .L68:
 497 03b0 779B          sbis 46-0x20,7
 498 03b2 FECF          rjmp .L68
 499 03b4 9FB9          out 47-0x20,r25
// super, endlich geschnallt das wollte ich schon vorher im ASM sehen
 500 03b6 9381          ldd r25,Z+3
[/code]

wie man sieht packt er die Berechunung des Zeigers p[] in Register X
rein um diesen dann nach Z zu kopieren. Nungut, dieser unnötige Umweg
würde mich ja nicht sonderlich stören wenn er nicht im ersten Zugriff
-> p[1] noch umständlich mit X hantieren würde und gleich später ganz
sauber mit LDD reg,Z+constant arbeiten würde. Defakto eine Inkonsistenz
in der Optimierung, bzw. Anti-Optimierung.

Bevor wieder gemeckert wird. Es geht mir hier nicht um den nonplusultra
optimierten Code, sondern um das Verständnis WIE der GCC zu WELCHEM
Zeitpunkt WAS macht. Denn das ist, meine ich, absolut unverhersehbar
beim GCC, und hat nichts mit "Optimierungen" zu tun.

Gruß Hagen

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


Lesenswert?

Wenn du mir den gesamten Code in compilierbarer Form mailst,
kann ich dir mal posten, was der GCC 4.1.0 draus machen
würde.

von Hagen R. (hagen)


Lesenswert?

Selbst ein

 volatile register uint8_t* p asm("r30") = (uint8_t*)&DisplayRAM[v];

verhindert nicht die Nutzung über das X Register im ASM.

Erst wenn man am Optimierungs-Level im Makefile rumändert ergibt sich
bei Level O1

509                 .L67:
 510 03d6 8AE0          ldi r24,lo8(10)
 511 03d8 989F          mul r25,r24
 512 03da C001          movw r24,r0
 513 03dc 1124          clr r1
 514 03de FC01          movw r30,r24
 515 03e0 E050          subi r30,lo8(-(DisplayRAM))
 516 03e2 F040          sbci r31,hi8(-(DisplayRAM))
 517 03e4 9181          ldd r25,Z+1
 518 03e6 9FB9          out 47-0x20,r25
 519 03e8 9081          ld r25,Z
 520                 .L68:
 521 03ea 779B          sbis 46-0x20,7
 522 03ec FECF          rjmp .L68
 523 03ee 9FB9          out 47-0x20,r25
 524 03f0 9381          ldd r25,Z+3

was super aussieht.
Bei O2 dann wieder

485                 .L72:
 486 039e 8AE0          ldi r24,lo8(10)
 487 03a0 989F          mul r25,r24
 488 03a2 D001          movw r26,r0
 489 03a4 1124          clr r1
 490 03a6 A050          subi r26,lo8(-(DisplayRAM))
 491 03a8 B040          sbci r27,hi8(-(DisplayRAM))
 492 03aa FD01          movw r30,r26
 493 03ac 1196          adiw r26,1
 494 03ae 9C91          ld r25,X
 495 03b0 1197          sbiw r26,1
 496 03b2 9FB9          out 47-0x20,r25
 497 03b4 9C91          ld r25,X
 498                 .L73:
 499 03b6 779B          sbis 46-0x20,7
 500 03b8 FECF          rjmp .L73
 501 03ba 9FB9          out 47-0x20,r25
 502 03bc 9381          ldd r25,Z+3
 503                 .L77:

Und bei O3 dann wieder ganz anderes, diesesmal nimmt er Y aber mit
Konstantem Offset !

491                 .L72:
 492 03aa 4AE0          ldi r20,lo8(10)
 493 03ac 949F          mul r25,r20
 494 03ae E001          movw r28,r0
 495 03b0 1124          clr r1
 496 03b2 C050          subi r28,lo8(-(DisplayRAM))
 497 03b4 D040          sbci r29,hi8(-(DisplayRAM))
 498 03b6 FE01          movw r30,r28
 499 03b8 3981          ldd r19,Y+1
 500 03ba 3FB9          out 47-0x20,r19
 501 03bc 9881          ld r25,Y
 502                 .L73:
 503 03be 779B          sbis 46-0x20,7
 504 03c0 FECF          rjmp .L73
 505 03c2 9FB9          out 47-0x20,r25
 506 03c4 9381          ldd r25,Z+3

bei Os dann eben

 483                 .L67:
 484 0398 8AE0          ldi r24,lo8(10)
 485 039a 989F          mul r25,r24
 486 039c D001          movw r26,r0
 487 039e 1124          clr r1
 488 03a0 A050          subi r26,lo8(-(DisplayRAM))
 489 03a2 B040          sbci r27,hi8(-(DisplayRAM))
 490 03a4 FD01          movw r30,r26
 491 03a6 1196          adiw r26,1
 492 03a8 9C91          ld r25,X
 493 03aa 1197          sbiw r26,1
 494 03ac 9FB9          out 47-0x20,r25
 495 03ae 9C91          ld r25,X
 496                 .L68:
 497 03b0 779B          sbis 46-0x20,7
 498 03b2 FECF          rjmp .L68
 499 03b4 9FB9          out 47-0x20,r25
 500 03b6 9381          ldd r25,Z+3

wobei ich diesen Modus voreingestellt hatte da er im Gesamtbild die
besten Resultate ergab.

Gruß Hagen

von Hagen R. (hagen)


Angehängte Dateien:

Lesenswert?

@Jörg:

super das ist doch mal ein Angebot, danke. Im Attachment die Sourcen.
Allerdings musst du dich da jetzt nicht sonderlich reinknienen da der
Source nur vor-experimenteller Natur ist. Ich studiere halt nur
parallel zur Entwicklung des C Sources wie sich der GCC im ASM verhält
damit ich ein Gefühl dafür bekomme. Das hat mir vor vielen Jahren auf
Intel CPUs, vor einigen Jahren auf Motorola CPUs geholfen und wird mir
heute beim GCC helfen. Letzendlich ist es nur ein par Minuten Aufwand
eine konzeptionell fertige ISR nach GCC-ASM zu portieren, dann haben
sich die "Probleme" erledigt.

Gruß hagen

von Hagen R. (hagen)


Lesenswert?

Das bsondere am AVR ist es ja das man Pi*Daumen behaupten kann "ein
kurzer Code ist auch ein schneller Code".

Gruß Hagen

PS: wollt ich nur mal los werden ;)

von Wolfram (Gast)


Lesenswert?

@Hagen:
deine Ausführungen zeigen das du das ganze durchgerechnet hast und das
du verstehst was du tust. Wenn ich den ganzen Source gesehen hätte
,hätte ich mir meine Bemerkung verkniffen.
Es kommt nur öfters vor, das einige Interrupts mit threads verwechseln
oder als Krönung noch ein printf in einem Timerinterrupt benutzen und
danach sah das Codestück aus.
Allerdings hast du einen Rechenfehler:
>Fazit: alle 5 Millisekunden sendet der AVR per SPI 10 Bytes
>was dann 625ns dauert und nicht durch andere ISRs wie die vom
UART/ADC
>unterbrochen werden darf.
10 Byte =80 Bit,SPItakt 8Mhz=0,125us
0,125ms*80=10us
natürlich immer noch im sicheren Bereich...
Wenn deine SPIübertragung mit voller Geschwindigkeit laufen soll ohne
unterbrochen zu werden benutze doch den SPItransferinterrupt. Das
könnte insgesamt besser werden.

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


Angehängte Dateien:

Lesenswert?

Compiliert leider nicht.  Erstens nimmst du Backslashes in

#include <avr\io.h>

Don't do that.  Windows akzeptiert auch Vorwärtsstriche, damit
machst du anderen die Arbeit leichter.  Dann geht's weiter mit:

uart.c: In function 'uart1_Read':
uart.c:457: error: assignment of read-only location

Habe mal das "const" da rausgeworfen, sieht irgendwie aber
suspekt aus.  Hier noch die Warnungen:

main.c:383: warning: passing argument 1 of 'uart1_Write' from
incompatible pointer type
main.c:384: warning: passing argument 1 of 'uart1_Write' from
incompatible pointer type
main.c:385: warning: passing argument 1 of 'uart1_Write' from
incompatible pointer type
main.c:386: warning: passing argument 1 of 'uart1_Write' discards
qualifiers from pointer target type
main.c:387: warning: passing argument 1 of 'uart1_Write' discards
qualifiers from pointer target type

Solltest du sicher auch auflösen.

Für meine Begriffe hast du den Code in main.c auch sehr reichlich
mit "volatile" ausgeschmückt...  Ich bin damit immer sehr
vorsichtig, weil das eben alle und jede Optimierung verhindert.

Der generierte Code für den Overflow-Handler ist im Anhang.

Btw., stell dich allmählich von SIGNAL uns SIG_XXX auf ISR
und XXX_vect um, wenn du neuen Code schreibst.

von Hagen R. (hagen)


Lesenswert?

@Jörg:

Sagte doch: das ist hingeschlampter Source um das SPI in Zusammenhang
mit der Auswertung kapazitiver tastenfelder zu testen ;)

>>>
uart.c: In function 'uart1_Read':
uart.c:457: error: assignment of read-only location

Habe mal das "const" da rausgeworfen, sieht irgendwie aber
suspekt aus.  Hier noch die Warnungen:
<<<

Danke, das const gehört da wirklich nicht rein.

>>>
Für meine Begriffe hast du den Code in main.c auch sehr reichlich
mit "volatile" ausgeschmückt...  Ich bin damit immer sehr
vorsichtig, weil das eben alle und jede Optimierung verhindert.
<<<

Das war auch der Sinn ;) Einfach um sicher zugehen und das ich mich auf
das Wesentliche -> die experimentelle Schaltung -> konzentrieren kann.
Wie man in den vorherigen Postings ersehen kann bin ich halt noch nicht
so sicher bei der Frage was macht GCC aus meinem Source, ergo um
unnötigen Ärger vorzubeugen die vielen volatiles. Ich mag es nicht
sonderlich wenn ich Fehler erstmal bei mir und meinem Source suche,
später feststellen zu müssen das irgendwas anderes, zb. der Compiler,
Mist gebaut hat. Das ist jetzt keine Unterstellung dem GCC gegenüber
sondern zb. Erfahrungen die ich erst letztens ständig mit einem
Compiler für Motorola CPUs für Palm Handhelds gemacht habe.


>>>
main.c:383: warning: passing argument 1 of 'uart1_Write' from
incompatible pointer type

Solltest du sicher auch auflösen.<<<

Korrekt, ist Debugcode der niemals in einem Produktivcode auftauchen
wird ;) Letzendlich macht der Compiler das daraus was ich ihm gesagt
habe.


>>>
Btw., stell dich allmählich von SIGNAL uns SIG_XXX auf ISR
und XXX_vect um, wenn du neuen Code schreibst.
<<<

Das ist mir neu, habe ich da was verpennt ?

@Wolfram: ja da hast du Recht (10*16*62.5ns == 10us), und die ISR wird
auch nicht alle 5ms aufgerufen sondern alle 666µs, sorry.

Gruß Hagen

von Hagen R. (hagen)


Lesenswert?

@Jörg: so obiger Post bezieht sich ja nicht auf das "Problem" selber
;)

Also
1
.L66:
2
        ldi r24,lo8(10)
3
        mul r25,r24
4
        movw r26,r0
5
        clr r1
6
        subi r26,lo8(-(DisplayRAM))
7
        sbci r27,hi8(-(DisplayRAM))
8
        movw r28,r26
9
        ldd r24,Y+1
10
        out 47-0x20,r24
11
        ld r24,X
12
.L68:
13
        sbis 46-0x20,7
14
        rjmp .L68
15
        out 47-0x20,r24
16
        movw r28,r26
17
        ldd r24,Y+3

hier scheint er aber wieder auf X zuzugreifen, was ich nicht ganz
nachvollziehen kann.
Wenn ich per

register type name asm("r30");

dem GCC explizit vorgebe das er das so benutzen soll, warum macht er es
dann nicht ?

Dann ist mir noch aufgefallen das die Berechnung von p[] eigentlich
auch ein par Umwege geht. Kann GCC nicht gleich die Berechnungen über
Register ZH:ZL machen ?

Zb. das "clr r1" ist doch überflüssig, oder ?

Gruß Hagen

von Hagen R. (hagen)


Lesenswert?

@Jörg:

vergiss den Rest das ist mir jetzt unwichtig.

>>>
Btw., stell dich allmählich von SIGNAL uns SIG_XXX auf ISR
und XXX_vect um, wenn du neuen Code schreibst.
<<<

Das ist wichtiger. Ich habe mir die WinAVR Page auf SourceForge
angeschaut, finde aber nichts darüber !?

Gruß Hagen

von Elektrikser (Gast)


Lesenswert?

Hallo,

ab AXR-LIBC Version 1.4 (oder früher?) wird das SIGNAL() als deprecated
bezeichnet. Man soll dann ISR() verwenden. Was sich da alles geändert
hat, findet man in der avr-libc-user-manual. Bei dem diesjährigen
Winavr ist das schon umgesetzt.

Ich bin auch gerade dabei ein paar alte Programme zu entrümpeln.

Gruß Elektrikser

von Hagen R. (hagen)


Lesenswert?

Habs, mittlerweile auch gefunden, nachdem ich trottel erstmal die
aktuelleste Version gezogen und installiert habe ;)

Gruß Hagen

von peter dannegger (Gast)


Lesenswert?

Man muß es dem GCC zugestehen, daß er an manchen Stellen ziemlich dumm
ist und nicht optimiert, sondern im Gegenteil sogar völlig nutzlos auf
16 Bit erweitert:
1
  if( (i & 0xFE) == 0xFE )
2
  fa:   99 27           eor     r25, r25
3
  fc:   8e 7f           andi    r24, 0xFE
4
  fe:   90 70           andi    r25, 0x00
5
 100:   8e 3f           cpi     r24, 0xFE
6
 102:   91 05           cpc     r25, r1
7
 104:   11 f4           brne    .+4
8
    PORTB = 0xFE;
9
 106:   8e ef           ldi     r24, 0xFE
10
 108:   88 bb           out     0x18, r24
11
 10a:   08 95           ret

Ist es nicht putzig, wie er R25 auf 0 setzt (eor r25,r25) und dann
sicherheitshalber gleich nochmal (andi r25,0x00). Danach zweifelt er
aber immer noch daran, daß es 0 ist und testet, ob es wirklich 0 ist
(cpc r25,r1).
Er hat nun zwar umständlich festgestellt, daß R24 = 0xFE ist, aber ist
sich wiederum nicht sicher und setzt es daher nochmals auf 0xFE.
Irgendwie scheint er keinerlei Vertrauen zu haben, daß der AVR Befehle
auch wirklich ausführt.

Manchmal kann er aber auch gut optimieren.



In Deinem Fall dürfte aber das SPI den Löwenanteil an CPU-Zeit
ausmachen (160 Zyklen), so daß eine Optimierung keinen merkbaren Effekt
hat. Du solltest daher der Lesbarkeit den Vorzug geben.


Ich würde aber beim Schreiben schon die Bytes in der richtigen
Reihenfolge ablegen, dann kann man statt Spaghetticode einfach ne
Schleife machen:

[/C]
SIGNAL(SIG_OUTPUT_COMPARE1A) {
  unsigned char i, *ptr;

  ptr = DisplayRAM + DisplayRow;

  for( i = 10; i; i-- ){
    while (!(SPSR & (1 << SPIF)));
    SPDR = *ptr++;
  }
}
[/C]


Peter

von Hagen R. (hagen)


Lesenswert?

Hi Peter,

Die Bytes liegen in korrekter Reihenfolge und werden auch in korrekter
Reihenfolge gesendet. Dazu ein bischen mehr Hintergrundinformationen.

5x 16 Bit Shiftregister, kaskadiert. Auf Grund ihrer Bauform und dem
Boardlayout steuert Register 5

Pixel 0 rot/grün, Pixel 1 rot/grün, Pixel 2 rot/grün usw.

Dabei liegt Pixel 0 rot am Ausgang 16, Pixel 0 grün am Ausgang 15,
Pixel 15 am Ausgang 14 usw.

Das SPI sendet LSB first.

Die Pixel sind im DisplayRAM als 16 Bit unsigned gespeichert, also Bit
0 DisplayRAM[0, 0] ist Pixel 0 rot, erste Zeile.

Das ist so konstruiert weil ich die SetPixel(X,Y,Farbe) Funktion schon
jetzt so geplant habe:

setPixel(x,y,Farbe)
{
  DisplayRam[y, X div 16] = Farbe << (x % 16)
}

Du kannst die vorstellen das div/mod 16 in ASM gut zu optimieren ist ->
SWAP.

Der versetzte Lesezugriff auf dem DisplayRAM erfolgt weil dadurch die
Warteschleife die aufs SPI wartet um 2 takte verkürzt wird. Ich lade
also, wenn ich noch aufs SPI warte schonmal das nächste Datenbyte. So
spare ich 2 taktzyklen in der Gesamtwartezeit.

Ist also alles in bester Ordnung:

1.) Datenstuktur im DiplayRAM ist optimiert auf effizientes Pixel
setzen
2.) Hardware ist angepasst auf optimales Boardlayout
3.) SPI sendet Daten so wie sie sein müssen das zum Schluß auch der
Pixel 0 -> DisplayRAM[0,0] bit 0,1 auch als Pixel links oben auf dem
Display leuchtet.

Dein Vorschlag mit der Schleife würde zwar weniger Code benötigen dafür
aber mindestens 2 Takte pro Byte mehr.

So, und wie ich es oben schon sagte: mir geht es nicht um meinen Code
ansich, ich weiß was ich wie tue und wenn ich nicht mehr weiter weis
frage ich euch, versprochen. In diesem Falle ging es mir nicht um den
Sinn meines Sources sondern um die Frage was der GCC daraus macht.

Gruß Hagen

von Hagen R. (hagen)


Lesenswert?

Achso: die 160 taktzyklen für das SPI lassen sich nicht vermeiden. Ist
einfach eine Timing Frage beim Refesh/Multiplexen des Displays. Davon
abgesehen ist das nicht weiter dramatisch da diese 10µs +50µs
kapazitive Sensortasten nur alle 666µs benötigt werden und den Rest der
Zeit verschläft der AVR eh meistens. Die par DFC77 Daten und das
aktualisieren der Uhrzeit und das Änderen der Darstellung auf dem
Display fallen zeitlich nicht ins Gewicht.

Gruß Hagen

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


Lesenswert?

Nochwas: wenn du dem Compiler mit asm("r30") irgendwie deinen
Willen von hinten herum aufdrängst, brauchst du allerdings
nicht wirklich erwarten, dass der Optimizer das in irgendeiner
Form in seine Betrachtung einzubeziehen in der Lage ist.  Solange
er auf eine derartige Optimierung nicht von selbst gekommen wäre,
wird er mit deiner ,,Hilfestellung'' nichts anfangen können.

Ansonsten hat der GCC-4-Code zwar ein paar überflüssige movw
r28,r26-Befehle, ansonsten fand ich ihn aber so schlecht nicht.
Insbesondere sind all die überflüssigen Schiebereien dort nicht
mehr drin.

Am Optimizer vom GCC 3 wird eh' keiner mehr was ändern, der wird
allerbestenfalls noch Bugfixes bekommen.

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.