Ich versuche gerade die die STM32F10x_StdPeriph_Lib_V3.1.2 von ST
Microelectronics mit dem yagarto Compiler zu übersetzen. Ich bekomme
aber bei der Datei stm32f10x_can.c einen internen Compilerfehler
gemeldet.
Soweit ich das nachvollziehen konnte liegt das an while Schleifen, die
auf die Änderung einer Variable warten. In der Art:
Wenn ich die Vereinbarung der Variable wait_ack wie folgt ändere:
1
volatileuint32_twait_ack=0x00000000;
Dann tritt der Fehler nicht mehr auf. Also scheint der compiler sonst,
die Variable wegzuoptimieren.
Mit dem Crossworks von Rowley konnte ich die Bibliothek übersetzen, ohne
das dieser Fehler aufgetreten ist. Kann man durch irgendeine
Compileroption zentral das Verhalten so ändern, das der Compiler über
solche Konstrukte nicht mehr stolpert?
Sven Woehlbier schrieb:
> das dieser Fehler aufgetreten ist. Kann man durch irgendeine> Compileroption zentral das Verhalten so ändern, das der Compiler über> solche Konstrukte nicht mehr stolpert?
Yep, bei -O0 macht er das nicht mehr.
Ansonsten kann man es dem Compiler nicht zum Vowurf machen, wenn er sich
regelkonform verhält. Der Fehler sitzt hier vor dem Bildschirm, nicht
dahinter.
http://www.mikrocontroller.net/articles/Compilerfehler#Interrupt-Programmierung
@A.K.
Da hast du natürlich recht. Ich habe auch schon festgestellt, das die
Übersetzung mit -O0 klappt. Ich frage mich nur warum der Compilerfehler
mit Crossworks nicht aufgetreten ist.
Mit ist das schon klar das das ein Programmierfehler ist. Aber warum
reagieren nicht alle GCC basierten Entwicklungsumgebungen in der
gleichen Weisen darauf?
Codesourcery bricht einfach ab.
Mit yagarto bekomme ich die folgende Meldung:
stm32lib\driver\src\stm32f10x_can.c: In function 'CAN_Init':
stm32lib\driver\src\stm32f10x_can.c:299: internal compiler error:
Illegal instruction
Please submit a full bug report,
with preprocessed source if appropriate.
See <http://gcc.gnu.org/bugs.html> for instructions.
Process terminated with status 1 (0 minutes, 0 seconds)
1 errors, 1 warnings
Ich denke Du hast recht. Ich werde mich am besten hinsetzen, und die
Programmierfehler beseitigen. Jedenfalls vielen Dank für Deine schnellen
Antworten!
Dieser doofe CAN Controller geht nur dann in den RESET-Status, wenn ich
mit -O0 kompilliere.
Der Compiler ist Schuld.
Basta.
Oder hat jemand eine Idee wiso das nicht geht???
Wenn bei korrektem Code der Compiler auf die Nase fliegt, ob via Abbruch
oder "internal compiler error", dann ist das richtige Vorgehen oben
schon von ihm selbst beschrieben worden:
Please submit a full bug report,
with preprocessed source if appropriate.
See <http://gcc.gnu.org/bugs.html> for instructions.
Im hiesigen Forum zu fragen bringt dich nicht weiter. Bei CS und
Crossworks kann man ggf, auch dessen Support bemühen. In diesem Fall ist
das möglicherweise vorzuziehen, der Reproduzierbarkeit wegen.
Dies hätte jedoch im anfangs gezeigten Code wenig Sinn gehabt, weil der
Compiler zwar dort auch nicht abnippeln sollte, aber der Anspruch
darauf, absturzfrei fehlerhaften Quellecode formal korrekt übersetzt zu
bekommen, als Bugreport schlecht rüberkommt.
Plan schrieb:
> Der Compiler ist Schuld.> Basta.> Oder hat jemand eine Idee wiso das nicht geht???
Was passiert denn oder sollte passieren, tut es aber nicht? Oben bei
Sven steht ein "internal compiler error" bzw. ein Abbruch. Deine
Beschreibung klingt jedoch so, als ob das Programm nicht so läuft wie
erwartet. Was eine gänzlich andere Situation wäre und dann eigentlich
nicht in den Thread passt.
Das übersetzen ansich macht der immer ohne Errors und ohne Warnings.
Nur der Betrieb, also Laden in CPU und Start, dann verhält sich das
Programm anders. (Ich debugge nicht, sondern mache einen INIT-Error auf
der LCD Anzeige)
Der Befehl:
CAN->MCR = 1;
Setzt den CAN Controller in Reset-Status.
Das IF:
if ((CAN->MSR & 1) == 0)
Frägt nach ob sich der CAN Controller in Reset befindet.
Bei -O0 Kompillierung ist das Bit in MSR gesetzt.
Bei -O1..3,s Kompillierung ist das Bit NICHT gesetzt.
Selbst wenn ich das if in eine while-Schleife umbaue und in der While
das MCR Register immer mit 1 beschreibe, geht das nicht.
Das Problem das ich habe ist nun ja nicht exakt das gleiche, aber
ähnlich
- Auch CAN Init Problem
- Auch behebbar mit -O0 Parameter
Nur ohne Compiler Fehler und das Problem zeigt sich erst zur Laufzeit.
Um mein Problem ein zu grenzen habe ich 2 Tage lang gesucht.
Die Definition der Register ist volatile, daher kein Programmierfehler
und der Compiler darf nichts wegoptimieren.
Der Rest des programmes scheint auch mit Optimierung zu laufen,
zumindest stürt er nicht ab.
(100KB Programm bei -O0)
Plan schrieb:
> Der Befehl:> CAN->MCR = 1;>> Setzt den CAN Controller in Reset-Status.>> Das IF:> if ((CAN->MSR & 1) == 0)>> Frägt nach ob sich der CAN Controller in Reset befindet.
Wobei es durchaus einen Moment dauern kann, bis das INAK Bit gesetzt
wird. Deshalb befindet sich dazwischen im FWlib Code ja die etwas
verunglückte Warteschleife.
Zeige doch mal den mit Optimierung (-O1 oder -Os) durch den Compiler
erzeugten Code dieser Funktion.
>Selbst wenn ich das if in eine while-Schleife umbaue und in der While>das MCR Register immer mit 1 beschreibe, geht das nicht.
Hab ich doch geschrieben! Geht auch nicht.
Mein Problem ist jetzt gelöst. Ich kann die STM32 Bibliothek komplett
mit dem Yagarto Compiler übersetzen. Allerdings trat ein ähnliches
Problem wie oben, noch in der Datei stm32f10x_gpio.c auf.
im Prinzip folgendermaßen:
1
inta,b,c;
2
3
a=2;
4
b=3+a;
5
c=4*b;
In dieser Art wird auch hier das Problem sein, daß die Variablen a und b
wegoptimiert werden. Was aber anscheinend zu Problemen führt, da die
Variablen an anderen Stellen ebenfalls benutzt werden. Jedenfalls war
auch hier die Lösung die Variablen volatile zu vereinbaren.
1
volatileinta,b,c;
Aber wie gesagt, seitdem gibt es beim Übersetzen der Bibliothek keine
Probleme mehr.
@Sven: Wenn du das soweit eindampfen kannst, dass der Kern des Problem
reproduzierbar übrig bleibt, dann wäre das sehr nützlich. Allein schon
um festzustellen, ob hier wirklich ein Problem beim Compiler vorliegt,
oder ob auch hier irgendwelche Annahmen des Programmierers nicht mit
GCCs durchaus agressiven Verständnis über Optimierbarkeit nicht
übereinstimmen.
Die obige Warteschleife ist ja deshalb problematisch, weil darin
uint32_t wait_ack = 0;
while (wait_ack != INAK_TimeOut)
wait_ack++;
steckt, was bekanntlich nicht als Warteschleife taugt und von GCC gern
zu
wait_ack = INAK_TimeOut;
eingedampft wird.
Dass die andere Abfrage im while volatile ist nützt hier nichts, weil es
für GCC keinen Zusammenhang zwischen dem volatilen Steuerregister und
der nicht volatilen Variablen gibt. Erst wenn die Variablen auch
volatile wird, dann entsteht dieser Zusammenhang.
Etwas als volatil zu kennzeichnen heisst ja streng genommen nur, dass
alle anderen volatilen Informationen davon beeinflusst werden können,
oder seinerseits dies beeinflussen. Aber nur diese. Alle volatilen
Informationen bilden also eine Menge von sich gegenseitig möglicherweise
beeinflussenden Information. Nicht volatile Informationen bleiben aussen
vor, sind nicht Teil dieser Menge.
Je mehr ein Compiler diesen Unterschied in Form agressiver Optimierung
ausnutzt, desto mehr Fälle tauchen auf, in denen es bisher nur deshalb
funktionierte, weil der Compiler bisher etwas weniger rücksichtslos
vorging. Solche Fälle gibt es reichlich und nicht immer lassen sie sich
vollständig portabel ohne Annahmen über das Verhalten des Compilers
lösen, beispielsweise wenn volatile Steuerregister das Verhalten des
Prozessors beeinflussen. Sowas lässt sich dem Compiler nicht mitteilen.
Memory Barriers sind so ein Thema, für das es m.W. bislang keine saubere
portable Lösung gibt.
@ A.K.
Die beiden problematischen Fälle habe ich ja schon geschildert, und das
die Ursache die agressive Optimierung ist, ist auch klar. Was möchtest
Du ganz konkret noch wissen?
Ich bin mir da nicht so sicher was der da macht mit O1. Ich vermute er
hat aus CAN_Init() eine Inline-Funkion gemacht.
@ A.K.
Wenn Du noch weitere Infos brauchst, dann kann ich alles posten. Jetzt
muss ich mir est mal noch ein Board mit STM32 zusammenlöten.
Die verunglückte Warteschleife ist in der FWlib nicht umsonst drin. Bei
dir fehlt sie ganz und die 2 Takte an der entscheidenen Stelle können
den Unterschied ausmachen. INAK folgt nicht unmittelbar INRQ, sondern
verzögert.
Ja, stimmt.
Ich nutze die FW-LIB 2.0.3 und da sieht der Code anders aus als bei
FWLib 3.1.2.
Ich habe mal versucht um zustellen auf 3.1.2 bin aber daran gescheitert,
dass in der Datei "startup_stm32f10x_cl.s" der Sprung in die Routine
main() mit einem "void HardFault_Handler(void)" Interrupt quittiert
wurde.
Aus der .s Datei wird der Assembler Befehle:
bl main
aufgerufen, danach kommt sofort der HardFault_Handler Interrupt. :/
Also hab ich erst mal die FWLib 2.0.3 gelassen, denn es geht ja alles.
Und ich hatte auch die Startup geschichten allesamt so aufgebaut um
einen Bootloader in den ersten 8K implementieren zu können. (Alles im
gleichen Quellcode und ohne Assembler) und das müsste ich erst wieder
mit der neuen FWLib hin bekommen (2-3 Tage Arbeit).
Ich versuche mal die FWLib 3.1.2 ein zu binden, aber ohne die
Startup-Sequenz aus dieser Lib, also nur die Dateien aus dem Ordner
"STM32F10x_StdPeriph_Driver" und "CM3".
Ich hab mal die Startup von mir angehängt, nur wenn es jemand
interessiert...
Im FW_Update.c ist dann die FW Update Routine:
Mit einem Strun gin die main().
Im Main erst ist die Initialisierung von data und bss.
[c]
int main(void)
{
NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x2000);
{ // Initialisieren Speicher
extern unsigned long _etext;
extern unsigned long _data; /* start address for the .data
section. defined in linker script */
extern unsigned long _edata; /* end address for the .data
section. defined in linker script */
extern unsigned long _bss; /* start address for the .bss
section. defined in linker script */
extern unsigned long _ebss; /* end address for the .bss
section. defined in linker script */
unsigned long *pulSrc, *pulDest;
// Copy the data segment initializers from flash to SRAM.
pulSrc = &_etext;
for(pulDest = &_data; pulDest < &_edata; )
*(pulDest++) = *(pulSrc++);
// Zero fill the bss segment.
for(pulDest = &_bss; pulDest < &_ebss; )
*(pulDest++) = 0;
}
: : :[c]
In der Startupxxx.s darf die Initialisierung nicht drin sein, denn nach
einem FW-Update simmen die Infos nicht mehr weil der Bootloader (erste 8
KB) nicht überschrieben werden.