Forum: Compiler & IDEs GCC Bug?


von Simon K. (simon) Benutzerseite


Lesenswert?

Versuche ich das SPI Dataregister (SPDR) in einer Funktion zu
beschreiben, ohne es vorher in der main() einmal beschrieben zu haben,
wird nichts auf den SPI Leitungen ausgegeben.

Habe ich schon bei 2 Programmen feststellen können.

Weiß jemand näheres dazu?

von Benedikt (Gast)


Lesenswert?

Poste mal den Code der Initialsiserung und der Ausgabe aufs SPDR
Register.

von Detlef A (Gast)


Lesenswert?

Was issn mit SPSR, Bit SPIF/WCOL (Mega 128 datasheet S.168), wartest Du
drauf, setzt Du zurück, Fragen über Fragen. Aber Compilerbug, nee!

Cheers
Detlef

von Simon K. (simon) Benutzerseite


Lesenswert?

Was? wo? nix Interrupt Flag !

Hier mal der Code:
1
int main(void)
2
{
3
       
4
       AD9832_DDR = (1<<AD9832_CS)|(1<<AD9832_MOSI)|(1<<AD9832_CLK);
5
       DDRD = (1<<PD1);
6
       
7
       SPCR = (1<<SPE)|(1<<MSTR)|(3<<SPR0);
8
       SPDR = 0xFF;       
9
10
11
       AD9832_PORT &= ~(1<<AD9832_CS);  
12
    
13
  AD9832_putc(0b11011000);
14
  AD9832_putc(0b00000000);
15
       
16
       AD9832_putc(0b11000000);
17
       AD9832_putc(0b00000000);   
18
19
            
20
       AD9832_putc(0b00110011);
21
       AD9832_putc(0b10100111);
22
       
23
       AD9832_putc(0b00100010);
24
       AD9832_putc(0b11000101);
25
       
26
  AD9832_putc(0b00110001);
27
  AD9832_putc(0b10100000);
28
  
29
  AD9832_putc(0b00100000);
30
  AD9832_putc(0b00000000);
31
  
32
33
       AD9832_PORT |= (1<<AD9832_CS);     
34
35
    
36
       while(1)
37
  {
38
  nop();
39
  }
40
}
41
42
43
44
void AD9832_putc(u08 c)
45
{
46
       while(!(SPSR & (1<<SPIF))) nop();                 // wait until
47
SPI transfer is complete
48
       SPDR = c;   
49
       while(!(SPSR & (1<<SPIF))) nop();                 // wait until
50
SPI transfer is complete    
51
}

Ohne dem SPDR = 0xFF in der Main klappt garnix am SPI.

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


Lesenswert?

Auf SPIF kannst du erst warten, wenn du etwas auf SPDR
ausgegeben hast, ansonsten ist es eifach erstmal noch nicht
gesetzt.

von Simon K. (simon) Benutzerseite


Lesenswert?

müsste ich das while vor dem SPDR in der Funktion einfach weglassen? ;)

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


Lesenswert?

Ja, es gehört nach der Ausgabe auf das SPDR hin (es sei denn,
man ist sich sicher, dass bis zur nächsten Ausgabe nach SPDR
sowieso genug Zeit vergeht, dass alle Daten rausgeschoben sind).

von Stefan K. (_sk_)


Lesenswert?

Ich schreibe beim Init des SPI einmal auf SPDR:
  SPDR      = 0;    // Dummy-Write, damit SPIF gesetzt wird

Dadurch kann ich VOR dem SPI-Transfer fragen und spare mir damit (ein
kleines bischen) Zeit.

Übrigens:
Schreibst Du gcc-Bug nur in den Titel, damit es mehr lesen, oder hast
Du das wirklich geglaubt? Ist wohl nicht Dein Ernst, dass eine so
offensichtliche Sache ein Compiler-Bug sein könnte?

Gruß, Stefan

von Detlef A (Gast)


Lesenswert?

Hi,

SPIF wird laut datasheet durch Lesen von SPSR gelöscht. Vermutung:
Schreiben auf SPDR scheint nicht zu gehen, wenn SPIF gesetzt ist,
deswegen dummy-read auf SPSR, selbst wenn man sicher ist, daß das Byte
draussen ist. Nachdem ich das gemacht habe, gings bei mir, habe aber
nich genauer draufgekuckt.

Cheers
Detlef

von Benedikt (Gast)


Lesenswert?

>Vermutung: Schreiben auf SPDR scheint nicht zu gehen, wenn SPIF gesetzt
ist
Das stimmt teilweise:
Wenn man einen Wert in SPDR schreibt, während ein Transfer läuft, wird
der neue Wert ignoriert. Ob das SPIF gesetzt oder gelöscht ist spielt
dabei aber keine Rolle. Ein dummy Read ist nicht notwendig.

Noch was interessantes:
Wie lange dauert es bis man neue Daten in SPDR schreiben kann, wenn der
Takt auf fclk/2 eingestellt ist ?
Theoretisch 16 CPU Takte.
In der Praxis sind es aber 18, ansonsten funktioniert es nicht. Keine
Ahnung warum, aber es ist so.

von peter dannegger (Gast)


Lesenswert?

In der Regel benötigen SPI-ICs auf /CS eine 1-0-Flanke nach Ende des
Datentransfers.

Es bringt also überhaupt nichts, vor dem Transfer auf das Ende des
letzten Transfers zu testen.

Daher einfach das Datenbyte ab in den Puffer und dann warten, bis es
raus ist.


Peter

von Simon K. (simon) Benutzerseite


Lesenswert?

Ich bin eigentlich der Ansicht das AVRGCC nicht mehr betreut wird. was
weiß ich ob da nen bug drin ist oder nicht.

@peter: ahoa, gut zu wissen. ich prüfe das mal bei meinem chip.


@alle: danke, weiß jetzt bescheid!

(danke frau rieger ;))

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


Lesenswert?

> Ich bin eigentlich der Ansicht das AVRGCC nicht mehr betreut wird.

Was bringt dich zu dieser Ansicht?

verwundert guck

von Simon K. (simon) Benutzerseite


Lesenswert?

Hab so den Eindruck, dass das unter die Räder gekommen ist. Wenn ich
schon den "Bug" (ist wohl eher kein feature?), dass als return wert
immer INT genommen wird, auch wenn man nur char brauch. Seh ich ehrlich
gesagt keinen Sinn drin

Aber ich kann mich auch täuschen ;)

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


Lesenswert?

> Wenn ich schon den "Bug" (ist wohl eher kein feature?), dass als
> return wert immer INT genommen wird, auch wenn man nur char brauch.

Ist ein Feature.  Soll die Benutzung von MOVW vereinfachen.  So haben
sich das Marek Michalkiewicz und Denis Chertykov zumindest mal
gedacht.

Da es dieser Art Bestandteil des ABIs geworden ist, dürfte es ziemlich
schwierig sein, dieses Feature wieder los zu werden.

von peter dannegger (Gast)


Lesenswert?

@Simon,

ja, der AVR-GCC ist sehr 16bit lastig. Wenns nur der Returnwert währe,
ginge es ja noch. Aber bei zusammengesetzten Ausdrücken erweitert er
oft erstmal schön umständlich alle Operanden auf 16Bit, rechnet 16Bit
und schmeißt dann das High-Byte wieder weg.
Selbst wenn man jeden einzelnen Ausdruck nach (unsigned char) castet,
läßt ihn das völlig kalt.
Erst wenn man eine Dummy-Variable nimmt, der man immer wieder die
Zwischenergebnisse zuweist, bequemt er sich zu 8-bittigem Rechen. Bloß
dann sieht die Source völlig unübersichtlich aus.


Ich vermute mal diese 16Bit-Umständlichkeit ist ein Zugeständnis an
kommerzielle Compilerhersteller, damit die ihre Produkte loswerden
können. Denn darin liegt mit Abstand das größte Potential, um Code zu
sparen und schneller zu sein.
Daher wird sich daran wohl auch in zukünftigen Versionen des AVR-GCC
leider nichts ändern.


Peter

von Simon K. (simon) Benutzerseite


Lesenswert?

Na super, naja dafür ists Freeware.

Was sind denn gute Compiler die sowas nicht machen und mindestens
genausogut zu bedienen sind wie ein AVR-GCC ? ;)

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


Lesenswert?

> Ich vermute mal diese 16Bit-Umständlichkeit ist ein Zugeständnis an
> kommerzielle Compilerhersteller, damit die ihre Produkte loswerden
> können.

> Daher wird sich daran wohl auch in zukünftigen Versionen des AVR-GCC
> leider nichts ändern.

Peter, meistens finde ich deine Beiträge gut und fundiert, auch wenn
ich viele Dinge anders angehe als du.

Aber hier hast du einfach nur riesigen Blödsinn von dir gegeben,
sorry, anders kann man das nicht ausdrücken.

Die 16Bit-Umständlichkeit ist in erster Linie das Erfordernis des
C-Standards.  Alle Ausdrücke mit ganzen Zahlen müssen erstmal im
Wertebereich von `int' ausgeführt werden (oder mehr, falls ein Term
mehr benötigt).

Wenn du drüber nachdenkst, wo der GCC herkommt (er war ursprünglich
ausschließlich für 32-bit-CPUs entwickelt worden), dann sollte dir
auch sofort klar werden, dass der AVR einer der wenigen vom GCC
unterstützten Prozessoren sein dürfte, bei dem es sich überhaupt
irgendwie lohnen würde, anschließend noch eine Optimierung einzubauen,
die die obere Hälfte des zu berechnenden int-Ausdrucks eliminiert,
sofern aus den übrigen Randbedingungen klar ist, dass man sie
weglassen kann, ohne die Standardkonformität zu gefährden.

Diese Optimierung muss wohl schlicht und ergreifend einfach mal jemand
schreiben -- dabei muss sie so geschrieben werden, dass sie sonst
nichts kaputt macht, d.h. sie muss von ausgiebigen Testscripts
begleitet sein, die nachweisen, dass die Standardkonformität davon
nicht gefährdet wird.  Ohne diese Testscripts bekommst du bei GCC (aus
hoffentlich verständlichem Grund) nichts akzeptiert, was mehr als nur
einen offensichtlichen Tippfehler korrigiert.  Zum Glück, kann ich nur
sagen: meinen 0b-Konstanten-Patch durfte ich nach dem Erstellen der
Testscripts auch nochmal reparieren...

Mit einer Gesamtquantität von vielleicht drei oder vier GCC-Hackern,
die sich mit dem AVR auskennen (Denis Chertykov, Marek Michalkiewicz,
Björn Haase, Svein Seldal fallen mir dabei so ein) ist es schlicht
eine Frage der Manpower und der Prioritäten, wann ein derartiger Patch
mal fertig sein könnte.  Es gibt durchaus Dinge, nach denen viel mehr
Leute rufen als nach den halben 16 bits: die Einführung von memory
spaces (RAM vs. ROM vs. EEPROM vs. ...) zum Beispiel (dafür gibt's
wohl einen standard proposal) oder den Support für ATmega256x.

Rücksichtnahme auf irgendwelche kommerziellen Konkurrenten, noch dazu
für einen Prozessor, der bei GCC eher unter ,,ferner liefen''
rangiert, ist sicher alles andere als ein Entwicklungsziel von GCC.
Ganz davon abgesehen, der IAR schlägt ihn so ziemlich immer
(wenngleich ich merken musste, dass das für die Codegeschwindigkeit zu
meiner Verwunderung schon nicht so völlig gesetzt ist), aber die
anderen Compiler dürften von ihrer Gesamtqualität (Standard-
konformität, generierte Codegröße und -geschwindigkeit) bis auf
pathologische Fälle, die sich natürlich immer konstruieren lassen,
zumindest nicht nennenswert besser als AVR-GCC sein.

von Patrick D. (oldbug) Benutzerseite


Lesenswert?

...es gibt da ja noch '-mint8'. Damit verschwinden auch die
überflüssigen 'hi8(x)' konstrukte. Ich habe allerdings noch keinen
blassen schimmer, was das an Nebenwirkungen mit sich bringt. Vielleicht
kannst Du, Jörg, da ganz kurz was zu schreiben. Ich stelle mir das so
vor, daß eben 8Bit berechnungen in einem extra Modul implementiert
werden, welches mit dieser Option übersetzt wird. Natürlich nur, wenn
man das auch wirklich braucht.

Kleines Beispiel?
1
int
2
main(void)
3
{
4
        unsigned char x = 8;
5
        volatile unsigned char y = 0;
6
7
        y = x * 2;
8
9
        return 0;
10
}

1. Kompiliert mit:

$ avr-gcc -S -gdwarf-2 -Os -omint8.S mint8.c
1
main:
2
.LFB2:
3
.LM1:
4
/* prologue: frame size=1 */
5
  ldi r28,lo8(__stack - 1)
6
  ldi r29,hi8(__stack - 1)
7
  out __SP_H__,r29
8
  out __SP_L__,r28
9
/* prologue end (size=4) */
10
.LM2:
11
  std Y+1,__zero_reg__
12
.LM3:
13
  ldi r24,lo8(16)
14
  ldi r25,hi8(16)
15
  std Y+1,r24
16
.LM4:
17
  ldi r24,lo8(0)
18
  ldi r25,hi8(0)
19
/* epilogue: frame size=1 */
20
  rjmp exit
21
/* epilogue end (size=1) */
22
/* function main size 11 (6) */

2. Kompiliert mit:

$ avr-gcc -mint8 -S -gdwarf-2 -Os -omint8.S mint8.c
1
main:
2
.LFB2:
3
.LM1:
4
/* prologue: frame size=1 */
5
  ldi r28,lo8(__stack - 1)
6
  ldi r29,hi8(__stack - 1)
7
  out __SP_H__,r29
8
  out __SP_L__,r28
9
/* prologue end (size=4) */
10
.LM2:
11
  std Y+1,__zero_reg__
12
.LM3:
13
  ldi r24,lo8(16)
14
  std Y+1,r24
15
.LM4:
16
  ldi r24,lo8(0)
17
/* epilogue: frame size=1 */
18
  rjmp exit
19
/* epilogue end (size=1) */
20
/* function main size 9 (4) */

von peter dannegger (Gast)


Lesenswert?

@Jörg,

manchmal provoziere ich gerne ein bischen :)

Hier mal zwei Beispiele für die 16Bit-Umständlichkeit:
1
00000062 <swap_odd_even>:
2
3
unsigned char swap_odd_even( unsigned char a )
4
{
5
  a = ((a & 0x55) << 1) | ((a & 0xAA) >> 1);
6
  62:   99 27           eor     r25, r25
7
  64:   9c 01           movw    r18, r24
8
  66:   25 75           andi    r18, 0x55       ; 85
9
  68:   30 70           andi    r19, 0x00       ; 0
10
  6a:   22 0f           add     r18, r18
11
  6c:   33 1f           adc     r19, r19
12
  6e:   8a 7a           andi    r24, 0xAA       ; 170
13
  70:   90 70           andi    r25, 0x00       ; 0
14
  72:   95 95           asr     r25
15
  74:   87 95           ror     r24
16
17
  return a;
18
}
19
  76:   82 2b           or      r24, r18
20
  78:   93 2b           or      r25, r19
21
  7a:   08 95           ret
22
23
0000007c <swap>:
24
25
26
unsigned char swap( unsigned char a )
27
{
28
  a = (a >> 4) | (a << 4);
29
  7c:   28 2f           mov     r18, r24
30
  7e:   22 95           swap    r18
31
  80:   2f 70           andi    r18, 0x0F       ; 15
32
  82:   99 27           eor     r25, r25
33
  84:   64 e0           ldi     r22, 0x04       ; 4
34
  86:   88 0f           add     r24, r24
35
  88:   99 1f           adc     r25, r25
36
  8a:   6a 95           dec     r22
37
  8c:   e1 f7           brne    .-8             ; 0x86 <swap+0xa>
38
  8e:   28 2b           or      r18, r24
39
40
  return a;
41
}
42
  90:   82 2f           mov     r24, r18
43
  92:   99 27           eor     r25, r25
44
  94:   08 95           ret

Daß bei 16Bit ein simples swap in eine Zählschleife mutiert ist
besonders extrem.

Aber das ist nicht immer der Fall, manche Ausdrücke optimiert er
ziemlich gut.
Es ist aber schwer, dahinter ein System zu finden. Ich glaube bei
Schiebeoperationen oder in if-Ausdrücken erweitert er besonders gerne
auf 16Bit.


Peter

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


Lesenswert?

Ich weiß, dass es die ,,16-Bit-Umständlichkeiten'' gibt.

Ich wollte nur klarstellen, dass deine Verschwörungstheorien
kompletter Unsinn sind.

Wenn du da mit helfen willst, die Optimierung zu verbessern: nur zu.
Nein, ,,ich weiß doch gar nicht, wie Compiler funktionieren!'' ist
keine zugelassene Ausrede.  Zur Geburt wussten wir's alle nicht,
dennoch gibt es Leute, die sich da reingedacht haben -- viel mehr als
die, die den GCC ursprünglich geschrieben haben.  Kann also nicht
undurchdringlich sein.

Wenn es mal ein Dutzend AVR-GCC-Hacker statt einer Hand voll gibt,
wird sich auch die Codeoptimierung verbessern.  Ansonsten
interessieren den größten Teil der GCC-Hacker wohl einfach nur die
`mainstream'-Targets (IA32, AMD64, vielleicht noch UltraSPARC,
PowerPC
und ein bisschen MIPS).  Deren Optimierungen am Compiler werden also
alle nur an diesen Prozessoren gemessen.

von A.K. (Gast)


Lesenswert?

AVR ist als GCC Target insofern eine Besonderheit (ebenso HC12), als die
Zielmaschinen nicht mit Wortbreite arbeitet, sondern mit halber
Wortbreite. Nun sind Shift-Operatoren nicht so leicht wegoptimierbar
wie Und/Oder. So ist byte(irgendwas|1) immer identisch mit
byte(irgendwas)|1, aber bei byte(irgendwas>>1) klappt das eben nicht
mehr.

Sicher, man könnte in den Compiler eine eher maschinenunabhängige
Funktion einbauen, die ausgehend vom Typ des Ergebnisses alle Ausdrücke
auf minimale Breite runterstuft, bei der das ohne Verfälschung vom
Ergebnis geht. Aber diese Stufe wäre auf allen "normalen"
Zielmaschinen bestenfalls überflüssig, oft sogar kontraproduktiv. Ohne
eine solche Stufe sind 8bit-Optimierungen immer nur Einzelaktionen für
bestimmte Fälle und blähen die Code-Templates auf.

von ,,,, (Gast)


Lesenswert?

> GCC Bug?

Ja, GCC ist ein Bug.

von peter dannegger (Gast)


Lesenswert?

@A.K.

wenn man sich mal diese Zeile ansieht:
1
  78:   93 2b           or      r25, r19

dann muß der Compiler aber schon sehr viel mitrechnen, damit er weiß,
daß R25 und R19 unabhängig von a an dieser Stelle immer 0 sind, denn
bei der 16Bit-Erweiterung eines unsigned char muß das High-byte ja
immer 0 sein.

D.h. er muß erkannt haben, daß vor dem Schieben um 1 mit 0x55 maskiert
wurde und so kein High-Byte ungleich 0 ensteht.

Dieser Aufwand erscheint mir viel größer, als sich gleich den Zieltyp
zu merken.


Bei der unteren Routine merkt er aber, daß ein High-Teil ungleich 0
ensteht und schmeißt ihn daher weg:
1
  92:   99 27           eor     r25, r25


Peter

von A.K. (Gast)


Lesenswert?

"und schmeißt ihn daher weg"

Das hat nichts mit Analyse zu tun. "a" ist 8bittig deklariert,
return(a) ist per ABI 16bittig, also steht da eine 8->16bit
Konvertierung von R18 nach R25:R24.

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.