Forum: Mikrocontroller und Digitale Elektronik stm32 lib compiler error


von Sven W. (woehlb)


Lesenswert?

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:
1
uint32_t wait_ack = 0x00000000;
2
3
/* Wait the acknowledge */
4
 while ( ((CANx->MSR & MSR_INAK) != MSR_INAK) && (wait_ack != INAK_TimeOut) )
5
{
6
    wait_ack++;
7
}

Wenn ich die Vereinbarung der Variable wait_ack wie folgt ändere:
1
volatile uint32_t wait_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?

von (prx) A. K. (prx)


Lesenswert?

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

von Sven W. (woehlb)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

Sorry, aber das ist kein Compilerfehler, das ist ein Programmierfehler.

von Sven W. (woehlb)


Lesenswert?

Im übrigen konnte ich mit Codesourcery G++ Lite diese Datei auch nicht 
mit -O0 übersetzen.

von (prx) A. K. (prx)


Lesenswert?

Was liefert der Compiler denn für Meldungen?

von Sven W. (woehlb)


Lesenswert?

Mit ist das schon klar das das ein Programmierfehler ist. Aber warum 
reagieren nicht alle GCC basierten Entwicklungsumgebungen in der 
gleichen Weisen darauf?

von (prx) A. K. (prx)


Lesenswert?

Weil CS, Yagarto und Crossworks möglicherweise verschiedene Versionen 
verwenden.

von Sven W. (woehlb)


Lesenswert?

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

von Sven W. (woehlb)


Lesenswert?

Mir fällt dabei gerade noch auf, daß der Fehler in der CAN_DeInit() 
gemeldet wird. Die obige Änderung betrifft aber die Funktion CAN_Init().

von Sven W. (woehlb)


Lesenswert?

Ich denke Du hast recht. Ich werde mich am besten hinsetzen, und die 
Programmierfehler beseitigen. Jedenfalls vielen Dank für Deine schnellen 
Antworten!

von Plan (Gast)


Lesenswert?

Bei mir ist es so, wenn ich mit -O0 kompilliere, dann klappt CAN_Init().

Alles anderen Optionen (1..3,s) schlägt CAN_Init() fehl.
1
  if (!CAN_Init(&CAN_InitStructure))
2
  {
3
    CAN_CountRX = -1; // CAN Initialisierung Fehler !
4
    return;
5
  } else CAN_CountRX = 0;

Dabei wird gleich zu Beginn der Fehler verursacht:
(Auszug ST FWLIB)
1
#define MCR_INRQ     ((u32)0x00000001) /* Initialization request */
2
#define MSR_INAK     ((u32)0x00000001)    /* Initialization acknowledge */
3
4
u8 CAN_Init(CAN_InitTypeDef* CAN_InitStruct)
5
//: : : : :
6
  /* Request initialisation */
7
  CAN->MCR = MCR_INRQ;
8
9
  /* ...and check acknowledged */
10
  if ((CAN->MSR & MSR_INAK) == 0)
11
  {
12
    InitStatus = CANINITFAILED;
13
  }

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

von (prx) A. K. (prx)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Daniel B. (dbuergin)


Lesenswert?

Bei mir lässt sich das File unter Linux ohne Probleme mit dem 
Codesourcery Compiler übersetzen:
1
-------- begin (mode: ROM_RUN) --------
2
arm-none-eabi-gcc (Sourcery G++ Lite 2009q3-68) 4.4.1
3
Copyright (C) 2009 Free Software Foundation, Inc.
4
This is free software; see the source for copying conditions.  There is NO
5
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
6
.......
7
**** Compiling C : Libraries//STM32F10x_StdPeriph_Driver/src/stm32f10x_can.c -> ROM_RUN/stm32f10x_can.o
8
arm-none-eabi-gcc -c -mthumb -Os -mcpu=cortex-m3   -DSTM32F10X_MD -DUSE_STDPERIPH_DRIVER -DUSE_EK_STM32F 
9
-DSTARTUP_DELAY  -DSTM32_USE_DMA -DMOD_MTHOMAS_STMLIB -DVECT_TAB_RAM -DROM_RUN -DSTM32F103RBT6 
10
-I./Libraries//STM32F10x_StdPeriph_Driver/inc -I./Libraries//CMSIS/Core/CM3 -Ifat_sd 
11
-I./Libraries//EEPROMEmulation_AN/include -I. -ffunction-sections -fdata-sections -Wall -Wextra -Wimplicit 
12
-Wcast-align -Wpointer-arith -Wredundant-decls -Wshadow -Wcast-qual -Wcast-align -pedantic 
13
-Wa,-adhlns=ROM_RUN/stm32f10x_can.lst -MD -MP -MF ROM_RUN/dep/stm32f10x_can.o.d -Wnested-externs  
14
-std=gnu99 Libraries//STM32F10x_StdPeriph_Driver/src/stm32f10x_can.c -o ROM_RUN/stm32f10x_can.o
15
......
Ist auch die Version 3.1.2 der Library

Gruss

von Plan (Gast)


Lesenswert?

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.

von Plan (Gast)


Lesenswert?

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)

von (prx) A. K. (prx)


Lesenswert?

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.

von Plan (Gast)


Lesenswert?

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

von Plan (Gast)


Lesenswert?

Den Code kann ich erst heute Mittag posten, muss weg.

von Sven W. (woehlb)


Lesenswert?

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
int a, 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
volatile int a, b, c;

Aber wie gesagt, seitdem gibt es beim Übersetzen der Bibliothek keine 
Probleme mehr.

von (prx) A. K. (prx)


Lesenswert?

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

von Sven W. (woehlb)


Lesenswert?

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

von Plan (Gast)


Lesenswert?

Jetzt bin ich wieder da...

Kompiliert CAN_Init() mit -O0:
1
u8 CAN_Init(CAN_InitTypeDef* CAN_InitStruct)
2
{
3
   1111c:  b480        push  {r7}
4
   1111e:  b085        sub  sp, #20
5
   11120:  af00        add  r7, sp, #0
6
   11122:  6078        str  r0, [r7, #4]
7
  u8 InitStatus = 0;
8
   11124:  f04f 0300   mov.w  r3, #0
9
   11128:  737b        strb  r3, [r7, #13]
10
  u16 WaitAck = 0;
11
   1112a:  f04f 0300   mov.w  r3, #0
12
   1112e:  81fb        strh  r3, [r7, #14]
13
  assert_param(IS_CAN_BS1(CAN_InitStruct->CAN_BS1));
14
  assert_param(IS_CAN_BS2(CAN_InitStruct->CAN_BS2));
15
  assert_param(IS_CAN_PRESCALER(CAN_InitStruct->CAN_Prescaler));
16
17
  /* Request initialisation */
18
  CAN->MCR = MCR_INRQ;
19
   11130:  f246 4300   movw  r3, #25600  ; 0x6400
20
   11134:  f2c4 0300   movt  r3, #16384  ; 0x4000
21
   11138:  f04f 0201   mov.w  r2, #1
22
   1113c:  601a        str  r2, [r3, #0]
23
24
  /* ...and check acknowledged */
25
  if ((CAN->MSR & MSR_INAK) == 0)
26
   1113e:  f246 4300   movw  r3, #25600  ; 0x6400
27
   11142:  f2c4 0300   movt  r3, #16384  ; 0x4000
28
   11146:  685b        ldr  r3, [r3, #4]
29
   11148:  f003 0301   and.w  r3, r3, #1
30
   1114c:  2b00        cmp  r3, #0
31
   1114e:  d103        bne.n  11158 <CAN_Init+0x3c>
32
  {
33
    InitStatus = CANINITFAILED;
34
   11150:  f04f 0300   mov.w  r3, #0
35
   11154:  737b        strb  r3, [r7, #13]
36
   11156:  e0f5        b.n  11344 <CAN_Init+0x228>
37
  }

Kompiliert CAN_Init() mit -O1:
1
0000bf3c <CAN_Init>:
2
  assert_param(IS_CAN_BS1(CAN_InitStruct->CAN_BS1));
3
  assert_param(IS_CAN_BS2(CAN_InitStruct->CAN_BS2));
4
  assert_param(IS_CAN_PRESCALER(CAN_InitStruct->CAN_Prescaler));
5
6
  /* Request initialisation */
7
  CAN->MCR = MCR_INRQ;
8
    bf3c:  f246 4300   movw  r3, #25600  ; 0x6400
9
    bf40:  f2c4 0300   movt  r3, #16384  ; 0x4000
10
    bf44:  f04f 0201   mov.w  r2, #1
11
    bf48:  601a        str  r2, [r3, #0]
12
13
  /* ...and check acknowledged */
14
  if ((CAN->MSR & MSR_INAK) == 0)
15
    bf4a:  685b        ldr  r3, [r3, #4]
16
    bf4c:  f013 0f01   tst.w  r3, #1
17
    bf50:  bf08        it  eq
18
    bf52:  2000        moveq  r0, #0
19
    bf54:  f000 809d   beq.w  c092 <CAN_Init+0x156>
20
    InitStatus = CANINITFAILED;
21
  }

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.

von (prx) A. K. (prx)


Lesenswert?

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.

von Plan (Gast)


Angehängte Dateien:

Lesenswert?

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:
1
void __boot FW_Main(void)
2
{
3
  SCB->VTOR = NVIC_VectTab_FLASH | (0x00 & (u32)0x1FFFFF80); // NVIC_SetVectorTable(NVIC_VectTab_FLASH, 0x0);
4
5
  while (1)
6
  {
7
    // Kein FW-Update durchführen
8
    myappvectors[1].__fun(); // main()
9
  }
10
}
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.

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.