Das uint64_t braucht schonmal 256byte SRAM und ~4kB Flash.
Dann wird bei jedem Aufruf gepusht und ein Stackframe angelegt, wie
verrückt.
Wenn dann noch ne 2.Instanz im Interrupt läuft, kann der SRAM schon mal
knapp werden und der Stack überschreibt Daten.
Außerdem braucht uint64_t im Interrupt viele CPU-Zyklen. Es kann daher
auch sein, daß die Interrupts zu oft kommen und welche verloren gehen.
Es kann gut sein, daß sogar double (= float) schneller und sparsamer
ist, als uint64_t.
Zumindest braucht es weniger Flash (~1kB).
Peter
Peter Dannegger schrieb:> Das uint64_t braucht schonmal 256byte SRAM und ~4kB Flash.
warum brauchen 64bit denn 256byte ram? Oder meinst du die passenden
routinen zum rechnen?
Peter II schrieb:> warum brauchen 64bit denn 256byte ram? Oder meinst du die passenden> routinen zum rechnen?
Weil das m.W. generischer nicht maschinenoptimierter Code ist, der vom
Compiler automatisch zur Verfügung gestellt wird.
Mir ist auch schon aufgefallen, dass __udivdi3 lange dauert. Allgemein
erzeugt der GCC im Zusammenhang mit uint64_t sehr schlechten Code.
Der Stack überläuft nicht, habe ich untersucht. Es sind ja auch nicht
die statischen Variablen die überschrieben werden, sondern die auf dem
Stack (fast am Anfang).
GCC ist freie Software. Jeder der möchte, und dem das Thema wichtig
genug ist, kann dort mit Bugfixes, Optimierungen und Erweiterungen
beitragen.
Wem __udivdi3 nicht zu pass ist, kann sich auch selbst einklinken:
Es geht mir eigentlich nicht um die Codegrösse oder um die
Geschwindigkeit, sondern um eine Lösung für dieses Problem.
Wenn es wirklich nur daran liegt, dass die Funktion nicht reentrant ist,
könnte sich Problem eventuell einfach beheben lassen. Leider kenne ich
mich in der libc nicht aus. Kann mir jemand sagen wo __divdi3
implementiert ist?
Johann L. schrieb:> Wem __udivdi3 nicht zu pass ist, kann sich auch selbst einklinken:
Ooch nöö :-(
Daß diese geheimen Tricks einem niemand von den GCC-Gurus sagen kann.
Ich dachte immer, daß man einen total neuen WINAVR backen muß.
1
Ohne my64bit.S
2
===================================
3
4.3.3
4
AVR Memory Usage
5
----------------
6
Device: atmega8
7
8
Program: 7872 bytes (96.1% Full)
9
(.text + .data + .bootloader)
10
11
Data: 280 bytes (27.3% Full)
12
(.data + .bss + .noinit)
13
14
15
Mit my64bit.S
16
====================================
17
4.3.3
18
AVR Memory Usage
19
----------------
20
Device: atmega8
21
22
Program: 648 bytes (7.9% Full)
23
(.text + .data + .bootloader)
24
25
Data: 24 bytes (2.3% Full)
26
(.data + .bss + .noinit)
Ist aber nur so schnell hingeschludert. Also nicht getestet und nicht
optimiert.
Vielleicht kanns ja mal jemand ausprobieren und berichten.
Peter
Hallo Peter,
ich habe mich noch für die Ausführungsgeschwindigkeit bei der Berechnung
des FTW für einen DDS-Baustein interessiert.
Timer1 dient mir dabei als Zähler mit Prescaler 1 und F_CPU = 14745600.
1) Mit der 64-Bit Lib von PeDa wurde das FTW berechnet.
1
constuint64_td=0x000100000000;
2
constuint64_tdds_takt=F_MHZ(180);
3
uint64_tfreq=F_MHZ(10);
4
5
start_t1_counter();
6
uint64_tftw=d*freq;
7
ftw=ftw/dds_takt;
8
stop_t1_counter();
stop_t1_counter() ist ein Macro und Stoppt den 16-Bit Timer1 und den
16-Bit Überlaufzähler.
Die Zeiten von start_t1_counter() und stop_t1_counter() wurden nicht mit
den Messergebnissen verrechnet.
64-Bit PeDa: *4,600ms*
2) Als Kettenbruch-Entwicklung benötigt die Berechnung des FTW mit
32-Bit Zahlen:
32-Bit Kettenbruch: *4,447ms*
Hier der Code
1
constuint32_tfreq32=F_MHZ(10);
2
uint32_tftw32=0;
3
4
start_t1_counter();
5
ftw32=freq32*24ul;
6
ftw32-=freq32/7ul;
7
ftw32+=freq32/264ul;
8
ftw32-=freq32/761203ul;
9
stop_t1_counter();
3) Matthias Hopf hat auch eine 64-Bit Lib geschrieben
- http://www.mshopf.de/proj/avr/uint64_ops.html
Damit ist die FTW Berechnung auch schnell möglich.
64-Bit M. Hopf: *4,592ms*
Hier der Code
@Uwe: Die Dauer von Algorithmen ist besser per verbrauchter Ticks zu
vergleichen, da diese unabhängig sind von der verwendeten
Takteinstellung — zumindest wenn man Implementierungen für AVR
vergleicht.
Für Peters Variante mit 4.6 ms sind das bei F_CPU = 14.7456 MHz rund
67000 Ticks.
Kann das sein? So grob über den Daumen würde ich für eine 64-Bit
Division 3000 ± 1000 Ticks veranschlagen.
Uwe S. schrieb:> 3) Matthias Hopf hat auch eine 64-Bit Lib geschrieben>> - http://www.mshopf.de/proj/avr/uint64_ops.html
Zumindest die Verwendung von Inline-Assembler ist nicht korrekt und bei
Gelegenheit gibt es daher falschen Code, d.h. nicht wie vom Autor
angedach.
Hallo Johann,
vielen Dank für überprüfen der Ergebnisse, du hast recht, die Zeiten/
Ticks stimmen nicht.
Ich musste noch das ISR Flag in der Timer1 Tickroutine löschen.
1
TIFR1=_BV(TOV1);
Hier die aktuellen Messwerte für die Ticks:
15 Tick für Start - Stop direkt nacheinander, wurde nicht heraus
gerechnet !
1514 Tick 64-Bit Lib von PeDa
2137 Tick 64-Bit Lib von M. Hopf
1303 Tick 32-Bit Kettenbruch, de0508
5495 Tick 32-Bit Schleife, de0508
Somit kann ich Peters Arbeit nur sehr loben und hoffe, dass der Code
noch weiter entwickelt wird.
> Viel eher geht dir einfach der Stack aus, der WDT ist zu> knapp eingestellt, oder es schlägt ein Compilerfehler zu wie
Habe das mit dem Stack noch einmal angeschaut. Habe das nach Vorschlag
von Peter (siehe Beitrag "Wie Stackermittlung ?") gemacht.
Der Stack beginnt bei 0x5FFF und endet bei 0x4C5A. Nach Auftreten des
Fehlers wurde der Stack bis zur Adresse 0x5C77 verwendet. Der WDT wird
nicht ausgelöst.
Habe Versucht das Problem mit einem kleinen Programm zu reproduzieren
leider ohne Erfolg. Werde in den nächsten Tagen noch weitere Versuche
durchführen.
Uwe S. schrieb:> Somit kann ich Peters Arbeit nur sehr loben und hoffe, dass der Code> noch weiter entwickelt wird.
Was mich etwas wurmt ist, daß ich den entscheidenden Tip nicht schon
eher bekommen habe.
Statt dessen habe ich solche "hilfreichen" Tips bekommen, wie: Schmeiß
Windows runter, installier Linux und compilier Dir Deinen AVR-GCC
selber.
Ich will keinem einen Vorwurf machen, für die Experten ist der Trick
wohl zu offensichtlich, die denken, das weiß doch jeder.
Eine Erweiterung auf signed und Schiebeoperatoren wäre möglich.
Dann müßte mir aber jemand helfen, daraus ne Lib zu machen, damit nur
die benötigten Funktionen gelinkt werden.
Die Addition/Subtraktion/Vergleich sind auch recht umständlich, aber
daran kann man nicht drehen, die werden direkt inline erzeugt.
Es sei denn, jemand weiß auch dafür ne Möglichkeit (ohne Linux
installieren zu müssen).
Peter
dude schrieb:> Der Stack beginnt bei 0x5FFF und endet bei 0x4C5A. Nach Auftreten des> Fehlers wurde der Stack bis zur Adresse 0x5C77 verwendet.
Dann ist doch der Stack o.k.
Peter
Ich hab mich auch mal an 64-Bit Disvision/Rest gemacht. Die Grafik zeigt
die Ergebnisse für signed 64-Bit Division. Die angerissenen Quadrate
haben eine Kantenlänge von 1000 Ticks.
1
| x | y
2
------------+---------+----------------
3
Dunkelgrün | GCC | ASM #0
4
Rot | GCC | ASM #1
5
Blau | GCC | ASM #2
6
Hellgrün | ASM | ASM #2
7
Violett | ASM #1 | ASM #2
8
9
GCC: avr-gcc 4.7
10
ASM #0: "normale" Routine, analog zum libgcc-Code für long
11
ASM #1: Ditto, aber getuned auf Speed
12
ASM #2: Ditto, noch mehr gepimpt
Was auf der ersten Winkelhalbierenden liegt bringt also keinen
Zeitgewinn. Was darunter liegt, bringt Zeitgewinn. Zum besseren
Vergleich sind Ursprungsgeraden eingezeichnet für Verhältnisse von
0.1, 0.2, 0.3, ... 1.0 fett, 2.0, 3.0, ... 10.0
Zeiten gemessen und Grafik erzeugt mit ATmega168.
Johann L. schrieb:> Was auf der ersten Winkelhalbierenden liegt bringt also keinen> Zeitgewinn. Was darunter liegt, bringt Zeitgewinn. Zum besseren> Vergleich sind Ursprungsgeraden eingezeichnet für Verhältnisse von>> 0.1, 0.2, 0.3, ... 1.0 fett, 2.0, 3.0, ... 10.0
^^^^^^^^^^^^^^^^^^
Käse. Die Ursprungsgeraden über der Winkelhalbierenden stehen für
10/9 ≈ 1.11
10/8 ≈ 1.25
10/7 ≈ 1.43
10/6 ≈ 1.67
10/5 = 2
10/4 = 2.50
10/3 ≈ 3.33
10/2 = 5
und 10 (steilste).
Peter Dannegger schrieb:> dude schrieb:>> Der Stack beginnt bei 0x5FFF und endet bei 0x4C5A. Nach Auftreten des>> Fehlers wurde der Stack bis zur Adresse 0x5C77 verwendet.>> Dann ist doch der Stack o.k.>>> Peter
Um nochmal was zu Ursprungspost zu sagen.
Man kann nur feststellen, ob der Stack globale Daten überschreibt.
Lokale Daten liegen dagegen mitten im Stack zusammen mit den gesicherten
Registern und Returnadressen.
Man kann höchstens testen, ob auf lokale Daten kleiner SP gezeigt wird.
Der aktuelle SP muß immer die kleinste Adresse sein.
Wird ein lokaler Datenframe angelegt, zeigt der SP immer dahinter, also
darunter (Stack wächst ja nach unten).
Wenn der Fehler nur bei Interrupts auftritt, würde ich mir mal das
Assemblerlisting des Interrupthandlers gründlich ansehen, ob auch alle
benutzten Register gesichert werden.
Und bei Unterprogrammen müssen alle Scratchpadregister gesichert werden.
Unterprogramme sollte man in Interrupts daher nach Möglichkeit
vermeiden.
Peter
dude schrieb:>> Viel eher geht dir einfach der Stack aus, der WDT ist zu>> knapp eingestellt, oder es schlägt ein Compilerfehler zu wie>> Habe das mit dem Stack noch einmal angeschaut. Habe das nach Vorschlag> von Peter (siehe Beitrag "Wie Stackermittlung ?") gemacht.
Für diesen Zweck verwende ich die Routine aus [1], die auch den
Platzverbrauch von ISRs erkennt.
IdR wird man den Platzverbrauch nämlich nicht in einer ISR ausdrucken
wollen bzw. man will Informationen über den Worst-Case-RAMverbrauch.
Genau den Zeitpunkt, wann dieser Worst-Case eintritt, wird man aber
schwerlich erwischen.
Diese Routine schützt natürlich nicht vor einem Programmabsturz in einer
ISR (aus welchem Grund auch immer), d.h. das Programm muss mindestens zu
der Stelle kommen, wo es den Verbrauch bestimmen/ausgeben soll.
Was ISRs und das ganze Programm angeht, so sollte man mindestens eine
grobe Vorstellung davon haben, wieviel RAM verbraucht wird. float- und
64-Bit Arithmetik in einer ATtiny2313-ISR wären nicht wirklich
zielführend.
Weiteres Problem mit ISRs kann sein, daß auf bestimmte Variablen nicht
atomar zugegriffen wird.
[1]
http://www.rn-wissen.de/index.php/Speicherverbrauch_bestimmen_mit_avr-gcc#Dynamischer_RAM-VerbrauchPeter Dannegger schrieb:> Johann L. schrieb:>> Wem __udivdi3 nicht zu pass ist, kann sich auch selbst einklinken:>> Ooch nöö :-(>> Daß diese geheimen Tricks einem niemand von den GCC-Gurus sagen kann.> Ich dachte immer, daß man einen total neuen WINAVR backen muß.
Naja, nun kennst du ja den "geheimen" Trick (auch wenn ich kein GCC-Guru
bin). Das sind die Geheimnisse der binutils und wie Objekte/Bibliotheken
gelinkt werden. Wer zuerst kommt, mahlt zuerst.
Peter Dannegger schrieb:> Was mich etwas wurmt ist, daß ich den entscheidenden Tip nicht schon> eher bekommen habe.
Die libgcc enthält Code, der zu aufwändig ist, als daß GCC ihn sinnvoll
inline ausgeben könnte. Einige Funktionen darin haben non-ABI-Interfaces
so daß man trefflich auf der Nase landen kann wenn man das nicht weiß.
Nehmen wir an, du möchtest auch die 8-Bit Division ersetzen und
verfährst analog, indem du __divmodqi4 ersetzt. Böse Falle, denn der
Compiler weiß, daß diese Funktion weder X nach Z ändert und Werte in
diesen Registern einen Aufruf unbeschadet überstehen.
Wenn du in deiner Implementierung aber vom Standard-ABI ausgehst und X
oder Z verwendet, ohne sie zu restaurören, fliegt dir alles um die
Öhren.
> Eine Erweiterung auf signed und Schiebeoperatoren wäre möglich.> Dann müßte mir aber jemand helfen, daraus ne Lib zu machen, damit nur> die benötigten Funktionen gelinkt werden.
Shifts sind bereits in [2] implementiert.
Die Arbeitsversion meiner 64-Bit Div/Mod ist im Anhang. Ob ich die Zeit
bekomme, das einzuspielen, weiß ich noch nicht. Es gibt nich so viel zu
tun in avr-gcc, so daß diese Sachen keine hohe Prio haben. Und unendlich
viel Zeit hab ich auch nicht für Implementierung, Testes gegen den alten
Code, Regression-Tests, ...
Eine Darstellung der verbratenen Ticks findest du in
Beitrag "Re: Stack Überschreiber beim Rechnen mit uint64_t"> Die Addition/Subtraktion/Vergleich sind auch recht umständlich, aber> daran kann man nicht drehen, die werden direkt inline erzeugt.
Das zu äbdern ist nicht einfach. Die Operationen selbst sind simpel zu
Implementieren; im avr-gcc gibt's schon generischen Code für 2-, 3- und
4-Byte Addition und Vergleich. Die Erweiterung für 8-Byte ist minimal.
Problem: Es gibt noch kein A = B, von dem man meinen könnte es sei
einfach. Die abzusehenden Probleme und der Aufwand für 64-Bit
Zuwesiungen sind aber so groß, daß es bislang niemand auch nur versucht
hat.
> Es sei denn, jemand weiß auch dafür ne Möglichkeit (ohne Linux> installieren zu müssen).
GCC-, binutils- und avr-libc-Quellen und Cygwin und du bist glücklicher
Compiler-Builder ;-)
[2]
http://gcc.gnu.org/viewcvs/trunk/libgcc/config/avr/lib1funcs.S?content-type=text%2Fplain&view=co
Johann L. schrieb:> Böse Falle, denn der> Compiler weiß, daß diese Funktion weder X nach Z ändert und Werte in> diesen Registern einen Aufruf unbeschadet überstehen.
Wenn es dafür einen offiziellen Mechanismus gibt, ist dagegen nichts
einzuwenden. Und dann wird auch nichts krachen, wenn man diesen
Mechanismus über die neue Funktion laufen läßt.
Beim Keil C51 kann man z.B. in einem ersten Paß die Registerbenutzung in
ein File schreiben und dann in einem weiteren die Registerbenutzung des
Aufrufers optimieren lassen.
Das File sieht z.B. so aus:
1
B0BF TEMP_CONVERSION
2
8000 TIMER_INIT
3
A000 UART_INIT
4
B081 UART_INT
Dumm wäre aber, wenn es kein Tool dafür gibt und die Informationen
einfach irgendwo händisch reingeschrieben wurden.
Peter
Peter Dannegger schrieb:> Johann L. schrieb:>> Böse Falle, denn der>> Compiler weiß, daß diese Funktion weder X nach Z ändert und Werte in>> diesen Registern einen Aufruf unbeschadet überstehen.>> Wenn es dafür einen offiziellen Mechanismus gibt, ist dagegen nichts> einzuwenden. Und dann wird auch nichts krachen, wenn man diesen> Mechanismus über die neue Funktion laufen läßt.
Klar, aber dazu muss man die Vereinbarung für jede Funktion kennen wie
für jede andere händisch geschriebene Assembler-Funktion in einer
Anwendung auch.
> Beim Keil C51 kann man z.B. in einem ersten Paß die Registerbenutzung in> ein File schreiben und dann in einem weiteren die Registerbenutzung des> Aufrufers optimieren lassen.> Das File sieht z.B. so aus:
1
> B0BF TEMP_CONVERSION
2
> 8000 TIMER_INIT
3
> A000 UART_INIT
4
> B081 UART_INT
> Dumm wäre aber, wenn es kein Tool dafür gibt und die Informationen> einfach irgendwo händisch reingeschrieben wurden.
Dazu müsste GCC solche Dateien scannen und die Codegenerierung darauf
auslegen. Solch einen Mechanismus gibt es im GCC nicht.
Zudem ist zB __divmodqi4 aus Sicht von avr-gcc keine Funktion, sondern
ein nomales Pattern wie zB eine Multiplikation auch. D.h. es wird so
getan, als gäbe es eine Instruktion(sfolge), die das macht, was
__divmodqi4 eben tut.
Diesen Grad an Dynamik, daß per Kommandozeilenoption oder per Datei
einstellbar ist, wie welche Maschineninstruktion aussieht und welche
Semantik sie hat, bietet GCC sich. Es sei denn, du schreibst ein
GCC-Plugin und hakst dich an entrprechender Stelle in den Compiler.
Und die Maschinenbeschreibung selbst ist schon kompliziert genug, wer so
was machen will muss eben in die Compilerquellen schauen und wissen, was
er tut und wie er diese GCC-Interne verwendet und verbiegt.
Soche Hacks sind umso erstaunlicher, als daß es sich bei GCC um freie
Software handelt, zu der jeder beitragen kann. Das Thema
"64-Bit-Arithmetik und ihre Effizienz in avr-gcc" geistert ja schon 100
Jahre durch die Foren und ständig wird sich beschwert und gemeckert und
drumherum gehackt, anstatt daß jemand, der entsprechende Algorithmen
schon hat, diese einfach in GCC einbaut und das Thema aus der Welt ist.
Peter Dannegger schrieb:> Wenn der Fehler nur bei Interrupts auftritt, würde ich mir mal das> Assemblerlisting des Interrupthandlers gründlich ansehen, ob auch alle> benutzten Register gesichert werden.
Das wird ziemlich aufwendig werden. Habe gehofft, dass man sich auf den
Compiler verlassen kann.
Johann L. schrieb:> IdR wird man den Platzverbrauch nämlich nicht in einer ISR ausdrucken> wollen bzw. man will Informationen über den Worst-Case-RAMverbrauch.> Genau den Zeitpunkt, wann dieser Worst-Case eintritt, wird man aber> schwerlich erwischen.
Nehme dem Zeitpunkt des Absturzes als Worst-Case an. Es zeigt sich aber,
dass der Stack genügend unbenutzter Speicher besitzt.
Noch was zu diesem Problem. Wir hatten vor ca. 15 Monaten einen
ähnlichen Fehler den wir aber nie richtig untersucht haben. Wir hatten
damals aber auch die 64 Bit Berechnungen unter Verdacht. Nach Änderung
der Berechnung war der Fehler "verschwunden". Speicher hatten wir damals
mehr als genug.
Habe während der Berechnung die Interrupts einzeln ausgeschaltet. Beim
Ausschalten des UART DRE Interrupts ist der Fehler verschwunden. Leider
könnte das Ausschalten dieses Interrupts auch einen zeitlichen Einfluss
auf den Programmablauf haben, da über diesen viele Debug Meldungen
gesendet werden.
Anbei noch das Listing des verdächtigen Interrupts. Die Funktion
USART_DataRegEmpty ist von Atmel.
1
ISR(USARTF0_DRE_vect)
2
{
3
USART_DataRegEmpty(&USART_data_F0);
4
}
ISR(USARTF0_DRE_vect)
{
1051e: 1f 92 push r1
10520: 0f 92 push r0
10522: 0f b6 in r0, 0x3f ; 63
10524: 0f 92 push r0
10526: 0b b6 in r0, 0x3b ; 59
10528: 0f 92 push r0
1052a: 11 24 eor r1, r1
1052c: 2f 93 push r18
1052e: 3f 93 push r19
10530: 4f 93 push r20
10532: 5f 93 push r21
10534: 6f 93 push r22
10536: 7f 93 push r23
10538: 8f 93 push r24
1053a: 9f 93 push r25
1053c: af 93 push r26
1053e: bf 93 push r27
10540: ef 93 push r30
10542: ff 93 push r31
USART_DataRegEmpty(&USART_data_F0);
10544: 8d ea ldi r24, 0xAD ; 173
10546: 9e e2 ldi r25, 0x2E ; 46
10548: c1 db rcall .-2174 ; 0xfccc <USART_DataRegEmpty>
}
1054a: ff 91 pop r31
1054c: ef 91 pop r30
1054e: bf 91 pop r27
10550: af 91 pop r26
10552: 9f 91 pop r25
10554: 8f 91 pop r24
10556: 7f 91 pop r23
10558: 6f 91 pop r22
1055a: 5f 91 pop r21
1055c: 4f 91 pop r20
1055e: 3f 91 pop r19
10560: 2f 91 pop r18
10562: 0f 90 pop r0
10564: 0b be out 0x3b, r0 ; 59
10566: 0f 90 pop r0
10568: 0f be out 0x3f, r0 ; 63
1056a: 0f 90 pop r0
1056c: 1f 90 pop r1
1056e: 18 95 reti
Noch die USART_DataRegEmpty selbst
dude schrieb:> Das wird ziemlich aufwendig werden. Habe gehofft, dass man sich auf den> Compiler verlassen kann.
Dein Listing zeigt ja, daß alle Scratchpadregister gesichert werden,
damit ist der Interrupt sicher.
Das wird durch den Funktionsaufruf bewirkt. Dann werden auch viele
unbenutzte Register gesichert.
dude schrieb:> Die Funktion> USART_DataRegEmpty ist von Atmel.
Dann sollte sie ja stimmen.
Ich sehe da nicht durch. Ich verliere bei Zeiger auf Zeiger auf Zeiger
leicht den Überblick. Zeiger verwende ich daher möglichst nur
eindimensional.
Wenn Du die Funktion inlinest, könnte der Compiler merken, daß die erste
Dimension auf konstante RAM-Adressen zeigt und die Zugriffe einfach
direkt ausführen. Dürfte einiges an Code sparen.
Peter
Hallo,
Peter Dannegger schrieb:> dude schrieb:>> Die Funktion>> USART_DataRegEmpty ist von Atmel.>> Dann sollte sie ja stimmen.
Sollen heißt nicht das sie tatsächlich stimmt.
dude schrieb:> Noch die USART_DataRegEmpty selbst> ...> uint8_t data = bufPtr->TX[usart_data->buffer.TX_Tail];> ...> bufPtr->TX_Tail = (bufPtr->TX_Tail + 1) & USART_TX_BUFFER_MASK;
Wie groß ist der buffer TX definiert?
Welchen Wert hat USART_TX_BUFFER_MASK?
dude schrieb:> Peter Dannegger schrieb:>> Wenn der Fehler nur bei Interrupts auftritt, würde ich mir mal das>> Assemblerlisting des Interrupthandlers gründlich ansehen, ob auch alle>> benutzten Register gesichert werden.>> Das wird ziemlich aufwendig werden. Habe gehofft, dass man sich auf den> Compiler verlassen kann.
Für ISRs sind mir keine Fehler bekannt.
> Johann L. schrieb:>> IdR wird man den Platzverbrauch nämlich nicht in einer ISR ausdrucken>> wollen bzw. man will Informationen über den Worst-Case-RAMverbrauch.>> Genau den Zeitpunkt, wann dieser Worst-Case eintritt, wird man aber>> schwerlich erwischen.>> Nehme dem Zeitpunkt des Absturzes als Worst-Case an. Es zeigt sich aber,> dass der Stack genügend unbenutzter Speicher besitzt.
Ist post mortem Analyse hier aussagekräftig?
> Noch was zu diesem Problem. Wir hatten vor ca. 15 Monaten einen> ähnlichen Fehler den wir aber nie richtig untersucht haben. Wir hatten> damals aber auch die 64 Bit Berechnungen unter Verdacht. Nach Änderung> der Berechnung war der Fehler "verschwunden". Speicher hatten wir damals> mehr als genug.
Diese Berechnungen verbrauchen wie gesagt sehr viel Resource. Bekannte
avr-gcc Bugs hab ich oben genannt; einer ist zB seit Anbeginn der Zeit
in avr-gcc. Übersetzen mit -Os und alle Versionen < 4.6.2 laufen auf
abort:
1
charc=1;
2
3
void__attribute__((noinline,noclone))
4
func_1(chara)
5
{
6
a=a>>7;
7
if(a)
8
c=a;
9
}
10
11
intmain(void)
12
{
13
func_1(42);
14
15
if(c!=1)
16
abort();
17
18
exit(0);
19
}
> Habe während der Berechnung die Interrupts einzeln ausgeschaltet. Beim> Ausschalten des UART DRE Interrupts ist der Fehler verschwunden. Leider> könnte das Ausschalten dieses Interrupts auch einen zeitlichen Einfluss> auf den Programmablauf haben, da über diesen viele Debug Meldungen> gesendet werden.>> Anbei noch das Listing des verdächtigen Interrupts. Die Funktion> USART_DataRegEmpty ist von Atmel.
Und was hat das mit 64-Bit Berechnung zu tun? Ich dachte es geht darum,
daß in einer ISR 64-Bit Arithmetik gemacht wird und diese ISR nicht
läuft wie soll?
> [...]> if (bufPtr->TX_Head == tempTX_Tail)> [...]
Das sieht so auf als gäbe es für TX_Tail und TX_Head implizite Annahmen.
Kann es sein, daß diese Annahme durch nicht-atomare Zugriffe auf
bufPtr — wie immer das definiert ist; mit dem C-Code kann man also nicht
wirklich was anfangen — zerstört wird?
Hallo,
Johann hatte einen Auszug in seine divmod-di.ss Lib gepackt, hier gibt
es nun auch eine Weiterentwicklung um eine mul64 und eine div64 Lib ist
nun auch etwas kleiner.
- http://gcc.gnu.org/viewcvs/trunk/libgcc/config/avr/lib1funcs.S
Ich habe für mich mal die beide als Assembler Routinen extrahiert, so
dass ich damit zugehörigen Routinen in der gcc 64Bit Lib ersetzen kann.
Anwendung: einfach in das Makefile eintragen, assemblieren und dazu
linken.
Das war's.
Johann L. schrieb:> Peter Dannegger schrieb:>> Johann L. schrieb:>>> Wem __udivdi3 nicht zu pass ist, kann sich auch selbst einklinken:>>>> Ooch nöö :-(>>>> Daß diese geheimen Tricks einem niemand von den GCC-Gurus sagen kann.>> Ich dachte immer, daß man einen total neuen WINAVR backen muß.>> Naja, nun kennst du ja den "geheimen" Trick (auch wenn ich kein GCC-Guru> bin). Das sind die Geheimnisse der binutils und wie Objekte/Bibliotheken> gelinkt werden. Wer zuerst kommt, mahlt zuerst.>> Eine Darstellung der verbratenen Ticks findest du in> Beitrag "Re: Stack Überschreiber beim Rechnen mit uint64_t">> [2] http://gcc.gnu.org/viewcvs/trunk/libgcc/config/avr/lib1funcs.S?content-type=text%2Fplain&view=co
Seit einigen Tagen habe ich ein ähnliches Problem. Mein ATMega2560
springt sporadisch (vielleicht alle 8 Stunden) in eine Endlosschleife.
Wann immer ich den Stackpointer auf einem LCD ausgeben lasse, ist dieser
einige kByte vom vermuteten Ende der globalen Variablen entfernt.
Ich nutze derzeit noch WinAVR-20080610 mit AVRStudio 4.14 und
Compiler-Option -Os. Es gibt ein paar globale uint64_t Variablen, von
denen eine als globale Zeitbasis dient und per Interrupt hochgezählt
wird:
ISR(TIMER_COMPA_vect)
{
microsecond+=16384;
}
Im Hauptprogramm wird diese dann regelmäßig übertragen in eine andere
uint64_t Variable Time0:
cli(); Time0=microsecond/1000; sei();
Kann es sein, dass das hier beschriebene Problem der Division mit
uin64_t auch auf mich zutrifft?
Ich erwäge nun, alle uint64_t Variablen aus meinem Programm zu verbannen
und statt dessen mit uint32_t, oder float-Variablen zu rechnen.
Macht dies eurer Ansicht nach Sinn, oder ist uint32_t genauso
problematisch?
Hallo Mikrofun R. schrieb:> Kann es sein, dass das hier beschriebene Problem der Division mit> uin64_t auch auf mich zutrifft?
Welches Problem ?
Ich verwende auch die 64 Bit und habe nur die Multiplikation und
Division ersetzt.
Dazu gibt es hier auch einige Zeit-Messungen anhand von Ticks.
> Welches Problem ?
Ich meine die vom Threadsteller genannten Programmabstürze.
Übrigens: Wie funktioniert das Ersetzen der Multiplikation und Division
im Detail? Mit den Libraries und der Entwicklungsumgebung kenne ich mich
nicht genügend gut aus. Muss ich irgendwelche Dateien im
WinAVR-Verzeichnis austauschen? Muss mein eigenes Programm angepasst
werden?
Wenn es noch mehrere K an Speicher hat, dann liegt es daran nicht. Für
die genennten Operationen reicht der Speicher aus.
Eher hat die Toolchain die du verwendest Probleme wie zB PR46779 oder
PR39633. Oder irgendwo gibt's nen Glitch wegen nicht-atomarem Zugriff.
Sind transiente Fehler denkbar?
> Eher hat die Toolchain die du verwendest Probleme wie zB PR46779
Bisher habe ich immer nur WinAVR aktualisiert. Wie kann ich
entsprechende Bugfixes einspielen? Die neueste zum Download angebotene
Version von WinAVR ist älter als PR46779.
Johann L. schrieb:
>Oder irgendwo gibt's nen Glitch wegen nicht-atomarem Zugriff.
Das ist ein interessanter Punkt. Das habe ich eben nochmal überprüft. In
meinen Interrupts werden nur globale uint8_t Variablen verändert, wenn
ich das richtig überblicke. Es gab eine Ausnahme mit einer uint64_t
Variablen, den ich nun korrigiert habe. Der entsprechende (externe
Hardware-) Intterupt war allerdings beim letzten Absturz wahrscheinlich
unbeteiligt.
Was meinst Du mit "transienten Fehlern"?
Nachtrag:
Inzwischen habe ich eine Möglichkeit für einen Puffer-Überlauf in einer
LCD-Routine gefunden. Wenn dort Zahlen mit mehr als 7 Zeichen ausgegeben
werden sollen, wird vermutlich der Stack incl. seiner Rücksprungadressen
überschrieben. Das passiert z. B. wenn eine uint64_t Variable
undefiniert geändert (und damit sehr groß) wird. Vielleicht kommt mein
Problem daher.
Mikrofun R. schrieb:> Wenn dort Zahlen mit mehr als 7 Zeichen ausgegeben> werden sollen, wird vermutlich der Stack incl. seiner Rücksprungadressen> überschrieben.
Dann hast Du es falsch programmiert.
Entweder man reserviert für die Ausgabe immer die für den Typ maximal
mögliche Stellenzahl. Dabei das '-' und die abschließende '\0' nicht
vergessen.
Oder man testet die Zahl vor der ASCII-Umwandlung auf ihre Größe und
gibt bei zu langer Zahl eine Fehleranzeige aus.
Auf keinen Fall aber darauf vertrauen, daß die den Wert liefernde
Funktion immer die richtige Größe liefert.
Peter
> Dann hast Du es falsch programmiert.
So ist es.
Trotzdem wäre ich dankbar, wenn hier jemand nochmal kurz erklären
könnte, wie man die in diesem Thread geposteten Patches in WinAVR
einpflegen kann.
Hallo Mikrofun,
wenn Du diese Erweiterung
Beitrag "Re: Stack Überschreiber beim Rechnen mit uint64_t"
meinst, ist das kein Patch !
Das ist eine (gcc avr) Assemblerdatei die nach dem Übersetzen über das
Makefile einfach dazu gelinkt wird.
Weist Du was ein Makefile macht ?
hier ein Auszug aus meinem Makefile:
Es ist Jahre her, dass ich Makefiles selbst erstellen musste. Seit dem
Umstieg auf AVRStudio wird das Makefile scheinbar bei jedem "Rebuilt
all" automatisch erstellt.
C-Libraries kann man dort einfach "dem Projekt hinzufügen". Diese
*.s-Dateien könnte ich unter "other files" einfügen, jedoch hege ich
Zweifel, ob das reicht.
Du kannst es wie ein C-File hinzufügen.
Der GCC weiß automatisch, daß er *.c compilieren und *.S assemblieren
muß.
Es ist also kein "other files".
Peter
Genau das hatte ich gestern versucht und einen Fehler erhalten. Jetzt
habe ich die Files nochmal direkt in den Ordner neben meine anderen
C-Files kopiert, eingebunden und das Kompilieren klappt. Keine Ahnung,
warum es am Anfang fehlschlug.
Nur um sicher zu gehen: Wie greife ich auf die neuen Routinen zu? Reicht
es, nach wie vor das *-Zeichen und das /-Zeichen zu verwenden?
Wenn ich ein paar uint64_t Variablen benutze, reduziert das Einbinden
dieser Routinen den Programmspeicher-Verbrauch immerhin von 17.7 % auf
16.6%, was ich sehr erfreulich finde.
Nach dem Umdeklarieren dieser uint64_t auf uint32_t Variablen erhöht das
Einbinden dieser Routinen den Programmspeicher-Verbrauch von 13.0 % auf
13.2 %.
Sind die uint32_t Routinen im gcc 4.3.3 eigentlich akzeptabel, oder sind
die auch verbesserungsbedürftig?