ARM GCC Assemblerprogrammierung

OP #5868922
Lesenswert?

1
static inline int32_t asmDelay(int32_t val)  { 
2
  asm volatile("loop%=:  subs  %0, %0, #1     \n\t"
3
               "         nop                  \n\t"
4
               "         bne   loop%=         \n\t"
5
               :  : "r" (val) ) ;
6
  }

Ich habe gerade meine erste Routine in Assembler auf nem ARM 
geschrieben. Sie scheint zu funktionieren (liefert den gewünschten 
delay), aber ich bin mir nicht sicher, ob die Prorammierung wirklich in 
Ordnung ist.

Kann bitte einer der Experten mal seinen Kommentar loslassen?
Und um einer Frage gleich vorzubeugen: Ich programmiere in Assembler 
weil mir das Spass macht (ich bin Masochist).
Persönliche Seite #5868989
Lesenswert?

Dr. Sommer schrieb:
> "r" ist falsch, weil der Wert ja geändert wird. Ich meine es müsste "+r"
> sein, und bei den Output Operands gelistet werden.

Jau.  Ansonsten konnte man bei

asmDelay(10);
asmDelay(10);

falschen Code bekommen.

> Wozu das %= ?

%= liefert eine für die Übersetzungseinheit eindeutige Zahl pro Inline 
Asm, so dass Code wie

asmDelay(10);
asmDelay(10);

nicht dazu führt, dass ein Label wie loop 2x auftaucht, was zu einen 
Assembler-Fehler führen würde.

Alternativ gehen lokale Labels:
1
static inline void asmDelay (int32_t val)
2
{ 
3
  asm volatile ("1:  subs  %0, %0, #1"   "\n\t"
4
                "    nop"                "\n\t"
5
                "    bne   1b"
6
                : "+r" (val));
7
}

Seltsam ist außerden, dass val signed ist.  Oder ist SUBS eine 
saturierte Subtraktion, so dass -1 -1 = 0 wird? (Ich kenn kein ARM-Asm).

Und asmDelay will einen Rückgabewert; alo entweder was zurückgeben oder 
void asmDelay (..).

> Das delay ist natürlich ungenau, hängt von Flash Latenz und so ab.
> Besser macht man das mit Timern.

Für längere, exakte Zeiten besser mit Timer.  Wenn es nur sehr kurze 
Zeiten sind, z.B. ne handvoll Ticks zum für eine langsame I/O Line bis 
sich Pegel stabilisiert haben, dann ist ein Delay einfacher und durchaus 
sinnvoll, da es nur um Mindestzeiten geht und Cache-, Pipeline- und 
Interrupt-Effekte nicht stören.  Außerdem verschendet man nicht einen 
ganzen Timer um 2 Ticks zu schnarchen.
Gast #5869017
Lesenswert?

Johann L. schrieb:
> Oder ist SUBS eine saturierte Subtraktion, so dass -1 -1 = 0 wird?

Nein, aber solange man nur positive Zahlen übergibt ist es egal. Bei 0 
bekommt man aber so oder so ein Problem, weil man da als Erstes einen 
Underflow bekommt und 2^32 Takte zählt. Daher am Besten als erstes auf 0 
prüfen.

Johann L. schrieb:
> Alternativ gehen lokale Labels:

Ja, so hätte ich das jetzt gemacht...
Gast #5869895
Lesenswert?

Ich würde auch "__asm__" statt "asm" schreiben, denn dann funktioniert 
es auch mit "-std=c99" o.ä. Statt %0 bevorzuge ich es auch, den 
Registern Namen zu geben. Zusammengefasst also z.B. so:
1
static inline void asmDelay (uint32_t val) {
2
  if (val == 0) return;
3
  __asm__ volatile (
4
    "1:  subs  %[val], %[val], #1\n"
5
    "nop\n"
6
    "bne 1b\n"
7
    : [val] "+r" (val) : : "cc");
8
}

Als nicht-geinlinete normale Funktion kommt dann heraus:
1
00000000 <asmDelay>:
2
   0:  b110        cbz  r0, 8 <asmDelay+0x8>
3
   2:  3801        subs  r0, #1
4
   4:  bf00        nop
5
   6:  d1fc        bne.n  2 <asmDelay+0x2>
6
   8:  4770        bx  lr
7
   a:  bf00        nop
Gast #5870220
Lesenswert?

Bist Du sicher, dass das NOP wirklich ausgeführt wird?

In der Doku der ganzen Cortex-M steht, dass der Core das NOP aus der 
Pipeline entfernen kann, bevor es die Execution Stage erreicht.

z.B. CM3 
https://developer.arm.com/docs/dui0552/latest/the-cortex-m3-instruction-set/miscellaneous-instructions/nop

=> die Verzögerung kann funktionieren, und wird es wahrscheinlich auch 
(fast) immer, eine Garantie dafür gibt es aber beim Cortex-M nicht.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren