Forum: Compiler & IDEs Optimierung mit/ohne volatile


von Klaus F. (kfalser)


Lesenswert?

Hallo Leute,
bis jetzt glaubte ich mit "volatile" mehr oder weniger sattelfest zu 
sein, aber das folgende hat mich doch verblüfft.

Ich habe ein Spartan-6 Design mit Microblaze und selbsgeschriebenem IP 
core, der einen 64 KB Speicherbereich andeckt.
In diesem Speicherbereich liegen 2 Tabellen zu 16 Worten (32 Bit), an 
den Offsets 0x100 und 0x140.
1
static const uint32_t MNTable_AB_offset  = 0x00000100;
2
static const uint32_t MNTable_CD_offset  = 0x00000140;
3
4
#define MNTable_AB            (uint32_t *) (XPAR_PRINTCORE_IF_BASEADDR + MNTable_AB_offset)
5
#define MNTable_CD            (uint32_t *) (XPAR_PRINTCORE_IF_BASEADDR + MNTable_CD_offset)
Eine Funktion
1
int scanWaveFormForMNs(uint16_t *waveform, int waveformlength, volatile uint32_t *mdtable, unsigned int mdtable_size, uint16_t offset)
2
{
3
    int i, count = 0;
4
    uint32_t mn;
5
    uint8_t mn_old = 0;
6
7
    for (i = 0; i < waveformlength; i++) {
8
        if ((*waveform++ & 0xF000) == 0xF000) {
9
            mn = (*waveform++ & 0xF000) >> 8;
10
            mn |= (*waveform++ & 0xF000) >> 12;
11
            if (mn != mn_old) {
12
              unsigned ii = i;
13
              mn_old = mn;
14
              mn <<= 10;
15
              ii += i != 0 ? offset : 0;
16
              mn |= ii & 0x3FF;
17
              mn |= 0xF0000000;
18
                mdtable[count] = mn;
19
                xil_printf("md[%d] = %x, rb = %x", count, mn, mdtable[count]);
20
                count++;
21
            }
22
            i += 2;
23
        }
24
    }
25
    return count;
26
}
füllt diese Tabellen.
Man muss jetzt nicht verstehen, was die Funktion berechnet, aber an der 
Stelle
mdtable[count] = mn;
sollte die Tabelle, und somit auch das IP beschrieben werden.

Der Aufruf schaut so aus:
1
scanWaveFormForMNs((uint16_t *) Kom_Buffer, WaveformLength, MNTable_AB, MNTableSize, 8-3);

Lässt man das volatile in
1
int scanWaveFormForMNs(uint16_t *waveform, int waveformlength, volatile uint32_t *mdtable, unsigned int mdtable_size, uint16_t offset)
weg, funktioniert das Ganze nicht, der Zugriff auf die Tabelle im IP 
wird nicht ausgeführt.
Man merkt dies auch daran, weil die höchsten 8 Bits in Hardware nicht 
existieren, und beim Zurücklesen mit mdtable[count] mit Nullen gefüllt 
werden.
Ohne volatile wird z.B. 0xF0000400 zurückgelesen, mit volatile 
korrekterweise 0x400.

Wieso können beim Optimieren einer Funktion Schreibzugriffe auf eine zu 
füllende Tabelle wegoptimiert werden?
Die Optimierung der Funktion kann ja nicht wissen, was im Rest des 
Programms mit diesen Daten passiert?

Optimierungsstufe ist O3.

: Bearbeitet durch User
von Frank (Gast)


Lesenswert?

Bei zugriffen auf Hardware Register ist volatile Pflicht.

Ansonsten geht so etwas auch in die Hose
1
PORTA |= (1<<0);
2
PORTA |= (1<<1);
wenn der Compiler anfängt zu optimieren macht er nur ein Read Modify 
Write, anstatt 2.

von Peter II (Gast)


Lesenswert?

Klaus F. schrieb:
> Man merkt dies auch daran, weil die höchsten 8 Bits in Hardware nicht
> existieren, und beim Zurücklesen mit mdtable[count] mit Nullen gefüllt
> werden.

warum sollte er auch ohne volatile die Daten aus mdtable[count] lesen?
1
mdtable[count] = mn;
2
xil_printf("md[%d] = %x, rb = %x", count, mn, mdtable[count]);

er macht einfach
1
mdtable[count] = mn;
2
xil_printf("md[%d] = %x, rb = %x", count, mn, mn);

daraus. Was für den Compiler das gleiche ergeben muss.

von Klaus F. (kfalser)


Lesenswert?

Ok, ich habe es wahrscheinlich nicht gut erklärt.
Das printf ist erst nachträglich dazugekommen, weil der IP Core nicht 
korrekt funktioniert hat.
Dass im printf der falsche, optimierte Wert ausgegeben ist erklärbar, 
das stimmt.
Warum die Register des IP Core aber nur mit "volatile uint32_t *mdtable" 
korrekt gefüllt werden, ist für mich nicht erklärbar.
Für die Funktion ist eine Tabelle die gefüllt werden muss.
Ob der Speicher, auf den der Zeiger zeigt im RAM steht oder im IP Core 
kann die Funktion nicht wissen.

von (prx) A. K. (prx)


Lesenswert?

Klaus F. schrieb:
> Ob der Speicher, auf den der Zeiger zeigt im RAM steht oder im IP Core
> kann die Funktion nicht wissen.

Möglicherweise schon. Beispielsweise wenn inlined.

Dazu kommen vielleicht Probleme durch Aliasing. Pointer zu casten ist 
zwar sehr beliebt. Aber nicht immer frei von Risiko.

von C-Programmierer (Gast)


Lesenswert?

Klaus F. schrieb:
> kann die Funktion nicht wissen.

Das ist der Fehlschluss hier.
Der Compiler kennt deine Hardwareregister nicht und darf 
selbstverständlich auch über Funktionsgrenzen hinweg optimieren.

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.