Forum: Compiler & IDEs [STM32] Compiler optimiert Funktion zu stark


von mr. mo (Gast)


Angehängte Dateien:

Lesenswert?

Guten Morgen zusammen,

ich habe ein wahrscheinlich einfach lösbares Problem. Ich versuche 
aktuell einen W5500 (Ethernet Controller) mit einem STM32F405 via SPI 
anzusteuern. Im Debug funktioniert auch alles, im Release optimiert der 
Compiler ein bisschen zu viel.

Programmiersprache ist C++, genutzt wird GCC 7.2.0, GDB 8.0.1 mit 
VisualGDB in Visual Studio Community 2017.

Um folgenden Codeausschnitt geht es:
1
uint8_t W5500::readWriteByte(uint8_t byte)
2
{
3
  volatile uint8_t val;
4
5
  // Schreibe Dummybyte
6
  while (!(SPI2->SR & SPI_FLAG_TXE));
7
  SPI2->DR = (uint16_t)byte;
8
9
  // Lese altes Byte falls OVR gesetzt
10
  if (SPI2->SR & SPI_FLAG_OVR)
11
    uint8_t tmp = SPI2->DR;
12
13
  // Lese neues byte
14
  while (!(SPI2->SR & SPI_FLAG_RXNE));
15
  //while (SPI2->SR & SPI_SR_BSY);
16
  val = SPI2->DR;
17
18
  return val;
19
}

Im Debug erfolgt die Zuweisung val = SPI2->DR; korrekt, siehe Screenshot 
06.png. Im Release wird die Zuweisung nicht korrekt ausgeführt, siehe 
Screenshot 05.png. Verbiete ich dem Compiler via 
__attribute__((optimize("O0"))) das Optimieren, funktioniert wieder 
alles, siehe Screenshot 04.png.

Ich habe schon an einigen Stellen mit volatile versucht die Optimierung 
zu verhindern, nur leider kein Erfolg.

Was macht er Compiler hier? Wie löse ich das Problem? Welche Infos 
werden noch benötigt?

Danke im Vorraus!

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

mr. mo schrieb:
> Was macht er Compiler hier?

Das kannst du dir anschauen mit zusätzlich -save-temps -fverbose-asm

* Überprüfe im ii-File, ob alle Definitionen korrekt sind; etwa ob 
SPI2->DR als volatile deklariert ist und / oder SPI2 als volatile*.

* Überprüfe im s-File, ob die Instruktionen so sind wie erwartet.

* val brauch nicht volatile zu sein.

Optimierten Code zu debuggen kann etwas verwirrend sein, das ist ganz 
normal und liegt in der Natur der Sache.  volatile zu verteilen ist da 
keine Lösung, weil du dann nämlich nicht mehr den Code debuggst, der 
schließlich laufen wird — unter der Annahme, dass die Release nicht mit 
überflüssigen volatiles gespickt sein wird.

Du kannst hier auch das ii-Fale anhängen, das ist dann auch für andere 
compilier- und nachvollziehbar, was für png nicht zutrifft

von Markus F. (mfro)


Lesenswert?

Da dürfte es sich um dieses: 
Beitrag "Register nicht korrekt gelesen? (STM32, SPI, KEIL µVision)"
Problem handeln.

von mr. mo (Gast)


Angehängte Dateien:

Lesenswert?

Markus F. schrieb:
> Da dürfte es sich um dieses:
> Beitrag "Register nicht korrekt gelesen? (STM32, SPI, KEIL µVision)"
> Problem handeln.

Mit der genannten Problemlösung hat es bei mir auch funktioniert. Jedoch 
möchte ich nicht ständig das empfangene Byte auslesen, wenn ich es nicht 
benötige. Daher überprüfe ich auch das OVR-Bit und lese anschließend die 
Daten.

Hintergrund ist eine zeitkritische Anwendung, daher schreibe ich auch 
die Ansteuerung des W5500 selber.

Im Screenshot "schreibenLesen.png" ist die nun funktionierende Variante, 
man sieht zwischen den Bytes eine Totzeit mit knapp 3µs.
Im Screenshot "schreibenLesenDebug.png" ist meine ursprüngliche 
Variante, welche im Release nicht funktioniert, ich spare mir 5µs ein. 
Bei vielen Paketen die Sekunde macht das einen Unterschied.

Das finale Lesen und Schreiben der Daten in den RX/TX Buffer des W5500 
erfolgt via DMA (>1000 Bytes). Nur lohnt sich der DMA nicht zum Auslesen 
der Statusregister des W5500 (5 Bytes), da kann man besser pollen :(

Irgendwie muss man es doch hinbekommen den korrekten Wert von Interesse 
aus dem Datenregister vom SPI zu lesen ohne vorher alle Werte 
auszulesen, welche uninteressant sind.

Ich hoffe mein Problem wird klar. Ja, ich versuche hier gerade µs zu 
optimieren, aber leider muss das sein.

von Markus F. (mfro)


Lesenswert?

mr. mo schrieb:
> Ja, ich versuche hier gerade µs zu
> optimieren, aber leider muss das sein.

Ich kenne mich mit STM32 nicht aus. Du scheinst statt des empfangenen 
SPI-Bytes lieber im Statusregister das Overflow-Flag abzufragen?

Warum soll das schneller sein, als das Byte, das Du nicht brauchst, 
auszulesen und anschliessend zu vergessen?

von Dr. Sommer (Gast)


Lesenswert?

mr. mo schrieb:
> Ich versuche aktuell einen W5500 (Ethernet Controller) mit einem
> STM32F405 via SPI anzusteuern.

Warum benutzt du denn nicht den im STM32 integrierten Ethernet 
Controller?!

Ich würde die Geschichte mit OVR auch weglassen. Warte bis das Byte 
ankommt, und lese es aus.

Wenn man glaubt dass der Compiler etwas kaputt optimiert sollte man 
natürlich die Disassembly anschauen - einfach mit dem Debugger rum 
stochern hilft nicht viel. Ich glaube aber nicht dass etwas kaputt 
optimiert wird, sondern das Programm einfach zu schnell läuft.

von Dr. Sommer (Gast)


Lesenswert?

Und ja, SPI->DR im Debugger auszulesen leert den Puffer und das Programm 
liest nix mehr

von Mr. Big (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Warum benutzt du denn nicht den im STM32 integrierten Ethernet
> Controller?!

Was an:

> mr. mo schrieb:
>> Ich versuche aktuell einen W5500 (Ethernet Controller) mit einem
>> STM32F405 via SPI anzusteuern.

hast Du nicht verstanden?

von Dr. Sommer (Gast)


Lesenswert?

Ich  verstehe nicht warum man das komplizierte und Performance kritische 
Ethernet Protokoll durch SPI quetscht, wenn man es auch direkt im MCU 
abhandeln könnte. Wir wir sehen muckt SPI ja schon rum.

von (prx) A. K. (prx)


Lesenswert?

mr. mo schrieb:
> Irgendwie muss man es doch hinbekommen den korrekten Wert von Interesse
> aus dem Datenregister vom SPI zu lesen ohne vorher alle Werte
> auszulesen, welche uninteressant sind.

In der Referenz ist dokumentiert, wie ST sich den Umgang mit SPI 
vorstellt. Weichst du davon ab, sind Schmutzeffekte möglich. So könnte 
ebensogut das vom Optimierungsgrad veränderte exakte Zeitverhalten des 
Codes in Zusammenhang mit der Vorgeschichte und dem OVR Flag eine Rolle 
spielen.

Für die Beurteilung des vom Compiler erzeugten Codes ist übrigens 
ebendieser erzeugte Code unverzichtbar. C und Debugger reichen nicht.

: Bearbeitet durch User
von Mr. Big (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Ich  verstehe nicht warum man das komplizierte und Performance
> kritische
> Ethernet Protokoll durch SPI quetscht, wenn man es auch direkt im MCU
> abhandeln könnte. Wir wir sehen muckt SPI ja schon rum.


Die MCU hat doch gar kein Ethernet. Soll er sich die Schnittstelle jetzt 
randenken oder was?

von Dr. Sommer (Gast)


Lesenswert?

Mr. Big schrieb:
> Die MCU hat doch gar kein Ethernet. Soll er sich die Schnittstelle jetzt
> randenken oder was?

Ah, tatsächlich. Ich war verwirrt davon dass im Datasheet des STM32F405 
auf der ersten Seite rechts unten "Ethernet" steht. Nun, auch dann würde 
ich mir überlegen nicht vielleicht einfach auf den F407 umzusteigen. Ist 
doch etwas sinnlos unbedingt den F405 mit Ethernet aufzurüsten wenn man 
einfach den F407 nehmen kann der das eingebaut hat.

von (prx) A. K. (prx)


Lesenswert?

Wobei man sich mit dem W5500 einige Software erspart. Das ist kein 
simpler Ethernet-Controller, sondern eine weitgehend fertige TCP/IP 
Lösung.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

A. K. schrieb:
> Das ist kein
> simpler Ethernet-Controller, sondern eine weitgehend fertige TCP/IP
> Lösung.
Da ist die Software halt im externen Chip drin. Man könnte auch LwIP 
o.ä. runterladen und das alles im STM32F407 haben. Ist dann effizienter 
und flexibler. Man muss nicht die AVR-Methodik, für alles und jedes ein 
externes IC mit integrierter Software zu nehmen, auf die 
leistungsfähigeren STM32 übertragen.

von Mr. Big (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Mr. Big schrieb:
>> Die MCU hat doch gar kein Ethernet. Soll er sich die Schnittstelle jetzt
>> randenken oder was?
>
> Ah, tatsächlich. Ich war verwirrt davon dass im Datasheet des STM32F405
> auf der ersten Seite rechts unten "Ethernet" steht. Nun, auch dann würde
> ich mir überlegen nicht vielleicht einfach auf den F407 umzusteigen. Ist
> doch etwas sinnlos unbedingt den F405 mit Ethernet aufzurüsten wenn man
> einfach den F407 nehmen kann der das eingebaut hat.


Naja, soviel Verstand wollen wir ihm doch zutrauen, dass er aus 
triftigen Gründen diese Frage stellt. Vielleicht ist der Controller 
vorgegeben, vielleicht braucht er die Performance und will daher den 
Stack extern abgearbeitet haben, was auch immer.

Und nicht, dass jetzt jemand einen Raspberry Pi oder sowas vorschlägt, 
weil man damit Python machen kann... ;)

von Dr. Sommer (Gast)


Lesenswert?

Mr. Big schrieb:
> vielleicht braucht er die Performance und will daher den
> Stack extern abgearbeitet haben, was auch immer.

Performance. Wenn man alles durch SPI quetschen muss. Ist klaro :)

Mr. Big schrieb:
> Naja, soviel Verstand wollen wir ihm doch zutrauen,
Wäre nicht die erste sinnlose Frage im Forum. Er verdächtigt den 
Compiler der Falsch-Optimierung, schaut aber nicht in die Disassembly. 
Das sagt schon einiges.

von mr. mo (Gast)


Lesenswert?

Dr. Sommer schrieb:
> Mr. Big schrieb:
>> vielleicht braucht er die Performance und will daher den
>> Stack extern abgearbeitet haben, was auch immer.
>
> Performance. Wenn man alles durch SPI quetschen muss. Ist klaro :)

Ich sehe du kennst den W5500 noch nicht. Das Protokoll ist extrem simpel 
und deutlich weniger aufwändig als den lwIP auf dem STM laufen zu haben. 
Wie Mr. Big bereits richtig erkannt hat, will ich den STM auch nicht mit 
dem Software-Stack auslasten. Laut WizNet hat der W5500 einen 
"hardwired" Stack, also da ist alles direkt aufs Silizium gebrannt ohne 
Software. Vielleicht gibts jemanden im Forum der sich damit mal befasst 
hat und Zusatzinformation geben kann.

> Mr. Big schrieb:
>> Naja, soviel Verstand wollen wir ihm doch zutrauen,
> Wäre nicht die erste sinnlose Frage im Forum. Er verdächtigt den
> Compiler der Falsch-Optimierung, schaut aber nicht in die Disassembly.
> Das sagt schon einiges.

Findest du das diese Frage hier sinnlos ist? Nicht jeder kann und weiß 
alles so gut wie du :) Für alle anderen gibt es Foren, um sinnlose 
Fragen zu stellen und sich Rat bei den erfahrenen Benutzern, wie z.B. 
dir, zu holen. Achja von dir kam bisher nichts sinnvolles zur 
Problemlösung, nur Kritik an meiner Umsetzung ohne die eigentliche 
Aufgabenstellung zu kennen.

Nun zurück zu den konstruktiven Posts:

Markus F. schrieb:
> Ich kenne mich mit STM32 nicht aus. Du scheinst statt des empfangenen
> SPI-Bytes lieber im Statusregister das Overflow-Flag abzufragen?
>
> Warum soll das schneller sein, als das Byte, das Du nicht brauchst,
> auszulesen und anschliessend zu vergessen?

Ich frage das nicht so häufig ab, nur wenn ich ein Byte empfangen will. 
Wie bereits von A.K. genannt ist das ein bisschen dreckig programmiert. 
Man kann ja mal schauen wie tolerant die Hardware ist.

A. K. schrieb:
> In der Referenz ist dokumentiert, wie ST sich den Umgang mit SPI
> vorstellt. Weichst du davon ab, sind Schmutzeffekte möglich. So könnte
> ebensogut das vom Optimierungsgrad veränderte exakte Zeitverhalten des
> Codes in Zusammenhang mit der Vorgeschichte und dem OVR Flag eine Rolle
> spielen.

Bestätigt auch meine Vermutung. Ich denke ich muss mich beim direkten 
Übertragen der Befehle an den W5500 (ohne Interrupts und DMA) an die 
gängige Vorgehensweise halten und mit den paar µs Wartezeit leben. Wenn 
ich den Interruptpin des W5500 nutze, reduziert sich auch das Auslesen 
der Statusregister des W5500 auf ein Minimum. Der Rest wird via 
Interrupt und DMA abgearbeitet, da treten beim Senden und Empfangen 
keine Wartezeit auf.

Danke für die Hilfe/Kritik soweit! Mein eigentliches Problem wurde ja 
bereits mit dem zweiten Post gelöst, danke Markus!

von ähm ja (Gast)


Lesenswert?

Der m4 hat auch Cache....
Hier aufpassen aus welchem RAM man arbeitet.

SRAM?
DTCM?

Je nach dem wo der Buffer liegt kann der Compiler das so schräg 
optimieren das es dann läuft...
Fügt man irgendwas hinzu und verschiebt Speicher läuft der quark 
schlagartig nicht mehr.

Das ist echt gruselig ...

Liegt meiner Meinung aber am RAM und dem buffering/cache

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

mr. mo schrieb:
> Jedoch möchte ich nicht ständig das empfangene Byte auslesen, wenn ich
> es nicht benötige.

Das halte ich aber für die einzig richtige Lösung. Das empfangene Byte 
auszulesen kostet auch so gut wie nichts. Die Schnittstelle wird davon 
überhaupt nicht mehr beansprucht, denn das Byte wurde ja längst über die 
Schnittstelle gelesen.

Das Herumstochern in irgendwelchen Statusregistern ist auf jeden Fall 
aufwändiger als das Auslesen des Bytes in einen Dummy.

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.