Forum: Compiler & IDEs [arm] Kompileroption -flto bewirkt daß der uart (ungefähr) doppelt so schnell läuft


von Bernd K. (prof7bit)


Angehängte Dateien:

Lesenswert?

Ich hab grad ein interessantes Phänomen. Das anbei angehängte minimale 
Makefile-Projekt kompiliert einwandfrei und läuft auch einwandfrei auf 
einem STM32nucleo board SOFERN man im Makefile in den CFLAGS die 
Option -flto entfernt.

Das interessante daran ist daß die Blinkzeit 500ms sich nicht ändert, 
jedoch die UART-Baudrate ist hinterher ungefähr doppelt so hoch, wenn 
man das Terminal auf dem PC auf 19200 einstellt kann man einige Zeichen 
des Strings "foobar\r\n" noch erahnen: 66 6F 6F E2 E1 72 8D 0A

Ohne die Option kommt es wie gewünscht sauber auf 9600 Baud raus: 66 
6F 6F 62 61 72 0D 0A

Die Blinkzeit jedoch bleibt exakt gleich.

Woran kann das liegen? Ist das ein Kompiler-Bug oder ein Bug in der 
HAL-Library oder eine fehlerhafte Anwendung meinerseits?

______
PS: Lässt sich dieses Minimalbeispiel eventuell noch schrumpfen (so um 
den Faktor 10 vielleicht)? Ich finde es schon einigermaßen ungeheuerlich 
wie ein so simples Minimalprogramm schon satte 4300 Byte konsumiert.

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Mal ins Blaue geraten (deine Archive hab ich nicht durchwühlt): 
Optimierung nicht aktiviert? Oder nicht LTO-sicher programmiert?

von Bernd K. (prof7bit)


Lesenswert?

Johann L. schrieb:
> Optimierung nicht aktiviert?

-Os denn Optimierung ist ja das Ziel der Übung.

> Oder nicht LTO-sicher programmiert?

Ich finde nichts im gcc manual zum Themembereich "LTO-sicher 
programmieren", es liest sich als sei es ausgereift und unproblematisch, 
hast Du evtl. mal ein paar weiterführende Links? Das einzige was mir bei 
nochmaligem Lesen noch aufgefallen ist war daß es sich nicht mit 
debug-Informationen verträgt, also hab ich die mal weggelassen, aber das 
hat keine Auswirkung.

von Dr. Sommer (Gast)


Lesenswert?

"LTO-sicher" programmieren ist ganz einfach: C(++) Standard -konform 
programmieren. Wenn man sich aber irgendwo auf undefiniertes Verhalten 
verlässt (zB casts oder signed integer over/underflow etc.) darf der 
Kompiler irgendwas machen, und bei eingeschalteter LTO macht er noch 
aggressiver irgendwas...

Technisch gesehen sind Funktionsnamen wie "__GPIOA_CLK_ENABLE" vom 
Standard schon verboten, daher ist es pures Glück dass diese vom 
Compiler überhaupt akzeptiert werden...

Versuche mal das Problem zu lokalisieren:
Lasse dir mal den Systemtakt auf dem Clock Output Pin ausgebem (MCO) und 
prüfe mit dem Oszilloskop ob der Takt mit und ohne LTO stimmt. Wenn er 
stimmt, ist der Fehler in der UART-Konfiguration, sonst in der 
Systemtakt-Konfiguration. Disassemblisiere mal das Kompilat, um zu 
schauen welche Werte in die Peripherie-Register geschrieben werden, und 
berechne ob sie stimmen (-> Reference Manual).

von (prx) A. K. (prx)


Lesenswert?

An sich klingt das nach einem angenehm einfachen Fall. Wenn die Bitrate 
der UART falsch ist, dann gibts nicht viele Stellen, an denen man suchen 
muss. Gibt weit schlimmere Fälle.

Aber vollständiges Einhalten des Standards muss grad im µC Kontext nicht 
unbedingt ausreichen. Ich hatte mal den Fall, dass Optimierung einen 
kompletten Interrupt-Handler wegoptimierte, weil der nirgends im 
Programm referenziert wurde. In der Vektortabelle wurde er zwar schon 
angesprochen, aber der als "weak external" von der Standardumgebung 
definierte Default-Handler reichte dem Linker völlig aus.

Je mehr Compiler/Linker vom Gesamtbild des Programms sehen, desto 
grösser ist die Chance, dass man auf solche Schmutzeffekte reinfällt. In 
meinem Fall ergab sich das als Folge von -ffunction-sections.

: Bearbeitet durch User
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Dr. Sommer schrieb:
> "LTO-sicher" programmieren ist ganz einfach: C(++) Standard -konform
> programmieren. Wenn man sich aber irgendwo auf undefiniertes Verhalten
> verlässt (zB casts oder signed integer over/underflow etc.) darf der
> Kompiler irgendwas machen, und bei eingeschalteter LTO macht er noch
> aggressiver irgendwas...

Ja, genau das meinte ich mit "LTO-sicher".  Die Probleme sind nicht 
compilerseitig, sondern es sind Fehler im Programm, die erst durch diese 
modulübergreifende Optimierung offensichtlich werden.


Welche Kommandos werden denn konkret ausgeführt, d.h. wie wird gcc etc. 
aufgerufen? Nicht als make-Schnippel, sondern als Kommando, das von 
make ausgeführt wird.

: Bearbeitet durch User
von Bernd K. (prof7bit)


Lesenswert?

Johann L. schrieb:
> Dr. Sommer schrieb:
>> "LTO-sicher" programmieren ist ganz einfach: C(++) Standard -konform
>> programmieren. Wenn man sich aber irgendwo auf undefiniertes Verhalten
>> verlässt (zB casts oder signed integer over/underflow etc.) darf der
>> Kompiler irgendwas machen, und bei eingeschalteter LTO macht er noch
>> aggressiver irgendwas...
>
> Ja, genau das meinte ich mit "LTO-sicher".  Die Probleme sind nicht
> compilerseitig, sondern es sind Fehler im Programm, die erst durch diese
> modulübergreifende Optimierung offensichtlich werden.
>
>
> Welche Kommandos werden denn konkret ausgeführt, d.h. wie wird gcc etc.
> aufgerufen? Nicht als make-Schnippel, sondern als Kommando, das von
> make ausgeführt wird.

So sieht das aus wenn ich das gesamte Projekt neu baue.
1
make clean all
2
rm -f *.o template.elf template.hex template.bin
3
arm-none-eabi-gcc -c -o stm32f4xx_hal.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal.c
4
arm-none-eabi-gcc -c -o stm32f4xx_hal_rcc.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_rcc.c
5
arm-none-eabi-gcc -c -o stm32f4xx_hal_gpio.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_gpio.c
6
arm-none-eabi-gcc -c -o stm32f4xx_hal_uart.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_uart.c
7
arm-none-eabi-gcc -c -o stm32f4xx_hal_dma.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_dma.c
8
arm-none-eabi-gcc -c -o stm32f4xx_hal_cortex.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_cortex.c
9
arm-none-eabi-gcc -c -o stm32f4xx_hal_pcd.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd.c
10
arm-none-eabi-gcc -c -o stm32f4xx_hal_pcd_ex.o -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections Drivers/STM32F4xx_HAL_Driver/Src/stm32f4xx_hal_pcd_ex.c
11
arm-none-eabi-gcc -Isrc/ -IDrivers/STM32F4xx_HAL_Driver/Inc/ -IDrivers/CMSIS/Device/ST/STM32F4xx/Include/ -IDrivers/CMSIS/Include/ -DSTM32F401xE -Os -flto -ffunction-sections -mlittle-endian -mthumb -mcpu=cortex-m4 -mthumb-interwork -Wl,--gc-sections -Wall -Wextra -Warray-bounds -Wno-unused-parameter -TDevice/gcc.ld src/main.c Device/startup_stm32f401xe.s src/stm32f4xx_hal_msp.c src/stm32f4xx_it.c src/system_stm32f4xx.c src/newlib_stubs.c stm32f4xx_hal.o stm32f4xx_hal_rcc.o stm32f4xx_hal_gpio.o stm32f4xx_hal_uart.o stm32f4xx_hal_dma.o stm32f4xx_hal_cortex.o stm32f4xx_hal_pcd.o stm32f4xx_hal_pcd_ex.o -o template.elf
12
arm-none-eabi-objcopy -O ihex template.elf   template.hex
13
arm-none-eabi-objcopy -O binary template.elf template.bin
14
arm-none-eabi-size template.elf
15
   text    data     bss     dec     hex filename
16
   2804    1104      36    3944     f68 template.elf

Wie gesagt, es ist nur eine Art Hello-World. Als Grundlage hab ich das 
hier genommen: 
https://github.com/metabr/stm32-nucleo-f401re-basic-template hab das 
USB-zeug rausgeworfen und stattdessen den UART konfiguriert, indem ich 
aus anderen Turorials und Beispielprojekten zusammengesuchte 
Codeschnipsel passend gemacht habe.

Ich muss dazu sagen daß ich eigentlich aus der AVR-Ecke komme, dort kann 
ich mittlerweile im Halbschlaf und mit verbundenen Augen ein neues 
Projekt aus dem Boden stampfen und es gibt erste Lebenszeichen von sich 
noch bevor die erste Tasse Kaffee leer ist. Bei ARM jedoch und im 
Speziellen bei dem STM32F401 den ich hier zum Spielen rumliegen habe 
steh ich noch ganz am Anfang, selbst die simpelsten Dinge erscheinen mir 
hier extremst verworren, der Aufbau der mitgelieferten Header und 
Libraries und vor allem deren Dokumentation liegen für mich noch 
weitgehend im Dunkeln (Ich suche immer noch den Anfang).


Meine main.c sieht im wesentlichen so aus:
1
int main(void) {
2
    HAL_Init();
3
    SystemClock_Config();
4
5
    /**
6
     * GPIO
7
     */
8
    
9
    GPIO_InitTypeDef  GPIO_InitStruct;
10
    __GPIOA_CLK_ENABLE();
11
    GPIO_InitStruct.Pin = GPIO_PIN_5;
12
    GPIO_InitStruct.Mode = GPIO_MODE_OUTPUT_PP;
13
    GPIO_InitStruct.Pull = GPIO_PULLUP;
14
    GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
15
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct); 
16
17
    /**
18
     * UART
19
     */
20
    
21
    GPIO_InitStruct.Pin = GPIO_PIN_2;
22
    GPIO_InitStruct.Mode = GPIO_MODE_AF_PP;
23
    GPIO_InitStruct.Speed = GPIO_SPEED_FAST;
24
    GPIO_InitStruct.Alternate = GPIO_AF7_USART2;
25
    HAL_GPIO_Init(GPIOA, &GPIO_InitStruct);
26
    
27
    __USART2_CLK_ENABLE();     
28
    UART_HandleTypeDef UartHandle;
29
    UartHandle.Instance        = USART2;
30
    UartHandle.Init.BaudRate   = 9600;
31
    UartHandle.Init.WordLength = UART_WORDLENGTH_8B;
32
    UartHandle.Init.StopBits   = UART_STOPBITS_2;
33
    UartHandle.Init.Parity     = UART_PARITY_NONE;
34
    UartHandle.Init.HwFlowCtl  = UART_HWCONTROL_NONE;
35
    UartHandle.Init.Mode       = UART_MODE_TX_RX;    
36
    HAL_UART_Init(&UartHandle);
37
38
    char buf[] = "foobar\r\n";
39
    
40
    while (1) {    
41
        HAL_GPIO_TogglePin(GPIOA, GPIO_PIN_5);
42
        HAL_UART_Transmit(&UartHandle, (uint8_t *)buf, 8, 0xFFFF);
43
        HAL_Delay(500);
44
    }
45
}

Und die SystemClock_Config(); sieht so aus (aber die hab ich direkt aus 
dem beispiel übernommen und dei scheint auch orginal aus dem Beispiel 
von ST für das Nucleo board zu stammen):
1
/**
2
  * @brief  System Clock Configuration
3
  *         The system Clock is configured as follow : 
4
  *            System Clock source            = PLL (HSI)
5
  *            SYSCLK(Hz)                     = 84000000
6
  *            HCLK(Hz)                       = 84000000
7
  *            AHB Prescaler                  = 1
8
  *            APB1 Prescaler                 = 2
9
  *            APB2 Prescaler                 = 1
10
  *            HSI Frequency(Hz)              = 16000000
11
  *            PLL_M                          = 16
12
  *            PLL_N                          = 336
13
  *            PLL_P                          = 4
14
  *            PLL_Q                          = 7
15
  *            VDD(V)                         = 3.3
16
  *            Main regulator output voltage  = Scale2 mode
17
  *            Flash Latency(WS)              = 2 
18
  * @param  None
19
  * @retval None
20
  */
21
static void SystemClock_Config(void) {
22
    RCC_ClkInitTypeDef RCC_ClkInitStruct;
23
    RCC_OscInitTypeDef RCC_OscInitStruct;
24
    HAL_StatusTypeDef status;
25
26
    /* Enable Power Control clock */
27
    __PWR_CLK_ENABLE();
28
29
    /* The voltage scaling allows optimizing the power consumption when the device is 
30
        clocked below the maximum system frequency, to update the voltage scaling value 
31
        regarding system frequency refer to product datasheet.  */
32
    __HAL_PWR_VOLTAGESCALING_CONFIG(PWR_REGULATOR_VOLTAGE_SCALE2);
33
34
    /* Enable HSI Oscillator and activate PLL with HSI as source */
35
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI;
36
    RCC_OscInitStruct.HSIState = RCC_HSI_ON;
37
    RCC_OscInitStruct.HSICalibrationValue = 0x10;
38
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
39
    RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_HSI;
40
    RCC_OscInitStruct.PLL.PLLM = 16;
41
    RCC_OscInitStruct.PLL.PLLN = 336;
42
    RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV4;
43
    RCC_OscInitStruct.PLL.PLLQ = 7;
44
    if((status = HAL_RCC_OscConfig(&RCC_OscInitStruct)) != HAL_OK)
45
    {
46
        Error_Handler();
47
    }
48
49
    /* Select PLL as system clock source and configure the HCLK, PCLK1 and PCLK2 
50
        clocks dividers */
51
    RCC_ClkInitStruct.ClockType = (RCC_CLOCKTYPE_SYSCLK | RCC_CLOCKTYPE_HCLK | RCC_CLOCKTYPE_PCLK1 | RCC_CLOCKTYPE_PCLK2);
52
    RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
53
    RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
54
    RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV2;  
55
    RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;  
56
    if(HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_2) != HAL_OK)
57
    {
58
        Error_Handler();
59
    }  
60
61
    /* Enable HSE Oscillator
62
    RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSE;
63
    RCC_OscInitStruct.HSEState = RCC_HSE_ON;
64
    RCC_OscInitStruct.PLL.PLLState = RCC_PLL_NONE;
65
66
    if((status = HAL_RCC_OscConfig(&RCC_OscInitStruct)) != HAL_OK)
67
    {
68
        Error_Handler();
69
    }
70
    */
71
}

Ich glaube nicht daß der Takt doppelt so schnell läuft, das scheint 
bereits im Normalzustand (also wenn der Fehler nicht auftritt) den 
höchstmöglichen Takt einzustellen.

von Bernd M. (bernd_m)


Lesenswert?

Lad dir mal das STM CUBE tool und die Lib für deinen F4.
Damit kannst du dir erstmal alles zusammen klicken.
Später nimmst du nur die Sachen die du wirklich brauchst.
Optimierung -Og maximale Optimierung mit brauchbaren Debug Infos.

Gruss,
Bernd

von Student (Gast)


Lesenswert?

Im Wiki steht, dass es zB möglich ist, dass Interruptvector und ISRs 
möglicherweise wegoptimiert werden. Dagegen soll __attribute(used) oder 
so ähnlich helfen. Hat es bei mir aber auch nicht.

von Bernd K. (prof7bit)


Lesenswert?

Ok, Leute, ich glaub ich hab die Ursache. War natürlich mein eigener 
Fehler (wie immer), hab nochmal in Ruhe über das drübergeschaut was ich 
gestern fabriziert habe und siehe da:
1
UART_HandleTypeDef UartHandle;

sollte ich wohl mal mit 0 initialisieren, besser ist das...
1
UART_HandleTypeDef UartHandle = {0};

Und o Wunder: nun stimmt die Baudrate :-)

von huhu (Gast)


Lesenswert?

Deswegen gibt's da eine init struct Funktion..
Die erstmal alles auf default setzt.


Ich hatte vor 2jahren erste mL Kontakt zum cortex.

Mittlerweile hab ich die stm/nxp/.. libs über..
Ich bau mir eigene Funktionen ..


Und oft spreche ich Register direkt an.
Das ganze gewabel mit den structs und so is mir zu nervig.


Die libs nutze ich nur als grobe Vorlage wenn ich mal wissen will was 
ich brauch.. oder kopiere mir den Teil da raus...


Seit dem sind die cortex M genau so einfach wie die AVR's ^^

von Trauter (Gast)


Lesenswert?

huhu schrieb:
> Und oft spreche ich Register direkt an.

Du traust dich ja was. Und wie ist die Reaktion, kommt ihr ins Gespräch?

Wat ein Gebrabbel. Es ist ganz einfach: Je proffessioneler desto 
abstrakter. Der Code soll wiederverwendbar sein, denn er kostet eine 
Menge Geld. Ob fertige LIBS vom Hersteller, vom Tool, vom ..., oder auch 
eigene. Aber LIBS und abstrakt!

von Bernd K. (prof7bit)


Lesenswert?

Das gemeine ist daß ich diese struct initialisierung 1:1 vom 
mitgelieferten Beispiel aus dem STM32Cube übernommen habe: 
http://pastebin.com/f3HyRgG3

huhu schrieb:
> Deswegen gibt's da eine init struct Funktion..
> Die erstmal alles auf default setzt.

Wo find ich die?

von huhu (Gast)


Lesenswert?

Ja.... Abstrakt ist schon richtig.


Die meisten AVR umsteiger stolpern aber über die libs der Hersteller..
Beim avr haben die meisten ins Datenblatt geschaut

Und da werden Register direkt per bittschieberei beschrieben

Wenn man das anfangs mal weglässt merkt man das es das gleiche ist...
Das struct zeug ist nur ein optisch komplizierterer Einstieg.

Später wird man es Evtl genau so machen...
Aber deswegen ist der cortex M nicht komplizierter

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.