Programmierte Logik, Teil 2: Einige Schaltungsbeispiele im GAL

Nachdem im letzten Teil der Artikelreihe die Grundlagen besprochen wurden, und man erahnen konnte, welche Möglichkeiten in diesen interessanten Bausteinen stecken, folgen diesmal einige Beispiele. Anhand von logischen Schaltungen werden die Möglichkeiten zu deren Implementierung in GALs aufgezeigt. Zuvor müssen wir uns aber noch ein wenig Theorie gönnen.

Wenn man ein GAL brennen will, muß man die entsprechende Fuse-Matrix in das Brennprogramm eingeben. Damit man nun beim späteren Brennen eines GALs mit demselben Inhalt nicht wieder die gesamte Matrix eintippen muß, wurde vom Joint Electron Device Engineering Council das JEDEC-Format als Standard bestimmt. Eine Fuse-Matrix, die sich in einer Datei mit diesem Format befindet, kann nun immer wieder zum Brennen eines Logikbausteins benutzt werden. Auch ist eine Weitergabe der Datei auf andere Rechner ohne Probleme möglich, da sie nur ASCII-Zeichen enthält. Ein weiterer Vorteil dieser Standardisierung ist, daß man solche Dateien auch mit anderer Brenn-Hard-und Software benutzen kann. Logik-Compiler (das sind Programme, die aus logischen Gleichungen oder Funktionstabellen Fuse-Matrixen erzeugen) benutzten dieses Format ebenfalls, so daß man ausgehend von den Logikgleichungen bis zum fertig gebrannten GAL jede beliebige Soft- und Hardware-Kombination (auch auf verschiedenen Rechnern) benutzen kann. Ich werde im weiteren nicht auf das komplette Format eingehen, da nur ein Teil zur Programmierung der GALs notwendig ist.

Das JEDEC-Format

Jeder Eintrag in einer JEDEC-Datei beginnt mit einem Steuerzeichen und endet mit dem Asterix (). Als erstes steht in dem File eine 02 (Steuerzeichen: <STX>). Nun können beliebige Angaben folgen (z.B. Namen, Versionsnummern, Hinweise...), die später nicht ausgewertet werden ( am Ende nicht vergessen!). Die folgenden Datenfelder können in beliebiger Reihenfolge in der Datei stehen.

Fuse-Feld: Es besteht aus maximal drei Teilen. Wenn in der folgenden Fuse-Liste nicht für alle Sicherungen der jeweilige Zustand angegeben wird, muß der folgende für die weggelassenen aufgeführt werden. Hierzu werden der Kennbuchstaben F und die Ziffern 0 (Verbindung) oder 1 (Verbindung wird nicht hergestellt), gefolgt von einem Asterix, benutzt. Dies gibt dem Anwender die Möglichkeit, nicht alle Sicherungszustände in seiner Datei aufführen zu müssen. Genutzt wird dies dann, wenn man nicht alle Ausgänge eines GALs verwendet, oder aber, wenn bei einem Ausgang nicht alle acht Zeilen benötigt werden. Die ungenutzten Zeilen braucht man so nicht in das JEDEC-File zu schreiben.

Bild 1: Bildung der Fuse-List-Prüfsumme

Die eigentliche Fuse-Liste beginnt mit einem L und der Adresse der ersten Sicherung, für die folgende, fortlaufende Reihe gilt. Hier können nun die Zustände für alle Sicherungen stehen, oder aber man teilt die Liste in mehrere Teile auf (jeder muß jedoch mit * enden und der nächste mit L und einer Adresse anfangen). Teilt man die Liste so auf, daß in jeder Reihe 32 (bzw. 40 beim GAL 20v8) Zustände stehen und sich die nächste Reihe bündig darunter befindet, so läßt sich leicht ersehen, welche Verbindungen hergestellt werden sollen. Man nimmt dazu die Bilder 2 und 3 aus der letzten Folge und vergleicht die Logikmatrix mit den Nullen aus der Liste. An diesen Stellen wird eine Verbindung zwischen den Zeilen und Spalten hergestellt.

Der dritte Teil ist eine Prüfsumme. Diese wird gebildet indem man die Fuse-Liste, wie in Bild 1 zu sehen, in Bytes aufteilt und diese aufaddiert (das letzte Byte wird, sofern die Anzahl der Sicherungen nicht durch acht teilbar ist, mit Nullen aufgefüllt). Das Ergebnis wird als 16-bit Zahl hinter den Buchstaben C notiert.

Sicherungsfeld: Eine weitere Angabe bezieht sich auf die Security Cell. Soll diese gebrannt werden (Fuse-Matrix des GALs ist danach nicht mehr auslesbar), schreibt man G0*, im anderen Fall G1*.

Kommentarfeld: Es besteht auch die Möglichkeit, Kommentare innerhalb der JEDEC-Datei unterzubringen. Der Kennbuchstabe hierfür ist N.

Am Schluß der Datei kommt noch eine Prüfsumme. Sie wird von dem Steuerzeichen <ETX> (Hex: 03) eingeleitet und ist eine 16-Bit-Summe aller ASCII-Zeichen des Files einschließlich der Steuerzeichen <STX> und <ETX>. Da manche Computer jedoch am Ende jeder Zeile noch Zeichen einfügen (Return...), wird in vielen Fällen an dieser Stelle statt der Prüfsumme einfach nur 0000 geschrieben. Die Datei endet direkt hinter der Zahl; es folgt hier kein *!

Nachdem nun klar ist, wie das JEDEC-Format aussieht, taucht natürlich die Frage auf, welche Adresse die Sicherungen in den GALs haben. Die Adresse der Logikmatrix läßt sich sehr einfach anhand der Bilder 2 und 3 aus der letzten Ausgabe (S. 165 oben) errechnen. Man muß die Nummer des Produkt-Terms mal der Anzahl der Spalten des GALs (beim 16v8 32, beim 20v8 40) nehmen und die Row-Adresse der Zeile, in der sich die Sicherung befindet dazuaddieren. [An dieser Stelle sei auf eine Tatsache hingewiesen, die zu Unstimmigkeiten führen kann: In den Datenbüchern über GALs (auch bei Lattice) beginnen die Produkt-Terme beim GAL 20v8 bei acht. Da sich bei der von mir gewählten Numerierung jedoch die Adressen im JEDEC-File leichter errechnen lassen habe ich diese auch für den Artikel übernommen.] Die übrigen Adressen entnimmt man Bild 2.

Bei den folgenden Beispielen werde ich jeweils nur die Fuse-Liste angeben [alle nicht aufgeführten Sicherungen werden gebrannt (0)].

Logische Gatter im GAL

Die Realisierung der logischen Grundgatter mittels eines GALs ist in Bild 3 zu sehen. Das NOT ergibt sich durch die Aufteilung der Eingänge in einen normalen und einen invertieren Eingang (3a). UND-verknüpft werden all die Eingänge, deren Sicherungen auf einer Zeile gebrannt wurden (3b). Alle Zeilen, die denselben Ausgangstreiber haben, werden untereinander ODER-verknüpft (3c). Die EXOR-Verknüpfung (exklusiv oder: der Ausgang wird nur dann Eins, wenn genau Einer der Eingänge eins ist) muß auf eine UND/ODER-Verknüpfung zurückgeführt werden (3d).

In Bild 4 ist die Übertragung eines logischen Verknüpfungsnetzes in ein GAL wiedergegeben. Zuerst werden anhand des Netzes die Logikgleichungen aufgestellt. Nun (sofern man nicht einen Logik-Compiler zur Hand hat) werden diese vereinfacht, was bei diesem Beispiel entfällt. Der nächste Schritt ist zu Überlegen, wie man diese Gleichungen am günstigsten in einem GAL unterbringt. In unserem Fall brauchen wir drei Eingänge und zwei Ausgänge. Da bei den Gleichungen maximal zwei ODER-Verknüpfungen Vorkommen, benötigen wir Ausgänge, die mindestens zwei Zeilen verODERn. In diesem Fall haben wir beim GAL (jeder Ausgang hat acht Eingangszeilen) keine Schwierigkeiten. Hätten wir jetzt eine Gleichung mit mehr als acht ODER-Verknüpfungen (nach der Vereinfachung), hätten wir mit einer Rückführung arbeiten müssen. D.h. wir hätten einen Teil der Gleichung auf einem Ausgang realisieren, dieses Ergebnis auf den nächsten Ausgang zurückführen und dort die restliche Gleichung implementieren müssen. Dadurch wäre natürlich ein Ausgang verlorengegangen [es sei denn, wir hätten das Ergebnis der Zwischengleichung benötigt; z.B. wenn eine Gleichung vollständig in einer weiteren enthalten ist: A = X * Y und B = (X * Y) + Z. In diesem Fall hätte man A auf einem Ausgang realisiert und das Ergebnis auf die Zeilen vom Ausgang B zurückgeführt. Wobei es jedoch aus Geschwindigkeitsgründen auch sinnvoll sein kann, die gleichen Terme für jeden Ausgang neu zu realisieren, denn durch die Rückführung liegt am Ausgang B erst nach der doppelten Zeit wie am Ausgang A das richtige Signal an]. Jetzt müssen wir uns nur noch entscheiden, welche Ein- und Ausgänge wir am GAL belegen. Die Aufstellung der Fuse-Matrix ist bei diesem Beispiel sehr einfach. Sie wird anschließend in den Baustein gebrannt, und wir haben unsere Logik im GAL. Sollte sich nun nachträglich an der Logik etwas ändern (z.B. ein ODER statt einem EXOR). ändern wir die entsprechenden Stellen in der Fuse-Matrix und brennen das GAL neu. Es muß nicht ein neues Layout für die Platine realisiert werden.

Bild 2: Adressen der GALs in der Fuse-Liste des JEDEC-Formats
Bild 3: Logische Gatter im GAL
Bild 4: Verknüpfungsnetz mit TTL-Logik und im GAL

Ein Zähler im GAL

Bei unserem nächsten Beispiel wird die Sache schon um einiges schwieriger. Wir wollen einen 4-Bit-Zähler realisieren, der nur aufwärts zählt, einen Takt, einen Rücksetz- (clear) sowie einen Setzeingang (preset) besitzt, mit dessen Hilfe man den Zähler auf einen bestimmten Anfangswert bringen kann (wird bei uns jedoch nicht verwendet, ist schmückendes Beiwerk).

  0000
  0001
  0010
  0011
  0100
  0101
  0110
  0111
  1000
  1001
  1010
  1011
  1100
  1101
  1110
  1111

*Bild 5: So wird digital gezählt*

Zuerst stellen wir die Logikgleichungen auf. Dazu überlegen wir uns, was ein Zähler eigentlich macht. Richtig, er zählt. Aber wie? Bild 5 gibt darüber Auskunft. Dort sind alle mit 4 Bit darstellbaren Zahlen der Größe nach sortiert (von oben nach unten) aufgeführt. Diesen Zyklus soll das GAL an vier seiner Ausgänge (A3 bis A0) wiedergeben, vorausgesetzt die Eingänge clear und preset sind beide inaktiv (low). Mittels dieser Informationen können wir nun die Gleichungen für die vier Ausgänge aufstellen. Zuerst für A0. Der Ausgang wechselt jedesmal wenn ein Taktimpuls kommt, seinen Wert von 0 auf 1 und umgekehrt. Da nun die von den Logikzeilen erzeugten Signale erst beim nächsten Taktimpuls übernommen werden (wir arbeiten mit Registern in den OLMCs), können wir den neuen Zustand erzeugen sowie der vorhergehende übernommen wurde. Bei A0 heißt dies, daß wir den Ausgang immer invertieren:

A0 = /A0

Wenn clear gesetzt wird, muß der Ausgang (wie alle anderen auch) auf 0 gesetzt werden (in unserem Fall erst beim nächsten Taktimpuls). Wir beachten dies, indem wir bei jedem UND-Term in unseren Gleichungen ein /clr schreiben:

A0 = /clr * /A0

Das gleiche gilt für preset, nur muß hier der Fall, daß der Ausgang gesetzt werden soll [es liegt am entsprechenden Eingang (E0 bis E3) eine 1], mitbeachtet werden. Bei uns ist das immer der letzte UND-Term jeder Gleichung. Dies ergibt für den ersten Ausgang die vollständige Logikgleichung:

A0 = /clr * /ps * /A0 + /clr * ps * E0

Der zweite Ausgang wird bei den Übergängen 01 -> 10 und 10-> 11 aktiv (1),so daß wir für diesen drei UND-Terme ver-ODERn müssen:

A1 = /clr * /ps * /A1 * A0
+ /clr * /ps * A1 * /A0
+ /clr * ps * E1

Beim nächsten Ausgang sind es bereits vier Übergänge: 011 -> 100, 100 -> 101, 101 -> 110 und 110 -> 111. Dies ergibt fünf VerODERungen:

A2 = /clr * /ps * /A2 * A1 * A0
+ /clr * /ps * A2 * /A1 * /A0 (i)
+ /clr * /ps * A2 * /A1 * A0 (i)
+ /clr * /ps * A2 * A1 * /A0
+ /clr * ps * E2

Der letzte Ausgang beschert uns mit seinen acht Übergängen (01 11 -> 1000,1000 -> 1001, 1001 -> 1010, 1010 -> 1011, 1011-> 1100,1100-> 1101,1101->1110 und 1110 -> 1111) neun UND-Terme:

A3 = /clr * /ps * /A3 * A2 * A1 * A0
+ /clr * /ps * A3 * /A2 * /A1 * /A0 (ii)
+ /clr * /ps * A3 * /A2 * /A1 * A0 (ii)
+ /clr * /ps * A3 * /A2 * A1 */A0 (iii)
+ /clr * /ps * A3 * /A2 * A1 * A0 (iii)
+ /clr * /ps * A3 * A2 * /A1 * /A0 (iv)
+ /clr * /ps * A3 * A2 * /A1 * A0 (iv)
+ /clr * /ps * A3 * A2 * A1 * /A0
+ /clr * ps * E3

Die Gleichung für den letzten Term bereitet uns bei der Realisierung im GAL Schwierigkeiten, da man für jeden Ausgang nur acht ODER-Verknüpfungen hat. Zum Glück kann man sich bei diesem Beispiel jedoch mittels Vereinfachungen der Geichungen helfen.

Könnte man das nicht machen (z.B. bei einem 5-Bit-Zähler), müßte man einen weiteren Ausgang heranziehen und, wie oben bereits erwähnt, die Gleichungen auf die beiden Ausgänge verteilen und mit einer Rückführung arbeiten. Bei unseren Zählern kann man jedoch sowohl bei A3 als auch bei A^ die Gleichungen vereinfachen. Schaut man sich die Gleichungen von A2 näher an, sieht man, daß sich die beiden UND-Terme vor der (i) nur minimal unterscheiden. Sie sind bis auf den Teil mit A0 identisch. Da dies jedoch heißt, daß sowohl dann, wenn A0 gesetzt ist, als auch, wenn der Ausgang nicht gesetzt ist, diese UND-Bedingung erfüllt werden soll, kann man die beiden Zeilen zusammenfassen, indem man den A0-Teil wegläßt. Dies gibt eine Ersparnis von einem ODER:

A2 = /clr * /ps * /A2 * A1 * A0
+ /clr * /ps * A2 * /A1 (i)
+ /clr * /ps * A2 * A1 * /A0
+ /clr * ps * E2

Beim letzten Ausgang ist die Ersparnis sogar noch größer. Man kann nach derselben Methode wie oben die Zeilen bei (ii), (iii) und (iv) zusammenfassen. Des weiteren kann man danach noch einmal die Zeilen, welche man bei (ii) und (iii) erhalten hat, zusammenfassen, wobei dort A1 wegfällt. So hat man am Ende vier ODER-Verknüpfungen gespart und kann die Gleichung mit einem Ausgang realisieren:

A3 = /clr * /ps * /A3 * A2 * A1 * A0
+ /clr * /ps * A3 * /A2 (ii & iii)
+ /clr * /ps * A3 * A2 * /A1 (iv)
+ /clr * /ps * A3 * A2 * A1 * /A0
+ /clr * ps * E3

Zusätzlich soll nun unser Zähler beim Erreichen der Zahl 11 (1011) wieder von 0 an anfangen. Dies ergibt als letzte Gleichung für clear (wobei clear_out der von außen kommende clear-Eingang sei):

clr = clear_out
+ A3 * /A2 * A1 * A0

In Bild 6 ist die Realisierung mittels TTL-Bausteinen wiedergegeben. Man braucht einen Zähler, einen Inverter und ein 4er UND. Der innerhalb der gestrichelten Linien befindliche Bereich ist nun die Logik, die wir in unser GAL brennen (Fuse-Liste siehe Bild 7). Man erkennt auch hier: Bei Änderungen der Zahl, ab der der Zähler wieder von vorne anfangen soll, braucht man nur das GAL neu zu programmieren, bei der TTL-Lösung bräuchte man für die Rückführung zum clear-Eingang eine neue Logik samt Verdrahtung.

Bild 6: Der 4-Bit-Zähler in TTL

Ein GAL als Ampel?

Als letztes Beispiel etwas zum Thema Zustandsautomaten. Wir wollen ein GAL zur Steuerung einer Verkehrsampel einsetzen. Wir gehen davon aus, daß der Takt zum Wechseln der Ampelphasen von außen (von einer Zeitgeberschaltung) gegeben wird. Als erstes haben wir uns zu fragen, welche Zustände es gibt. Klar, wie bei jeder Ampel: rot, rot-gelb, grün, gelb und wieder rot, wobei der letzte Zustand schon wieder der erste ist. Wir haben also vier Zustände. Zur Ansteuerung der einzelnen Ampellampen sehen wir jeweils einen Ausgang vor (rot, gelb und grün). Des weiteren numerieren wir die Ampelphasen von 0 bis 3 (binär) durch, wobei wir bei irgendeinem Zustand (hier: Ampel rot) beginnen. Wir erhalten dann folgende Tabelle , die zyklisch (d.h. immerfort) durchlaufen wird.

Bild 7: Fuse-Liste des 4-Bit-Zählers
Bild 8: Ein GAL als Ampelsteuerung

Nun werden die Gleichungen für die beiden Zustandsvariablen aufgestellt [in diesem Fall, da keinerlei Einwirkungen von außen auf den Zyklus erfolgen können, hat man einen 2-Bit-Zähler. Wenn Einflüsse da sind, müssen die entsprechenden Eingänge bei der Aufstellung der Gleichungen berücksichtigt werden (siehe Beispiel im Handbuch zum GAL-Prommer)]:

z0 = /z0

z1 = /z1*z0 + z1 * /z0

Die Gleichungen für die Ausgänge dürfen nur von den Zustandsvariablen abhängen. In unserem Fall ergibt sich:

r = /z0 * /z0 + /z1 * z0

y = /z1 * z0 + z1 * z0

g = z1 * /z0

Die Realisierung dieser Gleichungen im GAL (an zwei Ausgängen erzeugt man die Werte, die Zustandsvariablen und koppelt diese zurück) ist im Bild 8 zu sehen.

Die Beispiele in dieser Folge sollten einige Möglichkeiten aufzeigen, was man mit GALs alles machen kann. Sicherlich konnten es nur Stichproben sein. Am besten ist, man stellt selbst Versuche mit diesen Bausteinen an. Zu diesem Zweck folgt in der nächsten Folge das Selbstbauprojekt für einen GAL-Prommer. Auch werde ich dort das Thema Emulation von PALs durch GALs, das ich ja bereits für diese Folge angekündigt hatte, nachholen.

Literatur:

Lattice: GAL Handbook

mc 1/88: Programmierbare Logikbausteine

elrad 5/89: Die Logik durchgebrannter Sicherungen

JEDEC FORMAT, Datenbuch


Thomas Werner
Links

Copyright-Bestimmungen: siehe Über diese Seite
Classic Computer Magazines
[ Join Now | Ring Hub | Random | << Prev | Next >> ]