Hallo zusammen,
ich benutze in einem STM32-F4 die Hardware-CRC-Berechnung, dafür habe
ich eine Funktion, die folgendermaßen aussieht:
1
Tu32SingleCrc(Tu32Data)
2
{
3
CRC->CR=CRC_CR_RESET;
4
CRC->DR=Data;
5
return(CRC->DR);
6
}
Wenn ich nun ohne Optimierung compiliere, ist alles in Ordnung und der
Assembercode sieht folgendermaßen aus:
1
SingleCrc(unsigned long):
2
08019a94: push {r7}
3
08019a96: sub sp, #12
4
08019a98: add r7, sp, #0
5
08019a9a: str r0, [r7, #4]
6
08019a9c: str r1, [r7, #0]
7
142 CRC->CR = CRC_CR_RESET;
8
08019a9e: mov.w r3, #12288 ; 0x3000
9
08019aa2: movt r3, #16386 ; 0x4002
10
08019aa6: mov.w r2, #1
11
08019aaa: str r2, [r3, #8]
12
143 CRC->DR = Data;
13
08019aac: mov.w r3, #12288 ; 0x3000
14
08019ab0: movt r3, #16386 ; 0x4002
15
08019ab4: ldr r2, [r7, #0]
16
08019ab6: str r2, [r3, #0]
17
144 return (CRC->DR);
18
08019ab8: mov.w r3, #12288 ; 0x3000
19
08019abc: movt r3, #16386 ; 0x4002
20
08019ac0: ldr r3, [r3, #0]
Sobald ich aber die Optimierung (-O1 oder -O irgendwas) einschalte
funktioniert die Berechnung nicht mehr und der Assemblercode wurde zu
1
SingleCrc(unsigned long):
2
08012370: mov.w r3, #12288 ; 0x3000
3
08012374: movt r3, #16386 ; 0x4002
4
08012378: mov.w r2, #1
5
0801237c: str r2, [r3, #8]
6
143 CRC->DR = Data;
7
0801237e: str r1, [r3, #0]
8
144 return (CRC->DR);
9
08012380: ldr r0, [r3, #0]
optimiert.
Die Variablen aus der ST-Bibliothe sind als __IO also volatile
deklariert, daher wundert es mich, dass der Compiler hier überhaupt
etwas optimiert.
Ich bin kein Compilerexperte und noch weniger ein Assembler-Experte, hat
jemand eine Idee, was schiefläuft, und wie ich das verhindere?
Ich meine, ich könnte die Optimierung ja für diese Funktion ausschalten,
kein Problem, aber ich habe Angst, irgendwo andres im Code taucht das
gleiche Problem auf und ich bemerke es nicht.
Viele Grüße
Andreas
Andreas schrieb:> Die Variablen aus der ST-Bibliothe sind als __IO also volatile> deklariert, daher wundert es mich, dass der Compiler hier überhaupt> etwas optimiert.
Optimieren geht meistens immer, egal ob volatile oder nicht. Für deine
CRC Register muss er ja auch die Adressen laden um damit arbeiten zu
können.
Bei O0 macht er das für jedes Register einzeln. Wenn du optimieren
lässt, reicht ihm die Basisadresse und dann wird nur noch mit Offsets
hantiert.
08019a9e: mov.w r3, #12288 ; 0x3000
08019aa2: movt r3, #16386 ; 0x4002
08019aa6: mov.w r2, #1
08019aaa: str r2, [r3, #8]
143 CRC->DR = Data;
08019aac: mov.w r3, #12288 ; 0x3000
08019ab0: movt r3, #16386 ; 0x4002
->
08012370: mov.w r3, #12288 ; 0x3000
08012374: movt r3, #16386 ; 0x4002
08012378: mov.w r2, #1
0801237c: str r2, [r3, #8]
143 CRC->DR = Data;
0801237e: str r1, [r3, #0]
Schau mal in das LST-File ob du noch mehr Fragmente deiner Funktion
findest. Was mir z.B. fehlt sind die Übergabeparameter deiner Funktion.
Alternativ kann er sie beim Aufruf auch direkt in Register mappen und
nicht den Umweg über den Stack gehen.
Auf jeden Fall nicht einfach zu durchschauen.
Hast du mal nen Debugger bemüht?
Der Code sieht ziemlich korrekt aus. Das er kürzer ist, sollte bei
Optimierung nicht überraschen. Das ist die Aufgabe davon.
Volatile Zugriffe werden dort auch weiterhin so ausgeführt wie es sein
soll. Aber natürlich nur die. Es ist keineswegs so, dass eine volatile
Variable in einem Programm oder eine Funktion, sämtliche Optimierungen
im ganzen Programm oder der Funktion ausschaltet. Sondern nur
Zugriffsoptimierungen auf die betreffende Variable.
Ich habe mal die CPP-Datei und die lst-Datei angehangen. Ich blicke da
selber leider überhaupt nicht durch.
Mit dem Debugger gibts ein ganz interessantes Problem: Wenn ich die
Funktion einzeln durchsteppe, ist der berechnete CRC-Wert so lange
richtig, wie ich Einzelschritte mache. Sobald die Funktion im normalen
Modus durchlaufen wird, schlägt sie fehl.
Auch dafür habe ich leider bisher keine Erklärung.
Das alles tritt nur auf, wenn die Optimierung eingeschaltet ist.
Ich würde ja auch sagen, mein Problem liegt an anderer Stelle im Code,
aber es reicht schon, für die SingleCRC-Funktion die Optimierung
abzuschalten und alles ist in Ordnung.
Bei der Block-CRC-Funktion gibt es die Probleme übringens auch genau
dann, wenn man sie mit fester Blocklänge 1 ausführt. Sobald die Funktion
mehrmals durchlaufen wird, ist alles in Ordnung.
Bin für jeden Tipp dankbar, bin nämlich mit meinem Latein am Ende.
Habe schon versucht, alle Optimierungsflags einzeln zu aktivieren und zu
deaktivieren, aber irgendwie klappt das auch nicht, wie ich es mir
vorstelle.
Gruß
Andreas
Das klingt stark nach zu schnellem Code... D.h. es funktioniert wenn die
Registerzugriffe langsam nacheinander erfolgen (wie im Debugger oder
wenn ohne Optimierungen durchgeführt). Schau vllt mal im Datenblatt ob
es für diese Register bestimmte Anforderungen gibt und füge ggf ein paar
'asm volatile("nop");' ein...
Hallo,
laut Datenblatt braucht die Berechnung 4 AHB Clocks cycles.
Interessant ist, dass die Funktion in der Standard Library genauso
aussieht wie beim Threadersteller (abgesehen vom Reset am Anfang):
1
uint32_t CRC_CalcCRC(uint32_t Data)
2
{
3
CRC->DR = Data;
4
5
return (CRC->DR);
6
}
Ohne es zu probieren behaupte ich einfach mal ganz kühn, dass es dort
beim Optimieren auch zu Problemem kommen wird.
CU,
Jay
Hallo zusammen,
vielen Dank für den klasse Hinweis.
Ich war irgendwie felsenfest davon überzeugt, die CRC-Unit bräuchte nur
einen Taktzyklus. Da hätte ich vermutlich noch ewig gebraucht, bis ich
darauf gekommen wäre.
Nochmal vielen Dank!
@ A.K. Ich wollte das Problem nicht dem GCC unterstellen, für mich war
halt nur ein Zusammenhang zur Optimierung erkennbar.
Andreas schrieb:> @ A.K. Ich wollte das Problem nicht dem GCC unterstellen, für mich war> halt nur ein Zusammenhang zur Optimierung erkennbar.
Hatte ich auch nicht so aufgefasst. Unlängst war hier allerdings jemand
zugange, der sich über ähnliche "Mängel" am Compiler beklagte.
Woher soll denn der Compiler wissen wie hoch du deine AHB Clock
eingestellt hast ?! Und selbst wenn dann müßte er ja auch noch wissen
wie lange die verschiedenen CRC Einheiten brauchen zum berechnen.
Vieleich wird bei einer anderen Architektur gar ein Bit gesetzt wenn das
Ergebnis bereit ist oder ein Interupt signalisiert. Das ist doch nicht
die Aufgabe eines Compilers. Sondern des Datenblattes.