In vielen Anwendungen kann eine CPU, deren Befehlsausführung eine bekannte zeitliche Beziehung zu erwarteten Eingabestimuli aufweist, Aufgaben ausführen, die eine viel schnellere CPU erfordern würden, wenn die Beziehung unbekannt wäre. In einem Projekt, in dem ich ein PSOC zum Generieren von Videos verwendet habe, habe ich beispielsweise alle 16 CPU-Takte ein Byte Videodaten mit Code ausgegeben. Da das Testen, ob das SPI-Gerät bereit ist, und das Verzweigen, wenn nicht, 13 Takte dauern würde und das Laden und Speichern zum Ausgeben von Daten 11 Takte dauern würde, gab es keine Möglichkeit, das Gerät auf Bereitschaft zwischen Bytes zu testen. Stattdessen habe ich einfach dafür gesorgt, dass der Prozessor für jedes Byte nach dem ersten genau den Code von 16 Zyklen ausführt (ich glaube, ich habe eine echte indizierte Last, eine indizierte Dummy-Last und einen Speicher verwendet). Der erste SPI-Schreibvorgang für jede Zeile erfolgte vor dem Start des Videos. und für jedes nachfolgende Schreiben gab es ein 16-Zyklus-Fenster, in dem das Schreiben ohne Pufferüberlauf oder -unterlauf stattfinden konnte. Die Verzweigungsschleife erzeugte ein Unsicherheitsfenster mit 13 Zyklen, aber die vorhersagbare Ausführung mit 16 Zyklen bedeutete, dass die Unsicherheit für alle nachfolgenden Bytes in dasselbe Fenster mit 13 Zyklen passte (was wiederum in das 16-Zyklus-Fenster passte, in dem das Schreiben akzeptabel sein konnte auftreten).
Für ältere CPUs waren die Befehlszeitinformationen klar, verfügbar und eindeutig. Für neuere ARMs scheinen die Timing-Informationen viel vager zu sein. Ich verstehe, dass bei der Ausführung von Code aus dem Flash das Caching-Verhalten die Vorhersage erheblich erschweren kann. Daher würde ich davon ausgehen, dass Code mit Zykluszählung aus dem RAM ausgeführt werden sollte. Selbst wenn Code aus dem RAM ausgeführt wird, wirken die Spezifikationen etwas vage. Ist die Verwendung von zyklisch gezähltem Code immer noch eine gute Idee? Wenn ja, was sind die besten Techniken, damit es zuverlässig funktioniert? Inwieweit kann man mit Sicherheit davon ausgehen, dass ein Chiphersteller nicht stillschweigend in einen "neuen, verbesserten" Chip eintaucht, der in bestimmten Fällen die Ausführung bestimmter Anweisungen zyklisch verzögert?
Angenommen, die folgende Schleife beginnt an einer Wortgrenze, wie würde man anhand von Spezifikationen genau bestimmen, wie lange es dauern würde (angenommen, Cortex-M3 mit Null-Wartezustandsspeicher; für dieses Beispiel sollte nichts anderes über das System von Bedeutung sein).
myloop: mov r0, r0; Kurze einfache Anweisungen, um das Vorabrufen weiterer Anweisungen zu ermöglichen mov r0, r0; Kurze einfache Anweisungen, um das Vorabrufen weiterer Anweisungen zu ermöglichen mov r0, r0; Kurze einfache Anweisungen, um das Vorabrufen weiterer Anweisungen zu ermöglichen mov r0, r0; Kurze einfache Anweisungen, um das Vorabrufen weiterer Anweisungen zu ermöglichen mov r0, r0; Kurze einfache Anweisungen, um das Vorabrufen weiterer Anweisungen zu ermöglichen mov r0, r0; Kurze einfache Anweisungen, um das Vorabrufen weiterer Anweisungen zu ermöglichen fügt r2, r1, # 0x12000000 hinzu; 2-Wort-Anweisung ; Wiederholen Sie das Folgende, möglicherweise mit verschiedenen Operanden ; Addiert so lange Werte, bis ein Übertrag auftritt itcc Addscc r2, r2, # 0x12000000; 2-Wort-Anweisung plus zusätzliches "Wort" für itcc itcc Addscc r2, r2, # 0x12000000; 2-Wort-Anweisung plus zusätzliches "Wort" für itcc itcc Addscc r2, r2, # 0x12000000; 2-Wort-Anweisung plus zusätzliches "Wort" für itcc itcc Addscc r2, r2, # 0x12000000; 2-Wort-Anweisung plus zusätzliches "Wort" für itcc ; ... etc, mit mehr bedingten Zwei-Wort-Anweisungen Unter R8, R8, # 1 bpl myloop
Während der Ausführung der ersten sechs Befehle hätte der Kern Zeit, sechs Wörter abzurufen, von denen drei ausgeführt würden, so dass bis zu drei vorabgerufen werden könnten. Die nächsten Anweisungen bestehen aus jeweils drei Wörtern, so dass der Kern Anweisungen nicht so schnell abrufen kann, wie sie ausgeführt werden. Ich würde erwarten, dass einige der "it" -Anweisungen einen Zyklus benötigen, aber ich weiß nicht, wie ich vorhersagen soll, welche.
Es wäre schön, wenn ARM bestimmte Bedingungen spezifizieren könnte, unter denen das "it" -Befehls-Timing deterministisch wäre (z. B. wenn es keine Wartezustände oder Code-Bus-Konflikte gibt und die vorhergehenden zwei Befehle 16-Bit-Register-Befehle usw. sind). aber ich habe keine solche Spezifikation gesehen.
Beispielanwendung
Angenommen, man versucht, eine Tochterplatine für einen Atari 2600 zu entwerfen, um eine Komponentenvideoausgabe mit 480P zu generieren. Der 2600 verfügt über einen Pixeltakt von 3,579 MHz und einen CPU-Takt von 1,19 MHz (Punkttakt / 3). Für 480P-Komponentenvideo muss jede Zeile zweimal ausgegeben werden, was eine Punkttaktausgabe von 7,158 MHz impliziert. Da der Atari-Videochip (TIA) eine von 128 Farben mit einem 3-Bit-Lumasignal und einem Phasensignal mit einer Auflösung von ungefähr 18 ns ausgibt, ist es schwierig, die Farbe nur durch Betrachten der Ausgänge genau zu bestimmen. Ein besserer Ansatz wäre, Schreibvorgänge in die Farbregister abzufangen, die geschriebenen Werte zu beobachten und jedes Register in die TIA-Luminanzwerte einzuspeisen, die der Registernummer entsprechen.
All dies könnte mit einem FPGA durchgeführt werden, aber einige recht schnelle ARM-Geräte sind weitaus billiger als ein FPGA mit genügend RAM, um die erforderliche Pufferung zu bewältigen (ja, ich weiß, dass für die Volumes, die so etwas produzieren, die Kosten nicht hoch sind). t ein realer Faktor). Das Erfordernis, dass der ARM das eingehende Taktsignal überwacht, würde jedoch die erforderliche CPU-Geschwindigkeit erheblich erhöhen. Vorhersagbare Zykluszahlen könnten die Dinge sauberer machen.
Ein relativ einfacher Entwurfsansatz besteht darin, dass eine CPLD die CPU und den TIA überwacht und ein 13-Bit-RGB + -Synchronsignal erzeugt und dann ARM-DMA 16-Bit-Werte von einem Port abruft und sie mit dem richtigen Timing in einen anderen schreibt. Es wäre jedoch eine interessante Designherausforderung, zu sehen, ob ein billiger ARM alles kann. DMA könnte ein nützlicher Aspekt eines All-in-One-Ansatzes sein, wenn seine Auswirkungen auf die CPU-Zykluszahlen vorhergesagt werden könnten (insbesondere, wenn die DMA-Zyklen in Zyklen auftreten könnten, in denen der Speicherbus ansonsten inaktiv war), aber zu einem bestimmten Zeitpunkt im Prozess Der ARM müsste seine Funktionen zur Tabellensuche und Busüberwachung ausführen. Beachten Sie, dass der Atari 2600 im Gegensatz zu vielen Videoarchitekturen, bei denen Farbregister während der Austastintervalle geschrieben werden, während des angezeigten Teils eines Frames häufig in Farbregister schreibt.
Vielleicht wäre der beste Ansatz, ein paar diskrete Logikchips zu verwenden, um Farbschreibvorgänge zu identifizieren und die unteren Bits der Farbregister auf die richtigen Werte zu zwingen, und dann zwei DMA-Kanäle zu verwenden, um die eingehenden CPU-Bus- und TIA-Ausgangsdaten abzutasten, und einen dritten DMA-Kanal zum Erzeugen der Ausgangsdaten. Die CPU kann dann alle Daten von beiden Quellen für jede Abtastzeile verarbeiten, die erforderliche Übersetzung durchführen und für die Ausgabe puffern. Der einzige Aspekt der Aufgaben des Adapters, der in "Echtzeit" erfolgen müsste, wäre das Überschreiben von Daten, die in COLUxx geschrieben wurden, und dies könnte unter Verwendung von zwei gemeinsamen Logik-Chips erledigt werden.
quelle
Timing-Informationen sind verfügbar, können jedoch, wie Sie bereits betont haben, gelegentlich vage sein. In Abschnitt 18.2 und Tabelle 18.1 des Technischen Referenzhandbuchs für den Cortex-M3 finden Sie zahlreiche Timing-Informationen ( pdf hier ) und einen Auszug hier:
die eine Liste von Bedingungen für ein maximales Timing geben. Der Zeitpunkt für viele Anweisungen hängt von externen Faktoren ab, von denen einige Unklarheiten hinterlassen. Ich habe jede der Unklarheiten hervorgehoben, die ich im folgenden Auszug aus diesem Abschnitt gefunden habe:
Für alle Anwendungsfälle ist es komplexer als die Zählung "Dieser Befehl ist ein Zyklus, dieser Befehl ist zwei Zyklen, dies ist ein Zyklus ...", die in einfacheren, langsameren, älteren Prozessoren möglich ist. In einigen Anwendungsfällen treten keine Mehrdeutigkeiten auf. Wenn Sie auf Unklarheiten stoßen, schlage ich vor:
Diese Anforderungen geben wahrscheinlich die Antwort auf Ihre Frage "Nein, es ist keine gute Idee, es sei denn, die aufgetretenen Schwierigkeiten sind die Kosten wert" - aber das wussten Sie bereits.
quelle
Eine Möglichkeit, dieses Problem zu umgehen, besteht darin, Geräte mit deterministischen oder vorhersagbaren Timings zu verwenden, z. B. die Parallax Propeller- und XMOS-Chips:
http://www.parallaxsemiconductor.com/multicoreconcept
http://www.xmos.com/
Das Zählen von Zyklen funktioniert sehr gut mit dem Propeller (Assembler-Sprache muss verwendet werden), während die XMOS-Geräte über ein sehr leistungsfähiges Software-Dienstprogramm verfügen, den XMOS Timing Analyzer, der mit Anwendungen arbeitet, die in der Programmiersprache XC geschrieben sind:
https://www.xmos.com/download/public/XMOS-Timing-Analyzer-Whitepaper%281%29.pdf
quelle
Das Zählen von Zyklen wird problematischer, wenn Sie sich von Mikrocontrollern auf niedriger Ebene entfernen und auf Prozessoren für allgemeine Zwecke umsteigen. Die ersten haben in der Regel ein genau festgelegtes Anweisungs-Timing. Das liegt auch daran, dass ihre Architektur ziemlich einfach ist, sodass die Unterrichtszeiten fest und bekannt sind.
Ein gutes Beispiel dafür sind die meisten Microchip-PICs. Die Serien 10, 12, 16 und 18 haben eine sehr gut dokumentierte und vorhersehbare Befehlszeit. Dies kann ein nützliches Merkmal bei kleinen Steuerungsanwendungen sein, für die diese Chips vorgesehen sind.
Wenn Sie sich von extrem niedrigen Kosten verabschieden und der Designer daher mehr Chipfläche aufwenden kann, um eine höhere Geschwindigkeit durch eine exotischere Architektur zu erzielen, verlieren Sie auch die Vorhersehbarkeit. Schauen Sie sich moderne x86-Varianten als extreme Beispiele an. Es gibt mehrere Ebenen von Cache-Speichern, Speicher-Virtualisierung, Lookahead-Abruf, Pipelining und mehr, die das Zählen von Befehlszyklen nahezu unmöglich machen. Bei dieser Anwendung spielt es jedoch keine Rolle, da der Kunde an einer hohen Geschwindigkeit interessiert ist und nicht an einer Vorhersagbarkeit des Befehlszeitpunkts.
Sie können diesen Effekt sogar bei höheren Microchip-Modellen beobachten. Der 24-Bit-Kern (24-, 30- und 33-Bit-Serie) hat ein weitgehend vorhersagbares Befehls-Timing, mit Ausnahme einiger Ausnahmen, wenn Register-Bus-Konflikte vorliegen. In einigen Fällen fügt die Maschine beispielsweise einen Stillstand ein, wenn der nächste Befehl ein Register mit einigen indirekten Adressierungsmodi verwendet, deren Wert im vorherigen Befehl geändert wurde. Diese Art von Stall ist auf einem dsPIC ungewöhnlich, und die meiste Zeit kann man sie ignorieren, aber es zeigt, wie sich diese Dinge einschleichen, weil die Designer versuchen, Ihnen einen schnelleren und leistungsfähigeren Prozessor zu bieten.
Die grundlegende Antwort lautet also, dass dies Teil des Kompromisses ist, wenn Sie sich für einen Prozessor entscheiden. Für kleine Steuerungsanwendungen können Sie etwas Kleines, Billiges, Niedriges mit vorhersagbarem Befehlszeitpunkt auswählen. Wenn Sie mehr Rechenleistung benötigen, ändert sich die Architektur, sodass Sie auf ein vorhersehbares Anweisungs-Timing verzichten müssen. Glücklicherweise ist dies weniger ein Problem, wenn Sie zu rechenintensiveren und universell einsetzbaren Anwendungen gelangen, sodass die Kompromisse meiner Meinung nach einigermaßen gut funktionieren.
quelle
Ja, Sie können es immer noch tun, auch auf einem ARM. Das größte Problem bei einem ARM ist, dass ARM Kerne verkauft, keine Chips, und das Kern-Timing ist bekannt, aber was der Chip-Anbieter umgibt, variiert von Anbieter zu Anbieter und manchmal von Chip-Familie zu einer anderen innerhalb des Anbieters. Ein bestimmter Chip eines bestimmten Anbieters kann also durchaus deterministisch sein (wenn Sie beispielsweise keine Caches verwenden), ist jedoch schwerer zu portieren. Wenn Sie mit 5 und dort mit 11 Uhren arbeiten, ist die Verwendung von Timern problematisch, da die Anzahl der Anweisungen zum Abtasten des Timers und zum Ermitteln, ob Ihr Timeout abgelaufen ist. Nach den Klängen Ihrer bisherigen Programmiererfahrung bin ich bereit zu wetten, dass Sie wahrscheinlich mit einem Oszilloskop debuggen, so wie ich es tue. Sie können also eine enge Schleife auf dem Chip mit der Taktrate versuchen, den SPI oder den I2C betrachten oder eine beliebige Wellenform hinzufügen oder nops entfernen, Ändern Sie die Anzahl der Male durch die Schleife und stimmen Sie im Grunde. Wie bei jeder Plattform trägt die Nichtverwendung von Interrupts erheblich zur deterministischen Ausführung von Befehlen bei.
Nein, es ist nicht so einfach wie ein PIC, aber dennoch durchaus machbar, insbesondere wenn die Verzögerung / Taktung der Taktrate des Prozessors nahekommt. Bei einigen ARM - basierten Anbietern können Sie die Taktrate multiplizieren und etwa 60 MHz aus einer 8 - MHz - Referenz herausholen. Wenn Sie also eine 2 - MHz - Schnittstelle benötigen, anstatt alle 4 Anweisungen etwas zu tun, können Sie die Taktrate erhöhen (wenn Sie die haben) Energiehaushalt) und verwenden Sie dann einen Timer und geben Sie sich viele Uhren, um auch andere Dinge zu tun.
quelle