Forum: Compiler & IDEs ARM GCC Assemblerprogrammierung


von Martin O. (ossi-2)


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).

von Verzögert (Gast)


Lesenswert?

Gerade bei einer Delay Funktion ist es extrem wichtig diese in Assembler 
zu kodieren.

von Dr. Sommer (Gast)


Lesenswert?

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

Wozu das %= ?

Kennst du:
http://www.ethernut.de/en/documents/arm-inline-asm.html
https://hardwarebug.org/2010/07/06/arm-inline-asm-secrets/

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

von Johann L. (gjlayde) Benutzerseite


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.

: Bearbeitet durch User
von Martin O. (ossi-2)


Lesenswert?

Besten Dank für die hilfreichen Kommentare und Hinweise.
Wenn ich neue Probleme habe melde ich mich wieder.

von Dr. Sommer (Gast)


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...

von Markus F. (mfro)


Lesenswert?

auch eine Assemblerroutine sollte irgendeinen Wert zurückliefern, wenn 
sie int32_t deklariert ist

auch wenn's hier (wahrscheinlich) keine Rolle spielt "cc" sollte in die 
Clobber list (schließlich werden die Flags verändert)

von (prx) A. K. (prx)


Lesenswert?

Johann L. schrieb:
> 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).

SUBS = SUB mit Status. Unsigned val wär zutreffender.

: Bearbeitet durch User
von W.S. (Gast)


Lesenswert?

Martin O. schrieb:
> static inline int32_t asmDelay(int32_t val)  {
>   asm volatile("loop%=:......

Solange sowas dasteht, programmierst du in C und nicht in Assembler.

W.S.

von Dr. Sommer (Gast)


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

von Michael F. (Gast)


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.

von Peter D. (peda)


Lesenswert?

Ich würde besser einen freilaufenden Timer dafür verwenden, dann hat man 
wenigstens einen ungefähren Anhaltspunkt, wie groß das Delay wirklich 
ist.
Und die ARMs haben ja reichlich Timer bzw. der Timer kann ja noch andere 
Sachen machen.

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.