Forum: Mikrocontroller und Digitale Elektronik Problem mit Inline Assembler Routine (es wird zu viel optimiert)


von Der T. (Gast)


Lesenswert?

Hallo,

IDE: AtmelStudio 7.0.1931 (Optimierung steht auf -Os)
MCU: ATXMega128A1U mit ext. SDRAM

Ich habe mir ein paar Routinen (Write/Read Byte/Word/DWord/Block, Copy 
Block, ...) in Inline Assembler geschrieben, um auf den externen 
Speicher zugreifen zu können. Eine dieser Funktionen macht allerdings 
Probleme.

Funktion:
1
static inline bool himem_fill_block(uint8_t seg, uint16_t off, uint8_t val, uint16_t len)
2
{
3
  __asm__ __volatile__
4
  (
5
    "in __tmp_reg__,%4"  "\n\t"
6
    "out %4,%1"          "\n\t"
7
    "cp  %A3,r1"         "\n\t"
8
    "cpc %B3,r1"         "\n\t"
9
    "breq END%="         "\n\t"
10
    "LOOP%=:"            "\n\t"
11
    "st %a2+,%0"         "\n\t"
12
    "subi %A3,0x01"      "\n\t"
13
    "sbci %B3,0x00"      "\n\t"
14
    "brne LOOP%="        "\n\t"
15
    "END%=:"             "\n\t"
16
    "out %4,__tmp_reg__" "\n\t"
17
    :
18
    :  "r"  (val),
19
      "r"  (seg),
20
      "z"  (off),
21
      "d"  (len),
22
      "I"  (_SFR_IO_ADDR(RAMPZ))
23
  );
24
  return TRUE;
25
}

Mit dieser Funktion kann ich einen Speicherbereich initialisieren. An 
sich läuft die Funktion. Wenn allerdings der Offset zufällig die gleiche 
Größe wie die Länge hat, optimiert der Compiler etwas zu viel - trotz 
dem volatile.


Beispiel-Listing OK:  (seg=0x80, off=0x0010, val=0x11, len=0x000f)
1
LDI R18,0x0F    Load immediate
2
LDI R19,0x00    Load immediate
3
LDI R30,0x10    Load immediate
4
LDI R31,0x00    Load immediate
5
LDI R25,0x80    Load immediate
6
LDI R24,0x11    Load immediate
7
IN R0,0x3B    In from I/O location
8
OUT 0x3B,R25    Out to I/O location
9
CP R18,R1    Compare
10
CPC R19,R1    Compare with carry
11
BREQ PC+0x05    Branch if equal
12
ST Z+,R24    Store indirect and postincrement
13
SUBI R18,0x01    Subtract immediate
14
SBCI R19,0x00    Subtract immediate with carry
15
BRNE PC-0x03    Branch if not equal
16
OUT 0x3B,R0    Out to I/O location


Beispiel-Listing FAIL:  (seg=0x80, off=0x0010, val=0x11, len=0x0010)
1
LDI R30,0x10    Load immediate
2
LDI R31,0x00    Load immediate
3
LDI R25,0x80    Load immediate
4
LDI R24,0x11    Load immediate
5
IN R0,0x3B    In from I/O location
6
OUT 0x3B,R25    Out to I/O location
7
CP R30,R1    Compare
8
CPC R31,R1    Compare with carry
9
BREQ PC+0x05    Branch if equal
10
ST Z+,R24    Store indirect and postincrement
11
SUBI R30,0x01    Subtract immediate
12
SBCI R31,0x00    Subtract immediate with carry
13
BRNE PC-0x03    Branch if not equal
14
OUT 0x3B,R0    Out to I/O location


Wie kann ich den Compiler zwingen hier gefälligst nicht zu optimieren? 
;-)

Danke!

von Kaj (Gast)


Lesenswert?

Der T. schrieb:
> Wie kann ich den Compiler zwingen hier gefälligst nicht zu optimieren?
1
#pragma GCC push_options
2
#pragma GCC optimize ("O0")
3
4
your code
5
6
#pragma GCC pop_options

oder
1
void __attribute__((optimize("O0"))) foo(unsigned char data) {
2
    // unmodifiable compiler code
3
}

von Stefan F. (Gast)


Lesenswert?

Ich hätte nicht gedacht, dass der Compiler den Assembler Code verändert.

von Falk B. (falk)


Lesenswert?

Komplette Funktionen schreibt man besser in echtem Assembler. Das ist 
DEUTLICH einfacher, angenehmer und der kein C-Compiler der Welt pfuscht 
rein. Es gibt da eine sehr einfache, über sichtloiche App-Note zum 
Thema.

doc42055

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Der T. schrieb:

> IDE: AtmelStudio 7.0.1931 (Optimierung steht auf -Os)

Interessiert nicht. :)  Wenn schon, würde die Compilerversion 
interessieren.

> Speicher zugreifen zu können. Eine dieser Funktionen macht allerdings
> Probleme.

Mit anderen Worten: dein Inline-Assembler-Code ist nicht korrekt 
constrained.

> Wie kann ich den Compiler zwingen hier gefälligst nicht zu optimieren?

Es ist die Aufgabe des Compilers, Code zu optimieren. Deinen 
Inline-Assembler-Code selbst kann er natürlich dabei nicht anfassen, nur 
die Vor- und Nachbereitung. Die constraints dienen dazu, dass du ihm 
mitteilst, an welchen Stellen er was wie hinterlegen muss.

Soviel zur Vorrede. :-)  Jetzt muss ich aber auch erstmal gucken, was du 
da genau vergessen haben könntest …

Probier doch mal, die Länge als "+d" zu constrainen.

Korrekterweise müsstest du wohl auch den Offset als "+z" deklarieren, 
denn durch das Z+ änderst du ja den Z-Pointer innerhalb des Codes.

Ich bin übrigens nicht wirklich davon überzeugt, dass man das nicht auch 
gleich in C schreiben könnte.

von Der T. (Gast)


Lesenswert?

Danke Kaj! :-)

So läuft es:
1
static inline bool __attribute__((optimize("O0"))) himem_fill_block(uint8_t seg, uint16_t off, uint8_t val, uint16_t len)
2
{
3
....
4
}

von Der T. (Gast)


Lesenswert?

Jörg W. schrieb:

> Probier doch mal, die Länge als "+d" zu constrainen.
>
> Korrekterweise müsstest du wohl auch den Offset als "+z" deklarieren,
> denn durch das Z+ änderst du ja den Z-Pointer innerhalb des Codes.

Der Compiler mag das "+" nicht..?
1
    :  "r"  (val),
2
      "r"  (seg),
3
      "z"  (off),
4
      "+d"  (len),
5
      "I"  (_SFR_IO_ADDR(RAMPZ))

Dieser meckert dann mit dem Fehler: "input operand constraint contains 
'+'"

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Dann musst du ihn wohl bei den output operands angeben (also nach dem 
ersten Doppelpunkt).

Allerdings ändert sich dann leider die Nummerierung. Nummerierte 
Operanden sind aber eh bäh, besser sind benannte Operanden.  Also ein 
[foo] for das Constraint, und du kannst statt %0 %[foo] schreiben.

von Der T. (Gast)


Lesenswert?

Jörg W. schrieb:
> Dann musst du ihn wohl bei den output operands angeben (also nach dem
> ersten Doppelpunkt).
>
> Allerdings ändert sich dann leider die Nummerierung. Nummerierte
> Operanden sind aber eh bäh, besser sind benannte Operanden.  Also ein
> [foo] for das Constraint, und du kannst statt %0 %[foo] schreiben.


Das habe ich auch gerade ausprobiert - Pustekuchen!
Der Compiler optimiert hier ebenfalls.. :-(

Das Listing sieht fast genauso wie der FAIL oben aus.

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Ist mir jetzt gerade zu spät. Schau ich mir morgen nochmal an.

Beitrag #5791155 wurde von einem Moderator gelöscht.
von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Der T. schrieb:

> Wie kann ich den Compiler zwingen hier gefälligst nicht zu optimieren?

Das ist nicht der richtige Ansatz.  Der richtige Ansatz ist, korrekten 
Code zu schreiben, z.B. so:
1
static inline bool himem_fill_block (uint8_t seg, uint16_t off, uint8_t val, uint16_t len)
2
{
3
    if (len)
4
    {
5
      __asm__ __volatile__
6
      (
7
        "out __RAMPZ__, %[seg]"       "\n"
8
        "LOOP%=:"                     "\n\t"
9
        "st %a[off]+, %[val]"         "\n\t"
10
        "sbiw %[len], 1"              "\n\t"
11
        "brne LOOP%="                 "\n\t"
12
        "out __RAMPZ__, __zero_reg__"
13
        : [off] "+z" (off),
14
          [len] "+w" (len)
15
        : [val] "r" (val),
16
          [seg] "r" (seg)
17
        : "memory");
18
    }
19
20
  return true;
21
}

off und len werden verändert -> Müssen also zu den Outputs wie Jörg 
bereits schrieb.

Speicher wird verändert -> "memory" muss zu den Clobbers.

avr-gcc geht davon aus, dass in RAMP* immer 0 steht -> Der Inhalt von 
RAMPZ braucht nicht gemerkt zu werden, und am Ende kann RAMPZ auf 0 
gesetzt werden -> Kürzerer Code.

Am Anfang des s-Files wird __RAMPZ__ zur I/O-Adresse von RAMPZ definiert 
-> Weniger Operanden.

Die 16-Bit Operationen können in einem 16-Bit Befehl abgehandelt werden 
-> Kürzerer Code.

Der zu wartende asm-Code kann weiter verkürzt werden, indem der 
Vergleich von len aus dem asm herausgezogen wird.  Falls len zur 
Compilezeit bekannt ist, entfällt der Vergleich komplett.

stdbool.h (C99) definiert true und false.

von Der T. (Gast)


Lesenswert?

Danke Johann für deinen Betrag.

Assembler nutze ich zu wenig, so dass ich zu wenig in der Materie drin 
stecke. Ich programmiere fast ausschließlich in Hochsprache.

Ich werde mir dies näher ansehen und auch meine älteren Routinen ggf. 
Überarbeiten. Vielen Dank für deine Unterstützung. :-)

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.