Hallo,
ich versuche die Frage mal so abstrakt wie möglich zu stellen, stelle
aber auch das konkrete Problem vor, weil eh danach gefragt würde.
Abstrakt:
2 Prozessoren bekommen das gleiche Signal zur gleichen Zeit und müssen
es verwerten. Beide führen exakt das gleiche Programm aus, welches
unveränderbar ist.
Prozessor 1 läuft gepipelined, kann das Signal also nur verzögert
auswerten. Die Verzögerung beträgt dabei null oder eine Instruktion.
Prozessor 2 läuft immer ohne Pipeline, kann also prinzipiell jederzeit
auswerten und hat damit erstmal immer 0 Instruktionen Verzögerung.
Beide können nicht kommunizieren, müssen aber zur exakt gleichen
Instruktion das Signal auswerten.
Beide Bausteine sind aber insoweit veränderbar, das man z.b. beliebige
Delays auf das Signal geben kann.
Konkret:
Es geht um einen Prozessor und seinen Emulator. Um einen Emulator mit
exaktem Verhalten zu bekommen nehme ich den Zustand des
Systems(Register, Timer, etc) nach jeder Instruktion auf und tue das
gleiche vom Prozessor. Über ein Diff sehe ich ab wann es Unterschiede
gibt.
Das läuft soweit sehr gut, macht aber immer wieder Probleme bei
Interrupts z.b. wenn ein Timer abgelaufen ist. Im Emulator liegt die
Information sofort vor, im echten Prozessor hängt es davon ab wie lange
der Befehl davor dauert.
Dementsprechend laufen die Zustände auseinander, wenn der Interrupt
nicht bei der gleichen Instruktion auftrat. Manchmal ist nach dem
Interrupt Handler wieder alles gleich, aber nicht immer.
Ich möchte jetzt gerne sicherstellen das immer beide zur gleichen Zeit
in den Interrupt gehen. Im Idealfall trickse ich dabei nur im Emulator,
es stört aber auch nicht wenn der Prozessor geändert werden muss(FPGA
Softcore).
Weil die absolute Zeit für den Interrupt nicht so entscheidend ist nutze
ich derzeit die Lösung bei "instructionCount % 100 == 0" tatsächlich in
den Interrupt zu gehen. Das löst das Problem in 99% der Fälle, aber der
Jitter ist schon sehr groß.
Deswegen frage ich mal in die Runde ob jemand eine bessere Lösung hat
das Ganze synchron zu bekommen.
Nur eins vorneweg: das Pipelineverhalten nachbauen um es immer
garantiert genauso zu haben möchte ich ausdrücklich nicht.
Vielleicht hat ja noch jemand eine zündende Idee.
Danke und viele Grüße
FPGAzumSpass schrieb:> Nur eins vorneweg: das Pipelineverhalten nachbauen um es immer> garantiert genauso zu haben möchte ich ausdrücklich nicht.
Wenn das Ziel ist, es garantiert genauso zu haben, kannst du nicht die
aus naheliegenden logischen Gründen einzige Möglichkeit ausschliessen,
dies zu erreichen...
Formales logisches Denken ist nicht deine Stärke, oder?
Nein, das ist nicht das Ziel, habe ich vielleicht schlecht geschrieben.
Eigentlich bin ich auf der Suche nach Fehlern, z.b. Instruktionen die
sich anders Verhalten. Weil ich das aber auf diese sehr komfortable
Weise nur herausfinden kann, wenn beide sich zeitweise(!) nahezu gleich
Verhalten brauche ich diese Krücke.
Hinterher, wenn alles läuft, fliegt das wieder raus.
Eben deshalb ist es den Aufwand erst recht nicht wert beides exakt
gleich zu machen.
Das erkannt man z.b. auch dran, das es mir egal ist ob der Interrupt
etwas später oder früher kommt. Wenn der Nachbau exakt gleich sein
müsste wäre das entscheidend, ist es aber (zum Glück) nicht.
A. S. schrieb:> Wenn Du das Signal verzögern kannst, dann verzögern es doch um 0 oder 1> Takt.
Falls du es noch nicht bemerkt hast: genau das Nichtwissen, ob es um 0
oder 1 Takt zu verzögern ist, ist sein Problem. Ausserdem will er auch
nicht die einzige Lösung, die das sicherstellen könnte.
Kurzfassung: wohl ein Troll.
Ich wüsste ja schon gerne, woher die Vorstellung kommt, dass Pipelining
eine Reaktion um exakt einen Takt verzögern würde. Grundprinzip von
Rechnerarchitekturen aus dem Lehrbuch?
Die Realität sieht anders aus. Weshalb sich die Anforderung nicht von
der konkreten Implementierung des Prozessors trennen lässt.
A. K. schrieb:> Ich wüsste ja schon gerne, woher die Vorstellung kommt, dass Pipelining> eine Reaktion um exakt einen Takt verzögern würde.
Ich habe den Softcore halt genau so implementiert:
der Prozessor rechnet an Instruction A und die dauert x Takte.
Diese x Takte werden auf den Timer addiert wenn die Instruction fertig
ist. Dem Timer fehlen noch y Takte bis zum Interrupt.
Wenn x >= y, dann gibt es einen Interrupt request.
Dieser wirkt in der Pipeline im Execution Teil, davor gibt es noch
Instruction Fetch und Decode.
Wenn nun Fetch und Decode schneller sind als der Timer das x aus der
letzten Instruktion auf seinen Zählerstand addiert hat und den IRQ
request erzeugt hat, dann gibt es eine Instruktion Verzögerung, weil der
execution Teil schon weiterrechnen kann. Dies betrifft vor allem den
Fall, das die Pipeline schon gefüllt ist.
Dauern Fetch und Decode länger (z.b. Fetch von langsamen externem Ram),
dann gibt es keinen Takt Verzögerung, weil Execution noch nicht rechnen
kann und demzufolge den IRQ request bekommt, bevor die nächste
Instruktion ansteht.
Spätestens hier sollte klar sein, warum ich das nicht emulieren will: es
müsste alles beachten, z.b. auch Ram-Refresh, Cache Hit/Miss usw.
Weil das exakte Timing aber in der Anwendung unwichtig ist wäre der
Aufwand am Ende nutzlos.
Bis jetzt habe ich eben nur 2 Lösungen gefunden:
- einen Instruction Counter mitlaufen lassen und einen IRQ request nur
z.b. jeden n-ten Takt durchzulassen. Das klappt in den allermeisten
Fällen, sorgt aber, je besser es funktionieren soll, für mehr
"IRQ-Jitter"
- den Prozessor prinzipiell nach jeder Instruktion solange mit der
Execution warten lassen, bis der Interrupt auf jeden Fall da wäre. Das
macht ihn aber viel langsamer.
Ich dachte vielleicht gibt es jemanden der noch mehr Ideen dazu hat.
c-hater schrieb:> Ausserdem will er auch> nicht die einzige Lösung, die das sicherstellen könnte
Wo habe ich das geschrieben? Ich habe nirgends eine 100%ige Lösung
erwartet, sondern lediglich gefragt ob jemandem etwas einfällt das etwas
besser ist.
Das ich schon 2 Lösungen (genau genommen 3 mit der exakten) selbst
vorgelegt habe zeigt wohl deutlich das es mehrere Varianten.
Warum soll es also nicht noch andere geben?
Weil sie mir nicht einfallen?
Weil sie dir nicht einfallen?
Also so von mir selbst überzeugt bin ich nun nicht, das ich jeden Fall
den es geben könnte wissen kann.
Genau deshalb schreibe ich hier.
Wenn du das ins Lächerliche ziehen willst, weil du Dinge behauptest die
so nicht sind, ist das dein Ding. Wenn dir nicht Besseres einfällt, dann
musst du ja nicht schreiben.
Könnte man vielleicht das Pipelineverhalten ändern? Die Sparc(?) CPUs
haben glaub ich alle Register doppelt. Einmal für die normale Ausführung
und einmal für Interrupts. Dadurch kann man da sehr schnell in
Interrupts wechseln. Kein "Push & Pop" nötig...
Vielleicht könnte man so ähnlich auch zwei Befehlspipelines haben und
mitten in einem Befehl in den Interrupt wechseln und danach mit dem
Befehl weiter machen als ob nichts gewesen wäre?
Klingt schon fasst so als ob man dann einen zweiten (Co-)Prozessor für
Interrupts hätte...
Der Aufwand das ans Laufen zu bekommen ist wahrscheinlich so groß das es
sich nicht lohnt zu überlegen ob das funktionieren könnte ;-)
Das wäre so ähnlich tatsächlich denkbar: ich könnte alle Register und
Zustände vom Ende der vorigen Instruktion speichern und diese wieder
herstellen wenn ein Interrupt verspätet reinkommt.
Der Aufwand ist halt sehr groß dafür, sowohl Arbeitstechnisch, als auch
an Registern/Logik im Prozessor. Gerade eine solch große Änderung im
Prozessor selbst (der wichtigere Teil) wollte ich möglichst vermeiden.
Trotzdem vielen Dank fürs Überlegen. Genau solche Denkanstöße suche ich
noch.