Forum: Compiler & IDEs Stack Überschreiber beim Rechnen mit uint64_t


von dude (Gast)


Lesenswert?

Hallo,

habe sporadische Probleme beim Rechnen mit uint64_t. Der verwendete MCU 
ist ein ATXMega256a3b, WinAVR ist vom 20100110. Der Fehler äussert sich 
in einem Programmabsturz, da durch die Berechnung ein Teil des Stacks 
überschrieben wird. Die Adresse des Überschreibers liegt an einer 
höheren Adresse als der Stackpointer bei Ausführung der Berechnung, 
somit dürfte sich an der betroffenen Adresse nichts ändern.
Habe eine Funktion check_stack() geschrieben welche den Stack auf 
Änderung überprüft. Mit dieser Funktion konnte der Fehler lokalisiert 
werden.

Funktion welche Probleme macht:
uint32_t to_seconds(uint64_t time_ms)
{
  uint32_t t;
  check_stack();
  t = (uint32_t) (time_ms / 1000) + int_time_offset;
  check_stack();
  return t;
}

Der Fehler lässt sich beheben, wenn während der Berechnung die 
Interrupts deaktiviert sind (gewisse ISR rechnen auch mit uint64_t) oder 
die Berechnung mit 32 Bit durchgeführt wird.

Für die Division wird die Funktion __udivdi3 verwendet. Kann es sein, 
dass diese nicht reentrant ist?

Gruss


Compileroptionen:
CFLAGS = -Wall -gdwarf-2 -Os -funsigned-char -fshort-enums
LDFLAGS = -mrelax -Wl,-Map=test.map

Listing:
uint32_t to_seconds(uint64_t time_ms)
{
    5b2a:  6f 92         push  r6
    5b2c:  7f 92         push  r7
    5b2e:  8f 92         push  r8
    5b30:  9f 92         push  r9
    5b32:  af 92         push  r10
    5b34:  bf 92         push  r11
    5b36:  cf 92         push  r12
    5b38:  df 92         push  r13
    5b3a:  ef 92         push  r14
    5b3c:  ff 92         push  r15
    5b3e:  0f 93         push  r16
    5b40:  1f 93         push  r17
    5b42:  12 2f         mov  r17, r18
    5b44:  03 2f         mov  r16, r19
    5b46:  f4 2e         mov  r15, r20
    5b48:  e5 2e         mov  r14, r21
    5b4a:  d6 2e         mov  r13, r22
    5b4c:  c7 2e         mov  r12, r23
    5b4e:  b8 2e         mov  r11, r24
    5b50:  a9 2e         mov  r10, r25
  uint32_t t;
  check_stack();
    5b52:  0f 94 78 05   call  0x20af0  ; 0x20af0 <check_stack>
  t = (uint32_t) (time_ms / 1000) + int_time_offset;
    5b56:  21 2f         mov  r18, r17
    5b58:  30 2f         mov  r19, r16
    5b5a:  4f 2d         mov  r20, r15
    5b5c:  5e 2d         mov  r21, r14
    5b5e:  6d 2d         mov  r22, r13
    5b60:  7c 2d         mov  r23, r12
    5b62:  8b 2d         mov  r24, r11
    5b64:  9a 2d         mov  r25, r10
    5b66:  b8 ee         ldi  r27, 0xE8  ; 232
    5b68:  ab 2e         mov  r10, r27
    5b6a:  a3 e0         ldi  r26, 0x03  ; 3
    5b6c:  ba 2e         mov  r11, r26
    5b6e:  cc 24         eor  r12, r12
    5b70:  dd 24         eor  r13, r13
    5b72:  ee 24         eor  r14, r14
    5b74:  ff 24         eor  r15, r15
    5b76:  00 e0         ldi  r16, 0x00  ; 0
    5b78:  10 e0         ldi  r17, 0x00  ; 0
    5b7a:  0f 94 53 8b   call  0x316a6  ; 0x316a6 <__udivdi3>
    5b7e:  59 01         movw  r10, r18
    5b80:  6a 01         movw  r12, r20
    5b82:  60 90 06 2b   lds  r6, 0x2B06
    5b86:  70 90 07 2b   lds  r7, 0x2B07
    5b8a:  80 90 08 2b   lds  r8, 0x2B08
    5b8e:  90 90 09 2b   lds  r9, 0x2B09
    5b92:  6a 0c         add  r6, r10
    5b94:  7b 1c         adc  r7, r11
    5b96:  8c 1c         adc  r8, r12
    5b98:  9d 1c         adc  r9, r13
  check_stack();
    5b9a:  0f 94 78 05   call  0x20af0  ; 0x20af0 <check_stack>
  return t;
}
    5b9e:  b3 01         movw  r22, r6
    5ba0:  c4 01         movw  r24, r8
    5ba2:  1f 91         pop  r17
    5ba4:  0f 91         pop  r16
    5ba6:  ff 90         pop  r15
    5ba8:  ef 90         pop  r14
    5baa:  df 90         pop  r13
    5bac:  cf 90         pop  r12
    5bae:  bf 90         pop  r11
    5bb0:  af 90         pop  r10
    5bb2:  9f 90         pop  r9
    5bb4:  8f 90         pop  r8
    5bb6:  7f 90         pop  r7
    5bb8:  6f 90         pop  r6
    5bba:  08 95         ret

von Peter D. (peda)


Lesenswert?

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

von Peter II (Gast)


Lesenswert?

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?

von Peter D. (peda)


Lesenswert?

Peter II schrieb:
> warum brauchen 64bit denn 256byte ram?

Weil es im Map-File so steht:
1
 .data          0x00800100      0x100 c:/avr/winavr/bin/../lib/gcc/avr/4.3.3/avr5\libgcc.a(_clz.o)
2
                0x00800100                __clz_tab


Peter

von (prx) A. K. (prx)


Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?


von dude (Gast)


Lesenswert?

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).

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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:
1
#ifdef MYDIV
2
unsigned long long
3
__divdi3 (unsigned long long dividend, unsigned long long divisor)
4
{
5
    return 1;
6
}
7
#endif
8
9
long long a, b;
10
11
int main (void)
12
{
13
    return a/b;
14
}
1
$ avr-gcc div64.c -Os && avr-size a.out
2
   text    data     bss     dec     hex filename
3
   4392       0      16    4408    1138 a.out
4
5
$ avr-gcc div64.c -Os -DMYDIV && avr-size a.out
6
   text    data     bss     dec     hex filename
7
    138       0      16     154      9a a.out

von dude (Gast)


Lesenswert?

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?

von Peter D. (peda)


Angehängte Dateien:

Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

An der Reentrantfähigkeit der Implementierung liegt's garantiert nicht.

http://gcc.gnu.org/viewcvs/trunk/libgcc/longlong.h?content-type=text%2Fplain&view=co
http://gcc.gnu.org/viewcvs/trunk/libgcc/libgcc2.h?content-type=text%2Fplain&view=co
http://gcc.gnu.org/viewcvs/trunk/libgcc/libgcc2.c?content-type=text%2Fplain&view=co

Viel eher geht dir einfach der Stack aus, der WDT ist zu knapp 
eingestellt, oder es schlägt ein Compilerfehler zu wie

http://gcc.gnu.org/PR46779
http://gcc.gnu.org/PR39633

Falls der Stack ausgeht, hilft eine eigene Implementierung oder avr-gcc 
4.7 wo kein __clz_tab mehr verwendet wird.

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

Hallo Peter,

danke für den 64-Bit Code, ich habe ihn in ein kleines Programm 
eingebunden und getestet.

In Bild 2 wird das "ftw" berechnet:
1
    d = 0x000100000000;  // 2^32
2
    dds_takt = F_MHZ(180);
3
    freq = F_MHZ(10);
4
    ftw = d * freq; 
5
    ftw = ftw / dds_takt;

In Bild 3 "freq*2^32:" wird das Ergebnis ftw mit dds_takt multipliziert:
1
    ftw = ftw * dds_takt;

In Bild 4 "freq:" wird das Ergebnis ftw durch d geteilt:
1
    ftw = ftw / d;

In Bild 5 geht es um die Division durch 3, im Bild ist die Beschriftung 
falsch:
1
    volatile uint64_t a, b,c;
2
    a = 100000000;
3
    b = 3;
4
    c = a / b;

Auch liefert die 64-Bit Modulofunktion für 100000000 % 3 = 1 das 
richtige Ergebnis.

von Uwe S. (de0508)


Lesenswert?

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
const uint64_t d = 0x000100000000;
2
const uint64_t dds_takt = F_MHZ(180);
3
uint64_t freq = F_MHZ(10);
4
5
start_t1_counter();
6
 uint64_t ftw = 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
const uint32_t freq32 = F_MHZ(10);
2
uint32_t ftw32 = 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
1
const uint32_t dds_takt32 = F_MHZ(180);
2
uint32_t freq32 = F_MHZ(10);
3
uint64_t ftw = 0;
4
5
start_t1_counter();
6
 ftw = 0;
7
 // U64_UNION_O(ftw, 0) = 0;
8
 U64_UNION_O(ftw, 1) = freq32; // entspricht ftw = (1ull<<32) * freq32
9
 ftw = uint64_div32( ftw, dds_takt32 );
10
stop_t1_counter();


4) Dann gibt's noch eine 32-Bit Schleifenvariante.

32-Bit Schleife: *4,813ms*

Hier der Code
1
const uint8_t a_ftw_digit[] PROGMEM = {
2
// 23.86092942
3
 8, 6, 0, 9, 2, 9, 4, 2,
4
};
5
6
const uint32_t a_ftw_divisor[] PROGMEM = {
7
 10ul, 100ul,
8
 1000ul, 10000ul, 100000ul,
9
 1000000ul, 10000000ul, 100000000ul,
10
};
11
12
uint32_t freq32 = F_MHZ(10);
13
14
start_t1_counter();
15
 uint32_t ftw32 = freq32 * 23ul;
16
17
 for (uint8_t i = 0; i < 8; i++) {
18
   uint8_t digit = (uint8_t)pgm_read_byte( & a_ftw_digit[i] ); // wird optimiert
19
   uint32_t divisor = (uint32_t)pgm_read_dword( & a_ftw_divisor[i] );  // wird optimiert
20
   ftw32 += freq32 * digit / divisor;
21
 }
22
stop_t1_counter();

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

@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.

von Uwe S. (de0508)


Lesenswert?

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.

von dude (Gast)


Lesenswert?

> 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.

von Peter D. (peda)


Lesenswert?

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

von Peter D. (peda)


Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

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.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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).

von Peter D. (peda)


Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Angehängte Dateien:

Lesenswert?

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-Verbrauch

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.

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

von Peter D. (peda)


Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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.

von dude (Gast)


Lesenswert?

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
1
void USART_DataRegEmpty(USART_data_t * usart_data)
2
{
3
  USART_Buffer_t * bufPtr;
4
  bufPtr = &usart_data->buffer;
5
6
  /* Check if all data is transmitted. */
7
  uint8_t tempTX_Tail = usart_data->buffer.TX_Tail;
8
  if (bufPtr->TX_Head == tempTX_Tail){
9
      /* Disable DRE interrupts. */
10
    uint8_t tempCTRLA = usart_data->usart->CTRLA;
11
    tempCTRLA = (tempCTRLA & ~USART_DREINTLVL_gm) | USART_DREINTLVL_OFF_gc;
12
    usart_data->usart->CTRLA = tempCTRLA;
13
14
  }else{
15
    /* Start transmitting. */
16
    uint8_t data = bufPtr->TX[usart_data->buffer.TX_Tail];
17
    usart_data->usart->DATA = data;
18
19
    /* Advance buffer tail. */
20
    bufPtr->TX_Tail = (bufPtr->TX_Tail + 1) & USART_TX_BUFFER_MASK;
21
  }
22
}

void USART_DataRegEmpty(USART_data_t * usart_data)
{
    fccc:  cf 93         push  r28
    fcce:  df 93         push  r29
    fcd0:  fc 01         movw  r30, r24
  USART_Buffer_t * bufPtr;
  bufPtr = &usart_data->buffer;

  /* Check if all data is transmitted. */
  uint8_t tempTX_Tail = usart_data->buffer.TX_Tail;
    fcd2:  ec 01         movw  r28, r24
    fcd4:  ca 53         subi  r28, 0x3A  ; 58
    fcd6:  df 4f         sbci  r29, 0xFF  ; 255
    fcd8:  98 81         ld  r25, Y
  if (bufPtr->TX_Head == tempTX_Tail){
    fcda:  eb 53         subi  r30, 0x3B  ; 59
    fcdc:  ff 4f         sbci  r31, 0xFF  ; 255
    fcde:  80 81         ld  r24, Z
    fce0:  e5 5c         subi  r30, 0xC5  ; 197
    fce2:  f0 40         sbci  r31, 0x00  ; 0
    fce4:  a0 81         ld  r26, Z
    fce6:  b1 81         ldd  r27, Z+1  ; 0x01
    fce8:  89 17         cp  r24, r25
    fcea:  39 f4         brne  .+14       ; 0xfcfa 
<USART_DataRegEmpty+0x2e>
      /* Disable DRE interrupts. */
    uint8_t tempCTRLA = usart_data->usart->CTRLA;
    fcec:  13 96         adiw  r26, 0x03  ; 3
    fcee:  8c 91         ld  r24, X
    fcf0:  13 97         sbiw  r26, 0x03  ; 3
    tempCTRLA = (tempCTRLA & ~USART_DREINTLVL_gm) | 
USART_DREINTLVL_OFF_gc;
    fcf2:  8c 7f         andi  r24, 0xFC  ; 252
    usart_data->usart->CTRLA = tempCTRLA;
    fcf4:  13 96         adiw  r26, 0x03  ; 3
    fcf6:  8c 93         st  X, r24
    fcf8:  0b c0         rjmp  .+22       ; 0xfd10 
<USART_DataRegEmpty+0x44>

  }else{
    /* Start transmitting. */
    uint8_t data = bufPtr->TX[usart_data->buffer.TX_Tail];
    fcfa:  88 81         ld  r24, Y
    fcfc:  e8 0f         add  r30, r24
    fcfe:  f1 1d         adc  r31, r1
    fd00:  ed 57         subi  r30, 0x7D  ; 125
    fd02:  ff 4f         sbci  r31, 0xFF  ; 255
    fd04:  80 81         ld  r24, Z
    usart_data->usart->DATA = data;
    fd06:  8c 93         st  X, r24

    /* Advance buffer tail. */
    bufPtr->TX_Tail = (bufPtr->TX_Tail + 1) & USART_TX_BUFFER_MASK;
    fd08:  88 81         ld  r24, Y
    fd0a:  8f 5f         subi  r24, 0xFF  ; 255
    fd0c:  8f 73         andi  r24, 0x3F  ; 63
    fd0e:  88 83         st  Y, r24
  }
}
    fd10:  df 91         pop  r29
    fd12:  cf 91         pop  r28
    fd14:  08 95         ret

von Peter D. (peda)


Lesenswert?

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

von Volkmar D. (volkmar)


Lesenswert?

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?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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
char c = 1;
2
3
void __attribute__((noinline,noclone))
4
func_1 (char a)
5
{
6
    a = a >> 7;
7
    if (a)
8
        c = a;
9
}
10
11
int main (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?

von Uwe S. (de0508)


Angehängte Dateien:

Lesenswert?

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

von Mikrofun R. (mikrofun)


Lesenswert?

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?

von Uwe S. (de0508)


Lesenswert?

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.

von Mikrofun R. (mikrofun)


Lesenswert?

> 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?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

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?

von Mikrofun R. (mikrofun)


Lesenswert?

> 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.

von Mikrofun R. (mikrofun)


Lesenswert?

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"?

von Mikrofun R. (mikrofun)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Mikrofun R. (mikrofun)


Lesenswert?

> 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.

von Uwe S. (de0508)


Lesenswert?

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:
1
64BIT_DIR = 64Bit
2
# ---------------------------------------------------------
3
# Ticks 450
4
ASRC = $(64BIT_DIR)/libmul64.S
5
# ---------------------------------------------------------
6
# ca. 1320 Ticks
7
ASRC += $(64BIT_DIR)/libdiv64.S

von Mikrofun R. (mikrofun)


Lesenswert?

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.

von Peter D. (peda)


Lesenswert?

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

von Mikrofun R. (mikrofun)


Lesenswert?

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?

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.