Simulation von LDREX / STREX (Laden / Speichern exklusiv) in Cortex-M0

8

In dem Cortex-M3-Befehlssatz gibt es eine Familie von LDREX / STREX-Befehlen, so dass, wenn ein Ort mit einem LDREX-Befehl gelesen wird, ein folgender STREX-Befehl nur dann an diese Adresse schreiben kann, wenn die Adresse bekannt ist unberührt. In der Regel ist der Effekt, dass der STREX erfolgreich sein wird, wenn keine Interrupts ("Ausnahmen" im ARM-Sprachgebrauch) seit dem LDREX aufgetreten sind, aber andernfalls fehlschlagen.

Was ist der praktischste Weg, um ein solches Verhalten im Cortex M0 zu simulieren? Ich würde gerne C-Code für den M3 schreiben und ihn auf den M0 portieren lassen. Auf der M3 kann man etwa sagen:

%Vor%

um ein atomares Inkrement durchzuführen. Die einzigen Möglichkeiten, um ähnliche Funktionalität auf dem Cortex-M0 zu erreichen, wären entweder:

  1. Lassen Sie "ldrex" Ausnahmen deaktivieren und "strex" und "clrex" wieder aktivieren, mit der Anforderung, dass jedes "ldrex" bald danach entweder von "strex" oder "clrex" gefolgt werden muss.
  2. Haben "ldrex", "strex" und "clrex" eine sehr kleine Routine im RAM, wobei eine Anweisung von "ldrex" entweder auf "str r1, [r2]" oder "mov r0, # 1" gepatcht wird ". Lassen Sie die "ldrex" -Routine eine "str" ​​-Anweisung in die "strex" -Routine einstecken, und lassen Sie die "clrex" -Routine "mov r0, # 1" dort stecken. Habe alle Ausnahmen, die einen "ldrex" Sequenzaufruf "clrex" ungültig machen könnten.

Abhängig davon, wie die ldrex / strex-Funktionen verwendet werden, funktioniert das Deaktivieren von Interrupts möglicherweise vernünftig, aber es scheint eklig zu sein, die Semantik von "load-exclusive" zu ändern, um schlechte Nebenwirkungen zu verursachen. Die Code-Patching-Idee scheint, als würde sie die gewünschte Semantik erreichen, aber es scheint klobig.

(BTW, Nebenfrage: Ich frage mich, warum STREX auf dem M3 die Erfolgs- / Fehleranzeige in einem Register speichert und nicht einfach ein Flag setzt? Seine tatsächliche Operation erfordert vier zusätzliche Bits im Opcode, erfordert, dass ein Register verfügbar ist Halten Sie die Erfolg / Fehler-Anzeige und erfordert, dass ein "cmp r0, # 0" verwendet werden, um festzustellen, ob es erfolgreich war.Wäre erwartet, dass Compiler STREX intrinsisch vernünftig nicht vernünftig behandeln könnten, wenn sie das Ergebnis nicht erhalten in einem Register? Carry in ein Register zu bekommen dauert zwei kurze Anweisungen.)

    
supercat 21.04.2011, 14:59
quelle

3 Antworten

2

Nun ... Sie haben noch SWP übrig, aber es ist eine weniger mächtige atomare Anweisung.

Die Deaktivierung von Interrupts funktioniert jedoch sicher. : -)

Bearbeiten:

Kein SWP auf -m0, sorry supercat.

OK, es sieht so aus, als ob Sie nur noch Interrupts deaktivieren. Sie können gcc-compilable inline asm als Anleitung zum Deaktivieren und Wiederherstellen verwenden: Ссылка

    
domen 25.04.2011 10:22
quelle
1

Der Cortex-M3 wurde für ein Multitasking mit niedriger Latenz und niedrigem Jitter entwickelt, d. h. sein Interrupt-Controller arbeitet mit dem Kern zusammen, um die Anzahl der Zyklen seit der Interrupt-Triggerung zu garantieren, um die Handhabung zu unterbrechen. Das ldrex / strex wurde als eine Möglichkeit implementiert, mit all dem zusammenzuarbeiten (mit all dem, was ich unter Interrupt-Maskierung und anderen Details wie der atomaren Biteinstellung über BIT-Band-Aliase verstehe), als ein Einzelkern-, Nicht-MMU-, Nicht-Cache-μC habe wenig Gebrauch davon. Wenn es nicht implementiert würde, müsste eine Aufgabe mit niedriger Priorität eine Sperre halten und könnte kleine Inversionen erzeugen, die Latenz und Jitter erzeugen, was ein hartes Echtzeitsystem ist (es ist dafür ausgelegt, obwohl das Konzept viel zu weit gefasst ist). kann nicht bewältigen, zumindest nicht in der Größenordnung von der "Retry" -Semantik, dass ein fehlgeschlagener ldrex / strex hat.

Nebenbei bemerkt, spricht das Cortex-M0 streng nach Timings und Jitter und hat ein traditionelleres Interrupt-Timing-Profil (dh es bricht keine Anweisungen auf dem Kern ab, wenn ein Interrupt eintrifft) Jitter und Latenz. In dieser Hinsicht (wiederum streng nach Timing) ist es eher mit älteren Modellen (d. H. Dem arm7tdmi) vergleichbar, denen auch atomare Last / Modifizieren / Speichern sowie atomare Inkremente & amp; Dekrementierungen und andere kooperative Befehle mit niedriger Latenz, die eine häufigere Deaktivierung / Aktivierung von Interrupts erfordern.

Ich benutze so etwas in Cortex-M3:

%Vor%

Mit anderen Worten, es nimmt ldrex / strex in das bekannte Linked-Load und Store Conditional und implementiert damit auch die Compare-and-Swap-Semantik.

Wenn Ihr Code nur mit compare-and-swap funktioniert, können Sie ihn wie folgt für cortex-m0 implementieren:

%Vor%

Das ist das am häufigsten verwendete Muster, weil einige Architekturen es nur hatten (x86 kommt mir in den Sinn). Die Implementierung einer Emulation von LL / SC-Mustern durch CAS scheint hässlich zu sein, wo ich stehe. Insbesondere, wenn der SC mehr als ein paar Anweisungen außer LL ist, aber obwohl sehr üblich, empfiehlt ARM es nicht speziell im Cortex-M3-Fall, da beliebige Interrupts strex zum Scheitern bringen, wenn Sie fang an, zwischen ldrex / strex zu lang zu nehmen, wird dein Code viel Zeit in einer Schleife verbringen, die strex erneut versucht. Das missbraucht das Muster und benutzt es nicht.

Was Ihre Nebenfrage betrifft, so kehren im cortex-m3-Fall die strex in einem Register zurück, weil die Semantik bereits von übergeordneten Architekturen definiert wurde (strex / ldrex existiert in mehrkernigen Armen, die vor armv7-m implementiert waren) definiert, und danach, wo die Cache-Controller tatsächlich nach ldrex / strex-Adressen suchen, dh strex schlägt nur fehl, wenn der Cache die Datenlinie nicht nachweisen kann, wurden die Adresspunkte nicht modifiziert.

Wenn ich spekulieren würde, würde ich sagen, es war, weil diese Art Atomik in frühen Tagen in Bibliotheken gedacht wurde: Sie würden Erfolg / Misserfolg in Funktionen, die in Assembler implementiert sind, zurückbringen und dies müsste die ABI und Die meisten von ihnen (alles, was ich weiß) verwendet ein Register oder einen Stapel und nicht die Flags, um Werte zurückzugeben. Es könnte auch sein, weil Compiler besser Registerfärbung verwenden, als die Flags zu überkreuzen, falls eine andere Anweisung sie verwendet, dh eine komplexe Operation in Betracht zieht, die Flags erzeugt und in der Mitte eine ldrex / strex Sequenz und die Operation hat das kommt danach benötigt die Flags: der Compiler müsste die Flags sowieso in ein Register verschieben.

    
Alexandre Pereira Nunes 05.02.2015 22:53
quelle
0

STREX / LDREX sind für Multicore-Prozessoren, die auf gemeinsam genutzte Elemente im Arbeitsspeicher zugreifen, die über die Kerne gemeinsam genutzt werden. ARM hat einen ungewöhnlich schlechten Job bei der Dokumentierung, dass Sie zwischen den Zeilen in den Amba / Axi und Arm und Trm Dokumenten lesen müssen, um das herauszufinden.

Wie es funktioniert, wenn Sie einen Kern haben, der STREX / LDREX unterstützt und wenn Sie einen Speicher-Controller haben, der exklusiven Zugriff unterstützt, dann wenn der Speicher-Controller das Paar exklusiver Operationen sieht, ohne dass ein anderer Kern auf diesen Speicher dazwischen zugreift gib EX_OKAY anstatt OKAY zurück. Die Arm Docs sagen den Chip-Designern, wenn es ein Uniprozessor ist (nicht die Multicore-Funktion zu implementieren), dann müssen Sie nicht unterstützen Exokay nur Rückkehr OK, die aus einer Software Perspektive bricht das LDREX / STREX-Paar für Zugriffe, die diese Logik (die Software Spins in einer Endlosschleife, da es niemals erfolgreich zurückkehrt), der L1-Cache unterstützt es jedoch, also fühlt es sich so an, als ob es funktioniert.

Für Einzelprozessor und für Fälle, in denen Sie nicht auf Speicher zugreifen, der über die Kerne gemeinsam genutzt wird, verwenden Sie SWP.

Das -m0 unterstützt weder ldrex / strex noch swp, aber was bringt euch das im Grunde? Sie erhalten nur einen Zugriff, der nicht von einem Zugriff betroffen ist. Um zu verhindern, dass Sie auf sich selbst stampfen, deaktivieren Sie Interrupts nur für die Dauer, wie wir atomare Zugriffe seit dem dunklen Zeitalter gemacht haben. wenn du Schutz vor dir und einem Peripheriegerät willst, wenn du ein Peripheriegerät hast, das stören kann, dann gibt es keine Möglichkeit, das zu umgehen, und selbst ein Tausch hat vielleicht nicht geholfen.

Deaktivieren Sie also nur Interrupts im kritischen Bereich.

    
old_timer 14.03.2014 17:12
quelle

Tags und Links