Home | Artikel | LUGcamp Hardware

LUGcamp Vortrag vom 2003.05.29 - Hardware: Funktionsweise, Selbstbau, programmierbare Logik, Open Source


Inhalt

Einführung

Dieser Vortrag beschreibt was Hardware ist, wie sie funktioniert, wie man selber moderne Hardware herstellen kann auf der Basis von programmierbarer Logik, wie man Hardware Designs wie Software open source machen kann, und was die Folgen dieser Entwicklung sein könnten.

Es wird davon davon ausgegangen, dass die meisten Zuhörer Hardware bisher nicht kennen, ausser Platinen in den PC einstecken und drauf Software installieren. Und dabei feststellen, dass das grüne Plastikflachen sind, mit dunklen Linien und so komischen Teilen drauf verstreut. Aber ansonsten keine Ahnung haben, was in den Teilen drin abgeht.

Es wird angenommen, dass der Zuhörer programmieren kann (zumindest Scripts) und Konzepte wie Variablen und Zuweisungen versteht.

Binäre Mathematik

Bevor man die Elektronik anschaut, sollte man zuerst mal wissen, was die eigentlich anstellen soll. Also zuerst mal eine Einführung in Rechnen in Bits.

Man hat bereits im 17. Jahrhundert Rechenmaschinen gebaut. Blaise Pascal (1623-62), Sohn eines Steuerbeamten, hat damals einen mechanischen Addierer für seinen Vater gebaut. Ältere Zuhörer werden sich noch an die mechanischen Registrierkassen errinnern, die früher in den Läden benutzt wurden, die "300 70 5 + [ratter ratter]" Dinger. Dort war die selbige Mechanik drin, nur mit Elektromotor statt Kurbel.

Hinweis: Der Abakus ist viel älter, ist aber kein Rechner, sondern nur ein Speicher, gerechnet wird im Kopf, nur externer Speicher der Kopf entlastet, wie schriftlich rechnen, aber ohne Papierverbrauch.

Problem all dieser Rechner war, dass sie kompliziert waren, weil sie dezimal rechneten. Alleine eine 1-stellige Addition von 2 Ziffern hat 10 mögliche Ziffern bei jedem Operand, also 10*10=100 möglichen Kombinationen. Wenn man für Ergebnisse grösser 9 auch noch einen Übertrag will, gibt das eine zweite Einrichtung mit 100 Kombinationen. Will man mehrziffrige Zahlen Addieren hat man wegen dem Übertrag der 0 oder 1 sein kann, ab der zweiten Ziffer sogar 10*10*2=200 Möglichkeiten, mal 2 Einrichtungen. Multiplikation will sogar 10*10*10=1000 Kombinationen, weil man 10 mögliche Überträge hat.

Auch der Versuch von Charles Babbage in den 1880ern einen Computer zu bauen, um Tabellen von Navigationsdaten fehlerfrei zu rechnen, und ohne Person dazwischen zu drucken, ist letztlich daran gescheitert, dass dies zu kompliziert war, trotz Finanzierung durch die britische Marine, die gestrandete und gesunkene Schiffe einsparen wollte.

Der Computer wurde realistisch möglich, als man auf binäre Zahlen umstellte, also Zahlen mit nur 2 Ziffern. Das vereinfachte die Rechner gewaltig (nur noch max 2*2*2=8 Kombinationen, selbst für Multiplikation mit Übertrag). Die einzelnen Ziffern von binären Zahlen werden Bits (Binariy digITS) genannt.

      Beispiel binär Rechnen mit 8bit Zahlen (Bereich 0..255)

        7654 3210   Nummer n des Bits

        1
        2631
        8426 8421   Wert des Bits, 2^n, für n=0..7 = 1..128
        ---- ----
        0100 1100   Operand1   | =      64      +8+4     =  76
      + 0110 0111   Operand2   | =      64+32     +4+2+1 = 103
      ( .  . .      Übertrag)  |
        ---- ----              |                           ---
        1011 0011   Ergebnis   | =  128   +32+16    +2+1 = 179
    

Boolsche Logik

Computer bestehen als viele kleinen simplen Bauteilen, den Logik Elementen. Von denen muss man mehrere oder gar viele zusammensetzen um zu rechnen. Um die Zusammenhänge simpel zu beschreiben, ohne dauernd Skizzen zu machen, wird eine Notation namens Boolsche Logik verwendet. Diese ist eine sehr simple Mathematik mit nur 2 Ziffern, 0 und 1, wie es Bits auch sind. Aber statt so komplizierten Sachen wie Addition und Multiplikation sind hier definiert:
UND/AND/&
Wenn alle Operanden (a&b&...) 1 sind, ist das Ergebnis 1 (sonst 0). Dies ist zufällig identisch mit Multiplikation ohne Übertrag. Man merke: UND ist trotz naheliegendem Namen keine Addition, ein & ist nicht ein +. UND kann aber auch als "wenn a lass b durch" benutzt werden
ODER/OR/|
Wenn auch nur ein Operand (a|b|...) 1 ist, ist das Ergebnis 1. Dies entspricht einer etwas queren "Addition", wo alles über 0 gleich 1 ist (wir haben ja nur eine Stelle und nur 2 Ziffern!). ODER kann aber auch als "sammle Bits" benutzt werden
EXODER/XOR/^
Wenn a oder b gleich 1, aber nicht beide(!), dann ist das Ergebnis 1. Dies is zufällig die hintere Stelle einer Addition, dezimal 2 ist ja binär 10. EXODER kann aber auch z.B. 2 Bits vergleichen (1 bei ungleich). EXODER kann auch ein a gesteuert durch b invertieren

Als Tabelle sieht das so aus:

      a b   & | ^ 
      ---   -----
      0 0   0 0 0
      0 1   0 1 1
      1 0   0 1 1
      1 1   1 1 0
    

Schliesslich gibt es noch die NICHT/NOT/~ Funktion, mit nur einem Operand. Dessen Ergebnis ist nicht (= Gegenteil von) Operand, also: ~0=1, ~1=0. Man nennt diese Operation Inversion. Sie ist vergleichbar mit einer Negation (0-Operand). 2 mal Inversion heben sich auf, wie 2 mal Negation.

Die Kombination (a & b) | (c & d) ist übrigens so häufig das man diese auch ohne Klammern standardmässig wie (a * b) + (c * d) ausrechnet.

Diese Logik übrigens ist ein klassischer Fall von etwas das lange vor seiner Nützlichkeit entdeckt wurde (George Boole war Mathematiker im 19. Jahrhundert). Das war, wie man unschwer sieht, im 19. Jahrhundert nur eine mathematische Kuriosität, komplet nutzlos. Heute kann man damit Elektronik beschrieben, genauso wie man mit Formeln Programme schreiben kann.

Rechnen mit Boole

Mit dieser Booleschen Logik kann man nur tatsächlich richtige Additionen rechnen, wenn man mehrere der einfachen Elemente zusammensetzt.

Wenn man die eine einziffrige Addition anschaut, so sieht man, das diese ihre hintere Stelle ein EXODER ist. Und die vordere Stelle ist ein UND:

       a + b = ergebnis       dezimal
       0 + 0 = 00        |    0
       0 + 1 = 01        |    1
       1 + 0 = 01        |    1
       1 + 1 = 10        |    2
      a UND b_/  \_a EXODER b
    

Aufwendiger wird es wenn man dann die zweite oder folgende Ziffern will, da muss man noch den Übertrag, der bisher immer 0 war, berücksichtigen:

                    a + b + übertrag = ergebnis       dezimal
                    0 + 0 + 0        = 00        |    0
                    0 + 1 + 0        = 01        |    1
                    1 + 0 + 0        = 01        |    1
                    1 + 1 + 0        = 10        |    2
                    0 + 0 + 1        = 01        |    1
                    0 + 1 + 1        = 10        |    2
                    1 + 0 + 1        = 10        |    2
                    1 + 1 + 1        = 11        |    3
      (a UND b) ODER (a UND übertrag)_/  \_(a EXODER b) EXODER übertrag
                ODER (b UND übertrag)
    

Die hintere Ziffer ist schlicht a und b addieren (was nie mehr als 2 geben kann), und dann noch den Übertrag hinzuzählen (geht nie über 3). Vorne braucht man Tests auf "über 1", also ist wenn irgend eine der 3 Kombinationen 2 gibt, für 3 sind dann schlicht alle 3 Kombinationnen aktiv. Das ODER da ist ein "über 1 Sammler".

Schalten mit Boole

Neben Rechnen will ich hier noch ein zweites Beispiel. Ein Computer muss vor allem eins können: Daten auswählen. Auch so ein "Schalter" lässt sich mit Boolscher Logik machen.

Da UND ein "Durchlass" und ODER ein "Sammler" sein kann, kann man diese dazu verwenden. Dazu bekommt jeder Eingang ein UND, und einen "Steuereingang". Die Ausgänge aller UND werden von einem breiten ODER gesammelt.

Dies ist ein häufiger Sonderfall von (a & b) | (c & d), wo a und c die Eingänge e1 und e2 sind, und b und d die Steuereingänge s1 und s2 sind.

Sehr oft will, dass ein Ausgang gleich einen oder den anderen Eingang ist. Um zu wählen welchen, will man nur ein Auswahl-Signal. Wenn dies 0 ist soll der erste Eingang, bei 1 der zweite Eingang herauskommen.

      e1 e2 auswahl = ausgang
      0  0  0       = 0        = e1
      0  1  0       = 0        = e1
      1  0  0       = 1        = e1
      1  1  0       = 1        = e1
      0  0  1       = 0        = e2
      0  1  1       = 1        = e2
      1  0  1       = 0        = e2
      1  1  1       = 1        = e2
                       \_(ein1 & ~(auswahl)) | (ein2 & auswahl)
    

Hinten ist das UND ein normales "lass e2 durch wenn auswahl = 1". Vorne will man nun e1 durchlassen wenn auswahl = 0, also kehrt man auswahl um, mit ~, um dem UND ein 1 zu bieten. Man nennt so ein Teil überigens einen Multiplexer, hier genauer ein 2:1 Multiplexer.

Man kann auch 4 Eingänge mit 4 UND und entweder 4 Steuereingänge oder 2 Auswahl mit 4 Inverter Kombinationen und breitere (3 Anschlüsse) UND ausführen. Das ist dann ein 4:1 Multiplexer. Mit 8 Steuereingängen oder 3 Auswahl und 4-Anschluss UNDe, kann man auch einen 8:1 haben, usw. Mit n Auswahl kann man immer (2^n):1 multiplexen, weil man daraus 2^n Steuereingänge durch NICHT und (teil-vom)UND generierbar sind. Die n Auswahl Signale kann man als nBit Zahl auffassen, mit Wert 0..(2^n)-1, die man Adresse nennt.

Elektrizität und Elektronik

Strom durch Widerstände

Nun können wir mal mit Bits rechnen. Jetzt brauchen wir einen Rechner, der das für uns macht. Bits kann man nicht nur mit Elektronik machen, auch mit Relais Schaltern (Elektromechanik) oder gar purer Mechanik oder Hydraulik oder gar Seilzügen kann man dies. Aber Elektronik ist mit Anstand die schnellste und kompakteste Form. Grund dafür ist, dass hier nur Elektronen bewegt werden, und nicht ganze Atome. Der Gewichtsunterschied macht es aus.

Die Basis aller Elektronik ist der gesteuerte Umgang mit elektrizischem Strom. Strom kann nur fliessen, wenn elektrische Spannung ihn antreibt und ein elektrischer Leiter vorhanden ist, durch den er getrieben werden kann. Wieviel Strom dabei entsteht hängt ab von der Spannung die antreibt sowie dem Widerstand den der Leiter ihm entgegenstellt:

Strom = Spannung / Widerstand

Aus dem Grund, und weil Leistung (bzw Hitzeentwicklung) = Spannung * Strom ist, nimmt man heute übrigens die immer kleineren Spannungen, 5V, 3.3V, 2.8V, 2.5V, 1.8V, ... . Je weniger umso kühler, aber zuwenig und der Rechner wird unzuverlässig (leicht störbar). Also geht es in kleinen Schritten, und man ärgert sich über Spannungseinsteller einstellen auf den Motherboards.

Spannungsquellen

Spannung entsteht entweder Reibungshitze (Blitz), oder durch chemische Reaktionen (Batterie/Akku) oder durch an Drähten vorbei bewegten Magnete (Generator).

Diese Vorgänge sind zwar interessant, aber kompliziert und interessieren uns für die Verständnis von Computer Hardware nichts! Wir nehmen hier daher einfach an, das wir ein Netzteil haben, das Spannung liefert, solange wir ihm nicht zuviel Strom abverlangen (dann tut besseres Netzteil abschalten und schlechteres geht in Rauch auf). Im restlichen Vortrag interessiert uns daher nur der Leiter, in diesem Fall die Chips auf den Platinen.

Spannungsteiler

Wenn wir unsere Spannung an eine Reihe von Leitern anhängen dann ergibt sich ein Strom, der der Summe der Widerstände der Leiter entspricht.

Strom = Spannung / (Widerstand1 + Widerstand2 + Widerstand3)

        Strom            Spannung
      ---->-----.
                |        voll    100%
               .-.
               | | W1
               | | 50%
               `-'
                |        wenig    50%
               .-.
               | | W2
               | | 25%
               `-'
                |        weniger  25%
               .-.
               | | W3
               | | 25%
               `-'
                |        nichts   0%
      ----------'
    

Dabei stellt man dann fest, dass an jedem Widerstand genau ein Teil der Gesammtspannung "verbleibt" der seinem Teil am Gesammtwiderstand gibt.

Transistoren in Spannungsteiler

Wenn wir nun einen Transistor anschauen, so haben wir einen elektrisch steuerbaren variablen Widerstand. Uns interessieren hier nur die spannungsgesteuerten MOS-FET (Metall Oxid Silizium Feld Effekt Transistor) Transistoren, da ausschliesslich solche in den meisten modernen (nach 1980) Chips drin sind. "Normale" pn-FET sind in Computern irrelevant, und bipolare Transistoren sind seit dem Untergang von TTL Chips irrelevant.

Wenn man den Transistor in einen Spannungsteiler einbaut, und auf gross schaltet, bekommt er den grossen Teil der Spannung ab, wenn man ihn auf wenig schaltet, bekommt er wenig ab. Damit hat man eine variable Spannung am im Spannungsteiler:

        Strom            Spannung
      ---->-----.
                |        voll            voll
               .-.
               | | W1
               | |
               `-'
      --.       |------- T2 = klein fast nichts, sonst fast voll
        |   |---'
        `--||      T2 (n Typ)
            |->-.
                |
                |        nichts          nichts
      ----------'
    

Noch stärker wird es, wenn man 2 Transistoren nimmt, von denen jeweils einer gross und der andere klein hat, und diese dann wechselt. Sowas nennt man Komplementär-Betrieb, mit MOS-FET Transistoren CMOS Technik (C wie complementary).

        Strom            Spannung
      ---->-----.
                |        voll            voll
            |-<-'
        .--||      T1 (p Typ)
        |   |---.
      --|       |------- T1 = gross + T2 = klein fast nichts, sonst fast voll
        |   |---'
        `--||      T2 (n Typ)
            |->-.
                |
                |        nichts          nichts
      ----------'
    

Vor allem ist dann die Stromstärke bei unterem Transistor = klein erheblich kleiner. Und somit der Chip kühler. Daher ist es üblich 2 Transistoren einzusetzen, von denen jeweils einer grossen und einer kleinen Widerstand hat.

Transistoren Spannungsteiler in Serie

Da Transistoren ihr Widerstand durch Spannung gesteuert wird können nun wiederum andere Spannungsteiler durch diese wechselnde Spannungen unseres Spannungsteilers gesteuert werden.

Da MOS-FETs ohne Ansteuerung "zu" sind, also grosser Widerstand, und grosse Spannung kleinen Widerstand macht, und somit der Spannungsteiler kleine Spannung abgibt, und umgekehrt, haben MOS-FET Transistor-Spannungsteiler automatisch eine Signal unkehrende Funktion.

Der einfachster Fall ist, sich selber ansteuern. Folge ist: wenig->viel..vorne, worauf dann viel->wenig..vorne ist, wieder w->v und v->w und so weiter. Damit versucht der Spannungsteiler dauernd sich selber umzuschalten, und ist unstabil. Das nennt man einen Oszillator. Der schwingt sehr schnell, so schnell der Transistor schalten kann. Für langsamer bremst man den mit einem Quarz in der Rückleitung.

Ein Kreis von 2 Spannungsteiler hat genau die gegenteilige Wirkung: wenig->viel->wenig..vorne, bzw viel->wenig->viel..vorne, und ist damit bockstabil, ausser er wird von aussen gestört. Das nennt man daher ein Flipflop (FF).

Digitale Elektronik

Nun haben wir einerseits Boole kennengelernt, anderseits Elektronik. Nun vermischen wir die beiden. Das macht man, indem man Bits als Spannungen representiert. Bei standard TTL Logik (inzwischen total veraltet) waren das 0..0.8V = 0, 2.4..5.0V = 1, bei LVTTL (das was man heute zwischen Chips oft hat) ist 5V auf 3.3V reduziert. Man merke, dass 2/3 Spannung = 2/3 Strom = 4/9 Leistung und Wärmeentwicklung ist. Unterhalb von 3.3V gibt es dann viele (mehr als 10) inkompatible Varianten, Spannung aufzuteilen.

Wenn wir nun unseren Transistor Spannungsteiler mit 0V und 5V anschliessen, so sehen wir, dass bei 0V..0.8V Eingang der Ausgang 4.5..5V ergibt, bei 2.4..5V Eingang aber so 0..0.5V, wegen der Signalumkehrung. Also ist unser Transistor Spannungsteiler automatisch ein NICHT, bei der Definition von Spannung = Bits!

Nimmt man nun mehrere untere Transistoren so müssen alle mit 2.4..5V gefüttert werden, damit sie alle leiten und der Ausgang auf 0..0.5V geht. Damit ergibt sich automatisch ein NICHT-UND, (NOT-AND, kurz NAND). Daher ist NICHT-UND basierte Elektronik weit verbreitet. Daher besteht auch der erste Chip der 74xx TTL Reihe, der 7400, aus 4 2-Eingang NICHT-UND Elementen.

        Strom            Spannung
      ---->-----.        voll = 5V
                |
               .-.
               | | W1
               | |
               `-'      
                |------- T2 = klein UND T3 = klein fast nichts, s fast voll
            |---'        UND + nichts = NICHT-UND
      -----||      T2
            |->-.
                |
            |---'
      -----||      T3
            |->-.
                |
                |
      ----------'        nichts = 0V
    

Dies kann man auch komplementär machen, mit je 2 Transistoren pro Eingang. Dabei werden die unteren "gefaltetet" verdrahtet (zwischen T2 und T4), und die oberen "neben einender" verdrahtet. Jeder Eingang steuert sein Paar. Man kann dies für beliebige Einganszahlen "verbreitern". Ein CMOS Chip besteht nun aus vielen Zeilen mit Serien solcher n*2 Transistoren grossen NICHT-UND Gatter, und pro Zeile 2 Stromleitungen.

                                    |
      ---->------------------------)|(------------ voll = 5V
                |              |    |         |
            |-<-'          |-<-'    |     |-<-'
        .--||      T1  .--||      T3| .--||      T1
        |   |---.      |   |---.    | |   |---.
        |       |-----)|(------+----' |       |--
        |       |      |              |       | hier dann nächstes Gatter
        |       | .---)|(------.      |       |
        |   |---' |    |   |---'      |   |---'
        |--||     |T2  |--||      T4  |--||      T2
        |   |->---'    |   |->-.      |   |-
        |              |       |      |
        |              |       |      |
      -)|(------------)|(------------)|(---------- nichts = 0V
        |              |              |
    

Bisher können wir nur NICHT-UND herstellen. Wie steht es mit dem Resten der logischen Funktionen?

Digital Rechnen

Und wenn wir schon Boole und Elektronik vermischen, dann können wir auch gleich binäres Rechnen mit einrühren. Und damit können wir dann endlich digital Rechnen.

Für ein Bit zu berechnen reicht 1 2-Eingang EXODER und 1 2-Eingang UND. Das Paar nennt man einen Halb-Addierer, weil er nur a+b, aber nicht +Übertrag rechnen kann.

Für einen Voll-Addierer, muss wegen Übertrag, auf 2 2-Eingang EXODER, 3 2-Eingang UND, sowie 1 3-Eingang ODER gegangen werden (wobei die UND + ODER durch NICHT-UND + NICHT-UND ersetzt werden, weil kleiner und schneller). Dies muss man nun für die Anzahl Bits wiederholen.

Dies kostet bereits ordentlich viele Transistoren. EXODER ist ja selber 2 NICHT, 2 NICHT_UND und ein weiterer NICHT-UND = 2*2+2*4+4 = 16 Transistoren. Das mal 2 und die weiteren 3*4+1*6 addieren = 2*16+12+6 = 50 per Bit! Für 8bit Rechner sind bereits 8*50 = 400 Transistoren verbraucht, bei 32bit sind es bereits 32*50 = 1600.

Und das ist immer noch ein ineffizienter Addierer. Es muss jeweils für jedes Bit sein Übertrag-aus zum nächsten Bit sein Übertrag-ein, das nennt man kaskadieren. Und das ist langsam, weil das Bit durch 2 NICHT-UND pro Bit muss. Man kann dies beschleunigen, aber das kostet weitere Transistoren.

Und dann sollte ja noch die Subtraktion rein. Die kann man zum Glück recht einfach rechnen, indem man a-b = a+(-b) nimmt. Also den bestehenden Addierer und einen Negierer beim zweiten Operand. Negative Zahlen sind ausserhalb diesem Vortrag, zum Zeit sparen, also keine Erklärung wie ein Negierer funktioniert, aber der Aufwand ist hauptsächlich ein weiteres EXODER pro Bit. Kostet also massiv weitere bits*16 Transistoren, also 1/3 mehr.

Multiplikation oder gar Division brauchen noch viel mehr Aufwand. Für 32bit*32bit muss man entweder langsam 32 separate Additionen hintereinander in der selbigen Hardware (kostet 32 Taktzyklen) machen, oder für jede Addition eine Wiederholung der obigen Hardware (kostet also 32*1600 = 51200 Transistoren). Wie man sieht wurde dies aus gutem Grund erst so um 1990 (486er) im PC zum Standard, alles aufs mal zu rechnen.

Man sieht auch langsam, wohin all die Millionen Transistoren gehen.

Sequenzielle Logik

Bisher können wir nur rechnen, aber mit was rechnen wir? Irgendwo müssen unsere Zahlen herkommen. Schon eine Kettenrechnung (z.B. die obige Multiplikation in 32 Schritten) verlangt nach Zwischenergebnissen, die hinten abgenommen werden und vorne wieder reingefüttert müssen. Also müssen wir Bits (zwischen)speichern können.

Sobald wir Bits speichern können, können wir unsere Logik über mehrere Schritte laufen lassen. Dabei entsteht eine Abfolge (= Sequenz) von Speicherzuständen. Daher nennt man dies sequenzielle Logik. Logok ohne Speicher nennt man kombinatorische Logik, weil dort nur eine Kombination der aktuellen Eingänge möglich ist.

Wie speichert man nun Bits? Bei unserem Flipflop aus einem doppelten Spannungsteiler haben wir gesehen, dass das ganze stabil wird: In Bits können wir das schreiben als: b = ~(a), c = ~(b), wobei a = c. Damit können wir einen beliebigen Zustand, 0 oder 1, anlegen und er wird festgehalten, bis wir ihn überschreiben.

Zum so ein FF umschalten, muss man ihm von aussen "stören", also ein Bit "verteilt" zuführen, je nach 0 oder 1 muss dafür der eine oder der andere NICHT "angeschubst" werden, daher wird das auch Reset/Set FF (RS-FF) genannt.

Ein RS-FF zu benutzen ist mühsam. Zum dies einfacher machen gibt es verschiedenste "Verteiler" Schaltungen, die ich wegen Kompliziertheit und Irrelevanz der Details hier zum Zeit sparen weglasse. Das Resultat davon ist jedenfalls das heute üblich benutzte Data FF (D-FF), das 2 Eingänge hat: d für Daten, und clk (= Clock, = Takt) zum es "anschubsen", als "speicher jetzt" Signal. Dieses Signal ist ähnlich wie das "auswahl" Signal beim Multiplexer, keine Daten sondern Steuerung. Dieses Takt Signal ist natürlich genau das, was man an MHz oder GHz in den Prozessor einspeisen muss.

Speicher Chips

Wenn man nun eine Zahl, die ja aus mehreren Bits besteht, speichern will, dann braucht man pro Bit ein D-FF. So eine Sammlung von D-FFs mit einem gemeinsamen clk Signal nennt man ein Register. In so ein Register passt eine Variable. Und damit haben wir ein Konzept das jeder Programmierer kennt. Rechnen tut man ja indem man varible = variable OP variable; macht, oder Varianten davon (Konstanten sind wie Variablen, aber aus dem Program zu lesen, statt berechnet).

Will man mehrere Variablen (braucht ja jedes normale Program) so muss man eine ganze Sammlung Register haben, inklusive einem grossen n:1 Multiplexer zum aus ihnen auswählen. Das ganze ergibt dann zusammen einen Speicher. Neben dem, dass der Speicher in Bitgruppen Daten speichert, braucht er auch eine weitere Bitgruppe von n Bits um den Multiplexer zu ansteuern. Diese Bitgruppe nennt man die Adresse.

Jede Variable entspricht einer Speicherstelle, Array Variablen einer Serie von gleich grossen Speicherstellen. Variablennamen sind nichts anders als Symbole für die Adressnummer der Speicherstelle dieser Variable.

Da Adressen durch Bits representiert sind, kann man Adressen auch im Speicher als Daten abspeichern. Es ist dann möglich eine Adresse aus einer Variablen zu holen, in ein Register zu laden, mit dessen Inhalt den Speicher erneut zu addressieren, und mit den Daten die dann ausgewählt sind zu arbeiten. Sowas nennt man indirekte Addressierung. Die dafür benutzten Variablen sind die allseits bekannten und oft nicht verstandenen Pointer.

Heute gibt es 2 Sorten Speicher Chips:

Weil Speicher mit einem x:1 Multiplexer ausgewählt wird, der mit n Bits an Auswahl Signalen angesteuert wird (also ein (2^n):1 Multiplexer), haben Speicher Chips auch immer 2^n Bits Grösse, und das ergibt die bekannte Grössenserie 2^10 = 1024 = 1k, 2^20 = 1048576 = 1M, 2^30 = 1073741824 = 1G.

Computer und Prozessor

Nun können wir rechnen, und Daten speichern und herumschieben. Konstanten können wir aus dem Program seinem Speicherplatz holen. Aber was ist den nun ein Program selber? Jeder weiss dass ein Program als Binary File auftritt, also aus Bits besteht, die in den Speicher geladen werden. Aber was sind das für Bits.

Wie wir schon wissen rechnet der Addierer als kombinatorische Logik dauernd mit egal was vorne ansteht, egal ob das Ergebnis benutzt wird. Anderseits wissen wir dass man Addition/Subtraktion muss wählen können. Ebenso muss man x verschiedene Multiplexer steuern. Und schliesslich Register und Speicher mit clock Signalen versorgen.

Für jede Operation braucht es dazu eine Sequenz von solchen Steuersignalen. Das Program sollte nun dann eine Sequenz von diesen Sequenzen aufrufen. Dazu besteht es aus einer Liste von Nummern, die Gruppen von Steuersignalen auslösen.

      Ein Stück C Code als Beispiel:

      int b=10, c=5;
      int proc(int a) {
        b = a+b*c-30;
        return (b*4-1); }
      /* .. */
      int d;
      d=proc(5);

      Übersetzen (compilieren) wir das zu Assembler:

      b:      *
      c:      *
      proc:   load b, r2       ; r2 = b (r1 hat erster Parameter, a)
              mult c, r2       ; r2 = b*c
              add r2, r1       ; r1 = a+b*c
              sub 30, r1       ; r1 = a+b*c-30
              store r1, b      ; b = r1
              mult 4, r1       ; r1 = b*4
              sub 1, r1        ; r1 = b*4-1
              return           ; r1 ist Rückgabewert
                               : ..
      d:      *
              const 5, r1
              call proc

      Übersetzen (Assemblieren) wir das weiter zu Binary (16bit):

      0000: 000A               ; b ist 0x000A = 10
      0002: 0005               ; c ist 0x0005 = 5
      0004: LDM2 0000          ; LDM2 ist eine Zahl, Prozessorabhängig
                               ; die Systematik dieser Zahlen ergibt den
                               ; Befehlssatz des jeweiligen Prozessors
                               ; 0000 ist die Adresse von b
      0008: MUM2 0002          ; 0002 ist die Adresse von c
      000C: AD21               ; Addition braucht keine Adresse, nur Register
      000E: SUC1 001E          ; Subtraktion mit Konstante 0x1E = 30
                               ; Unterschied Konst 0x1E und Speicher 001E
                               ; (= Zahl für RETN) ist SUC1 vs SUM1 Befehl
      0012: ST1M 0000          ; speichern nach b (0000)
      0016: MUC1 0004          ; Multiplikation mit Konstante
      001A: DEC1               ; Decrement = Subtraktion 1
      001C: RETN               ; und ab 013A weiter
      001E: ....               ; ..
      0156: 0000               ; d mit 0 vorbesetzt, da kein = etwas
      0158: ....
      0134: LDC1 0005          ; die 5 für den Call
      0138: CALL 0006          ; und ab 0006 weiter
      013A: ST1M 0156          ; hier Resultat in r1 nach d speichern
    

Um dies auszuführen wird nun so vorgegangen: Mit einem speziellen Register (Programzähler (PC)) das momentan 0x0134 drin hat, wird auf den Speicher zugegriffen. Es kommt "LDC1" zurück. Dem PC wird 2 addiert, damit er für das nächste Wort vom Program bereits it. Der Prozessor führt dann die für LDC1 einverdrahtete Funktion aus: mit PC nochmals Zugriff (und danach PC+=2) und die Daten in Register 1 verfrachten. Dann ist der Befehl fertig.

Also nächster holen (und PC+=2), was CALL ergibt. Der wird ausgefüht als: PC+2 nicht in PC laden, sondern wegspeichern für den RETN später. Dann das 0006 in den PC laden -> nächster Befehl ist der LMR2 in 0006.

So geht das weiter, Befehl für Befehl, solange Strom da ist. Immer Befehl holen und auswerten und ausführen. Wer mal ein beliebiges C Program in den Assembler seines PCs sehen will, sollte die -S Option vom gcc anschauen.

Integrierte Schaltungen

In der ersten Hälfte 1960ern wurden Computer noch so gebaut, aus einzelnen Transistoren. Man nahm die für ein oder 2 Gatter und setzte sie auf eine kleine Platine (Modul), hier PDP-6 und setzte viele Module zu einem Rechner zusammen, hier PDP-8/i. Das Resultat war gross (1/2 19" für ein 12bit Rechner mit 6kByte Speicher, 3 19" für 36bit Rechner ohne seine max 1MByte Speicher), langsam (so 1-10MHz und erst noch mehrere Takte pro Befehl) und teuer (so $30'000 (12bit) bis $3'000'000 (36bit, gut ausgebaut)).

Die Rechner wurden kleiner, schneller und billiger, dadurch das man in der zweiten Hälfte der 1960er integrierte Schaltungen einführte. Zuerst mit SSI (small scale intergration, bis 30 Transistoren) TTLs nur ein Modul pro Chip (7400 ist 4 Stück 2-Eingang NICHT-UND). Dann mit MSI (medium scale integration, bis 300 Transistoren) immer mehr (7474 ist 2 volle D-FF, 7483 ist ein 4Bit Voll-Addierer), und dann mit LSI (large scale integration, bis 3000 Transistoren) noch mehr (74181 ist 4bit Addier+Sub+anderes, 74374 ist ein 8bit Register) und schliesslich ganz viel (74481 ist ein 4bit Ausschnitt eines ganzen Rechner Datenpfades).

Solche 74(x)xx Chips sind relativ universell, und wurden daher bis ca 1990 auch in professionellen Schaltungen verbaut. Heute sind die wegen langsam und viel Platzverbrauch und teuer (die Kosten vieler kleine Chips addieren sich) fast nur noch in Hobby und unkritischen professionellen Schaltungen drin.

Je grösser man Chips macht, umso mehr trift nun aber ein Problem auf, dass sie zu spezialisiert werden, nur für eine Funktion brauchbar, also nicht für jedermann nutzbar. Kombiniert man die so resultierenden geringeren Stückzahlen mit den immer grösseren Entwicklungskosten grosser Chips und sie werden zu teuer. Das ist so ab 1000-10'000 Transistoren der Fall.

Grosse Firmen wie IBM oder Telephonzentralen Hersteller können sich diese Kosten erlauben, einen eigenen 1000-1'000'000 Transitor Chip nur für sie entwickeln zu lassen und die Kosten über wenige 1000 Kopien bezahlen zu lassen. Sowas nennt man ein ASIC (= Application Specific Integrated Circuit). Der Rest hat keine Chance da mitzumachen.

Eine Spezialform von ASIC, Gate Arrays (GA) nutzen die CMOS Chip Struktur aus und baut die ganzen Transistor Zeilen und Stromversorgung nur einmal, und dann nur die Verdrahtung Kundenspezifisch. Das verringert die Entwicklungskosten um den Faktor 10, aber unterhalb von 10'000 Stück wirds auch so teuer. Ein einzelnes solches GA im ZX81 ersetzte die gleichwertigen 18 74(x)xx Chips des ZX80 (heute gibt es ein ZX81 Nachbau in FPGA und CPLD. Ebenso haben ein paar ASICs in meinem 386er ersetzen die fast gleichwertigen vielen 74(x)xx Chips in meinem 286er ersetzt.

Mikroprozessoren

Der Mikroprozessor löste dieses Mengenproblem für viele Aufgaben. Ein spezieller kleiner Prozessor auf einem Chip. Alle Ein- und Ausgänge sind/waren normale 74(x)xx Chips, oder ebenfalls standardisierte Prozessor IO Chips. Die ganze userspezifische Logik steckt im Program, in einem ebenfalls standardisierten Speicherchip. Man muss nur Program in den Speicher einbrennen (wie bei CD-R), oder bei moderen Flash Chips nur draufschreiben, wie bei einer HD.

Der Speicher kann, da universell und für jedes Program geeignet, beliebig gross hergestellt werden. Speicher sind so kleiner und billiger als viele einzelne Chips. Wie wir heute wissen, entstanden aus Mikroprozessoren am Schluss PCs, also ganze richtige Computer.

Der Erste Mikroprozessor, der Intel 4004 bestand aus 2300 Transistoren, rechnete mit 4 Bit, hatte 16 4bit Register, und konnte 4096*4bit ROM und 64*4bit SRAM addressieren. Er wurde ab Nov 1971 verkauft.

Als logische Konsequenz der Mikroprozessoren gab es danach die Mikrocontroller. Diese haben auf einem Chip den Prozessor und etwas Speicher und etwas IO, also ein ganzer simpler Computer auf einem Chip. Das ist was man üblicherweise heute in Gerätschaften findet, z.B. in Audio oder Video Geraten, oder in Haushaltsgeräten, oder in Maschinen oder Autos.

Mikroprozessoren sind erfolgreich als Computer, aber in der ursprünglichen Anwendung, viele Chips zu ersetzen haben die ein grosses Problem: sie sind zwar kleiner und billiger, aber langsam. Verdrahtete Logik arbeitet je nach Ausgabe etwa 30-1000 mal schneller als Programme, weil bei Logik alles gleichzeitig passiert, während ein Program jede einzelne Funktion sequenziell abarbeiten muss. Das sieht man gut bei Emulatoren, wo ein x00MHz PC gerade einen 1MHz C64 emulieren kann. Das ist der Preis den man zahlt dafür, dass der Mikroprozessor ein Program Befehl für Befehl abarbeitet.

Für manche Anwendungen (z.B. einer Lichtsignal Anlage oder Waschmachine) ist das akzeptabel, weil die Geschwindigkeit von verdrahteter Logik eh viel schneller als nötig war. Für andere Anwendungen reicht es gar nicht. Eine dieser Anwendungen ist open source Hardware, die beliebige Funktionen machen kann.

Programmierbare ROMs als Logik Ersatz

Ein Chip der im Zusammenhang mit Mikroprozessoren aufkam waren ROM Chips. ROM ist eine Form von Speicherchips, deren Inhalt aber nicht veränderbar ist (aber dafür auch ohne Strom erhalten bleibt), also nur für Programcode und Konstanten brauchbar. Auch diese wurden ab 1000 Transistoren interessant, nicht nur für Mikroprozessoren, sondern auch für "normale" Computer. Also wurden auch hier beliebige Grössen verkaufbar.

Ein Problem der ersten ROMs war, dass ihr Inhalt bei der Herstellung eingebaut werden muss, bevor das Gehäuse hergestellt wird. Die Struktur ist ähnlich DRAM, aber mit einem Metalltröpfchen (oder eben nicht) statt dem Kondensator. Also musste man sein Program dem ROM Hersteller schicken, damit er die Tröpfchen richtig setzt/weglässt. Das war zeitaufwendig, vor allem bei Bugfixes. Und teuer war der Prozess auch, wenn auch deutlich weniger langwierig und teuer als ein Gate Array oder gar ein ASIC fertigen lassen. Das kann man heute mit dem pressen von CDs vergleichen.

Dieses Problem wurde gelöst durch die Erfindung von PROM Chips (P = programmierbar), die erst beim Chip-Käufer programmiert werden. Die haben statt Metalltröpfchen kleine Nickelbückchen, welches an unerwünschten Orten vom Benutzer "weggebrannt" werden können. Die PROMs stehen zu ROMs im selbigen Verhältnis, wie CD-Rs zu gepressten CDs.

Später entstanden auch noch die mit UV-Licht löschbaren EPROMs (E = erasable) was übrigens eines der ersten Chips der Firma Intel waren. Die heutigen elektrisch löschbaren Flash Chips sind deren Nachfolger (mit Intel als grösster Hersteller), quasi die CD-RWs der Chips.

SRAM und DRAM ist im Vergleich zu all denen die HD der Chips, beliebig jederzeit beschreibbar, die allerdings ohne Strom alles vergessen.

ROMs/PROMs/EPROMs/Flash machen eigentlich nur eines: für jede Adresse (= Eingangs Kombination von a Bits) am Ausgang d Datenbits von sich geben. Damit kann man die auch als kombinatorische Logik Chips missbrauchen. Dazu lädt man in den Speicherchip die gewünschte Logik als Tabelle. So wurde z.B. ein PROM im Disk Controler vom Apple II benutzt.

Als kleines Beispiel würde unser Multiplexer von vorher eine Tabelle von 2^3x1 Bit benötigen:

      Adresse     Daten            Wir verdrahten:
      ...3210  76543210              A0 = e1
                                     A1 = e2
      ...a000:    ...d0              A2 = auswahl
      ...a001:    ...d1              A3... = andere Eingäange
      ...a010:    ...d0              a heisst Adressteil invariant, also
      ...a011:    ...d1                wiederholen für alle möglichen
      ...a100:    ...d0                Positionen
      ...a101:    ...d0              D0 = ausgabe
      ...a110:    ...d1              d heisst Daten irrelevant für hier
      ...a111:    ...d1                von anderen Gattern mit A3... benutzt
    

Als grösseres Beispiel würde ein 4bit Addierer, 9 Eingänge (4 a Bits, 4 b Bits, 1 Übertrag) und 5 Ausgänge (4 Ergebnis Bits, 1 übertrag) haben. Also braucht das ein 2^9x5bit = 512x5bit an PROM Platz. Und hier eine 512 Zeilige Tabelle, wesshalb ich das weglass. Das würde man minimal in einem 512x8 = 4kBit Chip implementieren. Wenn man aber z.B. einen 64kBit = 8192x8bit Chip hat, könnte man noch eine weitere 13-9 = 4 Eingang und 8-5 = 3 Ausgang Funktion mit einbauen. Oder man nimmt einen weiteren Eingang und verwendet diesen um zwischen Addition und Subtraktion Tabellen zu wählen.

Programmierbare Logik Arrays

Speicherchips als Logik zu missbrauchen hat ein Problem: sie sind darauf optimiert, möglichst viel Speicher mit möglichst wenig Adressen zu auswählen. Wenn wir nun für Logik möglichst viele Eingänge (also Adressen) wollen gibt das einen riesigen Chip. Z.B. ein 8bit Addierer+Subtrahierer sind bereits 2*8+1+1=18bit, ergibt 2^18 = 262144 = 256k mal die nun 9 Ausgangs Bits. Das wären bei den Standardgrössen von PROMs 2 Stück 512kx8 = 4Mbit Chips, also vor 1990 nicht implementierbar.

PROMs brauchen so viel Platz, weil sie voll dekodiert sind. Das heisst für jede Kombination der Eingänge hat es eine Zeile im Chip drin. Das gibt für n Adressleitungen, 2^n Adressen und somit 2^n Zeilen. Dies wird so verdrahtet:

      (P)ROM Ausschnitt, 3 Adressleitungen, 2^3=8 Adressen, 4bit breit

        I = Inverter, ergeben ~A0, ~A1, ~A2, ...
        o = verbunden, - = keine Verbindung
        U = UND um Adresse zu dekodieren, pro Adresse eine Zeile

      ... A2    A1    A0
          |__   |__   |__ 
          |  |  |  |  |  | 
          |  I  |  I  |  I                              Produktterm:
          |  |  |  |  |  |               |  |  |  |
      ..-----o-----o-----o--U - 0 ---..--x--x--x--x-    alle ~An UND = 0
          |  |  |  |  |  |               |  |  |  |
      ..-----o-----o--o-----U - 1 ---..--x--x--x--x-    nur A0 rest ~ UND = 1
          |  |  |  |  |  |               |  |  |  |
      ..-----o--o--------o--U - 2 ---..--x--x--x--x-    nur A1 rest ~ UND = 2
          |  |  |  |  |  |               |  |  |  |
      ..-----o--o-----o-----U - 3 ---..--x--x--x--x-    A1 UND A0 rest ~ = 3
          |  |  |  |  |  |               |  |  |  |
      ..--o--------o-----o--U - 4 ---..--x--x--x--x-    nur A2 rest ~ UND = 4
          |  |  |  |  |  |               |  |  |  |
      ..--o--------o--o-----U - 5 ---..--x--x--x--x-
          |  |  |  |  |  |               |  |  |  |
      ..--o-----o--------o--U - 6 ---..--x--x--x--x-
          |  |  |  |  |  |               |  |  |  |
      ..--o-----o-----o-----U - 7 ---..--x--x--x--x-    x = Datenbits
          |  |  |  |  |  |               |  |  |  |       = o oder -
          :  :  :  :  :  :               :  :  :  :
                                         |  |  |  |
                                         O  O  O  O     ODER der Bits

                                         |  |  |  |
                                     ..  D3 D2 D1 D0    Daten Ausgang
    

Die "I" sind Inverter, die "o" sind Verbindungen (- = keine Verbindung) die beim Herstellen eingebaut werden, die zusammen mit den "U" UND Elementen sorgen dafür, dass bei jeder möglichen Adresse ein Produktterm aktiv wird. Die x sind die Daten. Bei ROMs sind das die Metalltröpfchen. Bei PROMs die Metallbrückchen. Bei DRAMs die Transitor+Kondensatoren. Bei SRAMs die 6er Gruppen Transistoren. Beides wirken mit dem "O" ODER Elementen unten zusammen um die Daten zu sammeln. Das (P)ROM ist also eine NICHT+UND+ODER Logik (implementiert als NICHT+NICHT-UND+NICHT-UND).

Das ist so für Speicher sinnvoll, aber für Logik massive Platz Verschwendung. Das exponentiale Wachstum an Zeilen killt diese Lösung oberhalb von etwa 10..12 Eingängen.

Die Antwort darauf war ein Chip mit ebenfalls brennbarer Definiton der Zeilen, also auch "x" links. Das gibt ein PLA. So eins hat nur etwa 4*Ausgänge an Zeilen statt 2^Eingänge. Z.B wurde so ein im C64 ein PLA als Adress-Manager verwendet um die Speicher Chips auszuwählen. Mit 16 Eingängen und 8 Ausgängen wäre dazu ein 64kx8 = 512kbit PROM nötig gewesen. So ist es aber nur ein kleiner 2*16x4*8+8x4*8 = 1280 Bits PLA. Das zu einer Zeit als es bei ROMs noch mit 64kbit Schluss war.

Programmierbare Array Logik

PLAs haben ein kleineres Problem: die haben 2 programmierbare Sektionen und sind daher langsamer als PROMs. Dies wurde von der Firma MMI gelöst, indem man nur die Produkte links brennbar macht, und dann rechts fix 8 Zeilen jedem Ausgang zuordnet, also gewöhnliche hartverdrahtete 8-Eingang ODER verwendet. Das ergab dann die PALs. Z.B. ist ein PAL14L4 ein 20pin (2 Strom, 18 Daten) Chip, mit 14 Eingangspins, 14 Spalten, 32 Zeilen, und 4 Ausgangspins. Ganze 2*14*8*4 = 896 Bits.

Aber die PAL Entwickler gingen noch weiter und lieferten auch PALs mit Flipflops bereits drin, damit man nicht mit Ausgang-pin + externes FF + Eingangs-pin Pins verschwenden muss. Das waren die "R" Serie Register PALs. Z.B. ist ein PAL16R6 ein 20pin (2 Strom, 18 Daten) Chip, mit 8 Eingangspins, 16 Spalten (8 von Pins, 8 von Ausgängen), 64 Zeilen, 8 8-Eingang ODER, 6 FFs (andere 2 ohne FF), 8 Ausgangspins. Die restlichen 2 Pins sind einer clk für die FFs und einer um die FF Ausgänge abzuschalten. Alle PAL16Rx haben 2*16*8*8 = 2048 Bits.

Aber die "R" PALs waren recht inflexibel. Sie haben genau 2/4/6/8 FFs, fest verdrahtet. Die Firma AMD hat dann den flexiblen 22V10 erfunden, ist ein 24pin (2 Strom, 22 Daten), mit 12 Eingangspins, 22 Spalten, abgestuft 2*8+2*10..+2*16 = 120 Zeilen, 10 ODER, 10 wählbar aktiven FFs, sowie auch wählbar Ein-/Ausgänge, sogar in Betrieb schaltbar. Der 22V10 hat 5280 Bits. Diesen konfigurierbaren Ausgangs Mechanismus nennt man Makrozelle.

Später hat die Firma Lattice die kleineren 16V8 und 20V8 produziert. Ziel war, so klein wie 16R8 bzw 20R8, kann alle L und R Typen emulieren, und doch so viel wie möglich der 22V10 Features. Diese waren ein Riesenerfolg. Seit 15 Jahren werden daher nur noch "V" PALs eingesetzt.

Es gibt heute PALs bis zu 68pin mit 48 Ausgängen (Altera EP1810). Ebenso bis es solche mit bis zu 40pin mit und 24 externen Ausgängen aber 72 internen, weil 48 nur zum weiter intern verwerten (Atmel ATV2500).

Die heutigen PALCE Chipfamilien sind ausserdem noch löschbar, wie EPROMs. Sie sind auch durch Fehlprogrammierung nicht zerstörbar. Und sie sind heute immer noch von mehreren Firmen erhältlich, und daher billig. Also ideal für Elektronik-Einsteiger ihre Programmierversuche.

Leider fehlt ein standard Board Design, dass jeder dann mit PAL Designs füttern kann. Also muss man hier selber etwas löten oder wrappen oder stecken. Und da PALs sehr klein sind, ist es unwahrscheinlich, dass jemand ein universelles Board macht.

Komplexe Programmierbare Logik

PALs haben noch ein grosses Problem: sie wachsen langsam. Jeder Ausgang braucht 2*i*8 Bits., was für o Ausgänge 2*i*n*o Bits braucht. Das wächst bei R und V PALs, wo i grösser als sein muss als o, mit dem Quadrat der Ausgänge, also O(2) für die Informatiker. Trotz Moore-schem Gesetz (Chips werden alle 3 Jahre 4 mal so gross = exponential) ergibt dieses nur lineare Grössenzunahme. Doppelte Ausgänge alle 3 Jahre.

Aber für komplexe Schaltungen, mit viel Innenleben in einem Chip, muss man alle die internen (nicht nach aussen verdrahteten) Makrozellen auch als "Ausgänge" (und folglich auch Eingänge) des Arrays zählen. Wenn man eine quadratisch anwachsende Anzahl MZs haben will, wie bei ASICs, dann muss man die Bits auf O(1) reduzieren können.

Als einen Ausweg hat man die CPLDs erfunden. Diese haben b Blöcken mit je einer konstanten Arraygrösse, üblicherweise mit i=16..64, o=8..32 n=4..8, z.B. häufig ein 2*48*5*16 = 7680bit, pro Block. Diese gibt es nun dann je nach Hersteller in b=2/4/6/8/12/16/24/32 Grössenabstufung. Das gibt also für b*16 MZs bei unseren 7680 Bit = (2..32)*7680 = 15360..245760, ist also O(1).

Um die b Blöcke zu verbinden braucht es nun aber eine "Vermittler" Verdrahtung zwischen ihnen. Die hat selber b^2*i*2*o (in unserem Beispiel b^2*48*2*16 = b^2*1536bits = (2..32)^2*1536 = 6144..1572864) mögliche Positionen und ist daher wieder O(2). Aber von denen sind, da nicht jeder Ausgang mit jedem beliebigen Eingang verbindbar sein muss, nur ein Teil besetzt (sparse matrix). Das ergibt bei geschätztem 10% besetzt: (2..32)^2+1536*0.1 = 614..157286 Bits, also noch unter den 15360..245760 von oben.

Bis auf ca 512 MZs hinauf ist dieser Vermittler, der O(2) wächst, also noch kleiner als die O(1) wachsende Summe aller Blöcke. Darüber würden die CPLDs wieder nur linear wachsen. Weshalb über 512 MZs hinaus bei CPLDs Schluss ist. Bereits 384 FFs und 512 MZs sind sehr viel teuerer als 256 MZs, was mir nahe legt, dass die vielleicht mehr als 10% besetzt sind.

Neben den b*2/4/6/8/12/16/24/32 gibt es auch noch ein paar spezielle Grossformen, wie "Blöcken von Blöcken" mit 6*(8*20)=960 MZs (Philips/Xilinx XPLA2, gestorben), oder "Array von Blöcken" mit bis zu (7x5)*16=560 MZs (Altera MAX9000), oder gar ganz neu (2002) "Array von Blöcken" mit bis zu (7x6)*(8*16)=5376 MZs (Cypress Delta39K).

Field Programmierbare Logik Arrays

CPLDs sind immer noch sehr limitiert in ihrer Grösse. Selbst 512 MZs ist erst ca die Grösse eines Mikroprozessors um 1980 (8086/8088 hat 8+1+4+1=13 Register * 16bit = 224). Mehr Platz bieten die FPGA Chips.

Während PLA/PAL/CPLD aus PROMs mit verringerter Grösse bei vielen Eingängen entstanden sind, gingen die FPGA Erfinder einen ganz anderen Weg. Sie verwenden analog zu einzelnen TTL Gattern pro FF separate kleine PROMs, üblicherweise mit nur 4 Eingängen und 1 Ausgang, also ganze 16bit gross. Damit kann man je ein beliebiges Gatter mit max 4 Eingängen machen, z.B. ein 4-Eingang NICHT-UND, oder aber einen EXODER, oder aber einen ganzen Multiplexer. Ein 1bit Addierer braucht wegen 2 Ausgängen 2 solche mini-PROMs.

Unser kleines Beispiel mit dem Multiplexer von vorhin sieht dann so aus:

      Adresse         Daten    Wir verdrahten:
      3210                     A0 = e1, A1 = e2, A2 = auswahl, A3 = leer
      0000 = 0:         0
      0001 = 1:         1      Tabelle = 01010011 01010011
      0010 = 2:         0
      0011 = 3:         1
      0100 = 4:         0                          0      3
      0101 = 5:         0                           \    /
      0110 = 6:         1      Graphisch 4x4 Format: 0101
      0111 = 7:         1                            0011
      (wiederholung)                                 0101
      1000 = 8:         0                            0011
      1001 = 9:         1                           /    \
      1010 = 10 = A:    0                          C      F
      1011 = 11 = B:    1
      1100 = 12 = C:    0                 Das "rotierte R" Muster ist
      1101 = 13 = D:    0                   typisch für Multiplexer
      1110 = 14 = E:    1
      1111 = 15 = F:    1
    

Dafür das die PROMs klein sind, hat ein FPGA sehr viele davon (100e bis 10'000e), in einem Array von Zellen angeordnet:

        IOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIO
      IO[][][][][][][][][][][][][][][][]IO
      IO[][][][][][][][][][][][][][][][]IO
      IO[][][][][][][][][][][][][][][][]IO
      IO[][][][][][][][][][][][][][][][]IO    Struktur eines FPGA:
      IO[][][][][][][][][][][][][][][][]IO
      IO[][][][][][][][][][][][][][][][]IO    [] = ein Element von 16bit PROM
      IO[][][][][][][][][][][][][][][][]IO
      IO[][][][][][][][][][][][][][][][]IO    IO = eine Schaltung zum
      IO[][][][][][][][][][][][][][][][]IO         Daten in und aus dem
      IO[][][][][][][][][][][][][][][][]IO         Chip zu befördern
      IO[][][][][][][][][][][][][][][][]IO
      IO[][][][][][][][][][][][][][][][]IO
      IO[][][][][][][][][][][][][][][][]IO
      IO[][][][][][][][][][][][][][][][]IO
      IO[][][][][][][][][][][][][][][][]IO
      IO[][][][][][][][][][][][][][][][]IO
        IOIOIOIOIOIOIOIOIOIOIOIOIOIOIOIO
    
Auf einem Chip Photo (vermutlich XCV2000E, da 80 Zeilen von Blöcken) seht es so aus: Rand die weissen Teile die IO Schaltungen, die blau bis grünen Teile sind die [] Logik, die dunkelgrünen Teile sind zusätzliche Verdrahtung.

Wenn wir nun für 32bit 32 Stück Multiplexer benötigen, so nehmen wir einfach einen 32 Elemente langen Ausschnitt aus einer Spalte von Elementen. Für das nächste Schaltungsteil gibs weitere 32 Elemente der nächsten Spalte. So passt jede Schaltung in genug Elementen.

Ein Beispiel für FPGA Gebrauch ist mein PDP-10 Clone Projekt. Es hat auf der Projekt Website eine genauere Beschreibung (in Englisch). Man sieht auf dem Bild deutlich die Zellenstruktur, das 64x96 Zellen grosse Array eines XCV300. Man sieht auch die vertikalen Streifen von für alle Bits wiederholten Funktionen. Und man sieht viele Streifen mit dem "rotierten R" Muster, weil Multipleyer so weit verbreitet sind. Überhaupt ist der streifige Block der Teil der die Daten verarbeitet. Die "Anhängsel" oben sind die Steuerung, die so Sachen wie Multiplexer Auswahl oder D-FF Speichern Signale generiert.

Damit die mit vielen kleien Zellen noch grössere Menge an Verdrahtung nicht überhand nimmt, wurde hier eine Art "programmierbare Platine" erfunden. Da kann eine Zelle direkt nur mit seinen Nachbaren verbunden werden. Für weitere Verbindung gibt es "Verlängerungsteile" die bis zum Ziel "zusammengesteckt" werden können. Beispiel ist die Xilinx Virtex Verdrahtung, die ich mal aufskizziert habe. Damit kann man die Bitzahl des ganzen Chips O(1) halten, je nach Chip Familie etwa 128..256 Bit pro FF, hier mit 864 bits pro 4er Block an FFs, bzw 216 bits pro FF.

Damit können FPGAs mit der vollen "alle 3 Jahre mal 4 Grösse" wachsen, ohne irgendeine Limite nach oben. Die Hersteller liefern ganze Grössen-Serien. Z.B. die Xilinx Virtex Familie gibt es von 32x48 = 1536 FFs bis 128x192 = 24576 FFs. Mit der voll kompatiblen Spartan-II Serie wurde das auf 16x24 = 384 FFs hinunter erweitert. Mit den etwas ausgebautem Virtex-E auf 208x316 = 65728 FFs hinauf erweitert. Die Chips haben alle 216 Bit/FF, die sind also von 82944 bis 14197248 Bits gross.

Ich compiliere momentan für einen XCV300, der 64x96 = 6144 FFs hat, also 1327104 Bits gross ist. Ein CPLD von 6144 MZs hätte bei 10% 384*7680+384^2*1536*0.1 = 2937600+22649241 = 25586841 Bits. Ein PAL mit 6144 FFs hätte gar 2*5144*8*5144 = 603979776 Bits.

Eine Kuriosität haben FPGAs noch: Im Gegensatz zu PLA/PAL/CPLDs die PROM oder EPROM oder Flash Technologie verwenden, ist in den FPGAs SRAM Speicher üblich. Das hat einen Vorteil, dass man sie beliebig oft neu laden kann (ideal zum ein Board abwechslungsweise mit mehreren Designs zu fahren), oder sogar in Betrieb abändern kann. Auch für Debugging (schnell mal ein "printf" eines Signals auf einen Pin) oder für regelmässige Testläufe (was für Effekt hat Änderung) ist dies von Vorteil, da es Hacker/experimentale Programmiermethoden statt Ingenieur/durchgeplant erlaubt. Aber dafür müssen die FPGAs bei jedem Gebrauch "gebootet" werden, wie ein Rechner. Dafür muss man einen separaten PROM/EPROM/Flash Chip (Bootmedium) haben, und einen Bootmechanismus. Die Hersteller bieten spezielle autonome Chips mit dem Mechanismus drin, aber die sind schweineteuer. Üblich ist daher auch ein normaler PC BIOS Flash Chip mit einem Mikrocontroller. Bei FPGA basierten PCI Karten wird oft einfach der FPGA vom Treiber her geladen.

Es gibt seltene Hersteller von FPGAs mit einem PROM-artigen System namens "antifuse", bei dem Leiterbahnen im Chip "zusammengeschweisst" werden, etwas wie Thermit-Schweissen bei Eisenbahnschienen. Aber deren Herstellung macht Probleme, und hinkt in der Chipgrösse den SRAMs hinterher, was kleine und langame Chips ergibt. Ausserdem gibt es vom "Verschweissen" her Verschmutzung des Chips (noch mehr als bei Metallbrückchen durchbrennen), was der Technologie weitere Limiten setzt. Und experimentell kann man mit denen auch nicht arbeiten.

Compilieren von Konfigurationsbits

Es ist zwar schön wenn ein Chip existiert, aber nützlich wir er erst wenn man ihn auch benutzen kann. Dazu muss man die Chips programmieren. Dies wird konfigurieren genannt, da ja nicht ein Program wie bei einem Mikroprozessor abläuft, sondern vielmehr die Verknüpfung von Signalen definiert wird. Die Konfiguration besteht aus einem Haufen Bits, die dann in den Chip müssen, diese nennt man Bitstream, weil sie üblicherweise seriell eingeschrieben werden, zum Pins sparen. Um die Bits zu machen wird wie bei Prozessoren ein Compiler benötigt. Und da fangen die Probleme an: Die Chip Hersteller liefern dazu zwar Compiler/Tools. Diese haben aber 2 grössere Probleme.

Einerseits stammen die Methodologien, auf die diese Tools aufbauen, aus der ASIC Produktion, wo ein "Compiledurchlauf" Edit->Chip mehrere Wochen(!) dauert. Also ist da nichts mit "schnell mal Test" (und desshalb wird das auch nicht unterstützt). Es ergibt sich dadurch ein sehr rigoros geplanter Arbeitsstil mit Modellierung und Simulation, der vorgegeben (und erzwungen) wird, der mit dem typischen Hacker inkompatibel ist.

Anderseits sind alle diese Tools closed source, und zumeist nur für Windows NT und Solaris/Sparc, selten mal noch AIX oder HP-UX. Und die offiziellen Compiler sind Kommerzsoftware vom schlechtesten: bloatig, buggy, und mit einer "Der User muss nichts wissen" (und darf nichts wissen) Einstellung geschrieben. Und ausser den kostenlosen Abgaben für ausgewählte kleine Chips (für Schulungszwecke) sind die Tools schweineteuer, so $3000-30'000.

Kritiken an dieser Politik werden grundsätzlich abgewiesen: "Wir machen Tools, unsere User wollen die Chips verwenden, nicht selber nochmals Tools machen. Und wir erachten es als Behinderung unserer Möglichkeiten der Innovation, wenn User unsere Interna kennen und ihre Designs davon abhängen." Und für nicht-Hersteller Tools erachten sie Information unter NDA an kommerzielle closed source Compiler Hersteller als ausreichend. Die ganze Idee von open source und aus User-Zusammenarbeit entstehenden Tools, und warum die besser sind, ist ihnen völlig fremd, trotz ihnen X mal erklärt worden.

Man stelle sich vor, Intel und alle anderen Prozessor Hersteller hätten nach den 8bit Generation unisono erklärt: "Ihr braucht die Bits nicht mehr zu kennen, das behindert uns nur in neue Prozessoren entwickeln, wir liefern alles was ihr braucht: einen Fortran, Cobol und sogar Algol Compiler". PCs wären nie das was sie heute sind. Die Logik Chip Hersteller haben nach den PALs diese Strategie durchgezogen.

Die PALs sind, bis zum 22V10 hinauf vollständig dokumentiert. Darüber noch vereinzelt (Atmel ATV750 und ATV2500, nicht aber die Altera EP610/EP910/EP1810). Also kann man für die auch open source Compiler machen. Aber weder Freshmeat noch die Debian apt-get Liste zeigt einen PAL Assembler oder Compiler. Aber über Google fand ich zwei, die aber nur die 16V8 und 20V8 Teile können: galprog - GAL Programmer Software for Linux und Galassembler für 16V8 und 20V8 Gals.

Für CPLDs ist das Hauptproblem, dass die Hersteller deren Programmierung nichts dokumentieren. Also ist nix mit open source Compiler für diese schreiben. [Nachtrag: Das zumindest war mein Kenntnisstand bis Dienstag den 20. Mai. Einer meiner Testleser von diesem Vortrag hat darauf hingewiesen, dass die Lattice isp1000 Chips (32-96 IOs, 64-192 MZ) die er benutzt teilweise dokumentiert sind. Das scheint zu reichen um sie zu reverse engineeren, soweit ich mal schnell die Doku überflogen hab.]

Bei den FPGAs wär die Situation auch ebenso verloren, wenn nicht ein Hersteller eine Ausnahme gemacht hätte, die bis heute nachwirkt. 1990 hatte eine kleine Firma namens Algotronix einen primitiven FPGA namens CAL1024 gemacht und den voll dokumentiert. Die Firma wurde ca 1995 von Xilinx aufgekauft und deren fast fertiger CAL2 Chip kam als XC6216 auf dem Markt, ebenfalls mit voller Doku. Dieser wurde schnell der Liebling der ganzen Akademiker, weil sie dafür Compiler designen konnten, und damit mit eigenen Sprachen experimentieren konnten, statt den von den Herstellern vorgegebenen benutzen, z.B. an der ETH Informatik.

Leider war der XC6216 ausserhalb der Akademiker erfolglos, weil langsam. Also hat Xilinx ihn eingestampft. Das gab eine Menge unzufriedener Akademiker, was Firmen nicht mögen (weil schlechte Publicity). Also hat Xilinx ihren damals neuesten Virtex Chip teilweise dokumentiert, woher ich auch weiss, dass es pro 4-er Gruppe von FFs 48x18 = 864bit braucht. Ebenfalls haben die eine (closed source) Java Library namens JBits herausgegeben, mit der man den Chip relativ direct ansprechen kann, also in Java eigenen Compiler schreiben kann. Diese läuft wenigstens auf dem Linux JDK, und ist damit das, was ich momentan benutze. Noch besser aber, diese Doku und JBits sind zusammen gut genug, dass man definitiv damit den Chip Reverse-Engineeren kann! Aus dem Grund hab ich auch mein VirtexTools Projekt gestartet, das eines Tages die Chips offen benutzen lassen soll.

Chip Drucker

Zur Vollständigkeit noch etwas, das in der Zukunft liegt: Drucker, mit denen man direkt Chips ausdrucken kann. Diese haben einige Vorteile, z.B. das man nicht den fertigen Chip eines Herstellers Konfiguriert, sondern seinen eigenen Chip herstellt. Also hat man keine Probleme, wenn ein Hersteller den Chip nicht mehr macht (dafür aber wenn der "Drucker" Hersteller keine "Patronen" liefert). So ein "Chip Drucker" hat zu einer Chip Fabrik das selbige Verhältnis eines heutigen Druckers zu einer Druckerei.

Ein System das momentan in Entwicklung ist funktioniert mit Plastik. Basis ist statt Silizium etwas wie eine Projektorfolie. Darauf wird wie mit einem Tintenstrahl Drucker die Transistoren und Leiter aus einem organischen Material aufgetragen. An den dafür benötigten Chemikalien wird u.a. an der ETH Zürich geforscht. Meine Daten hab ich vom Professor Batlogg dort, der dies erforscht. Probleme hat das System aber: Platz geht recht gut (momentan so 100'000 Transistoren und wird sicher steigen), aber die Chips sind langsam, weil Kohlenstoff seine Elektronen gerne für sich behält (momentan so 1/3MHz, und Steigerung unsicher). Die Langsamkeit kann man aber umgehen (Nervenzellen haben ganze 300Hz, also 100 mal langsamer), was aber Fläche kostet. Aber viel schlimmer ist aber, dass die "Drucker" Hersteller vermutlich die Ansteuerung nicht frei geben werden, wenn man deren Verhalten bei WinPrintern anschaut. Also nur closed source Treiber, oder sogar Compiler.

Ein anderes System wäre ein Gate Array, das man mit Laser belichten kann, um die Verdrahtung zu machen. Würde die Geschwindigkeits Probleme lösen (weil Silizium), aber auch die selbigen Ansteuer und Rohmaterial Probleme haben. Und es gibt meines Wissens nach keine Firma die sowas herstellt oder vor hat.

Besser wär ein System, das einen rohen Chip nimmt, und diesen per Ionenimplantierung "beschreibt". Rohe Chips (oder zumindest Wafer) sind bei mehreren Herstellern zu haben. Und die Hersteller dieser Geräte, die vor allem an wissenschaftliche Laboratorien gehen, dokumentieren wie man den Strahl beliebig steuern kann. Deren Erweiterung auf Hardware herstellen könnte daher offen selber hergestellt werden. Problem hier ist, dass ein gebrauchter Ionenimplantator momentan bei $100'000 anfängt.

Der Wunsch open source Hardware Designs zu haben, die jeder User einfach herunterladen, "ausdrucken", modifizieren kann, mit nur open source Software, benötigt offene Chip Herstellungs Architekturen, und da steht es momentan schlecht drum aus. Momentan sind die Virtex FPGAs das was wir am besten in den Griff bekommen können.

Ausserhalb von Chips

Auch ausserhalb von Chips ist open source Hardware übrigens ein Thema. Die "Download und Druck" Ideologie, ganz wörtlich Drucken, kann man mit einem ganz gewöhnlichen Postscript Drucker und normaler Projektorfolie für Laserdrucker machen.

Bereits vor über 10 Jahren brauchte ich dringend einen in "Points" (1/72 Zoll) geeichten Massstab, um eine Graphik auszumessen, um sie in Postscript zu implementieren. Der Geistesblitz traf ein: mit dem Postscript Drucker selber einen Points Massstab ausdrucken. Mit der selbigen Methode könnte man beliebige Masstäbe, Kurvenlineale, etc herstellen. Aber auch Sternsuchkarten sind nur ein paar Folien mit einer Niete zusammengeheftet. Oder gar architektonische Modelle, wie die Bastelbögen früher in der Schule, einfach festes Papier in den Drucker, und raus damit.

Und die Limite ist nicht bei Projektorfolie oder festes Papier bearbeiten. An der ETH Architektur Abteilung wird gross mit Laser Blechschneide Maschinen gearbeitet. Einfach CAD Zeichung exportieren und "Drucken". Unter anderem der dort eingesetzte Kleiderbügel Halter, oder deren CD Racks, oder die Rechner Boxen, aber auch Modelle. Wobei da wieder die Probleme mit unbekannter closed source Ansteuerung auftreten. Und Zugriff auf solche Maschinen mit standard Datenformat hat nicht jeder.

Im Maschinenbau gibt es seit mindestens 20 Jahren Bearbeitungscenter, wo man beliebige Formen Fräsen/Bohren/Drehen lassen kann. Deren Ansteuerung ist sogar offen, wenn auch nicht standardisiert. Problem ist da wieder die massiven Kosten der Maschinen (>$100'000), und daher geringe Verfügbarkeit für open source Hardware User.

Sogar Kunststoffteile oder gar Guss kann man heute per Computer formen. In der ETH Architektur waren 2 Actua Solid Modeller in Betrieb, bis der Hersteller die nicht mehr wartete und Rohmaterial lieferte. Zumindest ein detailliertes Modell der Millenium Falcon mit etwa 30cm Durchmesser wurde einfach so zum Scherz hergestellt. Von sich aus ist das Teil ein 3D Tintenspritzer, dessen "Tinte" ein Kunststoff ist, der sich verfestigt zum Werkteil. Man kann den Kuststoff aber nachher auch als "verlorene Form" für Präzisionsguss Techniken verwenden. Ein anderes Verfahren verfestigt eine Flüssigkeit per Laserbelichtung.

Das Gebiet das man da beobachten sollte nennt sich offiziell "Prototypenbau". Das ist der Begriff fur industrielle Herstellung einzelner Teile während der Entwurfsphase für spätere Massenfabrikation. Unser Interesse wär die Prototypen gleich als Endprodukt zu benutzen.

Science Fiction

Was wär ein Vortrag über Technologie und Zukunft ohne etwas Science Fiction zum Abschluss? Unvollständig. Also hier ein paar Spekulationen in die Zukunft hinein.

Das erste was einem zu Hardware, die nach Bits automatisch hergestellt wird, einfällt ist logischerweise Star Trek, genauer deren Replikatoren. Die dürften der Traum jedes Hardwarebastlers sein. Es wundert nicht, dass einer der beiden Actua Solid Modeller and der ETH prompt den Hostnamen replikator.ethz.ch bekam. Das war übrigens auch der, der später die Millenium Falcon "ausdruckte" (ja, das ist Star Wars).

Wenn man die offiziellen ST Handbücher anschaut, die Paramount rausgibt (ja, ein Kollege hat die), wird es aber schnell klar, dass ein Gerät der Bauart nie wird brauchbar sein. Das Gerät soll nämlich mit Atomsynthese (aus roher Energie, vom Atomraktor der Enterprise) arbeiten. Das Resultat wär total radioaktiv! Definitiv unbrauchbar. Dito übrigens die Transporter (die sind Scanner+Drucker), die Replikatoren wurden davon abgeleitet (Atommuster berechnen statt scannen und mehrfach ausdrucken).

Nach dem, was nicht funktioniert, nun die Frage nach funktionierendem. Dieses muss bestehende Atome beliebig anordnen können. Ein Weg: Die oben aufgeführten mechanischen Systeme können alle nur ein Bauteil aus einem Material formen. Spätestens bei mehreren Teilen oder gar gemischtem Material ist da auch Schluss. Das kann heute kein mir bekanntes System. Und ein System dass das kann wird kompliziert (Aufbau und Rohmaterialversorgung). Solange man aber Montage (Zusammensetzen) an den User abdelegieren kann (so a la Ikea) ist es nur noch eine Frage von aus mehreren Materialien separate Teile machen. Das wäre noch mit mehreren Maschinen vereinfachbar, aber das kostet noch mehr Platz und Geld. Das setzt vermutlich eine "Copy-Shop" artige Infrastruktur voraus. Aber das würde viele User brauchen um profitabel zu sein, also ein Henne-Ei Problem. Da wird also noch viel passieren müssen. Eventuell Universitäten die solche Maschinen haben, und andere Leute dran lassen. Solche Maschinen werden aber vermutlich eh immer speziell aufbereitetes Rohmaterial brauchen, mit den selbigen Problemen wie man es heute mit den Tintenpatronen Herstellern hat.

Neben schlicht mehrere Maschinen und Montage gibt es doch ein universelles Fabrikationssystem, das ich als möglich erachte: Biotechnologie. Immerhin schafft es die Biologie aus nichts ausser Nahrung ganze Körper wachsen zu lassen, X Teile, X Materialien, alles fixfertig montiert. Das Verfahren ist bekannt, aber sehr kompliziert: DNA->tRNA->Proteine->Rest. Das könnte man mit *.dna File Download, "Ausdruck" mit DNA Synthesiser, in Nährlösung, Teile wachsen lassen, machen. Aber um das zu können, benötigt für jedes Teil spezifische DNA Sequenzen. Das zu programmieren wird der Horror schlechthin. Viel Forschung wird da nötig sein. Ich erwarte das nicht in den nächsten 30 Jahren, ev sind es auch 100 Jahre. Und DNA Synthesizer Viren werden die heutigen Microsoft Viren harmlos erscheinen lassen, das .dna File eines Influenzia Viruses ist nur der Anfang.


Home | Artikel | LUGcamp Hardware

Diese Seite ist von Neil Franklin, letzte Änderung 2003.07.07