Hallo zusammen,
ich lese im Moment ein paar Sachen über Compiler Optimierungen nach und
werde langsam paranoid. In meinem konkreten Fall geht es um eine
blockierende Warteschleife während der Initialisierung eines MEMS
Sensors, der seine beiden Spannungen VDD und VDDIO in bestimmter
Reihenfolge eingeschaltet bekommen möchte. Welcher Sensor genau kann ich
leider nicht sagen.
Der Busyloop sieht in etwa so aus und verwendet den CycleCounter vom
Cortex M3:
timer.c
RCC_GetClocksFreq(&_clocks);// read out current clock speeds to pre-calculate ticks to us conversion
18
cyclesPerUs=_clocks.SYSCLK_Frequency/1E6;
19
20
_isInitialized=true;
21
}
22
}
23
24
...
25
26
// todo: Interrupts sperren?
27
// todo: Opt full/none diskutieren
28
// todo: add correction for call offset
29
void__attribute__((optnone))// oder optfull? vermutlich eher, um overhead zu verringern.
30
TIMER_BusyWait(uint32_twaitTimeMs)
31
{
32
TIMER_BusyWaitUs(waitTimeMs*1000);
33
}
34
35
void__attribute__((optnone))
36
TIMER_BusyWaitUs(uint32_twaitTimeUs)
37
{
38
if(!_isInitialized)
39
{
40
while(1)// nur exemplarisch. Fehlerbehandlung halt
41
;
42
}
43
volatileuint32_tcyclesToWait=cyclesPerUs*waitTimeUs;// volatile to suppress optimization
44
volatileuint32_tstartCycles=DWT->CYCCNT;
45
while((DWT->CYCCNT-startCycles)<cyclesToWait)
46
{
47
__ASM("nop");
48
}
49
}
50
51
...
In sensor.c wird der Delay verwendet, um die Sensor Versorungsspannung
zu schalten. Nehmt bitte an, dass der "Timer" initialisiert wurde und
dass die Logik invertiert ist!
// Pin 11 and Pin 12 are connected to a PMOS Mosfet -> inverted logic
15
GPIO_SetBits(GPIOA,GPIO_Pin_11|GPIO_Pin_12);// *disable* sensor 3v3. Ensure state before pin is set to output PP. Default is analog in. Pullup ensures 3v3 level -> vdd & vddio is off
Jan K. schrieb:> Jetzt ist es aber ja so, dass der Compiler optimieren darf. Darunter> fällt auch ein reordering der Instruktionen sowie Speicherzugriffen.
Macht er in der Praxis nicht wenn was "volatile" gekennzeichnet ist.
Das ist in Deinem Falle der DWT->CYCCNT Registerzugriff, schau Dir mal
die Deklaration an.
Die ganzen "volatile" Zugriffe müssen alle so stattfinden wie sie im
Code stehen. D.h. sie dürfen weder umsortiert noch weggelassen werden -
selbst wenn dabei Zwischenergebnisse verworfen werden.
IIRC ist das nicht explizit so spezifiziert im C-Standard, aber ein
gäniges Verhalten in C Compilern.
was genau funktioniert denn nicht?
Jan K. schrieb:> - Sollte der busy wait loop optimiert werden oder nicht? Der eigentliche> Zeitgeber ist ja der Cyccnt und der ist volatile. Muss daher z.B.> cyclesToWait gar nicht volatile sein?
Optimierung oder nicht darf keinen Unterschied machen. Zugriffe auf
volatile-deklarierte Typen darf der Compiler auch nicht reordern.
Die cyclesToWait und startCycles müssen nicht volatile sein und DMB
sollte auch nicht notwendig sein.
Verdächtig ist das __ASM, wie wird das expandiert, bzw. wie sieht asm
von TIMER_BusyWaitUs aus?
rµ schrieb:> Verdächtig ist das __ASM, wie wird das expandiert, bzw. wie sieht asm> von TIMER_BusyWaitUs aus?
Das kann man - denke ich - bedenkenlos weglassen. Oder wozu soll das NOP
gut sein?
Im Übrigen kann's sein (zumindest beim M4, soweit ich weiß), daß der
Prozessor höchstselbst es einfach weglässt bzw. rausschmeißt:
NOP does nothing. NOP is not necessarily a time-consuming NOP. The
processor might remove it from the pipeline before it reaches the
execution stage.
Markus F. schrieb:> Verdächtig ist das __ASM, wie wird das expandiert,
Kennt Deine IDE keine Suchfunktion analog zu "grep"?
Das Macro steht in core_cm4.h (oder core_cm3.h für Cortex-M3).
Markus F. schrieb:> Das kann man - denke ich - bedenkenlos weglassen. Oder wozu soll das NOP> gut sein?
Stimmt, das kann man komplett weglassen.
Wär trotzdem interessant, was der Compiler daraus macht.
Jan K. schrieb:> Insbesondere in dem CMSIS_5 Link ist ein ähnlicher Fall, da wird die> Funktion zum Deaktivieren bestimmter Interrupts diskutiert und gesagt,> dass im Prinzip nichts den Compiler an einem reordering hindert.
Ich denke im Link meint man den Prozessor, nichts hindert einen
entsprechend ausgestatteten Prozessor daran, den devx-Zugriff erst nach
dem enableIRQ auszuführen.
Dem Compiler wird eine Memory Barrier herzlich wurscht sein.
Jim M. schrieb:> Markus F. schrieb:>> Verdächtig ist das __ASM, wie wird das expandiert,>> Kennt Deine IDE keine Suchfunktion analog zu "grep"?
Alarm! Zitatfälscher!
Jim M. schrieb:> Markus F. schrieb:>> Verdächtig ist das __ASM, wie wird das expandiert,
Das hab ich geschrieben, nicht Markus F.
> Kennt Deine IDE keine Suchfunktion analog zu "grep"?>> Das Macro steht in core_cm4.h (oder core_cm3.h für Cortex-M3).
Ja schon, aber erstens hab ich das Projekt nicht da, der Compiler ist
unbekannt, und und und.
Da das eigentliche Problem eh nicht bekannt ist wirds müßig...
rµ schrieb:> Jan K. schrieb:>> Insbesondere in dem CMSIS_5 Link ist ein ähnlicher Fall, da wird die>> Funktion zum Deaktivieren bestimmter Interrupts diskutiert und gesagt,>> dass im Prinzip nichts den Compiler an einem reordering hindert.>> Ich denke im Link meint man den Prozessor, nichts hindert einen> entsprechend ausgestatteten Prozessor daran, den devx-Zugriff erst nach> dem enableIRQ auszuführen.>> Dem Compiler wird eine Memory Barrier herzlich wurscht sein.
nah, es ist komplizierter, und in dem fix wird auch keine memory barrier
eingefügt sondern eine compiler barrier.
Jan K. schrieb:> ich lese im Moment ein paar Sachen über Compiler Optimierungen nach und> werde langsam paranoid. In meinem konkreten Fall geht es um eine> blockierende Warteschleife während der Initialisierung eines MEMS> Sensors, der seine beiden Spannungen VDD und VDDIO in bestimmter> Reihenfolge eingeschaltet bekommen möchte.
Mache es einfacher. Das ganze Zeugs gilt ja wohl nur für die
Einschalt-Phase des ganzen Konstrukts, also ganz am Anfang und es
scheinen auch bloß ein paar Millisekunden zu sein. Sowas macht man ganz
simpel mit einer popligen Trampelschleife - ohne ein RTOS zu bemühen und
ohne einen Timer dafür zu benützen. Schließlich passiert das Ganze ja
nur ein einziges Mal beim Einschalten.
Was die Compiler-Optimierungen betrifft, so habe ich hier schon
Schauergeschichten über den GCC gelesen. Danach benimmt der sich
gelegentlich glatt daneben und "optimiert" angeblich ganze Programmteile
hinweg. Kann ich nicht nachvollziehen, da ich den nicht nehme. Der Keil
hingegen erlaubt sich nach meiner Erfahrung keine derartigen
Extravaganzen.
Die Optimierungen beim Keil sehen häufig so aus, daß Zugriffe auf lokale
Variablen innerhalb von Funktionen durchaus auch mal umgestellt werden.
Aber die Zugriffe auf globale Variablen oder gar auf HW-Register finden
grundsätzlich GENAU SO statt, wie geschrieben. Allerdings macht der Keil
gelegentlich etwas dahingehend, daß er sich einen Zeiger kreiert, mit
dem die HW-Register oder globalen Variablen mit platzsparenderen
Maschinenbefehlen erreichbar sind, als wenn deren konkrete Adresse
jedesmal separat gebildet werden müßte. Das ändert jedoch am
tatsächlichen Zugriff nichts.
Nochwas:
Falls du in deiner Firmware selbst am blutigen Anfang keine
blockierenden Trampelschleifen haben willst, dann arbeite mit Events.
Sowas hab ich hier schon x-mal gepostet.
Also:
1. Den SysTick in Gang setzen und die Systemuhr damit einrichten
2. Event-System initialisieren
3. je nach Gusto dann eben einen odeer mehrere delayed Events starten.
4. Die Event-Queue in der Grundschleife in main pollen. Wenn dann nach
der Delay-Zeit der Event in de Queue erscheint, ihn eben abarbeiten.
W.S.
W.S. schrieb:> Was die Compiler-Optimierungen betrifft, so habe ich hier schon> Schauergeschichten über den GCC gelesen.
Ja, da werden gerne Schauermärchen erzählt, am Ende stellt sich jedesmal
raus daß derjenige den Code so geschrieben hat daß er überflüssige
Anweisungen ohne Wirkung enthielt oder beabsichtigte Wirkungen von
ansonsten offensichtlich wirkungslosem Code dem Compiler nicht bekannt
gemacht wurden, solche Teile dürfen natürlich selbstverständlich
komplett eliminiert werden (sollen sogar, warum sonst sollte man die
Optimierung einschalten wenn man das nicht wollte?).
> Danach benimmt der sich> gelegentlich glatt daneben und "optimiert" angeblich ganze Programmteile> hinweg.
Gehört bis zum Beweis des Gegenteils ins Reich der Märchen.