Hallo,
wenn ich den angehängten code mit gcc 5.4.1 kompiliere, läuft der code
bis in die Endlosschleife und läßt die LED im Sekundentakt blinken.
Wenn ich denselben Code mit gcc 6.3.1 kompiliere, sorgt memset dafür,
daß der HardFault_Handler aufgerufen wird.
Das ganze unter Linux (Ubuntu 17.10 vs. Ubuntu 18.04).
Anbei noch das Linker-Skript und das Makefile.
Ist jemandem sowas schonmal begegnet?
Bin für jegliche Hinweise dankbar.
Versuch mal, das Array "data" mit "__attribute__ ((aligned (4)))" zu
versehen. Sollte eigentlich nicht nötig sein, aber man kann damit
zumindest probieren, ob es was mit alignment zu tun hat.
Guck mal hier:
https://stackoverflow.com/questions/20587208/cortex-m3-cant-handle-memset
Kann also sein, daß er die Libraries durcheinanderkriegt. Wie sieht
eigentlich das Disassembly an der Stelle aus, wo memset aufgerufen wird?
Ist das niederwertigste Bit der Zieladresse wirklich auf 1 (thumb)?
Unwissender schrieb:
80014ee: 2240 movs r2, #64 ; 0x40
80014f0: 2100 movs r1, #0
80014f2: 4808 ldr r0, [pc, #32] ; (8001514 <main+0x68>)
80014f4: f000 f89c bl 8001630 <memset>
Also r0 als erster Parameter sollte die Adresse von data haben. Aber an
0x08000000 liegt doch das ROM?
Was sagt Dein mapfile denn, wo liegt data dort?
Außerdem ist es merkwürdig, daß der bl an eine gerade Adresse geht, das
sollte an eine ungerade gehen (für thumb-Mode). Das kann aber auch der
Disassembler sein, der das von sich aus schon lesbarer darstellen will
und deswegen automatisch das Bit 0 der Zieladresse auf 0 zurückdreht.
Unwissender schrieb:> 80014f2: 4808 ldr r0, [pc, #32] ; (8001514 <main+0x68>)
da wird für &data[0] doch eine Adresse aus dem Flash geladen? Ist da
nicht eher ein Fehler im Linkerfile?
die data Deklaration einfach in das main reinschieben.
Im Mapfile liegt das array aber richtig im Ram, komisch. Der Name für
das Array ist auch blöde weil das mapfile voll ist mit 'data', benenne
das besser anders.
Ist mir auch schon aufgefallen :-) Hab den Namen in data_xxx geändert.
Das Verschieben in den den heap hat auch nicts gebracht :-(
Anbei die aktuellen Dateien.
Oha.. also das Mapfile ist etwas schwer lesbar, weil die Variable "data"
ehißt, während zig andere Sachen da auch "data" heißen. Kannste das
Array mal in irgendwas Nettes umbennennen, wie "mystuff" oder so, und
dann nochmal ein Mapfile erstellen?
Nop schrieb:> Kannste das> Array mal in irgendwas Nettes umbennennen, wie "mystuff" oder so, und> dann nochmal ein Mapfile erstellen?
Schon geschehen :-) -> data_xxx
Unwissender schrieb:> Schon geschehen :-) -> data_xxx
Ah ja ok, aber da es jetzt auf dem Stack liegt, taucht es im Mapfile
natürlich nicht mehr auf. Geht das Programm denn jetzt wenigstens?
Das Array liegt im RAM und hat auch die richtige Länge. Wieso das im
Disassembly so aussieht, verstehe ich nicht, zumal das mit dem alten GCC
auch so aussah und funktioniert hat. Wie sehen denn die Zeilen davor
aus? Nicht immer wird das sauber sequentiell angezeigt. Möglicherweise
wird R0 als Adresse für den Rücksprung benutzt?!
weil du mit sizeof(data_xxx) nicht die Anzahl der Elemente bekommst. In
diesem Fall müsste es richtig sein, aber etwas besser ist dafür ein
Makro in der Art
Das ist aber nicht das, was da im Code steht. Hier mal aus dem 5er Dump
das Disassembly von memset:
1
0800152c <memset>:
2
800152c: b470 push {r4, r5, r6}
3
800152e: 0784 lsls r4, r0, #30
4
8001530: d046 beq.n 80015c0 <memset+0x94>
5
8001532: 1e54 subs r4, r2, #1
6
8001534: 2a00 cmp r2, #0
7
8001536: d041 beq.n 80015bc <memset+0x90>
8
8001538: b2cd uxtb r5, r1
9
800153a: 4603 mov r3, r0
10
800153c: e002 b.n 8001544 <memset+0x18>
11
800153e: 1e62 subs r2, r4, #1
12
8001540: b3e4 cbz r4, 80015bc <memset+0x90>
13
8001542: 4614 mov r4, r2
14
8001544: f803 5b01 strb.w r5, [r3], #1
15
8001548: 079a lsls r2, r3, #30
16
800154a: d1f8 bne.n 800153e <memset+0x12>
17
800154c: 2c03 cmp r4, #3
18
800154e: d92e bls.n 80015ae <memset+0x82>
19
8001550: b2cd uxtb r5, r1
20
8001552: ea45 2505 orr.w r5, r5, r5, lsl #8
21
8001556: 2c0f cmp r4, #15
22
8001558: ea45 4505 orr.w r5, r5, r5, lsl #16
23
800155c: d919 bls.n 8001592 <memset+0x66>
24
800155e: 4626 mov r6, r4
25
8001560: f103 0210 add.w r2, r3, #16
26
8001564: 3e10 subs r6, #16
27
8001566: 2e0f cmp r6, #15
28
8001568: f842 5c10 str.w r5, [r2, #-16]
29
800156c: f842 5c0c str.w r5, [r2, #-12]
30
8001570: f842 5c08 str.w r5, [r2, #-8]
31
8001574: f842 5c04 str.w r5, [r2, #-4]
32
8001578: f102 0210 add.w r2, r2, #16
33
800157c: d8f2 bhi.n 8001564 <memset+0x38>
34
800157e: f1a4 0210 sub.w r2, r4, #16
35
8001582: f022 020f bic.w r2, r2, #15
36
8001586: f004 040f and.w r4, r4, #15
37
800158a: 3210 adds r2, #16
38
800158c: 2c03 cmp r4, #3
39
800158e: 4413 add r3, r2
40
8001590: d90d bls.n 80015ae <memset+0x82>
41
8001592: 461e mov r6, r3
42
8001594: 4622 mov r2, r4
43
8001596: 3a04 subs r2, #4
44
8001598: 2a03 cmp r2, #3
45
800159a: f846 5b04 str.w r5, [r6], #4
46
800159e: d8fa bhi.n 8001596 <memset+0x6a>
47
80015a0: 1f22 subs r2, r4, #4
48
80015a2: f022 0203 bic.w r2, r2, #3
49
80015a6: 3204 adds r2, #4
50
80015a8: 4413 add r3, r2
51
80015aa: f004 0403 and.w r4, r4, #3
52
80015ae: b12c cbz r4, 80015bc <memset+0x90>
53
80015b0: b2c9 uxtb r1, r1
54
80015b2: 441c add r4, r3
55
80015b4: f803 1b01 strb.w r1, [r3], #1
56
80015b8: 42a3 cmp r3, r4
57
80015ba: d1fb bne.n 80015b4 <memset+0x88>
58
80015bc: bc70 pop {r4, r5, r6}
59
80015be: 4770 bx lr
60
80015c0: 4614 mov r4, r2
61
80015c2: 4603 mov r3, r0
62
80015c4: e7c2 b.n 800154c <memset+0x20>
63
80015c6: bf00 nop
Das ab Adresse 8001568 sieht doch stark nach Loop unrolling aus, was in
der C-Version nicht drin ist. Zumal das Makefile hier auf O0 steht und
daher kein automagisches Loop unrolling zu erwarten ist, zumal ich auch
erstaunt wäre, wenn GCC das tatsächlich bei unbekannter Loop-Menge so
optimieren könnte.
Also der speichert nach der Main im Flash die Adresse von data_xxx ab.
In der oberen Zeile addiert er auf den program counter 32 drauf, das
ergibt 8001484. Das, was an dieser Adresse steht, also 20000480, lädt er
in r0 - und das ist genau die Startadresse des Buffers, die memset sehen
will. Das paßt.
Der Dump ist jetzt aber mit lokalem data_xxx. Laß mal lieber den
6er-Dump mit globalem data_xxx angucken, denn dann verändern wir nur
eine Sache zur Zeit - den Compiler.
Mit der Adresse sehe ich das genauso, ist also doch richtig.
Bei der STM HAL/SPL Technik mit den Strukturen die gefüllt und an eine
Funktion übergeben werden hatte ich aber auch mal einen blöden Fehler
gesucht. Wenn die Struktur auf dem Stack liegt (wie die GPIO_InitTypeDef
hier auch) hat die zufällige Inhalte. Die Struktur muss also erst mit
einer passenden Init Funktion auf Defaultwerte gesetzt werden, für diese
Struktur wäre das
1
GPIO_StructInit(&g);
In deinem Testcode werden alle Felder gesetzt und es sollte ok sein,
aber das ist eine böse Falle wenn falsch initialisierte Felder irgendwas
auslösen können. Auch wenn ST die Struktur erweitert kann das passieren
und funktionierender Code Blödsinn machen.
Also wenn man sich mal im Dump für den 5er an Adresse 0800152c <memset>:
umguckt und das mit dem vergleicht, was beim 6er an 08001630 <memset>:
zu finden ist, dann sind das doch einige Veränderungen. Der Quelltext
mag sich nicht geändert haben, das Compilat aber schon.
Allerdings wird memset ja auch schon im CRT-Startupcode aufgerufen, in
080001b0 <_mainCRTStartup>:, ohne daß es schiefgeht.
1) Hast Du wirklich alle Objektdateien bereinigt und auch nachgeprüft,
daß wirklich alle weg sind?
2) Hast Du mal versucht, den Pfad des 5ers umzubenennen und dann mit dem
6er zu compilieren? Nicht daß da was mit den Suchpfaden aus Versehen
immer noch aus dem 5er genommen und dann mit dem 6er gemischt wird.
3) Kannst Du mit dem Debugger per Singlestep vorgehen und so genauer
checken, an welcher Adresse es abstürzt?
4) Du könntest den gesamten SPL-Anteil auskommentieren und Deine
toggle-Schleifen auf globalen volatile-Integern machen. Das würde den
Scope noch mehr reduzieren.
Nop schrieb:> 1) Hast Du wirklich alle Objektdateien bereinigt und auch nachgeprüft,> daß wirklich alle weg sind?
Das Build-Verzeichnis habe ich immer wieder gelöscht. Ich kompiliere und
linke in einem Schritt, so daß keine Objektdateien übrigbleiben. Ich
habe auch in sämtlichen tmp-Verzeichnissen geschuat, ob sich da noch was
altes versteckt. Nix ...
Nop schrieb:> 2) Hast Du mal versucht, den Pfad des 5ers umzubenennen und dann mit dem> 6er zu compilieren? Nicht daß da was mit den Suchpfaden aus Versehen> immer noch aus dem 5er genommen und dann mit dem 6er gemischt wird.
Auf meinem Entwicklungsrechner gibt es nach dem Updaten von Ubuntu auf
18.04 keinen arm-gcc 5 mehr. Ich habe auch nir eine einzige newlib
gefunden.
Den Enwicklungsrechner habe ich vorher gebackupt und kann so mit chroot
in diesem Backup mit arm-gcc 5 kompilieren.
Nop schrieb:> 3) Kannst Du mit dem Debugger per Singlestep vorgehen und so genauer> checken, an welcher Adresse es abstürzt?
Hab mit dem Debugger bisher kaum Erfahrung, mal sehen ob ich da was
beisteuern kann ...
Nop schrieb:> 4) Du könntest den gesamten SPL-Anteil auskommentieren und Deine> toggle-Schleifen auf globalen volatile-Integern machen. Das würde den> Scope noch mehr reduzieren.
Jepp, die SPL könnte ich noch rausschmeissen. Allerdings hat sich die
auch gar nicht verändert. Aber das ganze wird wieder etwas
überschaubarer.
Ich habe testweise mal einen aktuelleren gcc (7.2.1,
https://developer.arm.com/open-source/gnu-toolchain/gnu-rm) installiert.
Siehe da, damit läuft mein Programm tadellos.
Schein so, als ob der 6.3.1 Dummheiten macht? Kann ich mir aber auch
nicht so richtig vorstellen.
ist schon mysteriös, memset ist ja keine soooo seltene Funktion.
Um dem Compiler weiter auf den Zahn zu fühlen könntest du auch mal
andere Optimierungsstufen ausprobieren, als das -O0 im makefile durch
-Os oder -O1...3 ersetzen.
Unwissender schrieb:> Schein so, als ob der 6.3.1 Dummheiten macht? Kann ich mir aber auch> nicht so richtig vorstellen.
Versuch rauszufinden wie Du in Deiner IDE auf "Instruction Stepping"
umstellst, bei solchen Sachen ist das ein unverzichtbares Werkzeug (In
Eclipse ist das zum Beispiel der Button mit blauem i und gelbem Pfeil
rechts neben den anderen Debug-Buttons in der Toolbar).
Dann steppst Du das gaaaanz langsam und konzentriert Instruktion für
Instruktion durch und merkst Dir die letzte Adresse bevor er in den
Hardfault springt.
Dann machst Du es nochmal aber diesmal bleibst Du genau dort stehen (an
der Stelle an der es im nächsten Takt gekracht hätte) und schaust Dir
alle Register an und den Befehl der jetzt als nächstes ausgeführt werden
soll. Das wird Licht ins Dunkel bringen. Poste das Ergebnis hier.
Unwissender schrieb:> Bernd K. schrieb:>> Versuch rauszufinden wie Du in Deiner IDE>> Isch habe gar keine IDE :-)
Dann nimm ddd oder kdbg. Es gibt keinen Grund auf Werkzeuge zu
verzichten die einem das Leben einfacher machen.
IDE könntest Du Dir im Laufe der nächsten Zeit auch mal zulegen, Eclipse
zum Beispiel kann problemlos mit Makefile Projekten arbeiten und hat
auch ein gutes Debugger-Frontend und ein schönes Plugin für ARM und noch
mehr nützliche Sachen.
Wenn man's richtig hart will, kann man auch GDB auf der Kommandozeile
nehmen, funktioniert auch. Hab ich bisher aber nur ein, zweimal
gebraucht, und dann mit nem Linux dahinter statt bare metal.
arm-none-eabi-gcc (GNU Tools for ARM Embedded Processors 6-2017-q2-update) 6.3.1 20170620 (release) [ARM/embedded-6-branch revisio
4
n 249437]
Allerdings unter Windows und ich habe die SPL nicht, habe das in ein
mbed Projekt reingepackt. Die memset Zeile und die for Schleife wie in
deinem Test.
Läuft mit 6er und 7er Compiler, linker und compiler options sind
natürlich auch unterschiedlich, auber auch für das Bluepill Board mit
dem F103C8.
Wär nicht das erste Mal, daß Maintainer korrekten Code vom Upstream
verbasteln. Vielleicht ist beim Paketieren auch was schiefgeaufen. Oder
die Version ist von einem anderen Stand.
Herlichen Dank an alle, die hier was dazubeigetragen haben. Mein Problem
ist gelöst.
Und das mit dem Debugging und der IDE werde ich mir mal vornehmen :-)