2014-12-16 4 views
5

Ich möchte einen ARM Cortex M0 + für n Zyklen verzögern, ohne einen Timer zu verwenden, mit der kleinstmöglichen Code-Größe. (Ich denke, dies erfordert die Verwendung der Baugruppe.)Wie verzögert man einen ARM Cortex M0 + für n Zyklen, ohne einen Timer?

Eine Verzögerung von 0 Zyklen ist einfach kein Code. Eine Verzögerung von 1 Zyklus ist ein einzelner NOP. Eine Verzögerung von 2 Zyklen ist zwei NOPs.

An welchem ​​Punkt ist es (Code-Größe) effizient, Schleife zu starten?

Wie viele Zyklen braucht die engste Schleife? Was ist die Setup-Zeit?


Beitrag Antwort Anmerkungen:

Der folgende C-Code:

register unsigned char counter = 100; 
while (counter-- > 0) { 
    asm(""); 
} 

, wenn sie mit gcc kompiliert und O3 gibt:

mov r3, #100 
.L5: 
    sub r3, r3, #1 
    uxtb r3, r3 
    cmp r3, #0 
    bne .L5 

Dieses entweder zeigt, dass es immer noch Zweck in Hand-Codierung ARM-Assembly, oder (viel wahrscheinlicher), dass der C-Code oben ist nicht der beste Weg der Con Vey an den Compiler, was ich machen möchte.

Kommentare?

+3

Mit 'char' wird die Schleife verlangsamt. Wenn Sie stattdessen "int" verwenden, wird die Anweisung "uxtb" nicht mehr angezeigt. – TonyK

Antwort

8

Der Code auf genau abhängen wird, was n ist, und ob es muss dynamisch variabel sein, aber the M0+ core's instruction timings gegeben, ist ziemlich einfach Grenzen für eine bestimmte Routine zu etablieren.

Für die kleinstmögliche (6-Byte) komplette Schleife mit einem festen 8-Bit-Direkt Zählern:

movs r0, #NUM ;1 cycle 
1: subs r0, r0, #1 ;1 cycle 
    bne 1b   ;2 if taken, 1 otherwise 

mit NUM=1 bekommen wir mindestens 3 Zyklen, plus 3 Zyklen für jede zusätzliche Schleife bis zu NUM=255 bei 765 Zyklen (natürlich könnten Sie 2^32 Iterationen von NUM=0 haben, aber das scheint ein bisschen albern). Dies legt die untere Grenze für eine Schleife fest, die bei ungefähr 6 Zyklen praktisch ist. Mit einer festen Schleife ist es einfach, NOPs (oder sogar Nested Loops) innerhalb dieser zu packen, um jede Iteration zu verlängern, und davor/danach, um sie auf ein Nicht-Vielfaches der Schleifenlänge auszurichten. Wenn Sie eine Reihe von Iterationen in einem Register bereithalten können, bevor Sie warten müssen, können Sie die ursprüngliche mov verlieren und so ziemlich jedes Vielfache von 3 oder mehr Zyklen minus eins haben. Wenn Sie für eine variable Verzögerung eine Single-Cycle-Auflösung benötigen, werden die anfänglichen Setup-Kosten etwas höher sein, um den Rest zu korrigieren (ein berechneter Zweig in einen NOP-Schlitten würde ich dafür verwenden). Ich nehme an, dass, wenn Sie an dem Punkt des zykluskritischen Timings sind, Sie bereits Interrupts ausgeschaltet haben (andernfalls einen anderen Zyklus irgendwo für CPSID werfen), und dass Sie keine Buswartezustände haben, die zusätzliche Zyklen zu Instruktionsabrufen hinzufügen .

Zum Versuch, es in C zu tun: die Tatsache, dass Sie in eine leere asm hacken müssen, um die "nutzlose" Schleife davor zu bewahren, weg optimiert zu werden, ist ein Tipp. Die abstrakte C-Maschine hat keine Vorstellung von "Anweisungen" oder "Zyklen", daher gibt es einfach keine Möglichkeit, dies zuverlässig in der Sprache auszudrücken. Sich auf bestimmte C-Konstrukte zu verlassen, um zu geeigneten Instruktionen zu kompilieren, ist äußerst fragil - ändern Sie ein Compiler-Flag; den Compiler aktualisieren; Ändern eines entfernten Codes, der die Registerzuweisung beeinflusst, was die Befehlsauswahl beeinflusst; usw. - so ziemlich alles könnte den generierten Code unerwartet ändern, also würde ich sagen, handcodierte Baugruppe ist der sinnvolle Ansatz für zyklusgenauen Code.

+0

Danke das ist wirklich nützlich. Ich bin (Laufzeit) erzeuge Code (enthält nur GPIO Änderungen, zyklusgenaue Verzögerungen und eine Rückkehr) in RAM und aufrufend. – fadedbee

+0

Alle Kommentare zu den Post-Antwort-Notizen (in der Hauptfrage, oben)? – fadedbee

+0

Dynamisch eine feste Schleife wie diese zu generieren sollte ziemlich einfach sein - nehmen Sie eine Vorlage basierend auf 'movs rN # 0 ...', logisch - oder die Schleife zählen in die LSB der ersten Anweisung und fügen Sie 0-2 NOPs nach für Padding . Der Code _generation_ sollte auf jeden Fall in C aus Gründen der Vernunft passieren, aber ich denke, dass die Vorlage selbst Hand-Codierung gemäß meiner Bearbeitung benötigt. – Notlikethat

3

Die kürzeste ARM-Schleife, die ich denken kann, geht so:

mov r0, #COUNT 
L: 
subs r0, r0, #1 
bnz L 

Da ich das Gerät in Frage nicht habe, keine Ahnung von Timing. Diese sind vom Kern abhängig.