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_tW5500::readWriteByte(uint8_tbyte)
2
{
3
volatileuint8_tval;
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_ttmp=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
returnval;
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!
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
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.
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?
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.
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?
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.
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.
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?
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.
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.
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... ;)
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.
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!
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
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.