Autor Thema: Logisim CPU  (Gelesen 159921 mal)

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« am: 31. October 2012, 09:46 »
Huhu, hab mir eine CPU in logisim gebaut und möchte jetzt für die CPU ein OS programmieren. Nun hab mir schon einige Gedanken für das dateisystem gemacht:

die ganzen dateien werden im Ram gespeichert.

es gibt 2 große listen, die Dateiliste und die Verzeichniss liste.In denen werden die ganzen ID's gespeichert

/a/programm/Hirn.OS
 0        1      1024,1

nun jedes verzeichniss hat einen bestimmten pfad ID.Wird nach ner datei gesucht wird zunächst die Pfad's in ihre ID'n übersetzt und dann die ID'S zusammen addiert.

0+1 = 1 PfadID

dannach wird die datei Übersetzt:

DateiID = 1024
DateiPfadID = 1

Nun jetzt wird überprüft ob die PfadID mit der DateiPfadID übereinstimmt wenn das nicht der fall ist wird ne fehlermeldung angezeigt.Die DateiID für zur adresse im ram.

So, gibt ne Effektivere Möglichkeit für das Dateiensystem? Auserdem will ich für das projekt einen Compiler programmieren, hat wer ne Idee wie man das am besten macht ? :D

MfG Tufelix

Jidder

  • Administrator
  • Beiträge: 1 625
    • Profil anzeigen
Gespeichert
« Antwort #1 am: 31. October 2012, 12:29 »
Hi und Willkommen an Board ;)

Was mit bei deiner Idee nicht ganz klar ist, was das mit der Addition werden soll. Soll die Summe die Datei identifizieren? Wenn ja, könntest du Probleme bekommen, weil die Summen nicht eindeutig sind. Zum Beispiel 2+3=5 und 1+4=5. Meine Idee ist vielleicht nicht unbedingt effektiver, aber ich glaube die funktioniert wenigstens ;)

Ein Dateisystem ist häufig als Baumstruktur aufgebaut. Ein Verzeichnis ist ein Knoten in diesem Baum (= Liste der Dateien und Unterverzeichnisse), und eine Datei ist ein Blatt in diesem Baum. Ein Verzeichnis könnte als Liste von Dateieinträgen aufgebaut sein. Ein Dateieintrag könnte auch ein Verzeichniseintrag sein. Der Dateieintrag enthält Sachen wie Dateiname, Größe, Offset im RAM und Typ (Datei oder Verzeichnis). Wenn du zum Beispiel die Datei /users/steve/minecraft.exe suchst, dann suchst du im Wurzelverzeichnis nach dem Eintrag für "users". Dann prüfst du, ob es ein Verzeichnis ist, und wenn ja, suchst du bei dem im Eintrag angegebenen Offset nach dem Eintrag für steve. (Zum Beispiel rekursiv.) Im Verzeichnis "steve" suchst du dann nach dem Eintrag für minecraft.exe und kriegst so das Offset der Datei raus.

Dieses Prinzip ist zwar im Vergleich zu "echten" Dateisystemen relativ einfach, allerdings auch ineffizient. Zum Beispiel bekommst du ein Problem, wenn du eine Datei vergrößern willst, aber direkt hinter dieser Datei eine andere Datei liegt. Dann musst du die Datei fragmentieren oder verschieben. Genau dasselbe Problem bekommst du mit Verzeichnissen, wenn du eine feste Anzahl an möglicher Einträge reservierst. Das Problem ist dann, was wenn du mehr Dateien als vorgesehen reinschreiben willst. Du könntest das Konzept um Sektoren oder Cluster erweitern, und die Dateien nicht einfach als durchgehendes Array speichern, sondern als eine Art verkettete Liste, wobei die Sektor-/Clusternummern praktisch die next-Zeiger sind.

Zitat
Auserdem will ich für das projekt einen Compiler programmieren, hat wer ne Idee wie man das am besten macht ?
Hast du denn schon einen Assembler dafür geschrieben?
« Letzte Änderung: 31. October 2012, 12:31 von Jidder »
Dieser Text wird unter jedem Beitrag angezeigt.

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #2 am: 31. October 2012, 13:13 »
Danke für die coole Idee  :-) .

joar hab ich mal, ist aber eher ein übersetzer der ne Sprache einließt und dann die einzelne Befehle in der CodeDatei nacheinnander in den Opcode übersetzt.
« Letzte Änderung: 31. October 2012, 13:19 von Tufelix »

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #3 am: 31. October 2012, 16:13 »
du kannst auch alles in ein physikalisches verzeichnis werfen und den pfad sowie dateinamen als einheit betrachten ... aber, dann musst du dir überlegen, wie lang du die namen erlaubst

hätte den vorteil, dass nur ein verzeichnis durchsucht werden müsste, man bräuchte aber einen guten sortieralgo um einen verzeichnisbaum zu erstellen ...

OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #4 am: 31. October 2012, 17:32 »
mmh ich glaub so mache ich's :-D

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #5 am: 27. November 2012, 19:28 »
Nun, mein jetziger CPU hat kein richtige Jump-funktion. Man kann kein sprungziel angeben, es wird nur entweder eine 1 oder ne 0 im flagregister geschrieben...Daher hab ich mir überlegt einen einen Cpu zubauen, damit er wieder verwendbar wird soll der I/O ports bekommen, hab mir schon im mathe-unterricht was ausgetüftelt  :-D :


1 ich geh mal davon aus das wir 16 register haben werden, also ein instuktion-set mit 8 argument-bits, und 2x 4 bit registerbits

00000000|0000|0000|0000000000000000
Argument   RX   RX             NUM

2 Nun jetzt zum I/O port:

2.1 Es gibt 4 I/O gruppen die jeweils 8 I/O ports zusammenfassen.

Ein I/O port kann endweder ein ausgang oder ein eingang sein aber niemals beides.

Man kann die Ports über einen Opcode zu einem aus- oder einem Eingang machen, dazu später mehr :D.

2.2 Nun mal zu den befehlen:

Man steuert nicht jeden I/O port an sondern seine Gruppe.

Jeder Befehl für die I/O ports sind in etwa gleich auf gebaut:

00000000|0000|0000|00000000|00000000|
Argument RX/Gruppe   Num         Die Ports die aktiviert werden
              /\
              |
   Die Gruppenbits und die RX bits, wechseln je nach Befehl.

2.3 Nun jetzt mal ein Beispiel:

2.3.1 Eingang-Ports
   
Wir haben in der portgruppe "A"  die ersten 7 bits als eingang gesetzt(für ne tastatur) und der letzte als Ausgang(löschbit für die Tastatur).

Nun wenn wir jetzt Alle 7 bits einlesen möchten wählen wir zunächst die Gruppe aus (also 0001 ) , dann die bits die wir einlesen möchten (11111110) zudem braucht man ein register wohin die daten kommen(wir nehmen mal gx).

der dazugehörige Opcode sieht dan so aus:
00000000|0011|0001|00000000|11111110
Argument  RX    Grup.       Num    Aktivierte Bit's

Eventuell kann man das ganze so bauen das man mehrende gruppen auswählen kann, das ganze wird aber dann viel komplizierter.

2.3.2 Ausgänge:

So wir haben jetzt in Gruppe B 8 bits als Ausgänge markiert(7 für ne Console und der letzte ist der löschbit).

Wir wollen Jetzt mal nen Coolen Buchstaben Ausgeben dazu wählen wir mal unsere Gruppe aus(0010) dann setzen wir der Buchstabe im Num-Feld "10110100" und dann noch die bit's die aktiviert werden sollen (11111110)

Im Opcode sieht das ganze jetzt so aus:

00000000|0010|0000|10110100|11111110
Argument  Grup.  RX      Num        Ausgewählte Bit's

Wir können aber auch ein register Wählen aus dem dann die bits kommen.
Auch ist es möglich beides zu verwenden, also RX und Num-feld, in diesem Fall haben die Num-Bit's vorrang.
Muss aber noch ne lösung für ein paar probleme finden.

2.4 I/O Ports als Ein- bzw. als ausgänge zu markieren.

Nun wir nehmen wieder das Beispiel mit der Konsole, wenn wir die ersten 7 bits als ausgänge setzen wollen müssen wir zunächnächt die Bits auswählen(11111110) und dann die Gruppe(0010)

00000000|0010|0000|00000000|11111110|

so jeder der genannten Opcodes haben ein eigenes argument, die müssen wir aber noch bestimmen.

Nun was hält ihr von der Idee bzw. gibt es bessere ?

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #6 am: 27. November 2012, 19:46 »
also die idee scheint mir etwas unverständlich, aber ich hab mich auch nicht tiefgründig eingelesen, aber egal ... wie auch immer, wenn du noch keinen sprungbefehl hast, solltest du unbedingt einen einbauen, da sonst deine cpu nicht turing vollständig ist, aber wie auch immer.
Hmm in und out kannst du auch als memory mapped i/o organisieren, so hab ich das bei meinen entwürfen gemacht, das ist wesentlich einfacher ... ich glaube aber bei meinem neusten projekt hab ich auf eine mischung von beidem zurück gegriffen. Der Bildschirm ist aus geschwindigkeitsgründen in den speicher eingeblendet, genauso wie die fontmap. Rest hängt alles an einem seperaten bus, der aber eigentlich genauso aufgebaut ist, wie der RAM/CPU bus mit adress- und datenleitung.
also so hab ich das gelöst ... es gibt bestimmt noch bessere wege, oder zumindest andere wege.
OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #7 am: 27. November 2012, 19:59 »
wie schnell ist dieses memorie-mapping ? :D

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #8 am: 27. November 2012, 20:32 »
naja ... so schnell wie der ram halt ist, es kommt auf die implementierung an .... bei mir ist es z.B. so, dass du den Port nur implizit angeben kannst und auch nur in und von registern auf ports schreiben kannst, das heißt es ist schon insofern langsamer, als das du erst zwei register mit den werten laden musst, bevor du schreiben kannst ... beim ram hingegen ist es so gelöst, als das du sowas machen kannst wie const -> [adresse + offset], das kann durch die verwendung des zero registers in const -> [adresse] umgewandelt werden ... aber jetzt wo ich das hier schreibe, weiß ich garnicht mehr so recht, warum ich das mit mmio gemacht hab ... wenn ich ein paar mehr opcodes für die port zugriffe definieren würde, würde es wesentlich eleganter sein. Ich habs halt so gemacht. ... :D
OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #9 am: 27. November 2012, 23:48 »
Hallo,

Nun, mein jetziger CPU hat kein richtige Jump-funktion.
Die macht vieles einfacher. Stattdessen kannst du aber auch den Program Counter als normales Register definieren oder CALL/RETURN (notwendig für Funktionen) einbauen.

1 ich geh mal davon aus das wir 16 register haben werden, also ein instuktion-set mit 8 argument-bits, und 2x 4 bit registerbits
Die Argument-Bits nennt man klassisch Opcodes oder Instructions.

Ein I/O port kann endweder ein ausgang oder ein eingang sein aber niemals beides. Man kann die Ports über einen Opcode zu einem aus- oder einem Eingang machen, dazu später mehr.
Hmm, das kostet wertvolle Opcodes. Außerdem ist deine Zugriffsweise etwas ... gewöhnungsbedürftig, weil deine Ports nicht unabhängig voneinander sind.

Schaue dir mal Ports bei modernen Microcontrollern an (z.B. Atmel AVR). Da hast du pro Port x drei Register:
- DDRx gibt (pro Bit) an, ob der Port ein Eingang oder ein Ausgang ist.
- PORTx gibt (pro Bit) den Wert der Ausgangsbits an bzw. aktiviert bei Eingängen einen Pullup-Widerstand.
- PINx enthält den aktuellen Zustand der einzelnen Bits.

In einer simulierten CPU reichen eigentlich zwei Register (DIRx für die Richtung, PORTx für den Wert), die so breit sind, wie der Port selbst. Wenn du die I/O-Ports nicht mit Opcodes, sondern mit Registern (Speicheradressen, nicht CPU-Register!) steuerst, kannst du theoretisch beliebig viele davon haben.

Dein Tastaturbeispiel an Port A sähe in C dann ungefähr so aus:
DIRA = 0xFE;           /* Bits 7..1 als Ausgang, Bit 0 als Eingang */
wert = PORTA & 0xFE;   /* Lesen der oberen 7 Bits in eine Variable */
PORTA = PORTA | 0x01;  /* Setzen des Ausgangsbits auf 1 */
PORTA = PORTA & ~0x01; /* Löschen des Ausgangsbits */

Das finde ich persönlich weniger kompliziert und ist ähnlich flexibel. Die "Gruppen" sind hier die Port-Bezeichner.

Nun was hält ihr von der Idee bzw. gibt es bessere ?
Neben dem, was ich oben vorgestellt habe, gibt es noch MMIO. Da tut angeschlossene Hardware so, als wäre sie RAM. Die notwendigen Leitungen (Adressbits, Datenbits, Read-/Write-Bit) werden oft mit dem RAM gemeinsam genutzt; für die CPU besteht somit kein Unterschied zwischen RAM- und Hardwarezugriffen. Damit brauchst du die Registerwerte nicht immer lesen/ändern/schreiben, sondern kannst direkt drauf rumrechnen.

Gruß,
Svenska

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #10 am: 29. November 2012, 16:25 »
Nun, ein Port ist also eine Gruppe von Pins oder ? :-D Ich hab immer angenommen das ein port nur ein Pin am CPU/Mircocontroller ist. 

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #11 am: 29. November 2012, 17:13 »
Genau. Ist auch eher logisch, weil CPUs ungern mit einzelnen Bits arbeiten und Bitgruppen (8, 16, 32 Bit) bevorzugen.

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #12 am: 29. November 2012, 18:13 »
nun dann kann ich wohl mein selbstentworfendes konzept vergessen :D.  Nun Ich hab mal ein Flussdiagramm erstellt wie der CPU vielleicht nacher Ausehen wird, hab jetzt mal bei den I/O-Ports ein hybrid aus mmio und Svenska's Idee gewählt.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #13 am: 30. November 2012, 01:28 »
Hallo,

das sieht mal ... komplett anders aus, als man es normalerweise tut. :roll:

Zwei Dinge habe ich in realen Systemen gesehen, sind aber unpraktisch:
- Liegt der Stack nicht im RAM, hast du nur eine äußerst begrenzte Zahl von nested function calls. Praktisch verhinderst du damit jede Form von Rekursion. Gibt es in der Realität z.B. in Tastaturprozessoren oder Embedded Controllern in Notebooks.
- Ist der Stack nicht im RAM verschiebbar, verhinderst du jede Form von Multitasking. Darum kann es Projekte wie SymbOS nicht auf dem C64 geben, weil der 6502/6510 nur einen fixen Stack hat.

Ein paar weitere Dinge sind einfach nur unpraktisch bis kaputt:
- Der Adressbus fehlt. Adressen kann man zwar auch über den Datenbus laufen lassen, aber das kostet zusätzliche Logik, spart Leitungen und kostet Performance. ((Außerdem sind reale Bussysteme normalerweise deutlich langsamer als CPUs.))
- Dein RAM-Subsystem muss wissen, wie Hardwaregeräte angesprochen werden. Normalerweise reicht es, wenn die Hardware das selbst weiß. ((Außerdem werden optimierte Zugriffsmuster, z.B. Bursts, schwieriger umsetzbar.))
- Der Datenbus wird mit Dingen belastet, die niemanden interessieren sollten, z.B. Registerwerte.
- Du kannst zwar diverse Hardware mit MMIO ansprechen, allerdings nicht den ROM selbst. Normalerweise steht da nicht nur Code drin, sondern auch Wertetabellen, Strings und andere Dinge, die man lesen können möchte, nicht nur ausführen.
- Du hast eine (seltsame) Harvard-Architektur, d.h. dein RAM ist nicht ausführbar. Programme von Festplatte laden und starten geht nicht.
- Wie das "Jump" funktionieren soll, ist mir schleierhaft.

Aber viel Spaß beim Implementieren. :-) Wenn das funktioniert, möchte ich den Beweis sehen.

Gruß,
Svenska

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #14 am: 30. November 2012, 14:17 »
nun, ist nur ein konezpt :D.
Zitat
Der Adressbus fehlt. Adressen kann man zwar auch über den Datenbus laufen lassen, aber das kostet zusätzliche Logik, spart Leitungen und kostet Performance. ((Außerdem sind reale Bussysteme normalerweise deutlich langsamer als CPUs.))
Mhh ich wusste ich hab was vergessen. :-D

Zitat
- Der Datenbus wird mit Dingen belastet, die niemanden interessieren sollten, z.B. Registerwerte
Oje, dachte wenn man registerwerte in den ram laden möchte wäre das die einfachste möglichkeit.  :roll:

Zitat
- Du hast eine (seltsame) Harvard-Architektur, d.h. dein RAM ist nicht ausführbar. Programme von Festplatte laden und starten geht nicht.
In den ram werden die Programme geladen ?  :-o, jaja man lernt nie aus  :-D.

Zitat
- Liegt der Stack nicht im RAM, hast du nur eine äußerst begrenzte Zahl von nested function calls. Praktisch verhinderst du damit jede Form von Rekursion. Gibt es in der Realität z.B. in Tastaturprozessoren oder Embedded Controllern in Notebooks.
- Ist der Stack nicht im RAM verschiebbar, verhinderst du jede Form von Multitasking. Darum kann es Projekte wie SymbOS nicht auf dem C64 geben, weil der 6502/6510 nur einen fixen Stack hat.
Nun das heißt das ich den stack beerdige und stattdessen so Art Stackpointer einbaue der auf den Ram zugreift.

Hab mich ein wenig informiert und das hier gefunden http://www.rn-wissen.de/index.php/ATmega16_ATmega32_ATmega644, im Instuction-Set gibt es aber nicht sowas wie "register-Bits". Sind registerbits etwa unpraktisch ?  :?

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #15 am: 01. December 2012, 18:04 »
wie viel Leistung mehr, würde ein MultKern-CPU bringen?

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #16 am: 01. December 2012, 23:21 »
Hallo,

Oje, dachte wenn man registerwerte in den ram laden möchte wäre das die einfachste möglichkeit.  :roll:
Normalerweise nimmt man dafür Instruktionen, also "Kopiere Register X nach RAM-Adresse Y" oder "Nimm das Register Z als Adresse in den RAM und kopiere das, was da steht in das Register X".

In den ram werden die Programme geladen ?
Ist nicht zwingend, aber hilfreich. Lese dich mal zu Harvard-Architektur und von-Neumann-Architektur ein.

Nun das heißt das ich den stack beerdige und stattdessen so Art Stackpointer einbaue der auf den Ram zugreift.
Moderne CPUs benutzen zwei Register für den Stack. Eines, welches angibt, wo der Stack überhaupt liegt (damit es mehrere Stacks geben kann) und eins, welches angibt, wo man sich gerade im Stack befindet.

im Instuction-Set gibt es aber nicht sowas wie "register-Bits". Sind registerbits etwa unpraktisch ?
Was meinst du?

wie viel Leistung mehr, würde ein MultKern-CPU bringen?
Für n Kerne ist der Leistungszuwachs kleiner als n.
Andererseits ist es deutlich mehr Aufwand.

Gruß,
Svenska

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #17 am: 02. December 2012, 01:09 »
Zitat
Zitat
Oje, dachte wenn man registerwerte in den ram laden möchte wäre das die einfachste möglichkeit. 
Normalerweise nimmt man dafür Instruktionen, also "Kopiere Register X nach RAM-Adresse Y" oder "Nimm das Register Z als Adresse in den RAM und kopiere das, was da steht in das Register X".

wobei das schon ein guter ansatz ist, da er ja die werte irgendwie in den ram bekommen muss ...
allerdings würde dich empfehlen zwei busse zu nehmen, einen internen und einen externen. intern für den datenlauf innerhalb der cpu, also zwischen registern und der alu und einen externen als bussystem zwischen ram, geräten (bei mmio) und cpu

du kannst natürlich auch dein eigenes design wählen.


was du allerdings mit registerbits meinst, ist mir auch noch nicht so ersichtlich. meinst du damit tatsächlich, die codierung der register im opcode? wenn ja, dann brauchst du sowas unbedingt, denn irgendwie muss dein steuerwerk ja wissen, welches der register es wählen soll. es sei denn, du hast nur zwei register, der extrem spezialisiert sind. ....
OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #18 am: 02. December 2012, 14:32 »
Hallo Tufelix,


Wenn Du eine CPU bauen möchtest solltest Du vorher ein paar grundsätzliche Design-Entscheidungen treffen.

z.B.: RISC vs. CISC  oder anders:  simple Load/Modify/Store-Architektur oder was komplexeres.
CISC ermöglicht oft Befehle wie "add [ax],bx" was bedeutet das der Wert von Adresse aus AX geladen wird (in ein internes temporäres Zwischenregister das nicht explizit ansprechbar ist), dann der Wert von BX dazuaddiert wird und das Ergebnis wieder an die selbe Adresse (AX) in den Speicher zurückgeschrieben wird. Bei RISC benötigt man dazu oft 3 Befehle: "ld r3,[r1]  ;  add r3,r3,r2  ;  st [r1],r3" was bedeutet das der erste Befehl den Wert von Adresse R1 in das normale Register R3 lädt (R3 dient hier als Zwischenregister), der zweite Befehl dann R3 = R3 + R1 berechnet und der letzte Befehl das Ergebnis wieder in den Speicher schreibt. Der Hintergrund dieser Design-Entscheidung ist ob Du die Komplexität in Deine CPU packst, für die erste Variante benötigst Du eine recht komplexe State-Maschine (oft basierend auf Micro-Code), oder ob das der Compiler erledigen soll und Du dafür eine Menge Arbeit beim CPU-Design sparst. Für die zweite Variante spricht das es nur 2 Befehle geben muss die auf Speicher zugreifen können und Du gezielt dort die Komplexität von höherwertigen Speicheradressierungsmodi unterbringen kannst ohne die restlichen Befehle unnötig aufzublähen. Von der Performance sind beide Varianten etwa gleichwertig wenn man die CPU wirklich gut baut (heutige x86-CPUs setzten Befehle wie den oben genannten intern auch in mehrere Befehle um aber benötigen dafür vielfach mehr Transistoren im Decoder als z.B. eine ARM-CPU die pro Takt letztlich eine ähnliche Performance erreicht). Für den Einstieg würde ich trotzdem ganz klar RISC bevorzugen und alles was auch nur minimal komplex ist dem Compiler überlassen (der LLVM kann sowas bereits ziemlich gut).

Für den Stack benötigt man nicht unbedingt mehr als ein Register, alle modernen CPUs die das Flat-Memory-Model unterstützen kommen mit einem Register für den Stack aus. Für Hochsprachen lohnt es sich aber wenn es zusätzlich zum Stack-Pointer noch einen Frame-Pointer gibt, spätestens wenn VLA oder ähnliches unterstützt werden soll ist das sogar zwingend erforderlich. Multithreading lässt sich auch mit einem einzelnen Stack-Pointer-Register erledigen indem für jeden Thread der Stack in einem anderen Speicherbereich liegt, klar bedeutet dass das die einzelnen Stacks (von einem Prozess) nicht wirklich gegeneinander geschützt sind aber das sind sie bei allen anderen heutigen CPUs auch nicht. Interessanter ist schon eher die Design-Entscheidung ob Dein Stack nach oben oder nach unten wachsen soll (letzteres ist eher ein Relikt aus der Zeit als Programme noch grundsätzlich singlethreaded waren und es nur einen kleinen Speicherbereich gab der von unten kommend mit dem Heap und von oben kommend mit dem Stack belegt wurde wird aber trotzdem von vielen CPUs so genutzt bzw. von den Compilern so festgelegt (ARM/PowerPC/... können beides)).
Auch musst Du entscheiden ob es außer LD und ST noch Befehle wie PUSH und POP geben soll die implizit den Stack benutzen oder ob CALL/RET für die Rücksprungadresse den Stack implizit nutzen können oder ob ein extra Link-Register existieren soll. Beides hat Vorteile und Nachteile, und kann z.B. darüber entscheiden wie Aufwendig das Verschachteln von Interrupts/Exceptions wird.

Es gibt noch jede Menge anderer Design-Entscheidungen die Du treffen musst bevor Du anfangen kannst, z.B. wie Du bedingte Befehlsausführung realisieren willst (mit einem der möglichen Flag-Konzepte oder anders (siehe ALPHA)).


Auserdem will ich für das projekt einen Compiler programmieren, hat wer ne Idee wie man das am besten macht?
Da solltest Du vielleicht mal einen Blick auf den LLVM werfen. Dort gibt es auch Vorlagen für einen Assembler der dann auch Labels usw. beherrscht.


Um mal einen Überblick über die möglichen CPU-Architekturen zu bekommen empfehle ich zuerst den 8Bit-AVR und dann mal den AVR32 (für beides gibt es bei Atmel sehr gute Beschreibungen der Befehlssätze und der OpCode-Architektur). Auch die NIOS-Soft-CPU von Altera ist einen Blick wert vor allem da die Tools mit denen man daraus zusammen mit der Peripherie vollständige Systeme baut recht gut sind (und ebenfalls eine gute Doku für die Befehlsarchitektur vorhanden ist) aber das setzt Kenntnisse in der FPGA-Programmierung voraus.

Falls Du Programme von einem externen Speichermedium laden können möchtest dann ist die Harvard-Architektur IMHO nicht sehr empfehlenswert aber für die ersten Gehversuche ist das sicher keine so schlechte Wahl.


Grüße
Erik
Reality is that which, when you stop believing in it, doesn't go away.

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #19 am: 02. December 2012, 18:26 »
Nun ich hab mir jetzt ein paar Gedanken gemacht, und hab mein derzeitiges Konzept überarbeitet.

Ich hab mich für ein Risc-Prozessor mit einer einfachen Harvard-Architektur entschieden.



Es gibt insgesamt 13 allgemeine Register für Rechnungen, zudem noch das AdressPointer-Register (AdressP) , das StackPointer-Register (StackP) und das ProgrammPointer-Register (ProgramP) .
Bei einem JMP Befehl wird im Status-Register die Adresse für die Rom hinein geschrieben und falls die ALU eine 1 zurück gibt wird zu dieser Adresse gesprungen.
Im Instuktion-decoder werden die Befehle der Rom decodiert und an die jeweiligen Module weitergeleitet.

Ich hoffe der Entwurf ist besser wie der alte :-D.

Mfg Tufelix

 

Einloggen