Autor Thema: Logisim CPU  (Gelesen 118865 mal)

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #120 am: 11. January 2013, 20:42 »
Wenn du "8-Bit-CPU" auf CPUs mit maximal 256 Words adressierbarem(!) Speicher beschränkst, dann fallen da sämtliche Heimcomputer und alle CPUs raus, die man üblicherweise als 8-Bit-CPUs bezeichnet, wie etwa 6502, Z80, 8051, ... und ja, ich gebe zu, dass ein OS unter diesen Bedingungen ziemlich unmöglich ist.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #121 am: 11. January 2013, 21:55 »
Hm, auf einer 8-Bit-CPU noch von Words statt einfach Bytes zu reden fühlt sich irgendwie auch ein bisschen schräg an. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #122 am: 11. January 2013, 22:29 »
Hängt damit zusammen, dass die ganz kleinen 8-Bit-CPUs meist mit 10-, 12- oder 14-Bit-Befehle haben und der ROM entsprechend breit angebunden ist. :-) Die sind also auch hybrid.

Ich kenne keine 8-Bit-CPU, die ausschließlich 8-Bit hat, also mit 8-Bit-Opcodes und 8-Bit-Registern und 8-Bit-Daten.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #123 am: 12. January 2013, 13:06 »
Hallo,


... dann fallen da sämtliche Heimcomputer und alle CPUs raus, die man üblicherweise als 8-Bit-CPUs bezeichnet ...
Ja, aber das liegt daran das man zur Klassifizierung üblicherweise die Datenpfadbreite in der ALU heranzieht. Ich persönlich bin aber der Meinung dass das nicht so ganz korrekt ist, zumindest die vom CPU-Kern nativ unterstützte Adressbreite sollte ebenfalls etwas berücksichtigt werden. Stell Dir mal vor es würde jemand eine CPU bauen die zwar nur über 8Bit breite Register und eine entsprechende ALU verfügt aber dafür in der Lage ist immer 4 dieser Register zu einem Pointer zusammenzufassen um damit 4GB Speicher flach zu adressieren und dazu vielleicht sogar noch vollwertiges Paging beherrscht (so das sogar ein normales unbeschränktes Linux drauf laufen könnte), würdest Du das immernoch als 8Bit-CPU bezeichnen?

Von der Adressgröße beim Programmcode wollen wir erst gar nicht reden, es gibt z.B. AVRs die mehr als 64k-Befehle adressieren können (wobei jeder Befehl auch 16Bit groß ist) und die trotzdem noch offiziell als 8Bit-CPU klassifiziert werden.

Aber letztendlich ist das eine rein akademische Diskussion da in der Realität zur Zeit sogar die aller kleinsten CPUs (in Taschenrechnern und Waschmaschinen) langsam gegen 32Bitter ausgetauscht werden. Kleine Cortex-M0-µC sind teilweise bereits für unter 1 Dollar (bei großen Stückzahlen) zu bekommen und auch für die Entwickler ist es einfacher keinen exotischen Compiler zu benötigen der in der Lage ist mit dem zusammengestückelten Adressraum der 8Bit-Generation umzugehen.

... dass ein OS unter diesen Bedingungen ziemlich unmöglich ist.
Vor allem auch ziemlich nutzlos. Mehr als eine einfache Library (die ins eigentliche Programm fest mit einkopiliert werden muss) die ein paar der Funktionen eines OS anbietet (wie z.B. preemptives Multitasking oder einfache Kommunikationsmechanismen) macht auf CPUs wie dem AVR einfach keinen Sinn.

Ich kenne keine 8-Bit-CPU, die ausschließlich 8-Bit hat, also mit 8-Bit-Opcodes und 8-Bit-Registern und 8-Bit-Daten.
Ich auch nicht, also lassen wir das einfach so wie es ist. ;)


Hm, auf einer 8-Bit-CPU noch von Words statt einfach Bytes zu reden fühlt sich irgendwie auch ein bisschen schräg an.
Wenn "Word" im Sinne von "kleinste adressierbare Einheit" gemeint ist ergibt das durchaus Sinn. Für Audio gab es mal spezielle DSPs die 64kWords adressieren konnten aber diese Words immer 24Bit groß waren oder denke an die 4Bit-CPUs für Taschenrechner u.ä.


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 #124 am: 14. January 2013, 20:15 »
So ich hab jetzt mal ein Konzept für das instruktion-set kreiert:

Numer   Name   |Operanden   |Beschreibung      |Operation
---------------------------------------------------------------------
1   |ADD   |Rd,Rs      |Addiert      |Rd+Rs -> Rd   
---------------------------------------------------------------------
2   |ADCI   |Rd,Wert   |Addiere Wert dazu   |Rd+Wert -> Rd
---------------------------------------------------------------------
3   |Sub   |Rd,Rs      |Subtrahiert      |Rd-Rs -> Rd
---------------------------------------------------------------------
4   |SBCI   |Rd,Wert   |Subtrahiere wert ab   |Rd-wert -> Rd
----------------------------------------------------------------------
5   |MUL   |Rd,Rs      |Multipliziere       |Rd*Rs -> Rd
---------------------------------------------------------------------
6   |MLCI   |Rd,Wert   |Multipliziere mit Wert   |Rd*Wert -> Rd
---------------------------------------------------------------------
7   |DIV   |Rd,Rs      |Divison      |Rd/Rs -> Rd
---------------------------------------------------------------------
8   |DVCI   |Rd,Wert   |Dividiere mit Wert   |Rd/wert - Rd
---------------------------------------------------------------------
9   |AND   |Rd,Rs      |AND         |Rd•Rs -> Rd
---------------------------------------------------------------------
10   |ANDI   |Rd,Wert   |AND mit wert      |Rd•Wert -> Rd
---------------------------------------------------------------------
11   |OR   |Rd,Rs      |OR         |Rd v Rs -> Rd
---------------------------------------------------------------------
12   |ORI   |Rd,Wert   |OR mit wert      |Rd v Wert -> Rd
---------------------------------------------------------------------
13   |XOR   |Rd,Rs      |XOR         |Rd "XOR" Rs -> Rd
--------------------------------------------------------------------------
14   |XORI   |Rd,Wert   |XOR mit Wert      |Rd "Xor" Wert -> Rd
----------------------------------------------------------------------------
15   |NEG   |Rd      |Zweierkompliment   |$00-Rd -> Rd
----------------------------------------------------------------------------
16   |INC   |Rd      |Incrementieren      |Rd+1 -> Rd
-----------------------------------------------------------------------
17   |DEC   |Rd      |Decrementieren      |Rd-1 -> Rd
-----------------------------------------------------------------------

      Sprung- und Rücksprung Befehle
-----------------------------------------------------------------------
18   |JmP   |adr      |Springe zur adresse   |Adr -> PC
-----------------------------------------------------------------------
19   |JE   |Rd,RS,adr   |Springe wenn gleich   |Rd==Rs Adr->PC
-----------------------------------------------------------------------
20   |JG   |Rd,Rs,adr   |Springe wenn größer   |Rd>Rs Adr->PC
-----------------------------------------------------------------------
21   |JGE   |Rd,Rs,adr   |Springe wenn gr od. gl   |Rd=>Rs Adr->PC
-----------------------------------------------------------------------
22   |JL   |Rd,Rs,adr   |Springe wenn kleiner   |Rd<Rs Adr->PC
-----------------------------------------------------------------------
23   |JLE   |Rd,Rs,adr   |Springe wenn gl od. kl |Rd=<Rs Adr->PC
-----------------------------------------------------------------------
24   |JNE   |Rd,Rs,adr   |Spinge wenn nicht gl.   |Rd!=Rs   Adr->PC
-----------------------------------------------------------------------
25   |JNG   |Rd,Rs,adr   |Springe wenn nicht gr.   |Rd!>RS   Adr->PC
-----------------------------------------------------------------------
26   |JNGE   |Rd,Rs,adr   |Spr wenn nicht gl od gr|Rd!=>RS Adr->PC
-----------------------------------------------------------------------
27   |JNL   |Rd,Rs,adr   |Spr wenn nicht kleiner   |Rd!<Rs Adr->PC
-----------------------------------------------------------------------
28   |JNLE   |Rd,Rs,adr   |Spr Wenn nicht kl od gr|Rd!=<Rs Adr->PC
-------------------------------------------------------------------------
29   |JSR   |adr      |Sprung, ablage des PC    |adr->PC
-----------------------------------------------------------------------
30   |RETS   |      |Rücksprung aus UP   |(FP-1)->PC
-----------------------------------------------------------------------
31   |RETI   |      |Rücksprung aus ISR   |(FP-1 bis FP-32) -> Registerblock   
-------------------------------------------------------------------------
   
      Register-Transfer-Befehle
-------------------------------------------------------------------------
32   |MOV   |Rd,Rs      |Kopiere  Register   |Rs -> Rd   
-------------------------------------------------------------------------
33   |LDK   |Rd,Wert   |Lade Konstante      |Wert->Rd
-------------------------------------------------------------------------

         LOAD-Befehle
-------------------------------------------------------------------------
34   |LD   |Rd      |Lade aus Bussystem    |(AP)->Rd   
-------------------------------------------------------------------------
35   |LDPOI   |Rd      |Lade, Post-Increment   |(AP)->Rd, AP+1->AP
-------------------------------------------------------------------------
36   |LDPOD   |Rd      |Lade, Post-Decrement   |(AP)->Rd, AP-1->AP
-------------------------------------------------------------------------
37   |LDPRI   |Rd      |Lade,Pre-Increment   |(AP+1->AP)->Rd
-----------------------------------------------------------------------
38   |LDPRD   |Rd      |Lade,Pre-Decrement   |(AP-1->AP)->Rd
-----------------------------------------------------------------------
39   |LDAR   |Rd,Rx      |Lade,Add Register   |(AP+Rx)->Rd
-----------------------------------------------------------------------
40   |LDSR   |Rd,Rx      |Lade,Sub Register   |(AP-Rx)->Rd
-----------------------------------------------------------------------
41   |LDAW   |Rd,wert(8 bit)   |Lade, Add Wert      |(AP+wert)->Rd
-----------------------------------------------------------------------
42   |LDSW   |Rd,wert(8 bit)   |Lade, sub wert      |(AP-wert)->Rd
-----------------------------------------------------------------------

      Store-Befehle
-------------------------------------------------------------------------
43   |ST   |Rs      |Speicher,aus Bussystem   |(AP)<-Rs   
-------------------------------------------------------------------------
44   |STPOI   |Rs      |Speicher,Post-Increment|(AP)<-Rs, AP+1->AP
-------------------------------------------------------------------------
45   |STPOD   |Rs      |Speicher,Post-Decrement|(AP)<-Rs, AP-1->AP
-------------------------------------------------------------------------
46   |STPRI   |Rs      |Speicher,Pre-Increment   |(AP+1->AP)<-Rs
-----------------------------------------------------------------------
47   |STPRD   |Rs      |Speicher,Pre-Decrement   |(AP-1->AP)<-Rs
-----------------------------------------------------------------------
48   |STAR   |Rs,Rx      |Speicher,Add Register   |(AP+Rx)<-Rs
-----------------------------------------------------------------------
49   |STSR   |Rs,Rx      |Speicher,Sub Register   |(AP-Rx)<-Rs
-----------------------------------------------------------------------
50   |STAW   |Rs,wert(8 bit)   |Speicher, Add Wert   |(AP+wert)<-Rs
-----------------------------------------------------------------------
51   |STSW   |Rs,wert(8 bit)   |Speicher, sub wert   |(AP-wert)<-Rs
-----------------------------------------------------------------------

   Push/POP Befehle
-----------------------------------------------------------------------
52   |push   |Rs      |Push auf Stack      |(SP)<-Rs, SP-1->SP
-----------------------------------------------------------------------
53   |pop   |Rd      |pop von Stack      |(SP)->Rs, SP+1->SP
-----------------------------------------------------------------------

   Schiebe Befehle
-----------------------------------------------------------------------
54   |SHR   |Rd      |Shift logisch nach rechts
-----------------------------------------------------------------------
55   |SHL   |Rd      |Shift logisch nach Links
-----------------------------------------------------------------------
56   |SAR   |Rd      |Shift Arithmetisch nach Rechts
-----------------------------------------------------------------------
57   |ROR   |Rd      |Rotieren logisch nach rechts
-----------------------------------------------------------------------
58   |ROL   |Rd      |Rotieren logisch nach Links
-----------------------------------------------------------------------
59   |NOP   |   


(man ist das lang  :roll:. )
Nun hat jemand vielleicht Kritik, bzw ne idee was man besser machen könne  :-D

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #125 am: 14. January 2013, 20:42 »
also wie ich das sehe, hast du ein paar redundante befehle drin, vorallem bei den sprungbefehlen. mit redundante befehle mein ich nicht, dass man ein ldk rd, wert auch durch ein xor rd, rd; addci rd, wert darstellen kann, wogegen aber nichts spricht ;)

jge = jnl
jl = jnge
jle = jng

... liegt an dir die drinzuhalten, ich persönlich würde die durch andere mnemonics im assembler ersetzen, die dann in den selber befehl umgesetzt werden, spart opcodespeicher :D
ansonsten seh ich keine probleme, oh vllt solltest du nop auf opcode 00 legen, es spricht zwar nichts dagegen es auf 59 zu legen, aber ... nungut, ist nur persönlicher geschmack und zudem hat der logiksimulator (logisim) die ramzellen auf 0 initialisiert, desswegen bin ich der überzeugung ... wenn du allerdings microcode steuerung hast und bei opcode 0 deine steuerung zum laden des nächsten befehls liegt, dann solltest du es lassen ... obwohl ist das laden des nächsten befehls, sehen dass der nächste befehl das laden des nächsten befehls ist, nicht nop?

wie dem auch sei, viel spass und mfg :D

PS: ich sollte nach 20:00 keinen kaffee mehr trinken .... obwohl .... eig erst die richtige arbeitszeit :P
OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #126 am: 14. January 2013, 22:43 »
Mhh zunächst hab ich glaub vergessen zu erwähnen das Rd das Quellenregister und Rs das Zielregister ist...das nop an letze stelle steht hat sich so ergeben :-D, werd das aber das noch auf jeden fall ändern....

chris12

  • Beiträge: 134
    • Profil anzeigen
Gespeichert
« Antwort #127 am: 15. January 2013, 00:18 »
Rd und Rs ist natürlich jedem überlassen, wie man es nennt, aber ist es nicht sinnvoller Rd als Destination Register und Rs als Source Register zu sehen?
OS? Pah! Zuerst die CPU, dann die Plattform und _dann_ das OS!

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #128 am: 15. January 2013, 00:21 »
Weiß nicht, ob das bei so einer einfachen CPU sinnvoll ist, aber bei was richtigem würde ich auf die 0 kein nop legen, sondern etwas, was immer eine Exception wirft (wie ud2 auf x86).
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #129 am: 15. January 2013, 01:00 »
Hallo,

ASCII-Tabellen
| Kannst  | du          |
| auch    | auch        |
| so      | darstellen. |
(mit \[tt\])

Alternativ gehen natürlich auch \[code\]-Tags.

Die Opcodes der Reihe nach auflisten funktioniert zwar, ist aber nicht besonders schön.

Wenn du die ersten acht Bit für den Opcode reservierst, dann darfst du zwischen logisch getrennten Blöcken auch Lücken lassen. Schau dir diese Bitmuster an:
Arithmetik = 0x01..0x0F = 1..31  = 0000 xxxx (16 Opcodes)
Jumps      = 0x20..0x2f = 32..47 = 0010 xxxx (16 Opcodes)
Logik      = 0x30..0x37 = 48..55 = 0011 0xxx (8  Opcodes)
Shifts     = 0x38..0x3f = 56..63 = 0011 1xxx (8  Opcodes)


Wenn du das so ähnlich strukturierst, vereinfachst du deine Befehlsdekodierung ziemlich, weil du in jedem Befehl nur die hinteren n Bits beachten musst. Beispielsweise sagen die ersten 5 Bits "Arithmetik", die nächsten 2 Bits "Addieren", das folgende Bit sagt "Immediate-Wert oder Register". Dann folgen 4 Bit "Destination-Register", dann 4 Bit "Source-Register" und dann 16 Bit "Immediate-Wert". Für Jumps wären es 5 Bit "Jump", 2 Bit "Bedingung" (immer/gleich/größer/größergleich), 1 Bit "absolut/relativ", dann wieder 4 Bit "Destination-Register", 4 Bit "Source-Register", 16 Bit "absolute oder relative Adresse".

Damit zerteilt dein Befehlsdekoder den Befehl an festen Bitgrenzen in immer kleinere Bestandteile, die du in die Subsysteme nicht mehr weitergeben musst. Das macht die Logik einfacher zu verstehen und übersichtlicher. Bei 32-Bit-Befehlen auf einer 16-Bit-CPU darfst du sogar richtig Bits verschwenden (im Fall oben: Entweder die 4 Bit "Source-Register" oder die 16 Bit "Immediate-Wert" sind egal). Ein NOP kannst du dir übrigens sparen und im Assembler durch ein "MOV R0, R0" ersetzen (macht ARM z.B. so).

Es wird natürlich Befehle geben, auf die so eine Struktur nicht passt. Die kannst du aber alle so legen, dass die ersten 5 Bits "11111" sein müssen, dann hast du 27 Bits für diese Sonderfälle frei.

Gruß,
Svenska

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #130 am: 15. January 2013, 14:45 »
 
Zitat
Rd und Rs ist natürlich jedem überlassen, wie man es nennt, aber ist es nicht sinnvoller Rd als Destination Register und Rs als Source Register zu sehen?
Mhh ich glaub ich hab bei meiner Antwort Rd(drain) mit Rs(source) verwechselt  :roll:

Zitat
ASCII-Tabellen
| Kannst  | du          |
| auch    | auch        |
| so      | darstellen. | (mit \[tt\])

Alternativ gehen natürlich auch \[code\]-Tags.
Mh was sind ASCII Tabellen...und wie stelle ich die Normal dar ?  und Wofür brauch ich die ?  :-D

Zitat
Wenn du die ersten acht Bit für den Opcode reservierst, dann darfst du zwischen logisch getrennten Blöcken auch Lücken lassen. Schau dir diese Bitmuster an:
Arithmetik = 0x01..0x0F = 1..31  = 0000 xxxx (16 Opcodes)
Jumps      = 0x20..0x2f = 32..47 = 0010 xxxx (16 Opcodes)
Logik      = 0x30..0x37 = 48..55 = 0011 0xxx (8  Opcodes)
Shifts     = 0x38..0x3f = 56..63 = 0011 1xxx (8  Opcodes)
So hab ichs bei meinem alten CPU gemacht...wollte aber jetzt eigentlich 32 register haben...aber naja ich kanns ja mal mit 16 versuchen ....

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #131 am: 15. January 2013, 15:38 »
Hallo Tufelix,


Wo das NOP liegt ist mMn eher egal aber ich möchte Kevin zustimmen und würde dafür sorgen das wenn der Befehl komplett aus 0-Bits besteht das dann eine Undefined-OpCode-Exception kommt. Das selbe auch wenn der Befehl komplett aus 1-Bits besteht da viele FLASHs und EEPROMs im unprogrammierten Zustand immer 0xFF liefern. Aber in der Realität wird das nur bei sehr wenigen CPUs so gehandhabt und die funktionieren auch alle also ist es wahrscheinlich relativ egal. Auf NOP ganz verzichten würde ich aber trotzdem nicht, ein NOP zeichnet sich meiner Meinung nach dadurch aus das dieser von keinem anderen Befehl abhängig ist und auch kein anderer Befehl vom NOP abhängt, bei einem MOV R0,R0 ist das nicht so einfach gegeben da es auch andere Befehle geben kann die R0 benutzen. Eine Option wäre natürlich diesen MOV R0,R0 im Decoder als Sonderfall zu behandeln der dann innerhalb der eigentlichen Pipeline als echter NOP dargestellt wird (und auch der Disassembler zeigt für die Bitkombination die MOV R0,R0 entspricht immer NOP an) aber dieser NOP eben keinen eigenen OpCode hat.


Da Du ja 32 Bit pro Befehl hast würde ich den Aufbau minimal ändern:
6 Bit  | 5 Bit | 5 Bit |  16 Bit
OpCode |  RD   | RS(1) | Konstante oder RS2

64 maximal mögliche Befehle dürften für Dein Konzept komplett ausreichen (Du hast wirklich einige Redundanz drin) und 32 anstatt 16 Register sind auf jeden Fall nicht zu verachten. Ein drittes Register (aus dem aber nur gelesen werden kann) ist ebenfalls eine gute Idee da damit zerstörungsfreie 3-Operanden-Befehle möglich werden (alle anständigen RISC-CPUs die was auf sich halten können sowas und die Compiler lieben das abgöttisch).


Bei den bedingten Sprüngen reichen 6 Varianten völlig aus. Im Assembler Mnemonik solltest Du folgende 18 Varianten haben: JE, JNE, JA, JAE, JB, JBE, JNA, JNAE, JNB, JNBE, JG, JGE, JL, JLE, JNG, JNGE, JNL, JNLE (die ersten 2 sind für signed und unsigned, die nächsten 8 für unsigned und die letzten 8 für signed). Damit sind alle Vergleichsmöglichkeiten zwischen 2 Integer-Werten abgedeckt. Die beiden ersten Varianten JE und JNE müssen so auch als OpCode verfügbar sein, die anderen 16 Mnemoniks lassen sich mit 4 OpCodes darstellen : JA ist das selbe wie JNBE und JAE ist das selbe wie JNB usw. und schon sind es nur noch diese 8 Möglichkeiten : JA, JAE, JB, JBE, JG, JGE, JL, JLE (ich hab mal alle die Varianten mit N gestrichen). Zur weiteren Halbierung muss Dein Assembler in der Lage sein die Register zu vertauschen : JB R1,R2,Destination ist das selbe wie JA R2,R1,Destination usw. und schon sind es nur noch 4 richtige Vergleiche (2 für unsigned und 2 für signed), zusätzlich zu JE und JNE.
Der Aufbau für Deine bedingten Sprünge wäre also:
OpCode (6 aus 64) | RA (5Bit) | RB (5Bit) | Destination (16Bit)
wobei RA und RB immer nur gelesen werden (diese Befehle also nie irgendwelche Register ändern), ob Destination absolut oder relativ ist bleibt erst mal Dir überlassen aber ich würde für eine einfache CPU bei der es keine Möglichkeit gibt Code von einem externen Datenträger zu Laden bei absolut bleiben, spätestens der Linker weiß ja an welche Adresse er jeden Befehl packt und kann deswegen immer absolute Sprünge bauen (und der Decoder in der CPU wird einfacher wenn er nicht das absolute Sprungziel erst berechnen muss).

Bei Deinen mathematischen Befehlen (da schließe ich AND/OR/XOR mit ein) würde ich einen vergleichbaren Aufbau nutzen:
OpCode (12 aus 64) | RD (5Bit) | RS1 (5Bit) | Konstante (16Bit) oder RS2 (5Bit)
Die OpCodes sind mMn : ADD, ADDI, SUB, SUBI, SUBR, SUBRI, AND, ANDI, OR, ORI, XOR, XORI (wobei SUBR durch SUB mit vertauschten RS1 und RS2 ersetzt werden kann aber ich lass den mal der Konsistens wegen drin).
Befehle mit I am Ende funktionieren so : RD <= RS1 [Operator] Konstante
Befehle ohne I am Ende entsprechend : RD <= RS1 [Operator] RS2
Bei allen diesen Befehlen ist es egal in welcher Reihenfolge die beiden Quell-Operanden stehen nur bei SUB nicht und deswegen gibt es den SUBRI damit eben auch RD <= Konstante - RS1 möglich ist (ARM hat sowas auch aber dort vor allem deswegen weil nur einer der beiden Operanden ein Shifter-Operand sein kann und diese Fähigkeit eben nicht nur dem Subtrahend sondern auch dem Minuend zur Verfügung stehen soll).

INC und DEC wird als eigenständiger OpCode nicht benötigt da sich das mit ADD und SUB darstellen lässt. NEG ist gut aber läst sich mit SUBRI RD,RS1,0 darstellen, dafür hast Du NOT (alle Bits invertieren) vergessen aber der lässt sich mit XOR RD,RS1,0xFFFF darstellen.

MOV und LDK sollten auf jeden Fall beide so bleiben und hätten diesen Aufbau :
MOV | RD (5Bit) | RS (5Bit) | ungenutzt
LDK | RD (5Bit) | ungenutzt | Konstante (16Bit)
Wenn bei MOV RD und RS identisch sind lassen sich damit Spezial-Fälle umsetzen: z.B. wenn RD == RS == 0 zutrifft könnte damit ein NOP gemeint sein. Mit den Werten 1..31 in RD/RS lassen sich noch andere Spezial-Fälle abdecken. Solche Spezial-Fälle in einen vorhanden Befehl mit hinein zu kodieren spart Dir zwar OpCodes aber macht dafür den Decoder komplizierter, in der Pipeline hat das aber keine Konsequenzen da die Spezial-Fälle dort eh als eigenständige Befehle benötigt werden.

Zu Shiften und Rotieren reichen 4 Befehle da ROR sich durch ROL abbilden lässt und diese müssten folgenden Aufbau haben:
OpCode (4 aus 64) | RD (5Bit) | RS (5Bit) | ein Bit das unterschiedet ob der Anzahl der Shifts bzw. Rotationen als Konstante oder aus einem Register kommt | unbenutzt (10 Bit) | Konstante (4Bit) oder RS2 (5Bit)
woraus dann diese Mnemoniks entstehen : SHR, SHRI, SHL, SHLI, SAR, SARI, ROL, ROLI was in C so aussieht RD = RS [Operator] Konstante oder Variable (in RS2).

Bis jetzt sind es gerade mal 24 Befehle und wir haben schon fast alles was wichtig ist, es fehlen nur noch Multiplikation/Division, unbedingte Sprünge bzw. Funktionsaufrufe und die Speicherzugriffe.
Aber dazu schreibe ich später noch was wenn Du das wirklich möchtest (meine Beiträge ziehen sonst wieder so arge Kritik wegen ihrer übergebührlichen Länge auf sich ;) ).


Grüße
Erik
« Letzte Änderung: 15. January 2013, 15:58 von erik.vikinger »
Reality is that which, when you stop believing in it, doesn't go away.

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #132 am: 15. January 2013, 17:02 »
Braucht man mov denn jetzt noch in Hardware? Mit drei Operanden ist ein mov r1,r2 doch das gleiche wie ein add r1,r2,0.

Und fürs Shiften und Rotieren gab es doch diese coolen riscigen Bitschnippelbefehle, die erst rotieren und das Ergebnis dann maskieren. Wobei ich keine Ahnung habe, wie doof sowas umzusetzen ist. ;)
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Svenska

  • Beiträge: 1 792
    • Profil anzeigen
Gespeichert
« Antwort #133 am: 15. January 2013, 17:34 »
Hallo,

Mh was sind ASCII Tabellen...und wie stelle ich die Normal dar ?  und Wofür brauch ich die ?  :-D
Damit kannst du Tabellen darstellen, ohne Tabellen benutzen zu müssen. Schau dir mal deinen Beitrag und meinen Beitrag an - bei mir sind in der Liste alle Zeichen gleich breit und alle Gleichheitszeichen stehen direkt übereinander. Das erhöht die Lesbarkeit. :-)

Im Zweifelsfall ist es ein Spezialfall von ASCII-Art, nur halt für Tabellen benutzt.

wollte aber jetzt eigentlich 32 register haben...aber naja ich kanns ja mal mit 16 versuchen ....
Dann nimmst du halt 5 Bit für "Source-Register" und 5 Bit für "Destination-Register"... ich wollte dir nur zeigen, wie du es einfacher machen kannst. Niemand hindert dich, die Bitaufteilung so zu machen, wie du sie für richtig hältst; aber fixe Bitgrenzen machen es viel einfacher zu implementieren.

Wo das NOP liegt ist mMn eher egal aber ich möchte Kevin zustimmen und würde dafür sorgen das wenn der Befehl komplett aus 0-Bits besteht das dann eine Undefined-OpCode-Exception kommt. Das selbe auch wenn der Befehl komplett aus 1-Bits besteht da viele FLASHs und EEPROMs im unprogrammierten Zustand immer 0xFF liefern.
Einverstanden, bedenke allerdings, dass es hier um eine simulierte CPU geht. :-)

Auf NOP ganz verzichten würde ich aber trotzdem nicht, ein NOP zeichnet sich meiner Meinung nach dadurch aus das dieser von keinem anderen Befehl abhängig ist und auch kein anderer Befehl vom NOP abhängt, bei einem MOV R0,R0 ist das nicht so einfach gegeben da es auch andere Befehle geben kann die R0 benutzen.
Ein guter Compiler darf, wenn R0 in Benutzung ist, auch ein MOV R1, R1 für NOP benutzen. Ansonsten gehe ich mal davon aus, dass Tufelix keine CPU mit superskalarem Pipelining baut, sondern eher eine von den Fähigkeiten aufgebohrte 8-Bit-Technologie mit breiteren Registern. Da ist es dann ziemlich egal, wie ein NOP nun dargestellt wird. Ich bezweifle, dass Tufelix die 16-Bit-Über-RISC-CPU mit Hardware-Realisierung im ASIC entwickeln will...

Braucht man mov denn jetzt noch in Hardware? Mit drei Operanden ist ein mov r1,r2 doch das gleiche wie ein add r1,r2,0.
MIPS hat ein Nullregister, mit dem das geht. Konstanten finde ich aber flexibler (außerdem kriegst du ein zusätzliches Allzweckregister geschenkt). Tufelix hat bisher dazu nichts geschrieben. :-P

Und fürs Shiften und Rotieren gab es doch diese coolen riscigen Bitschnippelbefehle, die erst rotieren und das Ergebnis dann maskieren. Wobei ich keine Ahnung habe, wie doof sowas umzusetzen ist. ;)
Dazu brauchst du einen Barrel-Shifter und zusätzliche Bits im Opcode, daher wurden die bei Thumb/Thumb-2 m.W. abgeschafft. Der originale 32-Bit-ARM-Befehlssatz hat sowas aber im Befehl codiert, ja.

Gruß,
Svenska

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #134 am: 15. January 2013, 20:45 »
Braucht man mov denn jetzt noch in Hardware? Mit drei Operanden ist ein mov r1,r2 doch das gleiche wie ein add r1,r2,0.
MIPS hat ein Nullregister, mit dem das geht. Konstanten finde ich aber flexibler (außerdem kriegst du ein zusätzliches Allzweckregister geschenkt). Tufelix hat bisher dazu nichts geschrieben. :-P
Ich rede vom mov, das braucht kein Nullregister, wenn man ein addi hat. Das ldk zu emulieren bräuchte ohne Nullregister zwei Befehle, das stimmt (xor r1, r1, r1; addi r1, r1, $1234)

Zitat
Dazu brauchst du einen Barrel-Shifter und zusätzliche Bits im Opcode, daher wurden die bei Thumb/Thumb-2 m.W. abgeschafft. Der originale 32-Bit-ARM-Befehlssatz hat sowas aber im Befehl codiert, ja.
Bits im Opcode haben wir ja massig übrig.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

Tufelix

  • Beiträge: 103
    • Profil anzeigen
Gespeichert
« Antwort #135 am: 15. January 2013, 22:16 »
Zitat
MIPS hat ein Nullregister, mit dem das geht. Konstanten finde ich aber flexibler (außerdem kriegst du ein zusätzliches Allzweckregister geschenkt). Tufelix hat bisher dazu nichts geschrieben. :-P
Nun ich behalte glaub den Mov- und den ldk-Befehl. Für den NOP-Befehl benutze ich ein MOV R1,R1 mein CPU wird erstmal ohne Pipeline arbeiten, da wird es sicherlich keine Probleme wegen der abhängigkeit geben...

Werde dann wohl auch die 3-Operanten Befehle einbauen...

Bei den Jumps mach ich das so wie Wikinger Beschrieben hat.(also das mit den 6 Varianten).

Zitat
Dazu brauchst du einen Barrel-Shifter und zusätzliche Bits im Opcode, daher wurden die bei Thumb/Thumb-2 m.W. abgeschafft. Der originale 32-Bit-ARM-Befehlssatz hat sowas aber im Befehl codiert, ja.
Barrel-shifter könnte ich eigentlich auch einbauen... dann werd ich auf die ersten 4 bits der Konstanten-bits die distanz-bits legen.

Nun kann man eigentlich etwas an den Load und Store-Bits verbessern ?

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #136 am: 18. January 2013, 14:33 »
Hallo,


Wobei ich keine Ahnung habe, wie doof sowas umzusetzen ist.
Ziemlich doof, glaub mir.
Außerdem ist ARM32 meines Wissens nach die einzigste real existierende CPU die sowas überhaupt hat (auch in ARM64 ist das wohl nicht mehr drin), was sicher auch etwas über die Nützlichkeit dieses Features aussagt.


mein CPU wird erstmal ohne Pipeline arbeiten, da wird es sicherlich keine Probleme wegen der abhängigkeit geben...
Ja, bei mit ohne Pipeline sollte das keine Probleme mit der Performance o.ä. machen.

Werde dann wohl auch die 3-Operanten Befehle einbauen...
Das alleine bringt schon einiges an Performance weil weniger Befehle für die selbe Sache benötigt werden.

Barrel-shifter könnte ich eigentlich auch einbauen
Ich denke das solltest Du fürs erste lassen, damit wird Deine CPU um einiges komplexer und vor allem wird die Codierung der Befehle deutlich komplexer. Versuch doch erst mal eine möglichst einfache CPU zuverlässig ans laufen zu bekommen bevor Du Dir die nächste Schwierigkeitsstufe vornimmst. ARM hat diese Shifter-Operanden auch nur bei manchen Befehlen und für Konstanten, letzteres Problem hast Du nicht weil Du in Deine 32Bit Befehle eine 16Bit-Konstante immer ohne Verrenkungen komplett rein bekommst.

dann werd ich auf die ersten 4 bits der Konstanten-bits die distanz-bits legen.
Hä, wie meinen?

Nun kann man eigentlich etwas an den Load und Store-Bits verbessern?
Versuch doch erst mal Dein Konzept als ganzes mit den jüngsten Vorschlägen dieses Thread neu aufzubauen und dann sehen wir wie weiter verbessert werden kann.

Zu MUL/DIV möchte ich aber dennoch was schreiben: Du solltest Dir überlegen ob Du das Produkt der Multiplikation in voller Bitgröße haben willst was eben immer 2 Ziel-Register erfordert, solange Du nur die untere Hälfte des Produkts hast brauchst Du auch nicht zwischen signed und unsigned unterscheiden aber wenn Du mehr willst musst Du das beachten. Auch für die Division könnte es sich lohnen wenn Du zwei Ziel-Register ansprechen kannst da es ja dort noch den Rest gibt (wird u.a. für den %-Operator von C benötigt). Bei der Division muss wimre immer zwischen signed und unsigned unterschieden werden. Ein Speicher-Lesebefehl mit Pre/Post-Inkrement/Dekrement hat auch immer 2 Ziel-Register (das wo die gelesenen Daten rein gelegt werden und das Adressregister das modifiziert wird).


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

kevin

  • Administrator
  • Beiträge: 2 767
    • Profil anzeigen
Gespeichert
« Antwort #137 am: 18. January 2013, 17:01 »
Ich glaube, wir reden aneinander vorbei, Erik. Ich hab mal das Internet befragt und vermute, dass du das Feature meinst, das den zweiten Operanden shiften kann, bevor er verarbeitet wird. Auch eine lustige Idee, aber das habe ich nicht gemeint. ;)

Ich rede von Instruktionen wie rlwimi oder rlwinm auf PPC.
Thou shalt not follow the NULL pointer, for chaos and madness await thee at its end.

erik.vikinger

  • Beiträge: 1 277
    • Profil anzeigen
Gespeichert
« Antwort #138 am: 18. January 2013, 19:33 »
Hallo,


Ich glaube, wir reden aneinander vorbei, Erik.
Ja, das glaube ich jetzt auch. Fragt sich nur was Tufelix dachte.

Ich rede von Instruktionen wie rlwimi oder rlwinm auf PPC.
Ja, diese beiden Befehle sind cool. rlwimi hab ich auf meiner CPU auch aber etwas anders, es geht dabei ja im Prinzip darum aus einem Register von einem beliebigen Bitoffset eine bestimmte Anzahl an Bits (zusammenhängend aber ggf. mit Umlauf) aus einem Register raus zu holen und in ein anderes Register an einem anderen beliebigen Bitoffset rein zu packen (auch wieder zusammenhängend mit optionalem Umlauf) und die übrigen Bits des Zielregisters unverändert zu lassen. lrwinm hab ich aber nicht als Einzelbefehl da sich sowas auch einfach mit einer Rotation und einem anschliessendem AND darstellen lässt (wobei ich das dank des Shifter-Operanden, den ich doch vorgesehen habe, auch als Einzelbefehl haben kann aber dafür die Maske bereits in einem anderen Register vorhanden sein muss) und ich auch nicht so ganz sicher bin ob man sowas oft braucht.


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 #139 am: 19. January 2013, 11:10 »
Zitat
Hä, wie meinen?
Mhh ich glaub ich hab da was verwechselt. :roll:

Zitat
Ja, das glaube ich jetzt auch. Fragt sich nur was Tufelix dachte.
Dachte an Shift-Befehle so wie wikinger beschrieben hatte...
Zitat
Zu Shiften und Rotieren reichen 4 Befehle da ROR sich durch ROL abbilden lässt und diese müssten folgenden Aufbau haben:
OpCode (4 aus 64) | RD (5Bit) | RS (5Bit) | ein Bit das unterschiedet ob der Anzahl der Shifts bzw. Rotationen als Konstante oder aus einem Register kommt | unbenutzt (10 Bit) | Konstante (4Bit) oder RS2 (5Bit)
woraus dann diese Mnemoniks entstehen : SHR, SHRI, SHL, SHLI, SAR, SARI, ROL, ROLI was in C so aussieht RD = RS [Operator] Konstante oder Variable (in RS2).

Wofür braucht man eigentlich die Sprungbefehle JA,JAE,JB,JBE. Kann man da nicht für JA auch ein JE Befehl nehmen und für JB ein JL ?

Zitat
Zu MUL/DIV möchte ich aber dennoch was schreiben: Du solltest Dir überlegen ob Du das Produkt der Multiplikation in voller Bitgröße haben willst was eben immer 2 Ziel-Register erfordert, solange Du nur die untere Hälfte des Produkts hast brauchst Du auch nicht zwischen signed und unsigned unterscheiden aber wenn Du mehr willst musst Du das beachten. Auch für die Division könnte es sich lohnen wenn Du zwei Ziel-Register ansprechen kannst da es ja dort noch den Rest gibt (wird u.a. für den %-Operator von C benötigt). Bei der Division muss wimre immer zwischen signed und unsigned unterschieden werden. Ein Speicher-Lesebefehl mit Pre/Post-Inkrement/Dekrement hat auch immer 2 Ziel-Register (das wo die gelesenen Daten rein gelegt werden und das Adressregister das modifiziert wird).
Mhh wenn ich jetzt schon 2 Quellregister hab könnte ich eigentlich ja auch 2 Zielregister einbauen...was passiert eigentlich wenn man bei den 2. Zielregister das selbe Register angibt wie beim ersten Zielregister ?
« Letzte Änderung: 19. January 2013, 11:18 von Tufelix »

 

Einloggen