Hallo,
ich kämpfe da gerade mit einem 'Absturz' meines C-Codes rum. Die CPU ist
ein AVR atxmega128a4u, Compilerversion ist 4.9.2 (das was man halt
aktuell bei Microchip runterladen kann), der Code läuft in der
App-Section, Füllgrad von Flash und Data unter 50%.
Die fragliche Stelle ist eine switch-Anweisung:
1
intrun_bridge_interface(void)
2
{
3
unsignedcharrxdat;
4
switch(bridge_if_state)
5
{
6
default:
7
return(-1);
8
caseBIF_START:
9
bif_init_uart();
10
bif_enable_rx();
11
bridge_if_state=BIF_GET_LENGTH;
12
return(0);
13
caseBIF_GET_LENGTH:
14
usw......
wobei bridge_if_state wie folgt definiert ist:
1
typedefenum{BIF_START,
2
BIF_GET_LENGTH,
3
BIF_COLLECT_MESSAGE,
4
BIF_CHECK_TX,
5
BIF_WF_TXCOMPLETE,
6
BIF_CLEAN_UP,
7
BIF_BROKEN}t_bridge_if_state;
Im Debugger sehe ich, dass der Prozessor bei der switch-Anweisung ins
Nirvana springt. Tante googel flüstert mir was von einem 'alten' Bug in
tablejump, deshalb habe ich dann mal die Compileroption -fno-jump-tables
ausprobiert und siehe da, das Switch-Statement geht jetzt.
Kann ich das noch genauer einkreisen / beschreiben, mache ich hier
selbst einen Fehler (DAU?) und falls es ein Bug in gcc ist, wo kann ich
das melden?
Wolfgang schrieb:> Tante googel flüstert mir was von einem 'alten' Bug in tablejump
Referenz für dieses Geflüster?
Das Einzige, was mir da in den Sinn käme ist das bekannte Problem,
wenn der Linker mitten in eine Sprungtabelle ein "Trampolin"
reinknallt. Das kann aber erst bei Controllern mit mehr als 128 KiB
Flash passieren (und dort auch nur einmal an jedem Vielfachen von
128 KiB). Damit fällt diese Variante bei dir aus.
Vorschlag: mach Nägel mit Köpfen und poste einen compilierbaren Code,
an dem man das Problem erkennen kann. Meistens sitzt das Problem am
Ende doch vor der Tastatur und nicht im Compiler …
Hallo,
ich würde es ja auch annehmen, dass das Problem vor der Tastatur ist ...
bei google hatte ich nach 'avr bug tablejump' gesucht und Einträge
gefunden, die auf die 64k Grenze zurückzuführen sind. Das hat mich dazu
gebracht, einfach mal -fno-jump-tables auszuprobieren.
Das komplette Projekt will ich nicht posten, aber ich kann mal die
Ausschnitte aus dem lss-File einkopieren:
Mit Jumptable:
Wolfgang schrieb:> ich würde es ja auch annehmen, dass das Problem vor der Tastatur ist ...> bei google hatte ich nach 'avr bug tablejump' gesucht und Einträge> gefunden, die auf die 64k Grenze zurückzuführen sind.
Habe mir das nochmal angesehen. Ja, 64 KiB könnte passen, allerdings
werden die Sprungtabellen aus ebendiesem Grunde vom Linkerscript
normalerweise direkt hinter die Vektortabelle gelegt. Das wird also
nur dann ein Problem, wenn du insgesamt mehr als knapp 64 KiB an
Sprungtabellen hast.
Das ist bei dir ganz bestimmt nicht der Fall. ;-)
Davon abgesehen: bei mir benutzt tablejump nur r30/r31. Bei dir
wird auch r24 noch gelöscht. Das deutet daraufhin, dass es sogar
mehr als 64 KiB behandeln kann.
> Das komplette Projekt will ich nicht posten, aber ich kann mal die> Ausschnitte aus dem lss-File einkopieren:
(Disassembly ohne eingestreuten Sourcecode ist einfacher zu lesen.)
> 2050: 80 91 c9 20 lds r24, 0x20C9 ; 0x8020c9 <bridge_if_state>> 2054: 90 e0 ldi r25, 0x00 ; 0> 2056: 87 30 cpi r24, 0x07 ; 7> 2058: 91 05 cpc r25, r1> 205a: 08 f0 brcs .+2 ; 0x205e> <run_bridge_interface+0x12>> 205c: 5d c1 rjmp .+698 ; 0x2318> <run_bridge_interface+0x2cc>> 205e: fc 01 movw r30, r24> 2060: 88 27 eor r24, r24> 2062: e2 50 subi r30, 0x02 ; 2> 2064: ff 4f sbci r31, 0xFF ; 255> 2066: 8f 4f sbci r24, 0xFF ; 255
Subtrahieren von 0xFFFF02 => Addieren von 0x0000FE. Dort steht
deine eigentliche Sprungtabelle. Diese wird dann vermutlich
Adressen ab 0x206a enthalten, allerdings durch 2 geteilt, also
0x1035 und knapp darüber. Das sind die Einsprungpunkte für den
eigentlichen Code.
Hallo Jörg,
das 0xFE muß man dann auch mal 2 nehmen, oder. Bei 0xFE liegt noch die
Interrupt-Sprungtabelle. Nach dieser Sprungtabelle beginnend ab 0x01FC
liegt dann der passende Sprungverteiler mit den 7 Adressen:
1
1f0:0c94f60ejmp0x1dec;0x1dec<__bad_interrupt>
2
1f4:0c94f60ejmp0x1dec;0x1dec<__bad_interrupt>
3
1f8:0c94f60ejmp0x1dec;0x1dec<__bad_interrupt>
4
1fc:3610cpser3,r6
5
1fe:6110cpser6,r1
6
200:b510cpser11,r5
7
202:4311cpser20,r3
8
204:6b11cpser22,r11
9
206:7411cpser23,r4
10
208:8211cpser24,r2
11
20a:722dmovr23,r2....
Das wären dann die Ziele 1036, 1061, 10b5 usw.
Servus Wolfgang
Hallo,
Header sind da, Funktionen sind deklariert.
Ich hatte zum Testen und zum Erhärten der Analyse da schon mal überall
vor der switch-Anweisung und gleich in jedem case Port-Wackler
reingebaut (also PORTx.OUTSET, delay_us(n), PORTx.OUTCLR). Mit
unterschiedlichen n lassen sich da Tracemarken setzen, die man mit dem
Scope gut in Echtzeit beobachten kann. Da sehe ich den Toggler noch vor
dem switch, aber in keinem case mehr.
Wie gesagt, compiliere ich mit -fno-jump-tables, dann läuft der Code.
Und das ist auch alter Code, den ich schon länger in Betrieb habe. Ich
habe in dem Projekt jetzt halt wieder was gemacht (an anderer Stelle)
und dann stürzte mir der Code ab - permanenter Reboot. Es hat etwas
gedauert, bis ich die Stelle mit dem Tschüss gefunden hatte.
Und wie immer halt die Frage, was wurde verändert? Ich hatte wegen einem
anderen Projekt auf die aktuelle Compilerversion upgedatet. Ich kann
auch mal versuchen, wieder auf eine ältere Compilerversion zu gehen.
Debugger ist momentan ein bischen schwierig, die Baugruppe ist verbaut
und die Schaltung hat keinen Erdbezug. Da müßte ich erst umbauen. Und in
der Zielhardware habe ich keinen JTAG-Zugang.
Servus Wolfgang
Hallo,
ich hätte mal probiert, eine ältere Version zu installieren -
Fehlermeldung, man kenne den 128a4u nicht.
Dann habe ich nochmal (zur Sicherheit) den Code mit Port.out statements
verwanzt, also zu Beginn einen Puls mit 2us, dann 1us, dann in JEDEM
case zu Beginn einen Puls:
1
t_cr_taskrun_bridge_interface(void)
2
{
3
unsignedcharrxdat;
4
PORTB.OUTSET=(1<<OUT0p);
5
delay_us(2);
6
PORTB.OUTCLR=(1<<OUT0p);
7
delay_us(1);
8
PORTB.OUTSET=(1<<OUT0p);
9
delay_us(1);
10
PORTB.OUTCLR=(1<<OUT0p);
11
12
switch(bridge_if_state)
13
{
14
default:
15
PORTB.OUTSET=(1<<OUT0p);
16
delay_us(1);
17
PORTB.OUTCLR=(1<<OUT0p);
18
return(-1);
19
caseBIF_START:
20
PORTB.OUTSET=(1<<OUT0p);
21
delay_us(1);
22
PORTB.OUTCLR=(1<<OUT0p);
23
bif_init_uart();
24
bif_enable_rx();
25
bridge_if_state=BIF_GET_LENGTH;
26
return(0);
27
caseBIF_GET_LENGTH:
28
PORTB.OUTSET=(1<<OUT0p);
29
delay_us(1);
30
PORTB.OUTCLR=(1<<OUT0p);
31
if(bif_rx_ready())// there are data
32
{
33
set_connect_status(BIF_CONNECTED);
34
last_bridge_rx=get_systick_int16();
35
rxdat=bif_rx_read();
36
if(rxdat)
37
{if(rxdat==BRIDGE_PING)
38
{// empty message
39
bridge_if_state=BIF_CHECK_TX;
40
return(0);// sofort umschalten (und hoffen,
41
// dass die StepControl auch schnell ist)
42
// return(1100L/SYSTICK_PERIOD);
43
}
44
else
45
{// real message
46
#if (DEBUG_BIF_RX == 2)
47
pc_send_hex(rxdat);
48
#warning testausgabe empfangenes Zeichen
49
#endif
50
bridge_rx_crc=read_crc_array(rxdat);
51
bridge_rx_paket[0]=rxdat;// save size
52
bridge_rx_total=rxdat+1;
53
bridge_rx_index=1;
54
bridge_if_state=BIF_COLLECT_MESSAGE;
55
return(0);
56
}
57
}
58
}
59
else
60
{
61
// nothing received, connection broken, we wait infinite
Es kommen nur die ersten 2 und dann reboot.
Übersetzen mit Option CFLAGS += -fno-jump-tables - schon sind 3 Pulse
da.
Soweit ich den Assembler verstehe: der Compiler legt die Sprungtabelle
bei 1FC an, die Adressen dort zeigen auch auf meine Cases.
Ehrlich gesagt bin ich mit meinem Latein am Ende und ich verstehe nicht,
warum das passiert. Und ich habe Riesenbammel, irgendwas an einem
bestehenden Projekt zu pflegen.
Servus Wolfgang
Wolfgang schrieb:> Kann ich das noch genauer einkreisen / beschreiben, mache ich hier
Der Compiler legte die Sprungtabellen nach .progmem.data, wenn diese
Section "überlief", d.h. nicht mehr per LPM lesbar war, wurde Unsinn
gelesen. Das betraf vor allem Code, der per -Ttext an eine andere
Stelle geschubst wurde (z.B. für einen Bootloader).
Das ist aber seit v4.9.2 behoben [*]
https://gcc.gnu.org/PR63223
Dann gabe es ein Problem wenn der switch-Wert > 16 Bits ist, behoben in
v7:
https://gcc.gnu.org/PR71676
Wegen eines Problemes mit const-merging wanderten die Sprungtabellen
dann nach .text. Dies war aber kein Problem mit switch / case sondern
mit der Ablage bestimmter Konstanten, Fix in v6.2:
https://gcc.gnu.org/PR71151
Schließlich wanderten die Sprungtabellen nach .jumptables, weil sie dort
besser aufgehoben sind (kein Verbrauch von .progmem.data, keine Sprünge
über die Tabellen hinweg). Also nur eine Optimierung, behoben in v8:
https://gcc.gnu.org/PR81075
[*] Alle Versionen bezogen auf FSF.
Wolfgang schrieb:> Und dann hangelt sich der Compiler halt so durch das switch-Statement> durch ...
In dem von dir gezeigten Code wird keine Tabelle verwendet, d.h.
zumindest für diese Codestelle hat -fno-jump-tables keinen Effekt: der
Compiler hat sich da für eine andere Strategie ohne Tabelle entschieden.
Johann L. schrieb:> In dem von dir gezeigten Code wird keine Tabelle verwendet,
Das sehe ich nicht so: da ist doch der Sprung nach _tablejump2_
drin.
Ich habe von Wolfgang mittlerweile den kompletten Code und das
generierte ELF-File und werde mir das mal ansehen.
Jörg W. schrieb:> Johann L. schrieb:>> In dem von dir gezeigten Code wird keine Tabelle verwendet,>> Das sehe ich nicht so: da ist doch der Sprung nach __tablejump2__> drin.
Ah ja, da:
Wolfgang schrieb:> 2060: 88 27 eor r24, r24> 2062: e2 50 subi r30, 0x02 ; 2> 2064: ff 4f sbci r31, 0xFF ; 255> 2066: 8f 4f sbci r24, 0xFF ; 255> 2068: 0c 94 1f 63 jmp 0xc63e ; 0xc63e <__tablejump2__>
Die gs() Tabelle befindet sich dann ab Flash-Adresse -2*(0xffff02) =
0x1fc. Zu prüfen wäre dann:
* Ob die richtige Multilib-Variante von tablejump2 vorliegt (avrxmega7).
* avrxmega7 hat RAM > 64 KiB, d.h. gcc geht davon aus dass RAMPD/X/Y/Z
null sind.
* .trampolines nicht-leer ist falls Code > 128KiB indirekt angesprungen
wird, d.h. Adressen solcher Funktionen genommen werden (was die
Sprungtabelle u.U. macht).
* Die Lokatierung von .trampolines konsistent mit EIND ist, welches aus
__vectors initialisiert wird.
Irgendwann gab's mal den Fehler, dass RAMP* in ISR in falscher
Reihenfolge restauriert wurden; war aber wimre transient und in keiner
Release.