eXamen.press
eXamen.press ist eine Reihe, die Theorie und Praxis aus allen Bereichen der Informatik für die Hochschulausbildung vermittelt.
G¨unter Kemnitz
Technische Informatik Band 2: Entwurf digitaler Schaltungen
123
G¨unter Kemnitz TU Clausthal Institut f¨ur Informatik Arbeitsb. Hardwareentwurf u. Robotik Julius-Albert-Strasse 4 38678 Clausthal-Zellerfeld Deutschland
[email protected]
ISSN 1614-5216 ISBN 978-3-642-17446-9 e-ISBN 978-3-642-17447-6 DOI 10.1007/978-3-642-17447-6 Springer Heidelberg Dordrecht London New York Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet u¨ ber http://dnb.d-nb.de abrufbar. c Springer-Verlag Berlin Heidelberg 2011 Dieses Werk ist urheberrechtlich gesch¨utzt. Die dadurch begr¨undeten Rechte, insbesondere die der ¨ Ubersetzung, des Nachdrucks, des Vortrags, der Entnahme von Abbildungen und Tabellen, der Funksendung, der Mikroverfilmung oder der Vervielf¨altigung auf anderen Wegen und der Speicherung in Datenverarbeitungsanlagen, bleiben, auch bei nur auszugsweiser Verwertung, vorbehalten. Eine Vervielf¨altigung dieses Werkes oder von Teilen dieses Werkes ist auch im Einzelfall nur in den Grenzen der gesetzlichen Bestimmungen des Urheberrechtsgesetzes der Bundesrepublik Deutschland vom 9. September 1965 in der jeweils geltenden Fassung zul¨assig. Sie ist grunds¨atzlich verg¨utungspflichtig. Zuwiderhandlungen unterliegen den Strafbestimmungen des Urheberrechtsgesetzes. Die Wiedergabe von Gebrauchsnamen, Handelsnamen, Warenbezeichnungen usw. in diesem Werk berechtigt auch ohne besondere Kennzeichnung nicht zu der Annahme, dass solche Namen im Sinne der Warenzeichen- und Markenschutz-Gesetzgebung als frei zu betrachten w¨aren und daher von jedermann benutzt werden d¨urften. Einbandentwurf: KuenkelLopka GmbH Gedruckt auf s¨aurefreiem Papier Springer ist Teil der Fachverlagsgruppe Springer Science+Business Media (www.springer.com)
Vorwort
Die Technische Informatik umfasst zwei ganz unterschiedliche Grundausrichtungen, die im applikativen Bereich stark miteinander verflochten sind: • •
die technische Basis der Informatik und die technischen Anwendungen der Informatik.
Die technische Basis der Informatik ist die Digitaltechnik, die ihrerseits die Grundlage für den Entwurf hochintegrierter Schaltkreise bildet. Hochintegrierte Schaltkreise sind wiederum der Kern der Rechnerhardware. Die andere, nicht weniger bedeutende Grundausrichtung ist die informationstechnische Erfassung und Steuerung technischer Systeme. Hier ist die Basis die Nachbildung technischer Zusammenhänge durch Zahlen, Algorithmen, Regeln etc., d.h. durch Modelle, die ein Rechner verarbeiten kann. Darauf setzen die stärker applikativ orientierten Gebiete wie die rechnergestützte Mess-, Steuer- und Regelungstechnik sowie die Nachrichtentechnik auf. Technische Anwendungen stellen spezielle Anforderungen an die Rechnerhardware und die darauf laufende Software, nämlich • • • •
Echtzeitfähigkeit, geringe Abmessungen, geringer Stromverbrauch, hinreichende Verlässlichkeit etc.
An dieser Schnittstelle haben sich wichtige Teilgebiete der Informatik herausgebildet, die sich z.B. unter den Überschriften »Eingebettete Systeme« und »Echtzeitsysteme« mit Systemen aus Hard- und Software beschäftigen, die hauptsächlich für technische Anwendungen gedacht sind. Die Einsatzgebiete solcher Systeme reichen von der Waschmaschine mit Mikrorechner über das Handy bis zum Autopiloten in einem Flugzeug. Die beiden Grundausrichtungen der Technischen Informatik – technische Basis und technische Anwendungen – repräsentieren unterschiedliche Denkwelten. Die digitale Welt der Informatik unterscheidet nur »0« und »1«. Die
vi
Vorwort
logischen Grundbausteine – UND, ODER, Inverter, ... – sind sehr einfach zu verstehen und ihre Funktionen sind exakt definiert. Aus ihnen werden hierarchisch zuerst kleine Teilsysteme, aus diesen wieder größere Teilsysteme und aus diesen wieder komplette Rechner und Rechnersysteme zusammengesetzt. Bei den heutigen hochintegrierten Schaltkreisen mit vielen Millionen von Transistoren gibt es keinen Menschen mehr, der sagen kann, wozu jeder der Transistoren da ist. Die Entwurfstechnik für wirklich große Systeme besteht inzwischen vereinfacht darin, einen Algorithmus zu entwickeln, der einen Algorithmus entwickelt, der das System entwickelt. Das ist eine vollkommen andere Denkwelt als bei der Anwendung der Informatik in der Technik. Die informationstechnische Erfassung, Modellierung und Steuerung technischer Systeme arbeitet immer mit Näherungen. Ein technisches System verhält sich nur zu einem Teil deterministisch. Der deterministische Teil ist meist nichtlinear. Die Nachbildung erfolgt durch große Gleichungssysteme, die sich nur lösen lassen, wenn sie durch lineare Gleichungssysteme angenähert werden. Die Grundlagen hierfür sind die Physik und die Mathematik. Es ist die Genauigkeit, die es zu beherrschen gilt, nicht die Größe. Dieses Buch behandelt die technische Basis der Informatik: die Modellbildung, die Simulation, den (rechnergestützten) Entwurf und den Test digitaler Schaltungen. Ein Rechner ist in diesem Rahmen eine Beispielschaltung.
Clausthal-Zellerfeld, Oktober 2010
Günter Kemnitz
Inhaltsverzeichnis
1
Modellbildung und Simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1 Entwurf digitaler Schaltungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.1 Der Umgang mit großen Schaltungen . . . . . . . . . . . . . . . . 1.1.2 Modelle und Entwurfsraum . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.3 Entwurfsablauf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.1.4 VHDL als formale Beschreibungsplattform . . . . . . . . . . . . 1.1.5 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 1.2 Funktion, Struktur und Simulation . . . . . . . . . . . . . . . . . . . . . . . . 1.2.1 Signale und Datentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.2 Signalflussplan . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.3 Imperative Funktionsmodelle . . . . . . . . . . . . . . . . . . . . . . . 1.2.4 Ereignisgesteuerte Simulation . . . . . . . . . . . . . . . . . . . . . . . 1.2.5 Strukturbeschreibung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.6 Testrahmen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2.7 Instanziierung von Komponenten . . . . . . . . . . . . . . . . . . . . 1.2.8 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 1.3 Laufzeittoleranz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.1 Glitches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.2 Das Verzögerungsmodell einer Signalzuweisung . . . . . . . . 1.3.3 Simulation mit Halte- und Verzögerungszeiten . . . . . . . . 1.3.4 Laufzeitanalyse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3.5 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 1.4 Register . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.1 Register als Abtastelemente . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.2 VHDL-Abtastprozesse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.3 Verarbeitung plus Abtastung . . . . . . . . . . . . . . . . . . . . . . . 1.4.4 Register-Transfer-Funktion . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.5 Taktversatz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.4.6 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 1.5 Asynchrone Eingabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.1 Abtasten asynchroner Signale . . . . . . . . . . . . . . . . . . . . . . .
1 1 1 4 6 9 15 16 17 20 22 33 37 40 41 41 44 44 46 48 51 52 55 56 57 60 61 62 64 66 66
viii
Inhaltsverzeichnis
1.5.2 Entprellen von Tasten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.3 Asynchrone Initialisierung . . . . . . . . . . . . . . . . . . . . . . . . . . 1.5.4 Asynchrone parallele Schnittstelle . . . . . . . . . . . . . . . . . . . 1.5.5 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 1.6 Sequenzielle Schaltungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6.1 Endlicher Automat . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6.2 Vom Zustandsgraph zur VHDL-Beschreibung . . . . . . . . . 1.6.3 Unzulässige Zustände, Systemabsturz und Watchdog . . . 1.6.4 Entwurf eines Zahlenschlosses . . . . . . . . . . . . . . . . . . . . . . . 1.6.5 Operationsablaufgraph . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.6.6 Beispiel Wegemessung mit Quadratur-Encoder . . . . . . . . 1.6.7 Software-orientierte Ablaufbeschreibung . . . . . . . . . . . . . . 1.6.8 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 2
67 68 70 72 74 74 78 82 84 86 89 91 92
Synthese und Logikoptimierung . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95 2.1 Register-Transfer-Synthese . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96 2.1.1 Beschreibung und Extraktion von Registern . . . . . . . . . . 97 2.1.2 Kombinatorische Schaltungen . . . . . . . . . . . . . . . . . . . . . . . 99 2.1.3 Kombinatorische Schaltungen mit Abtastregistern . . . . . 106 2.1.4 Latches . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111 2.1.5 Constraints . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116 2.1.6 Entwurfsfehler und Fehlervermeidung . . . . . . . . . . . . . . . . 119 2.1.7 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 122 2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra . . . . . . . . . 125 2.2.1 Umformungs- und Vereinfachungsregeln . . . . . . . . . . . . . . 125 2.2.2 Optimierungsziele . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 2.2.3 Schaltungsvereinfachung mit Konjunktionsmengen . . . . . 129 2.2.4 KV-Diagramm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131 2.2.5 Verfahren von Quine und McCluskey . . . . . . . . . . . . . . . . 138 2.2.6 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 141 2.3 Binäre Entscheidungsdiagramme . . . . . . . . . . . . . . . . . . . . . . . . . . 143 2.3.1 Vereinfachung binärer Entscheidungsdiagramme . . . . . . . 144 2.3.2 Zweistellige Operationen mit ROBDDs . . . . . . . . . . . . . . . 146 2.3.3 Umsetzung von ROBDDs in minimierte Schaltungen . . . 148 2.3.4 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 149 2.4 Zahlendarstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150 2.4.1 Stellenwertsystem . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151 2.4.2 Vorzeichenbehaftete Zahlen im Zweierkomplement . . . . 153 2.4.3 Festkommazahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 155 2.4.4 Gleitkommazahlen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 156 2.4.5 Zahlendarstellung und -verarbeitung in VHDL . . . . . . . . 158 2.4.6 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 159 2.5 Addierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 2.5.1 Ripple-Addierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 161 2.5.2 Serieller Addierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163
Inhaltsverzeichnis
ix
2.5.3 Schneller Übertragsdurchlauf . . . . . . . . . . . . . . . . . . . . . . . 167 2.5.4 Hierarchische Addierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 2.5.5 Carry-Save-Addierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 171 2.5.6 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 172 2.6 Weitere Rechenwerke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 2.6.1 Subtrahierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 173 2.6.2 Zähler . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 2.6.3 Negation und Betragsbildung . . . . . . . . . . . . . . . . . . . . . . . 174 2.6.4 Multiplizierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 2.6.5 Dividierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 177 2.6.6 Vergleicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 182 2.6.7 Verschiebung und Rotation . . . . . . . . . . . . . . . . . . . . . . . . 182 2.6.8 Synthese-Suchmuster und Optimierung . . . . . . . . . . . . . . . 184 2.6.9 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 187 3
VHDL im Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 3.1 Imperative Beschreibungsmittel . . . . . . . . . . . . . . . . . . . . . . . . . . . 189 3.1.1 Operatoren, Ausdrücke und Zuweisungen . . . . . . . . . . . . . 191 3.1.2 Fallunterscheidungen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 194 3.1.3 Schleifen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 195 3.1.4 Funktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 198 3.1.5 Unreine Funktionen und globale Variablen . . . . . . . . . . . . 204 3.1.6 Operatorfunktionen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 205 3.1.7 Prozeduren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206 3.1.8 Nebenläufige Prozeduren . . . . . . . . . . . . . . . . . . . . . . . . . . . 209 3.1.9 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 212 3.2 Anwendungsspezifische Datentypen . . . . . . . . . . . . . . . . . . . . . . . . 214 3.2.1 Zahlentypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 214 3.2.2 Aufzählungstypen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 218 3.2.3 Physikalische Typen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 219 3.2.4 Attribute von elementaren Datentypen . . . . . . . . . . . . . . . 220 3.2.5 Felder . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 221 3.2.6 Zuordnungslisten für Feldelemente . . . . . . . . . . . . . . . . . . . 224 3.2.7 Bitvektoren . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 226 3.2.8 Verbund . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 230 3.2.9 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 233 3.3 Ein- und Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 235 3.3.1 Das Dateikonzept von VHDL . . . . . . . . . . . . . . . . . . . . . . . 235 3.3.2 Textausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 236 3.3.3 Texteingabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 240 3.3.4 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 242 3.4 Beschreibungsschablonen für digitale Schaltungen . . . . . . . . . . . . 244 3.4.1 Auslagerung kombinatorischer Funktionen in Packages . 244 3.4.2 Bäume statt Ketten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 250 3.4.3 Blockspeicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 252
x
Inhaltsverzeichnis
3.4.4 Ein objektorientiertes FIFO-Modell . . . . . . . . . . . . . . . . . . 262 3.4.5 UART – schrittweise Modellentwicklung . . . . . . . . . . . . . . 265 3.4.6 Entwicklung eines CORDIC-Rechenwerks . . . . . . . . . . . . . 278 3.4.7 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 286 3.5 Methoden und Funktionsbausteine für den Test . . . . . . . . . . . . . 288 3.5.1 Pseudo-Zufallstest und Mehrversionsvergleich . . . . . . . . . 289 3.5.2 Vorab berechnete Eingaben und Soll-Werte . . . . . . . . . . . 294 3.5.3 Spezifikationstest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 296 3.5.4 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 300 4
Vom Transistor zur Schaltung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 301 4.1 Entwurf und Modellierung von CMOS-Gattern . . . . . . . . . . . . . . 301 4.1.1 MOS-Transistoren als Schalter . . . . . . . . . . . . . . . . . . . . . . 301 4.1.2 Geschaltete Transistornetzwerke . . . . . . . . . . . . . . . . . . . . . 305 4.1.3 Signale mit mehreren Quellen . . . . . . . . . . . . . . . . . . . . . . . 307 4.1.4 FCMOS-Gatter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 309 4.1.5 Bussignale und deaktivierbare Treiber . . . . . . . . . . . . . . . . 312 4.1.6 Gatter mit Pull-Up- und Pull-Down-Elementen . . . . . . . 313 4.1.7 Transfergatter und Multiplexer . . . . . . . . . . . . . . . . . . . . . . 315 4.1.8 Geometrische Beschreibung . . . . . . . . . . . . . . . . . . . . . . . . . 317 4.1.9 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 320 4.2 Zeitverhalten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 322 4.2.1 Simulation des elektrischen Verhaltens . . . . . . . . . . . . . . . 323 4.2.2 Verzögerungsparameter . . . . . . . . . . . . . . . . . . . . . . . . . . . . 329 4.2.3 Parameterbestimmung mit Ringinvertern . . . . . . . . . . . . . 332 4.2.4 Gatter mit mehreren Eingängen . . . . . . . . . . . . . . . . . . . . . 333 4.2.5 Simulation mit geschalteten Transistorbreiten . . . . . . . . . 335 4.2.6 Reduktion auf zwei Zeitparameter . . . . . . . . . . . . . . . . . . . 339 4.2.7 Gepufferte CMOS-Gatter . . . . . . . . . . . . . . . . . . . . . . . . . . . 341 4.2.8 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 342 4.3 Speicherzellen, Latches und Register . . . . . . . . . . . . . . . . . . . . . . . 344 4.3.1 Dynamische Speicherzellen . . . . . . . . . . . . . . . . . . . . . . . . . 344 4.3.2 RS-Flipflop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 345 4.3.3 D-Flipflops und Latches . . . . . . . . . . . . . . . . . . . . . . . . . . . . 346 4.3.4 Übernahme an der Taktflanke . . . . . . . . . . . . . . . . . . . . . . . 347 4.3.5 Register aus Master-Slave-Flipflops . . . . . . . . . . . . . . . . . . 349 4.3.6 Taktversorgung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 351 4.3.7 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 355 4.4 Schreib-Lese-Speicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 357 4.4.1 SRAM . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 358 4.4.2 Mehrportspeicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 365 4.4.3 Assoziativspeicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 4.4.4 Dynamische Speicher (DRAM) . . . . . . . . . . . . . . . . . . . . . . 371 4.5 Festwertspeicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 375 4.6 Programmierbare Logikschaltkreise . . . . . . . . . . . . . . . . . . . . . . . . 376
Inhaltsverzeichnis
4.6.1 4.6.2 4.6.3 4.6.4 5
xi
Programmierbare Tabellenfunktionen . . . . . . . . . . . . . . . . 377 Programmierbare UND-Matrix . . . . . . . . . . . . . . . . . . . . . . 378 Weitere programmierbare Elemente . . . . . . . . . . . . . . . . . . 379 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380
Komplexe Beispielentwürfe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 381 5.1 Pipeline-Verarbeitung und Speicherengpass . . . . . . . . . . . . . . . . . 381 5.1.1 Prinzip der Pipeline-Verarbeitung . . . . . . . . . . . . . . . . . . . 381 5.1.2 Ausbalancieren einer Pipeline . . . . . . . . . . . . . . . . . . . . . . . 383 5.1.3 Engpass Speicher . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384 5.2 FIR-Filter mit Blockspeichern . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 384 5.2.1 Planung der Speicherstruktur und der Pipeline-Abläufe 385 5.2.2 Der gesamte Operationsablauf . . . . . . . . . . . . . . . . . . . . . . 387 5.2.3 Datentypen und Bearbeitungsmethoden . . . . . . . . . . . . . . 388 5.2.4 Das erste komplette Simulationsmodell . . . . . . . . . . . . . . . 390 5.2.5 Ersatz der Zahlentypen durch Bitvektortypen . . . . . . . . . 393 5.2.6 Ein- und Ausgabe über Signale . . . . . . . . . . . . . . . . . . . . . . 395 5.2.7 Umformung in eine Automatenbeschreibung . . . . . . . . . . 397 5.2.8 Zusammenfassung und Übungsaufgaben . . . . . . . . . . . . . . 402 5.3 Point-of-Interest-Berechnung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 402 5.3.1 Die Zielfunktion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 403 5.3.2 Grobkonzept der Hardware-Struktur . . . . . . . . . . . . . . . . . 403 5.3.3 Optimierung der 2D-FIR-Filter . . . . . . . . . . . . . . . . . . . . . 405 5.3.4 Entwurf der Filter-Pipeline . . . . . . . . . . . . . . . . . . . . . . . . . 406 5.3.5 Nachladen des Bildausschnittsspeichers . . . . . . . . . . . . . . . 408 5.3.6 Suche der betragsmäßig größten Filterantworten . . . . . . . 410 5.3.7 Klassifikation . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 412 5.3.8 Sortieren der klassifizierten Punkte . . . . . . . . . . . . . . . . . . 412 5.3.9 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414 5.4 Entwurf eines RISC-Prozessors . . . . . . . . . . . . . . . . . . . . . . . . . . . . 414 5.4.1 Die Pipeline für Verarbeitungsbefehle . . . . . . . . . . . . . . . . 415 5.4.2 Lade- und Speichereinheit . . . . . . . . . . . . . . . . . . . . . . . . . . 417 5.4.3 Sprung-Pipeline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 418 5.4.4 Entwurf eines Befehlssatzes für einen Minimalprozessor 420 5.4.5 Konstanten- und Datentypdefinitionen . . . . . . . . . . . . . . . 423 5.4.6 Das Rechenwerk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 424 5.4.7 Test des Rechenwerks . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 427 5.4.8 Schaltung zur Auswertung der Sprungbedingung . . . . . . 429 5.4.9 Das Pipeline-Objekt . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 430 5.4.10 Die Übergangsfunktion für einen Pipeline-Schritt . . . . . . 432 5.4.11 Test der Übergangsfunktion . . . . . . . . . . . . . . . . . . . . . . . . 435 5.4.12 Zusammenfassung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 438
xii
Inhaltsverzeichnis
6
Lösungen zu den Übungsaufgaben . . . . . . . . . . . . . . . . . . . . . . . . . 439 6.1 Modellbildung und Simulation . . . . . . . . . . . . . . . . . . . . . . . . . . . . 439 6.1.1 Entwurf digitaler Schaltungen . . . . . . . . . . . . . . . . . . . . . . . 439 6.1.2 Funktion, Struktur und Simulation . . . . . . . . . . . . . . . . . . 440 6.1.3 Laufzeittoleranz . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 442 6.1.4 Register . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 444 6.1.5 Asynchrone Eingabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 446 6.1.6 Sequenzielle Schaltungen . . . . . . . . . . . . . . . . . . . . . . . . . . . 447 6.2 Synthese und Logikoptimierung . . . . . . . . . . . . . . . . . . . . . . . . . . . 450 6.2.1 Register-Transfer-Synthese . . . . . . . . . . . . . . . . . . . . . . . . . 450 6.2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra . . . 452 6.2.3 Binäre Entscheidungsdiagramme . . . . . . . . . . . . . . . . . . . . 456 6.2.4 Zahlendarstellung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 457 6.2.5 Addierer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459 6.2.6 Weitere Rechenwerke . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 459 6.3 VHDL im Detail . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 463 6.3.1 Imperative Beschreibungsmittel . . . . . . . . . . . . . . . . . . . . . 463 6.3.2 Anwendungsspezifische Datentypen . . . . . . . . . . . . . . . . . . 465 6.3.3 Ein- und Ausgabe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 466 6.3.4 Beschreibungsschablonen für digitale Schaltungen . . . . . 469 6.3.5 Methoden und Funktionsbausteine für den Test . . . . . . . 471 6.4 Vom Transistor zur Schaltung . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 472 6.4.1 Entwurf und Modellierung von CMOS-Gattern . . . . . . . . 472 6.4.2 Zeitverhalten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 474 6.4.3 Speicherzellen, Latches und Register . . . . . . . . . . . . . . . . . 476 6.5 Komplexe Beispielentwürfe . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 476 6.5.1 FIR-Filter mit Blockspeichern . . . . . . . . . . . . . . . . . . . . . . 476
A
Ergänzungen zu VHDL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 A.1 Syntax, Schlüsselworte, Bezeichner, Konstanten . . . . . . . . . . . . . 479 A.1.1 Syntaxregeln für die Beschreibung von Sprachkonstrukten . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 A.1.2 Schlüsselworte . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 479 A.1.3 Syntaxregeln für Bezeichner . . . . . . . . . . . . . . . . . . . . . . . . 480 A.1.4 Zeichen- und Zeichenkettenkonstanten . . . . . . . . . . . . . . . 481 A.1.5 Darstellung von Zahlenwerten . . . . . . . . . . . . . . . . . . . . . . . 481 A.1.6 Vordefinierte Attribute . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 482 A.2 Die Bibliothek »Tuc« . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 484 A.2.1 Textausgabe (Tuc.Ausgabe) . . . . . . . . . . . . . . . . . . . . . . . . 484 A.2.2 Kontrollierter Simulationsabbruch (Tuc.StopSim_pack) 486 A.2.3 Texteingabe (Package Tuc.Eingabe) . . . . . . . . . . . . . . . . . . 487 A.2.4 Arithmetische Operationen (Tuc.Numeric_Sim und Tuc.Numeric_Synth) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 489 A.2.5 Pseudo-Zufallstest (Tuc.Zufallstest) . . . . . . . . . . . . . . . . . . 492
Inhaltsverzeichnis
xiii
Sachverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 493 Literaturverzeichnis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 501
1 Modellbildung und Simulation
1.1 Entwurf digitaler Schaltungen Die heutigen digitalen Schaltkreise, Baugruppen und Systeme bestehen aus Millionen von logischen Bausteinen. Kein Mensch ist mehr in der Lage, diese Bausteine auch nur zu zählen. Wie werden so große Schaltungen erfolgreich entworfen? Diese Frage hat zu einer starken Annäherung des digitalen Schaltungsentwurfs an die Methoden und Techniken der Software-Entwicklung geführt. 1.1.1 Der Umgang mit großen Schaltungen Die Prinzipien, auf denen der Entwurf, die Beschreibung und der Test von großen Schaltungen basieren, lauten • •
rechnergestützt, hierarchisch, parametrisiert, Synthese und Simulation.
Hierarchie digitaler Systeme Digitale Geräte bestehen aus Baugruppen, Baugruppen aus Schaltkreisen und anderen Bauelementen, Schaltkreise aus Funktionsblöcken etc. (Abb. 1.1). Eine hierarchische Beschreibung verweist auf jeder Hierarchieebene auf die verwendeten Bauteiltypen und beschreibt, wie Instanzen von diesen zum übergeordneten System zusammengesetzt werden. Die Bauteiltypen werden oft als fertige Produkte oder Entwurfsbeschreibungen übernommen und idealerweise mehrfach genutzt. Der manuelle Arbeitsaufwand wird von der Typenvielfalt und weniger von der Systemgröße bestimmt. Der Entwickler muss sich mit dem Anschlussverhalten der Bausteine auf der betrachteten Hierarchieebene, aber nicht mit deren interner Realisierung befassen. Die Auflösung der Hierarchie in eine flache Beschreibung aus Tausenden oder Millionen von Gattern oder Transistoren G. Kemnitz, Technische Informatik, eXamen.press, DOI 10.1007/978-3-642-17447-6_1, © Springer-Verlag Berlin Heidelberg 2011
2
1 Modellbildung und Simulation Gatter
Funktions- Funktionsbl¨ocke einheiten
Schaltkreise
Baugruppe
Ger¨ate
& &
...
& & &
Abb. 1.1. Die Hierarchie digitaler Systeme
und deren weitere Verarbeitung (Analyse, Simulation, Synthese, Platzierung und Verdrahtung) erfolgen rechnergestützt. Parametrisierte Schaltungen Ein parametrisiertes Schaltungsmodell ist ein Datensatz für einen Algorithmus, der die Schaltung generiert. In einer hierarchischen Beschreibung sind UND-Gatter mit zwei, drei etc. Eingängen unterschiedliche Bauteiltypen. Um die Anzahl der zu unterscheidenden Typen im System überschaubar zu halten, werden ähnliche Funktionen zu Funktionsklassen zusammengefasst. Ein parametrisiertes UND könnte z.B. den Grundtyp UND und den Parameter Eingangsanzahl haben. Andere mit parametrisierten Modellen beschreibbare Schaltungsklassen sind Speicher, Register, Rechenwerke und Datenflussumschalter. Die Generierung der aufgelösten Schaltungen erfolgt genau wie die Auflösung der Hierarchie erst im Rechner, so dass nur der Rechner, nicht aber der Entwickler mit der tatsächlichen Schaltungsgröße und Typenvielfalt konfrontiert wird. Synthese Die Synthese sucht für eine in einer Hardware-Beschreibungssprache beschriebene Funktion eine optimierte Schaltung. Sie ist üblicherweise in folgende Schritte untergliedert [16] (Abb. 1.2): • • • •
Register-Transfer-Synthese, Generierung, Logikoptimierung und Technologieabbildung.
Die Register-Transfer-Synthese arbeitet ähnlich wie ein Software-Compiler. Die Beschreibung der Zielfunktion wird zuerst analysiert, in eine interne Datenstruktur übersetzt und letztendlich auf einen Datenflussgraphen, d.h. eine Schaltung abgebildet. Logische Operatoren werden durch Logikgatter, arithmetische Operatoren durch Rechenwerke, Fallunterscheidungen durch Datenflussumschalter, Signale und Variablen durch Verbindungen oder Register etc.
1.1 Entwurf digitaler Schaltungen
3
Zielfunktion, Steuerparameter Register-Transfer-Synthese extrahierte GS
parametrisierte GS
Schaltungsteile aus universellen GS
Schaltungsgeneratoren Synthese
RS aus universellen GS Logikoptimierung OS aus universellen GS Technologieabbildung OS aus vorentworfenen GS Platzierung und Verdrahtung Layout, Fertigungsdaten
GS Grundschaltung RS Rohschaltung OS optimierte Schaltung
Abb. 1.2. Synthese
nachgebildet. Die extrahierten Gatter, Rechenwerke, Datenflussumschalter, Register etc. sind zum Teil parametrisierte Modelle mit variabler Anschlussanzahl, die an Schaltungsgeneratoren weitergereicht werden, die dafür die Schaltungen generieren. Die verbleibenden Teilschaltungen – Nachbildungen der Ausdrücke mit den logischen Grundoperatoren (Inverter, UND2, ODER21 etc.) – werden einzeln oder zusammen mit den generierten Teilschaltungen unter Ausnutzung der Gesetze der Schaltalgebra und anderer Regeln optimiert. Die nachfolgende Technologieabbildung ersetzt die universellen Grundschaltungen durch vorentworfene (technologieabhängige) Grundschaltungen. Das ist keine einfache Eins-zu-eins-Abbildung, sondern oft eine Zusammenfassung mehrerer universeller Grundschaltungen zu einer Schaltung aus mehreren vorentworfenen Schaltungen, z.B. mehrere UND-Gatter mit zwei Eingängen zu einem NAND-Gatter mit mehr als zwei Eingängen und einem Ausgabeinverter. Nach der Synthese folgt der geometrische Entwurf. Er umfasst die Anordnung der Teilschaltungen auf einem Schaltkreis oder einer Leiterplatte (Platzierung) und die geometrische Anordnung der Verbindungen (Verdrahtung). Aus der geometrischen Beschreibung werden abschließend die Fertigungsdaten und die tatsächlichen Verzögerungszeiten für die Simulationsmodelle berechnet. Die Schaltungsgeneratoren für parametrisierte Schaltungen und der Einsatz vorentworfener Teilschaltungen spielen in diesem Konzept eine Schlüsselrolle. Die Algorithmen für die Logikoptimierung verkraften nur Schaltungen aus bis zu einigen Hundert Gattern. Die Generierung erzeugt bereits lokal optimierte Schaltungen und unterliegt nicht dieser Größenbeschränkung. Damit die auf dem Standardweg zu verarbeitenden Schaltungsbeschreibungen 1
UND- und ODER-Gatter mit zwei Eingängen
4
1 Modellbildung und Simulation
die beherrschbare Größe nicht überschreiten, werden die generierten und vorentworfenen Teilschaltungen teilweise erst nach der Optimierung, nach der Technologieabbildung oder bereits als fertige Layouts zur Gesamtschaltung hinzugefügt. Der Entwickler arbeitet bei der Synthese ähnlich wie bei einem SoftwareEntwurf überwiegend mit Funktionsbeschreibungen in einer Programmiersprache. Die »Millionen von Einzelgattern« erscheinen nur als Aufwands- und Gütekenngrößen (Flächenverbrauch, Geschwindigkeit etc.) in den Meldungen der Entwurfswerkzeuge. Simulation Beim Entwurf großer Schaltungen entstehen zahlreiche Fehler, die idealerweise vor der Fertigung gefunden und beseitigt werden. Das erfordert ausgiebige Simulationen. Eine Schaltungssimulation benötigt zwei Verhaltensmodelle, ein Modell für das Testobjekt und einen Testrahmen. Das Simulationsmodell des Testobjekts ist praktisch ein ausführbares Programm, das die Ausgabesignale und die internen Signale aus den Eingabesignalen berechnet. Der Testrahmen ist gleichfalls ein ausführbares Programm. Er enthält das Testobjekt als Teilprogramm, erzeugt die Eingabesignale und wertet die Ausgabesignale aus. Jede Simulation ist ein Kompromiss zwischen dem Programmier- und dem Rechenaufwand auf der einen Seite und der Leistungsfähigkeit bei der Fehlererkennung auf der anderen Seite. Die Entwicklung ausreichend aussagekräftiger Simulationsmodelle und die Auswertung der Simulationsergebnisse ist meist erheblich aufwändiger als der eigentliche Schaltungsentwurf. 1.1.2 Modelle und Entwurfsraum Definition 1.1 (Modell) Ein Modell ist ein Mittel, um einen Zusammenhang zu veranschaulichen. Es stellt die wesentlichen Sachverhalte dar und verbirgt unwesentliche Details. Definition 1.2 (formales Modell) Ein formales Modell ist eine eindeutige Beschreibung der wesentlichen Merkmale, in der Regel ein auf einem Rechner abarbeitbares Simulationsmodell. Definition 1.3 (informales Modell) Ein informales Modell ist eine Beschreibung wesentlicher Zusammenhänge in einer anschaulichen Form ohne Rücksicht auf Eindeutigkeit oder Vollständigkeit. Definition 1.4 (halbformales Modell) Ein halbformales Modell ist eine Beschreibung, aus der Fachleute in der Regel dasselbe herauslesen. Moderne informationsverarbeitende Systeme werden objektorientiert entworfen. Objektorientierung ist in ihrem Kern eine Teile-und-Herrsche-Methode:
1.1 Entwurf digitaler Schaltungen
5
Gliedere den Entwurf in überschaubare und damit beherrschbare Teilaufgaben.
Testbeschreibung
geometrische Beschreibung
Hierarchieebene
Strukturbeschreibung
Beschreibungsart
Funktionsbeschreibung
Ausgangspunkt sind die zu verarbeitenden Datenobjekte. Datenobjekte, die in derselben oder in ähnlicher Weise verarbeitet werden, werden zu Klassen zusammengefasst und um Bearbeitungsmethoden ergänzt. Beim digitalen Schaltungsentwurf sind die Objekte Schaltungsbeschreibungen, Simulationsdaten, Testdaten und vieles mehr. Die Methoden sind Analyse, Visualisierung, Verarbeitung etc. Der Entwurfsraum beschreibt das Klassifizierungsschema der Entwurfsobjekte und die Beziehungen zwischen ihnen. Datenobjekte sind im Sinne der Informatik Modelle, die jeweils bestimmte Sachverhalte beschreiben. Bei manuellen Entwürfen werden überwiegend informale und halbformale Modelle entwickelt, bei denen die Anschaulichkeit im Vordergrund steht. Die rechnergestützte Verarbeitung verlangt formale Modelle, die die bearbeiteten Sachverhalte eindeutig und vollständig beschreiben. Der Entwurfsraum für einen digitalen Schaltungsentwurf untergliedert die Entwurfsobjekte außer nach der Modellkategorie (informal, halbformal und formal) noch nach der Hierarchieebene, der sie zugeordnet sind (Gesamtsystem, Teilsystem etc.), und nach der Beschreibungsart (Funktionsbeschreibung, Strukturbeschreibung, geometrische Beschreibung und Testbeschreibung, Abb. 1.3).
Gesamtsystem Teilsysteme Funktionsbl¨ ocke
Entwurfsraum
Gatter Transistoren
Abb. 1.3. Entwurfsraum digitaler Schaltungen
Eine Funktionsbeschreibung ist ein Algorithmus oder Formalismus, der die Abbildung der Eingaben einer Schaltung auf ihre Ausgaben beschreibt. Das kann ein ganz normales Programm sein. Funktionsbeschreibungen dienen zur Simulation der Zielfunktion und als Funktionsvorgabe für die Schaltungssynthese. Eine Strukturbeschreibung beschreibt die Zusammensetzung eines Systems aus Teilsystemen. Sie enthält zusätzliche Informationen über die Realisierung und kann durch Einsetzen der Funktionsbeschreibungen für die Teilschaltungen in eine Funktionsbeschreibung des Gesamtsystems umgerechnet
6
1 Modellbildung und Simulation
werden. Jeder Struktur ist damit eindeutig eine Funktion zugeordnet. Umgekehrt kann jede Funktion durch praktisch unbegrenzt viele Schaltungen nachgebildet werden, die sich im Aufwand und anderen Optimierungskenngrößen unterscheiden. Die Suche nach einer geeigneten Struktur für eine Funktion ist praktisch eine Optimierungsaufgabe. Eine geometrische Beschreibung beschreibt die Abmessungen und die Anordnung der einzelnen Teilschaltungen und Verbindungen auf einer Leiterplatte oder auf einem Schaltkreis. Sie enthält die Strukturbeschreibung, ist aber für strukturbasierte Algorithmen viel zu überladen. Die Struktur ist eindeutig zugeordnet. Umgekehrt kann jede Strukturbeschreibung durch unbegrenzt viele unterschiedliche geometrische Beschreibungen nachgebildet werden. Das Ergebnis des geometrischen Entwurfs bildet die Grundlage für die Generierung der Fertigungsdaten sowie für die Berechnung der tatsächlichen Verzögerungszeiten, des tatsächlichen Schaltungsaufwands etc. Die Testbeschreibungen werden zur Aufdeckung von Entwurfs- und Fertigungsfehlern benötigt. Sie beschreiben Testabläufe, Testeingaben, Soll-Ausgaben, Probemöglichkeiten, Plausibilitätskriterien für die Ausgaben etc. Die Entwicklung der Testbeschreibungen verursacht in der Regel mehr als die Hälfte des Gesamtentwurfsaufwands (Abb. 1.4). Funktionsbeschreibung x1 x2
y = x2 ∨ x1
Strukturbeschreibung &
y
Testbeschreibung: Testbeispiele Kontrollfunktionen
x2 x1 0 0 0 1 ...
y 1 0 ...
Geometrie
&
Entwurfsschritt mit vielen M¨oglichkeiten eindeutige Zuordnung
Abb. 1.4. Beschreibungsarten und ihre Beziehungen untereinander
1.1.3 Entwurfsablauf Das Voranschreiten eines Entwurfsprojekts lässt sich als eine Bewegung in dem Entwurfsraum in Abb. 1.3 beschreiben. Es gibt zwei Grundstrategien: Top-Down- und Bottom-Up-Entwurf. Ein Top-Down-Entwurf beginnt mit der Spezifikation der Anforderungen. Zuerst werden die Ziele und die wesentlichen Anforderungen zusammengestellt. Darauf aufbauend werden schrittweise informale und halbformale bis hin zu formalen, im Rechner simulierbaren Funktionsbeschreibungen entwickelt. Der Eintrittspunkt in den rechnergestützten Entwurf ist die obere linke Ecke des Entwurfsraums. Von dort aus startet die Entwicklung der Struktur. Zuerst wird die Gesamtfunktion entworfen und
1.1 Entwurf digitaler Schaltungen
7
durch eine Struktur aus miteinander kommunizierenden Teilschaltungen nachgebildet. Danach wird rekursiv absteigend mit den Teilschaltungen genauso verfahren. Später im Entwurfsprozess werden die Teilschaltungen platziert und verdrahtet. Der Entwurfsraum füllt sich tendenziell von oben links nach unter rechts. Nach jedem Entwurfsschritt folgen Simulationen und andere Kontrollen, ob die gefundene Lösung die Entwurfsziele befriedigt (Abb. 1.5). Entwurfsbeschreibung, Randbedingungen Funktion
Struktur Geometrie Top-DownEntwurf
BottomUp-Entwurf Simulationsmodelle Testdaten
grob Funktionseinheiten Funktionsbl¨ ocke Gatter Transistoren
fein Fertigungsdaten Konfigurationsdaten
Abb. 1.5. Entwurfsstrategien
Ein Bottom-Up-Entwurf durchläuft den Entwurfsraum in entgegengesetzter Richtung. Aus Bauteilen mit bekannter Funktion und Geometrie werden größere Funktionseinheiten zusammengesetzt. Die Optimierungsziele hinsichtlich • • • •
der der des der
Größe, Geschwindigkeit, Stromverbrauchs und Testbarkeit
lassen sich dabei besser berücksichtigen. Aber es ist schwer, auf diese Weise eine vorgegebene Zielfunktion nachzubilden. Der Bottom-Up-Entwurf dient deshalb meist dazu, Schaltungen quasi auf Vorrat zu konstruieren und in Bibliotheken oder in Form von Schaltungsgeneratoren für den Top-Down-Entwurf bereitzustellen. Vorentworfene Schaltungen kann es für jede Hierarchieebene geben: Gatter, Rechenwerke, Prozessoren, Speichergeneratoren etc. Praktische Entwürfe arbeiten entsprechend nach einer Mischstrategie, bei der sich Top-Down- und Bottom-Up-Entwurf in der Mitte treffen. Mensch-Maschine-Interaktion Die vielen unterschiedlichen Beschreibungsarten und die Werkzeuge2 zu ihrer Bearbeitung werden vom Rechner in einer für den Entwickler handhabbaren 2
übliche Bezeichnung für Entwurfsprogramme
8
1 Modellbildung und Simulation
Form verwaltet. Um die Daten- und Werkzeugverwaltung in einem Entwurfssystem für digitale Schaltungen zu verstehen, muss man danach fragen, wie der Entwickler damit arbeitet. Er bekommt informale Zielvorgaben und setzt sie manuell in formale, für den Rechner verständliche Funktionsbeschreibungen um. Die Schnittstelle für die Rechnereingabe bilden Programmiersprachen und Eingabemasken. Rechnerintern werden alle neuen und geänderten Eingabedaten zuerst analysiert. Die Analyse generiert Fehlermeldungen oder Erfolgsmeldungen und weiterverarbeitbare Datenobjekte. Die Werkzeuge für die Weiterverarbeitung sind Simulatoren, Analysewerkzeuge zur Bestimmung von Kennwerten, z.B. für die Geschwindigkeit, Synthesewerkzeuge zur Umwandlung von Funktions- in Strukturbeschreibungen und einige mehr. Der Entwickler gibt nach jeder Eingabeänderung das zu bearbeitende Entwurfsobjekt3 und das Bearbeitungsziel (nur Analyse, auch Simulation etc.) vor. Der Rechner arbeitet, wenn kein Fehler auftritt, alle Schritte bis zu diesem Entwurfsziel ab und liefert Rückmeldungen in Form von Erfolgsmeldungen, Warnungen, Gütekennzahlen etc. Aus diesen Rückmeldungen schätzt der Entwickler ab, was weiter zu tun ist. Entweder er bessert iterativ nach, indem er seine Eingabebeschreibung ändert und die Berechnung neu startet, oder er beginnt mit dem nächsten Entwurfsschritt. Insgesamt ähnelt dieser Prozess einem Regelkreis mit dem Entwickler als Regler und dem Rechner als Regelstrecke (Abb. 1.6). Entwurfsziele Entwickler R¨ uckmeldungen: Fehlermeldungen Warnungen G¨ utekennzahlen Simulationsergebnisse vom Rechner erzeugte Entwurfsbeschreibungen
Manuelle Beschreibungen: Entwurfsbeschreibungen Randbedingungen Testbeispiele Entwurfssystem Entwurfsergebnisse: Fertigungsdaten Konfigurationsdaten Dokumentationen Testdaten Simulationsmodelle
Abb. 1.6. Die Mensch-Maschine-Interaktion in einem Entwurfsprozess
Entwurfsautomatisierung Ein Entwurfsschritt beinhaltet in der Regel Entscheidungen zwischen vielen Alternativen. Das gilt für die Festlegung der Funktion, die strukturelle 3
in einer hierarchischen Beschreibung das oberste Objekt in der Beschreibungshierarchie
1.1 Entwurf digitaler Schaltungen
9
Nachbildung der Zielfunktion und für die geometrische Anordnung und Verdrahtung der Strukturelemente. Entwurfswerkzeuge zur Automatisierung von Entwurfsschritten arbeiten deterministisch. Das Ergebnis ist eine Funktion der Entwurfseingabe sowie von optionalen Steuerparametern. Ohne Steuerparameter legt die Entwurfsvorgabe die Lösung, die gefunden wird, eindeutig fest. Bei dieser Einschränkung des Lösungsraums fallen natürlich nicht nur schlechte, sondern auch gute Lösungen weg (Abb. 1.7). viele M¨oglichkeiten (Entwurfsschritt)
Menge der m¨oglichen Entwurfsergebnisse L¨osungsmenge
eindeutig (Probe) Vorgabe Aufgabe Funktion Struktur
Ziel Funktion Struktur Geometrie
Ergebnisbesch¨ankung durch die Automatisierung Menge der guten L¨osungen
Abb. 1.7. Beschränkung des Lösungsraums von Entwurfsschritten durch die Automatisierung
Die Beschränkung des Lösungsraums hat zur Folge, dass ein einzelner automatisierter Entwurfsschritt im Durchschnitt schlechtere Ergebnisse als eine gute Entwurfsentscheidung in einem Handentwurf liefert. Gut und schlecht bezieht sich dabei auf Kenngrößen wie den Schaltungsaufwand, die Geschwindigkeit etc. Aber die Automatisierung hat auch prinzipielle Vorzüge. Der Rechner ist viel schneller und kann in kürzerer Zeit viel mehr Lösungsmöglichkeiten untersuchen und vergleichen. Daraus folgt eine spezielle Optimierungsstrategie. Ähnlich wie bei einem genetischen Algorithmus wird die Eingabebeschreibung variiert. Gute Lösungsdetails werden mit anderen guten Lösungsdetails kombiniert. Ein mit dieser Art der Optimierung gut vertrauter Entwickler ist durchaus in der Lage, Schaltungen zu entwerfen, die ähnlich wenig Chipfläche benötigen und vergleichbar schnell sind wie ein professioneller Handentwurf. Dazu kommen die Vorteile einer Entwurfsautomatisierung, nämlich • • •
deutlich kürzere Gesamtentwurfszeiten, besser vorhersagbare Güte- und Aufwandskennzahlen für die Projekt- und Kostenplanung und eine geringere zu erwartende Anzahl von Entwurfsfehlern.
1.1.4 VHDL als formale Beschreibungsplattform Definition 1.5 (Syntax einer Programmiersprache) Die Syntax einer Programmiersprache ist ein System von Regeln, nach denen erlaubte Konstrukte (Bezeichner, Anweisungen etc. bis hin zu kompletten Programmen) aus Zeichen eines Zeichenvorrats zusammengesetzt werden.
10
1 Modellbildung und Simulation
Definition 1.6 (Entwurfseinheit) Eine Entwurfseinheit (entity*) ist eine abgegrenzte Funktionseinheit, die über Anschlusssignale mit anderen Entwurfseinheiten kommuniziert. Definition 1.7 (Entwurfsbeschreibung) Eine Entwurfsbeschreibung (architecture*) beschreibt die innere Struktur oder Funktion einer Entwurfseinheit. Definition 1.8 (Testrahmen) Ein Testrahmen (testbench*) ist eine Entwurfseinheit zur Beschreibung eines Testablaufs. Definition 1.9 (Anschlusssignal) Ein Anschlusssignal (port*) ist ein Signal, über das eine Entwurfseinheit mit ihrer Umgebung kommuniziert. Definition 1.10 (Prozess) Ein Prozess ist ein Programmrahmen für eine imperative Beschreibung eines Teilsystems in einem nebenläufig arbeitenden Gesamtsystem. Definition 1.11 (Package) Ein Package ist eine Sammlung von Vereinbarungen und Unterprogrammen für die Nutzung in Entwurfseinheiten und anderen Packages. Definition 1.12 (Bibliothek) Eine Bibliothek (library*) ist eine Sammlung von Entwurfseinheiten und Packages. (* – VHDL-Schlüsselwort zur Vereinbarung oder Referenzierung eines Objekts von diesem Typ.) Eine formale, manuell lesbare Beschreibung erfolgt in der Informatik vorzugsweise in einer künstlichen Sprache. In den Anfangszeiten des digitalen Schaltungsentwurfs hatten viele Entwurfswerkzeuge ihre eigenen Sprachen zur Beschreibung der Entwurfsobjekte. Inzwischen haben sich zumindest für die manuellen Aufgabenbereiche wenige universelle, standardisierte HardwareBeschreibungssprachen durchgesetzt. Die in Europa verbreitetste Hardware-Beschreibungssprache ist VHDL. Andere Hardware-Beschreibungssprachen sind Verilog und System-C [31]. Das Akronym VHDL steht für V H D L
VHSIC (Very High Speed Integrated Circuits) Hardware Description Language.
Mit VHDL können Funktionen, Strukturen und Testabläufe vom Gesamtsystem bis herunter zum Gatterniveau formal beschrieben werden (Abb. 1.8). Als Begleitmaterial zum Buch wurden zahlreiche Beschreibungsbeispiele in VHDL entwickelt und mit dem frei verfügbaren Simulator GHDL [3] und dem ebenfalls für Studenten frei verfügbaren Entwurfssystem ISE von Xilinx [5] getestet. Die Beispiele stehen im Internet unter [27] und laufen
Strukturbeschreibung
Gesamtsystem
+
+
+
Teilsysteme
+
+
+
Funktionsbl¨ ocke
+
+
+
Gatter
+
+
+
Transistoren
·
·
·
BeschreibungsEbene art in der Hirarchie
11
TestBeschreibung
Funktionsbeschreibung
geometrische Beschreibung
1.1 Entwurf digitaler Schaltungen
¨blich +u · m¨oglich
Abb. 1.8. Mit VHDL beschreibbare Entwurfsobjekte
unter Linux und Windows. Der VHDL-Simulator GHDL ist ein Kommandozeilenprogramm, das VHDL-Programme in die Sprache »C« und weiter in ausführbare Programme übersetzt. Die Visualisierung der berechneten Signalverläufe erfolgt mit dem gleichfalls unter Linux und Windows frei verfügbaren Programm GTKWAVE [4]. Das Entwurfssystem ISE dient zum Entwurf digitaler Schaltungen mit programmierbaren Logikschaltkreisen von Xilinx. Für die Beispiele genügt die für Studenten freie Web-Version. Um die beschriebenen Schaltungen nicht nur zu analysieren und zu simulieren, sondern auch auszuprobieren, wird zusätzlich ein Prototyp-Board benötigt. Eine gute und kostengünstige Wahl ist das Spartan-3-Board von Digilent Inc. [2], für das es gute Praktikumsanleitungen und sogar ein Lehrbuch mit Übungsbeispielen gibt [17]. Das Grundkonzept von VHDL VHDL wurde ursprünglich zur Simulation digitaler Schaltungen entwickelt, lehnt sich an die Programmiersprache Ada an und ist unter IEEE Std.1076 standardisiert [9]. Ein VHDL-Projekt besteht aus Entwurfseinheiten und Packages, die in Bibliotheken verwaltet werden. Entwurfseinheiten dienen zur Beschreibung von Schaltungsmodellen und Testrahmen. Packages sind Sammlungen von Datentypen, Konstanten und Unterprogrammen. Abbildung 1.9 zeigt die Grundstruktur einer VHDL-Entwurfseinheit. Die Beschreibung der Sprache erfolgt in der Backus-Naur-Form (BNF). Schlüsselworte sind fett und Platzhalter (Nicht-Terminal-Symbole) für einzusetzende Beschreibungselemente kursiv dargestellt. Beschreibungselemente in [...] sind optional. Sie können, müssen aber nicht enthalten sein. Beschreibungselemente in {...} können beliebig oft enthalten sein. VHDL unterscheidet nicht zwischen Groß- und Kleinschreibung. Hier im Buch wird für alle vordefinierten Bezeichner aus standardisierten Bibliotheken zur Unterscheidung von selbst definierten Bezeichnern die Schriftart capitalized verwendet. Kommentare
12
1 Modellbildung und Simulation
beginnen mit »--«. Weitere Regeln, die Liste der Schlüsselworte, Bildungsregeln für Bezeichner etc. sind im Anhang A.1.1 nachzulesen. Das Standardwerk zu VHDL ist [11]. Lehrbücher sind z.B. [16, 17, 26, 31, 33, 37, 41].
–- Vorspann 1: library ieee; 2: use ieee.std_logic_1164.all; –- Schnittstellenbeschreibung 3: entity Bezeichner_Entwurfseinheit is 4: [generic (Liste_der_Konfigurationsparameter );] 5: [port(Liste_der_Anschlusssignale);] 6: end entity; –- Beschreibung der Funktion oder Struktur 7: architecture Beschreibungsname of Bezeichner_Entwurfseinheit is 8: {Vereinbarung} 9: begin 10: {Beschreibungselement_für_die_Struktur_oder_Funktion} 11: end architecture; Abb. 1.9. Beschreibungsstruktur einer VHDL-Entwurfseinheit
Die Beschreibung einer Entwurfseinheit besteht aus dem Vorspann, einer Schnittstellenbeschreibung und einer oder mehreren Beschreibungen der Funktion oder Struktur. Der Vorspann importiert Bezeichner für Datentypen, Unterprogramme etc. aus Packages in den Namensraum der Entwurfseinheit. Zeile 1 definiert den Bezeichner ieee4 als Bibliothek. Zeile 2 importiert alle Vereinbarungen aus dem Package ieee.std_logic_1164. Die Schnittstelle einer Schaltung beschreibt die Anschlusssignale (Zeile 5) und für parametrisierte Entwurfsbeschreibungen auch die Konfigurationsparameter (Zeile 4) als Liste. Die Beschreibung des Innenlebens erfolgt getrennt von der Schnittstelle (Zeile 7 bis 11) und hat einen eigenen Bezeichner (Zeile 7). Der Beschreibungsname dient in übergeordneten Strukturbeschreibungen zur Unterscheidung zwischen unterschiedlichen Beschreibungsversionen mit derselben Schnittstelle. Im Vereinbarungsteil der Beschreibung (Zeile 8) werden hauptsächlich die internen Signale und Konstanten festgelegt. Der Anweisungsteil (Zeile 10) kann drei Arten von Beschreibungselementen enthalten: • • • 4 5
Beschreibungen zur Einbindung von Teilschaltungen, Prozesse als Rahmen für imperative Beschreibungen und nebenläufige Anweisungen5 . ieee ist eine Bibliothek mit standardisierten Packages, die in jeder VHDLUmgebung verfügbar ist. nebenläufige Signalzuweisungen und nebenläufige Prozeduraufrufe als Kurzschreibweisen für spezielle Prozesse mit nur einer Anweisung
1.1 Entwurf digitaler Schaltungen
13
Zur Simulation muss eine Entwurfseinheit als Teilschaltung in einen Testrahmen eingebettet werden. Ein Testrahmen ist eine Entwurfseinheit ohne Konfigurationsparameter und ohne Anschlüsse. Das kleinste ausführbare VHDLProgramm ist ein Testrahmen mit einem Prozess, der eine Meldung ausgibt und sich beendet (Abb. 1.10). 1: entity HalloWelt is 2: end entity; 3: architecture Verhalten of HalloWelt is 4: begin 5: process 6: begin –- Meldung ausgeben und Prozess beenden 7: report "Hallo Welt"; 8: wait; 9: end process; 10: end architecture;
⇒WEB-Projekt: P1.1/HalloWelt.vhdl
Abb. 1.10. Ausführbares VHDL-Programm (Zeile 1-2: Schnittstelle; Zeile 3-10: Beschreibung; Zeile 5-9: Prozess; Zeile 7: Textausgabe; Zeile 8: Anweisung, die den Prozess schlafen legt)
Ein Prozess ist der Programmrahmen für eine imperative Beschreibung. Die eingerahmte Anweisungsfolge wird zur Simulation in eine Endlosschleife übersetzt und zyklisch Schritt für Schritt abgearbeitet. Nach der letzten Anweisung folgt wieder die erste Anweisung. Jeder Prozess muss deshalb mindestens eine Warteanweisung enthalten, die ihn schlafen legt und den Kontrollfluss an den Simulator zurückgibt. Im Beispiel hat die Warteanweisung keine Weckbedingung, d.h., der Prozess terminiert dauerhaft. Da es der einzige Prozess ist, endet auch die gesamte Simulation nach der Textausgabe. Die Simulation soll mit dem Simulator GHDL erfolgen. Die Befehle für die Analyse, das Übersetzen und den Start der Simulation des VHDL-Programms lauten: Kommando
Aktion
ghdl -a HalloWelt.vhdl (analyze) ghdl -m HalloWelt (make) ghdl -r HalloWelt (run)
Syntaxtest und Ablegen der vorübersetzten Beschreibung in der Arbeitsbibliothek »work« Zusammensetzen aller Beschreibungsteile zu einem ausführbaren Programm Start der Simulation
Das Beispielprogramm gibt nach dem Simulationsstart nur die Meldung
14
1 Modellbildung und Simulation HalloWelt.vhdl:7:5:@0ms:(report note): Hallo Welt
aus und beendet sich. Außer Entwurfseinheiten gibt es in VHDL eine zweite Art von Beschreibungen. Das sind die Packages. Ein Package besteht aus einem Kopf mit Vereinbarungen von Datentypen, Konstanten, Unterprogrammen etc. und einem Körper mit den Beschreibungen der Unterprogramme. Im Package in Abb. 1.11 sind im Kopf eine Textkonstante und eine Funktion vereinbart. Die Funktion bildet aus einem Eingabetext einen Ausgabetext. Aus der Beschreibung im Package-Körper ist abzulesen, dass der übergebene Text einfach nur unverändert zurückgegeben wird.
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
package HalloWelt_pack is constant c: string := "Hallo Zeichenkette"; function SchreibeText(s: string) return string; end package; package body HalloWelt_pack is function SchreibeText(s: string) return string is begin return s; end function; ⇒WEB-Projekt: P1.1/HalloWelt_pack.vhdl end package body;
Abb. 1.11. Package für das Projekt »Hallo Welt«; Zeile 1-4: Kopf; Zeile 5-10: Körper; Zeile 2: Konstantenvereinbarung; Zeile 3: Funktionsvereinbarung; Zeile 6-9: Funktionsbeschreibung
Zur Nutzung des Packages muss es zuerst analysiert und in eine Bibliothek – standardmäßig die Arbeitsbibliothek work – abgelegt werden. Von dort aus können die Vereinbarungen – im Beispiel die der Konstanten »c« und der Funktion »SchreibeText(...)« – von jeder Entwurfseinheit und jedem anderen Package importiert werden. Abbildung 1.12 zeigt ein erweitertes Hallo-Welt-Programm, das die beiden Package-Vereinbarungen nutzt. Der Bibliotheksbezeichner work für die Arbeitsbibliothek ist vordefiniert. Der Vorspann beginnt deshalb gleich mit der Use-Anweisung (Zeile 1). Die Schnittstellenbeschreibung (Zeile 2 und 3) ist wieder leer. Die beiden ersten Warteanweisungen (Zeile 9 und 11) haben eine Wartezeit als Weckbedingung und legen den Prozess für 1 ms Simulationszeit6 schlafen. Nach dem Aufwachen gibt der Prozess jeweils die nächste Meldung aus. Die erste Meldung (Zeile 8) erscheint zur Simulationszeit »@0ms«. Die zweite Meldung mit der Textkonstante »c« (Zeile 10) wird zur Simulationszeit »@1ms« und die dritte Meldung mit dem Rückgabewert der Funktion (Zeile 6
Die Simulationszeit ist eine Rechengröße im Simulator, die im Beispiel von den beiden ersten Warteanweisungen jeweils um die Wartezeit weitergestellt wird.
1.1 Entwurf digitaler Schaltungen
15
12) zur Simulationszeit »@2ms« ausgegeben. Die letzte Warteanweisung (Zeile 13) legt den Prozess dauerhaft schlafen und beendet die Simulation. 1: use work.HalloWelt_pack.all; 2: entity HalloWelt1 is 3: end entity; 4: architecture Verhalten of HalloWelt1 is 5: begin 6: process 7: begin 8: report "Hallo Welt"; 9: wait for 1 ms; 10: report c; 11: wait for 1 ms; 12: report SchreibeText("Hallo Funktionsaufruf"); 13: wait; 14: end process; ⇒WEB-Projekt: P1.1/HalloWelt1.vhdl 15: end architecture; Abb. 1.12. Nutzung des Packages
Zur Ausführung werden zuerst das Package und dann die Entwurfseinheit analysiert. Dann wird aus beiden ein ausführbares Programm gebaut und dieses gestartet: ghdl ghdl ghdl ghdl
-a HalloWeltPack.vhdl -a HalloWelt1.vhdl -m HalloWelt1 -r HalloWelt1
Der Aufruf mit Option »m« bedeutet »make«. Er baut das ausführbare Simulationsobjekt neu zusammen und aktualisiert, falls die Quelldateien geändert wurden, vorher automatisch die Objektdateien in den Bibliotheken. Die Dateien mit den VHDL-Beschreibungen und ein Hilfetext dazu stehen im Web unter [27]. 1.1.5 Zusammenfassung und Übungsaufgaben Der Entwurf digitaler Schaltungen gleicht heute in vielerlei Hinsicht einem Software-Entwurf. Er erfolgt rechnergestützt und in einer Hochsprache. Die Beschreibungen sind hierarchisch strukturiert. Die Routinearbeit erledigt der Rechner. Der manuelle Entwurfsaufwand entfällt überwiegend auf den Test und die Fehlersuche. Mit den Millionen von Gattern der fertigen Schaltung beschäftigt sich ein Hardware-Entwickler genauso wenig wie ein Software-Entwickler mit den Millionen von Maschinenbefehlen seines fertigen Programms. Es gibt aber auch Unterschiede. Der Hardware-Entwurf
16
1 Modellbildung und Simulation
kennt zwei zusätzliche Beschreibungsarten: Strukturbeschreibungen und geometrische Beschreibungen. Das vergrößert den Entwurfsraum und die Entwurfskomplexität erheblich. Die Kenngrößen für die Optimierung – Aufwand, Geschwindigkeit, Stromverbrauch etc. – haben bei Hardware einen höheren Stellenwert und sind erst später im Entwurfsprozess genau bestimmbar. Ein Hardware-Entwurf ist dadurch kein vollständig automatisierbarer Vorgang wie das Übersetzten eines Programms, sondern eine Art manuell gesteuerte heuristische Suche. Ein Hardware-Entwickler nutzt in wesentlich größerem Umfang vorentworfene Teilsysteme und investiert wesentlich mehr Zeit in die Optimierung, den Test und das Ausprobieren unterschiedlicher Alternativen. Das erfordert ein größeres Detailverständnis darüber, wie der Rechner Entwurfsund Testbeschreibungen analysiert, simuliert und in Schaltungen übersetzt. Eine Schlüsselstellung nimmt dabei die Hardware-Beschreibungssprache ein, über die der Entwickler mit dem Rechner kommuniziert. Aufgabe 1.1 Was ist in VHDL eine Entwurfseinheit und was ist ein Package? Aufgabe 1.2 a) Was sind eine Funktionsbeschreibung, eine Strukturbeschreibung und eine geometrische Beschreibung? b) Wozu dient der Testrahmen in einem Simulationsmodell? c) Was sind die Vorteile und die Nachteile formaler und informaler Modelle? Aufgabe 1.3 Schreiben Sie ein VHDL-Programm, das fünfmal hintereinander im Abstand von zwei Millisekunden simulierter Zeit eine Meldung mit dem Nachrichtentext »Hallo« ausgibt. Testen Sie das Programm mit GHDL oder einem anderen VHDL-Simulator. Aufgabe 1.4 Schauen Sie sich im Anhang A.1.3 die Bildungsregeln für Bezeichner an. Sind die Zeichenfolgen »To«, »3dimensional«, »bereit«, »tTyp« und »_escape« zulässige VHDL-Bezeichner?
1.2 Funktion, Struktur und Simulation Definition 1.13 (Kombinatorische Schaltung) Eine kombinatorische Schaltung (engl. combinational circuit, im deutschen Sprachraum auch Schaltnetz) ist eine digitale Schaltung ohne Speicherverhalten, die kontinuierlich binäre Eingabesignale auf binäre Ausgabesignale abbildet.
1.2 Funktion, Struktur und Simulation
17
Definition 1.14 (Kombinatorische Funktion) Eine kombinatorische Funktion beschreibt das logische Eingabe-Ausgabe-Verhalten einer kombinatorischen Schaltung ohne Berücksichtigung des Zeitverhaltens. Definition 1.15 (Signal) Ein Signal ist der zeitliche Werteverlauf einer physikalischen Größe. Definition 1.16 (Datentyp) Der Datentyp beschreibt den Wertebereich eines Datenobjekts und optional die Codierung der darstellbaren Werte.
1.2.1 Signale und Datentypen In der digitalen Schaltungstechnik wird die zu verarbeitende Information durch Signale dargestellt. Ein Signal ist der zeitliche Werteverlauf einer physikalischen Größe. In einer Schaltung ist die physikalische Größe in der Regel die Spannung zwischen der signalführenden Leitung und dem Bezugspunkt (⊥). Die Digitaltechnik unterscheidet nur zwischen großen und kleinen Werten. Dazwischen gibt es einen Sicherheitsbereich. In diesem Bereich ist der logische Wert ungültig und darf nicht weiterverarbeitet werden. Der Sicherheitsbereich verhindert eine Fehlklassifikation von »0« als »1« und umgekehrt (Abb. 1.13).
Quelle Potenzial ϕ(x)
x (Signal)
Empf¨ anger
u(x) = ϕ(x) Signal
St¨orung
groß Sicherheitsbereich klein
Signalwechsel
Signalwert erweitert
bin¨ ar
x=1
x=1
x =X
nicht darstellbar
x=0
x=0
Abb. 1.13. Signal zur Darstellung von Logikwerten
Binäre Datentypen Jedes Datenobjekt hat einen Typ, der den Wertebereich und die Wertedarstellung beschreibt. Ein zweiwertiges Datenobjekt wird umgangssprachlich als Bit bezeichnet. Die vordefinierten bitorientierten VHDL-Datentypen sind: type bit(1) is (’0’, ’1’)(2) ; type boolean(1) is (false, true)(3) ; type std_logic(4) is ( ’U’, ’X’, ’0’, ’1’ , ’Z’, ’W’, ’L’, ’H’, ’-’ );
18
1 Modellbildung und Simulation
((1) – definiert im Package std.standard; (2) – die Elemente sind druckbare Zeichen; (3) – die Elemente sind symbolische Konstanten; (4) – definiert im Package ieee.std_logic_1164; ’U’ – nicht initialisiert; ’X’ – unbestimmt, ’Z’ inaktiv/hochohmig; ’W’, ’L’, ’H’ – schwache, von einer normalen Quelle überschreibbare Werte; ’-’ – beliebig). Die Datentypen bit und std_logic stellen die beiden zu unterscheidenden Logikwerte als druckbare Zeichen dar. Im Weiteren gilt die Zuordnung, dass kleine Werte durch das Zeichen »0« und große Werte durch das Zeichen »1« dargestellt werden. Der Typ std_logic kann zusätzlich zu »0« und »1« auch eine Reihe von Pseudo-Werten darstellen, die alle bei der Simulation eine spezielle Bedeutung haben. Der Pseudo-Wert für ungültig ist »X«. Der Typ boolean stellt binäre Informationen mit den symbolischen Wahrheitswerten false und true dar. Halbformale Beschreibungen (Datenblätter etc.) verwenden statt »0« und »1« oft die Bezeichnungen »inaktiv« und »aktiv«. Diese Bezeichnungen beziehen sich auf die Funktion eines Signals. Es gibt z.B. Freigabesignale für Treiber, Initialisierungssignale für Register und Schreib- und Lesesignale für Blockspeicher. Aktiv bedeutet, dass die zugeordnete Funktion ausgeführt wird, und inaktiv, dass sie nicht ausgeführt wird. Ein aktives Schreibsignal bewirkt einen Schreibvorgang, ein aktives Lesesignal einen Lesevorgang. Der aktive Signalwert, der die Funktion auslöst, kann dabei entweder »groß« oder »klein« sein. Ist der aktive Signalwert der große Wert, spricht man von einem high-aktiven und sonst von einem low-aktiven Signal. Bitvektoren Größere Informationseinheiten werden in einer digitalen Schaltung mit mehreren Bits dargestellt. Eine Information aus n Bits erlaubt es, bis zu N ≤ 2n
(1.1)
Werte zu unterscheiden (Abb. 1.14 a). Umgekehrt verlangt die Unterscheidung von N > 2 Werten – z.B. von N Zahlenwerten – einen Bitvektor mit einer Bitanzahl von mindestens n ≥ log2 (N ) (1.2) Signalwerte aus mehreren Einzelbits sind gültig, wenn alle Bits gültig sind (Abb. 1.14 b). Bitvektoren der Sprache VHDL sind eindimensionale Felder mit dem Elementtyp bit bzw. std_logic: type bit_vector is array(4) (natural range <>)(5) of bit(6,7) ; type std_logic_vector is array (natural range <>) of std_logic;(8)
((4) – Schlüsselwort für die Definition von Feldern; (5) – Beschränkung des Indexbereichs; (6) – Elementtyp; (7) – definiert im Package std.standard; (8) – definiert im Package ieee.std_logic_1164). Den einzelnen Werten eines Bitvektors können wieder Bedeutungen zugeordnet sein, z.B. die Zustände
1.2 Funktion, Struktur und Simulation n=1 n=2 n=3 n=4
a)
b0 b1 b2 b3 ···
0 0 0 0
1 0 0 0 2
0 1 0 0 4
1 1 0 0
0 0 1 0
1 0 1 0
0 1 1 0
1 1 1 0
0 0 0 1
1 0 0 1
0 1 0 1
1 1 0 1
0 0 1 1
1 0 1 1
0 1 1 1
19
1 1 ··· 1 ··· 1 ···
N =8
N = 16 Zahlenwert (dezimal): 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
...
x0 x1 ux0
b) Bezugspunkt
ϕ(x0 ) x ux1
ϕ(x1 ) x = x1 x0
10
11 11
Spannungsverlauf
01
00 00
10
Signalwert ung¨ ultig
11
01 t
Abb. 1.14. Informationsdarstellung mit Bitvektoren a) Bitanzahl und die Anzahl der darstellbaren Werte b) Signalverlauf und Gültigkeitsfenster
einer Steuerung oder Zahlenwerte. Für die Darstellung von Zahlenwerten gibt es aber auch eigene Typen. Der vordefinierte Basistyp für ganze Zahlen ist integer. Von integer sind zwei Untertypen abgeleitet: subtype natural is integer range 0 to integer’high; subtype positive is integer range 1 to integer’high;
(’high – Attribut für Zahlentypen, das den größten darstellbaren Wert zurückgibt). Zahlentypen werden z.B. für die Elementauswahl in Vektoren und Feldern benötigt. Die Beschreibung »(natural range <>)« beschränkt den Indexbereich auf den Wertebereich von natural. Das bedeutet, dass für die Indexgrenzen und Indexwerte nur ganzzahlige Werte von null bis zum Maximalwert von integer zugelassen sind. Wir kommen auf die hier eingeführten Datentypen später zurück. Simulierte Zeit Signale sind zeitliche Werteverläufe, die durch Wert-Zeit-Tupel dargestellt werden. Die Zeit hat in VHDL den vordefinierten Basistyp time. Zeitangaben bestehen immer aus einem Wert und einer Maßeinheit. Zulässige Maßeinheiten der Zeit sind fs (Femtosekunden), ps (Pikosekunden), ns (Nanosekunden), us (Mikrosekunden), ms (Millisekunden), sec (Sekunden), min (Minuten) und hr (Stunden) (siehe später physikalische Typen, Abschnitt 3.2.3). Für Verzögerungszeiten ist davon ein Untertyp abgeleitet, der nur nicht negative Zeiten darstellen kann: subtype delay_length is time 0 fs to time’high;
20
1 Modellbildung und Simulation
1.2.2 Signalflussplan Der Signalflussplan (Schaltplan oder Signalflussgraph) stellt ein signalverarbeitendes System als Graph mit den Verarbeitungsbausteinen als Knoten und den Signalen als Kanten dar. Die Kanten sind gerichtet. Jedes Signal hat eine Quelle, die den Signalverlauf einspeist, und einen oder mehrere Empfänger, die den Signalverlauf auswerten7 . Der Signalfluss einer kombinatorischen Schaltung ist darüber hinaus zyklenfrei und verläuft von den Eingängen zu den Ausgängen. In der graphischen Darstellung verläuft der Signalfluss vorzugsweise von links nach rechts. Abbildung 1.15 zeigt ein Beispiel. Die Eingabesignale x1 bis x4 werden von der Schaltungsumgebung bereitgestellt. Die drei Gatter bilden jeweils die Eingabesignale auf der linken Seite des Symbols auf das Ausgabesignal auf der rechten Seite ab.
x1 x2 x3 x4
G1 z & 1 G2 z & 2
G3 ≥1
y
Signal Verarbeitungsbaustein mit Anschl¨ ussen Symbol f¨ ur die Funktion
Abb. 1.15. Beispiel für einen Signalflussplan
Logische Grundbausteine Signalflusspläne sind Strukturbeschreibungen. Auf der Gatterebene sind die Grundbausteine logische Gatter mit den Funktionen UND, ODER etc. Ihre Symbole sind gleich mehrfach in unterschiedlicher Weise standardisiert. Die im Weiteren verwendeten Symbole lehnen sich an den Standard IEC 60617-12 an (Abb. 1.16). Auf der linken Symbolseite werden die Eingabesignale und auf der rechten Symbolseite das Ausgabesignal angeschlossen. Die Gattersymbole außer dem Treiber und dem Inverter können auch mehr als zwei Eingänge haben. Ein weiterer Grundbaustein ist der Multiplexer, ein Signalflussumschalter. Er hat mindestens zwei Dateneingänge xi und einen Auswahleingang s (engl. select). In Abhängigkeit vom Wert am Auswahleingang wird eines der Dateneingabesignale an den Ausgang y weitergeleitet. Signalverarbeitende Bausteine mit Funktionen, für die es kein spezielles Symbol gibt, werden im Weiteren durch abgerundete Rechtecke mit der Angabe der nachgebildeten Funktion dargestellt. 7
Bei Signalen mit mehreren Quellen ist gleichzeitig nur eine Quelle aktiv.
1.2 Funktion, Struktur und Simulation logische Funktion Treiber (Identit¨at): y <= x
Symbol x
logische Funktion
Symbol
y
Inverter: y <= x ¯
x
21
y
UND: y <= x0 ∧ x1 ∧ . . .
x0 xn−1
.. .
&
y
NAND: y <= x0 ∧ x1 ∧ . . .
x0 xn−1
.. .
&
y
ODER: y <= x0 ∨ x1 ∨ . . .
x0 xn−1
.. .
≥1
y
NOR: y <= x0 ∨ x1 ∨ . . .
x0 xn−1
.. .
≥1
y
x0 XOR: y <= x0 ⊕ x1 ⊕ . . . xn−1
.. .
=1
y
XNOR: x0 y <= x0 ⊕ x1 ⊕ . . . xn−1
.. . ==
y
0 1
y
Multiplexer: wenn s = 0 dann y <= x0 sonst y <= x1
x0 x1
allg. Funktion: y <= f (x)
s
.. . xn−1 x0
f ()
.. y0 . ym−1
Abb. 1.16. Symbole für die logischen Grundgatter (<= – Signalzuweisung)
Signalflusspläne auf der Funktionsblockebene Auf den Hierarchieebenen oberhalb der Gatterebene sind die Verarbeitungsbausteine Teilschaltungen mit komplexeren Funktionen, die zum Teil auch über Bitvektoren, die ihrerseits wieder Zahlen, Aufzählungstypen und aus diesen zusammengesetzte Typen repräsentieren können, verbunden. Wegen der großen Vielfalt möglicher Funktionen haben die Teilschaltungen auf diesen Beschreibungsebenen meist das allgemeine Symbol und einen Verweis auf eine Funktionsbeschreibung. Es gibt aber auch komplexe Standardschaltungen mit speziellen Symbolen, z.B. Register, Rechenwerke und Blockspeicher. Signale mit Typen, die mehr als zwei Werte darstellen können, – Bitvektortypen, Zahlentypen etc. – werden als dicke Linien gezeichnet. In einer formalen Darstellung ist an jede dieser Linien der Datentyp zu schreiben. Bei Bitvektoren wird oft nur die Bitanzahl angegeben. Einmündungen und Abzweigungen von Einzelbits und Teilvektoren werden durch schräge Anschlusslinien symbolisiert. Abbildung 1.17 zeigt eine Beispielschaltung. Der Signalvektor x wird hier von den beiden linken, nur halb dargestellten Teilschaltungen geliefert. Ein Teil der Signale (x2 bis x6 ) wird von einer Funktion f (x) auf einen Signalvektor y abgebildet. Von dem Vektor y reicht der Multiplexer bei x7 = 0 den unteren und sonst den oberen Teilvektor weiter. Signalflusspläne sind oft nur informale Modelle, die bestimmte Aspekte und Strukturmerkmale veranschaulichen. Informal bedeutet, dass die Schaltungen nicht vollständig oder nicht eindeutig beschrieben sind. Es können z.B. Signale fehlen, Typangaben weggelassen sein oder die Signalauswahl bei Verzweigungen und Einmündungen ist nicht eindeutig. Das ist der Tribut an die Anschaulichkeit. Für formale, simulierbare und synthetisierbare Beschreibun-
22
1 Modellbildung und Simulation ···
···
x1...5
x6,7
x2...6 x
f (x)
y
y1...4
4
y5...8
4
x7
0 1
4
z
···
Bitanzahl Multiplexer kombinatorische Schaltung Abzweigung von Teilvektoren Zusammenfassung zu einem Signalvektor
Abb. 1.17. Signalflussplan auf der Funktionsblockebene
gen ist ein Programm in einer Hardware-Beschreibungssprache besser geeignet. 1.2.3 Imperative Funktionsmodelle In einem imperativen Funktionsmodell wird die Abbildung der Signalverläufe an den Eingängen auf die Signalverläufe an den Ausgängen einer Schaltung durch nacheinander abzuarbeitende Anweisungen beschrieben. Die wichtigsten Beschreibungselemente sind Zuweisungen, Ausdrücke und Fallunterscheidungen. In den späteren Kapiteln kommen noch Schleifen und Unterprogramme hinzu. Zum Erlernen einer Hardware-Beschreibungssprache ist es zweckmäßig, die behandelten Beschreibungskonstrukte mit einem Simulator auszuprobieren. Abbildung 1.18 zeigt einen Testrahmen für den Test der Beschreibungselemente, die in diesem Abschnitt behandelt werden. Im Vorspann werden die Bibliotheksbezeichner definiert und Vereinbarungen aus Packages importiert. Bis auf Weiteres genügen zwei Packages. Das standardisierte Package ieee.std_logic_1164 ist in jeder VHDL-Entwicklungsumgebung verfügbar und definiert die Typen std_logic und std_logic_vector sowie die Operatoren und Funktionen für diese beiden Typen. Das Package »Tuc.Ausgabe« stellt eine Minimalmenge von Funktionen für die Textausgabe der Simulationsergebnisse bereit. Um es nutzen zu können, muss zuerst die Bibliothek »Tuc« erzeugt werden. Die Anleitung dazu und auch alle behandelten Beispiele stehen im Web [27]. Der Testrahmen selbst ist eine Entwurfseinheit ohne Anschlusssignale mit einem Prozess, der die zu testenden Anweisungen enthält. Prozesse werden in eine Endlosschleife übersetzt. Damit die zu testenden Anweisungen nur einmal ausgeführt werden, muss am Ende der Anweisungsfolge eine Warteanweisung ohne Weckbedingung stehen (vgl. Abschnitt 1.1.4). In den nachfolgenden VHDL-Beispielen werden, damit diese kurz und übersichtlich bleiben, nur die Vereinbarungen und die Anweisungen aufgelistet. Diese sind getrennt nach Vereinbarungen für die Entwurfseinheit, Vereinbarungen für den Testprozess, Anweisungen für den Testprozess und
1.2 Funktion, Struktur und Simulation
23
weiteren nebenläufigen Beschreibungselementen der Entwurfseinheit an den gekennzeichneten Stellen in Abb. 1.18 einzufügen. Die vollständigen Simulationsbeschreibungen stehen im Web. Der Dateiname steht jeweils im Kasten rechts unter der Beschreibung. Zusätzlich sind auf der Web-Seite Hilfetexte zur Ausführung der Simulationen und zur Visualisierung der berechneten Signalverläufe zu finden.
–- Vorspann library ieee; use ieee.std_logic_1164.all; library Tuc; use Tuc.Ausgabe.all; entity test is end entity; architecture TR of test is –- Vereinbarungen der –- Entwurfseinheit ... begin
Testprozess: process –- Vereinbarungen im Testprozess ... begin –- Anweisungsfolge im Testprozess ... wait; end process; –- weitere nebenläufige –- Beschreibungselemente ... end architecture;
Abb. 1.18. Testrahmen für VHDL-Beschreibungselemente
Definition von Datenobjekten und Zuweisungen Die Definition von Datenobjekten erfolgt in VHDL für alle Objekttypen nach demselben Schema: constant Bezeichner {,Bezeichner }: Typ [:=Anfangswert]; variable Bezeichner {,Bezeichner }: Typ [:=Anfangswert]; signal Bezeichner {,Bezeichner }: Typ [:=Anfangswert];
Hinten dem Schlüsselwort für den Objekttyp werden ein oder mehrere neue Bezeichner eingeführt. Diesen wird ein Datentyp und optional ein Anfangswert zugeordnet. Signale beschreiben die Kommunikation zwischen Prozessen und können nur als Schnittstellensignale oder interne Signale einer Entwurfseinheit vereinbart werden. Variablen als imperative Beschreibungsmittel lassen sich nur in imperativen Beschreibungsumgebungen, d.h. in Prozessen und Unterprogrammen, vereinbaren. Der Anfangswert ist der Wert, mit dem ein Datenobjekt zu Beginn der Simulation initialisiert wird. Ohne explizite Angabe legt der Datentyp den Anfangswert fest. Bei Aufzählungstypen wird das Datenobjekt ohne explizite Angabe des Anfangswertes mit dem ersten Wert der Aufzählung und bei Zahlentypen mit dem Wert der linken Bereichsgrenze initialisiert (siehe später
24
1 Modellbildung und Simulation
Abschnitt 3.2). Die nachfolgende Tabelle zeigt die impliziten Anfangswerte der eingeführten elementaren Datentypen aus Abschnitt 1.2.1. bit std_logic boolean character ’0’
’U’
false
nul
integer
natural
positive
integer’low
0
1
(’low – Attribut, das den kleinsten darstellbaren Wert eines Zahlentyps zurückgibt).
Bei der Vereinbarung von Bitvektoren ist der Indexbereich anzugeben. Die Vereinbarung kann aufsteigend oder abfallend erfolgen, z.B.: signal s: std_logic_vector(0 to 3) := "1100"; signal f: std_logic_vector(3 downto 0) := "1100";
Für std_logic_vector ist der Indexbereich auf den Wertebereich von natural beschränkt. Bei aufsteigender Vereinbarung wird bei der Zuweisung einer Bitvektorkonstante der erste Bitwert der Konstanten an das Vektorelement mit dem kleinsten und der letzte Bitwert an das Vektorelement mit dem größten Index zugewiesen. Bei einer absteigenden Vereinbarung ist die Zuordnung umgekehrt, der erste Bitwert wird wie bei der Zahlendarstellung dem Vektorelement mit dem größten Index zugeordnet. Die beiden Signalvereinbarungen oben weisen den einzelnen Vektorelementen zueinander gespiegelte Anfangswerte zu: Vektorelement s(0) s(1) s(2) s(3) Anfangswert
1
1
0
0
f(3)
f(2)
f(1)
f(0)
1
1
0
0
Ohne die Angabe des Anfangswertes werden die Vektorelemente mit den typspezifischen Anfangswerten initialisiert. Für Elemente vom Typ bit ist das »0« und für Elemente vom Typ std_logic »U«. Textausgabe Bereits die Kontrolle der Anfangswerte, mit denen die Datenobjekte zum Simulationsbeginn initialisiert werden, erfordert eine Simulation mit Textausgaben. Der vordefinierte Datentyp für Einzelzeichen ist character. Das ist ein Aufzählungstyp. Der Wertebereich ist der Latin-1-Zeichensatz, der wiederum den ASCII-Zeichensatz als Teilmenge enthält (siehe Anhang A.1.4, Abb. A.1). Die großen deutschen Umlaute »Ä«, »Ö« und »Ü« und das Zeichen für »ß« fehlen und sind selbst in Kommentaren verboten. Die kleinen Umlaute »ä«, »ö« und »ü« sind erlaubt. Alle druckbaren Zeichen sind in einfache Hochkommas eingerahmt. Die nicht druckbaren Zeichen, z.B. »LF« für den Zeilenvorschub, sind symbolische Konstanten. Zeichenketten sind eindimensionale Felder von Zeichen: type string is array (positive range <>) of character;
1.2 Funktion, Struktur und Simulation
25
Der Indexbereich von Zeichenketten ist auf den Zahlenbereich positive beschränkt. Der kleinste zulässige Indexwert ist »1«. Zeichenkettenkonstanten werden in doppelte Hochkommas eingerahmt, z.B. "Text". Bis auf Weiteres genügen folgende Textverarbeitungsfunktionen: •
Konkatenation (Verketten) von Zeichen und Zeichenketten mit dem Operator »&«, z.B. "Hallo" & ’ ’ & "Welt" 7→ "Hallo Welt"
• •
Umwandlung der Werte von Datenobjekten in eine Textdarstellung mit der im Package »Tuc.Ausgabe« überladenen Funktion str(Datenobjekt) und Schreiben eines Textes auf die Standardausgabe mit write(Zeichenkette).
Im folgenden Beispiel werden eine Variable vom Typ integer und eine Variable von Typ positive mit impliziten Anfangswerten vereinbart. Im Anweisungsteil werden beide Anfangswerte mit einem vorangestellten Text auf dem Bildschirm des Simulationsrechners ausgegeben: –- Vereinbarungen im Testprozess variable a: integer; variable b: positive; –- Anweisungsfolge im Testprozess write("Wert von a: " & str(a)); write("Wert von b: " & str(b));
⇒WEB-Projekt: P1.2/Test_Anfangswerte.vhdl
Zur Simulation werden die beiden Variablenvereinbarungen und die beiden Anweisungen in den Testrahmen in Abb. 1.18 an den gekennzeichneten Stellen eingefügt, das VHDL-Programm analysiert, zusammengebaut und ausgeführt. Für die Variable a wird der kleinste mit integer darstellbare Wert und für die Variable b der kleinste mit positive darstellbare Wert ausgegeben: > Wert von a: -2147483648 > Wert von b: 1
Der Testrahmen mit den beiden Vereinbarungen und den beiden Anweisungen steht in der Datei »Test_Anfangswerte.vhdl« im Unterverzeichnis »P1.2« auf der Web-Seite [27]. Zum Erlernen von VHDL ist es zu empfehlen, mit den kleinen vorgegebenen Beispielen zu beginnen, diese zu testen, abzuwandeln und wieder zu testen. Das ist der einfachste Weg zur Einarbeitung in eine neue Beschreibungssprache und in eine neue Entwicklungsumgebung. Variablenzuweisungen und Ausdrücke Variablen sind imperative Beschreibungsmittel. Sie können nur innerhalb von Prozessen und Unterprogrammen vereinbart und genutzt werden und dienen dort zur Aufbewahrung von Zwischenergebnissen. Die Wertzuweisung erfolgt genau wie die Zuweisung der Anfangswerte bei der Deklaration mit dem Zuweisungsoperator »:=«:
26
1 Modellbildung und Simulation Variablenname := Ausdruck ;
Links vom Zuweisungsoperator steht der Bezeichner der Variablen und rechts ein Ausdruck zur Berechnung des zuzuweisenden Wertes. Der Wert eines Ausdrucks kann eine Konstante, der Wert einer Variablen, der Wert eines Signals, das Ergebnis einer Operation oder das Ergebnis einer Funktion sein. Die Definition ist rekursiv. Die Operanden und Aufrufparameter der Funktionen sind wiederum Ausdrücke, d.h., es können entweder Konstanten, Variablen, Signale, Operationen oder Funktionen sein (Abb. 1.19 a). In der digitalen Schaltungstechnik spielen vor allem die logischen Ausdrücke mit dem Wertebereich {0, 1} und den Operatoren Invertierung, UND, ODER und exklusives ODER (XOR, modulo-2-Addition) eine Rolle. Abbildung 1.19 b zeigt die Wertetabellen und die Schlüsselworte der von VHDL unterstützten logischen Operatoren.
(logischer Ausdruck)
a)
Konstante
Variable v1
0
v2 . . .
1
Signal s1
s2 . . .
ein- oder zweistellige logische Funktion ( ⋄ )
mit ⋄ ∈ {∧, ∨, ⊕} b)
( ⋄ )
Inverter UND NAND ODER NOR XOR XNOR x ¯1 x1 ∧ x2 x1 ∧ x2 x1 ∨ x2 x1 ∨ x2 x1 ⊕ x2 x1 ⊕ x2
x2 x1 0 0 0 1 1 0 1 1 VHDLSchl¨ usselwort
1 0
0 0 0 1
1 1 1 0
0 1 1 1
1 0 0 0
0 1 1 0
1 0 0 1
not
and
nand
or
nor
xor
xnor
Abb. 1.19. Logische Ausdrücke a) rekursive Definition b) Wertetabellen und VHDL-Schlüsselworte der von VHDL unterstützten logischen Operatoren
Für std_logic sind die Operatoren für den Teilwertebereich {0, 1} in derselben Weise überladen. In Verhaltensmodellen, die auch den Pseudo-Wert »X« (ungültig) nutzen, wird der Wert »X« wie ein Signalwert behandelt, der entweder »0« oder »1« sein kann. Eine logische Verknüpfung eines ungültigen Wertes mit einem anderen Wert ergibt in der Regel wieder einen ungültigen Wert. Ausgenommen davon sind die UND-Verknüpfung mit »0«, die immer »0« ergibt, und die ODER-Verknüpfung mit »1«, die immer »1« ergibt: X=X
X∧0=0 X∧1=X X∧X=X
X∨0=X X∨1=1 X∨X=X
X⊕0=X X⊕1=X X⊕X=X
(1.3)
1.2 Funktion, Struktur und Simulation
27
Ein Ausdruck ist eine Textbeschreibung für einen baumartigen Berechnungsfluss. Beginnend mit den Operationen in den innersten Klammern werden diese ausgeführt und die Ergebnisse an die Operationen in den übergeordneten Klammern weitergegeben. Abbildung 1.20 a zeigt das am Beispiel für den Ausdruck (¯ x1 ∧ x2 ) ∨ (¯ x3 ∧ (1 ⊕ x4 )) (1.4) Bei der Nachbildung durch eine Schaltung wird der Berechnungsfluss durch einen Signalfluss nachgebildet. Aus den Kanten des Berechnungsbaums werden Signale und aus den Operatoren signalverarbeitende Bausteine. Die Graphenstruktur bleibt komplett erhalten (Abb. 1.20 b). a) Berechnungsbaum
b) Signalflussplan
( ∨ ) ( ∧ ) x1
x2
( ∧ ) ( ⊕ ) x3
1
x4
Ergebnisweitergabe Operation
x1 x2 x3 1 x4
& =1
≥1
&
Signal Verarbeitungselement
Abb. 1.20. Berechnungs- und Signalfluss für den Ausdruck aus Gleichung 1.4
Kombinatorische Funktionen mit mehreren Ausgängen verlangen eine Beschreibung mit mehreren Ausdrücken. Bei der Simulation werden die Teilergebnisse der Berechnungsbäume der einzelnen Ausdrücke in Variablen zwischengespeichert. Im Signalflussplan werden aus den Berechnungsbäumen strukturgleiche Datenflussgraphen, die ihre Zwischenergebnisse in Signalen weitergeben. Ausdrücke als Grundlage der Logikoptimierung Jeder Ausdruck ist in einen Berechnungsbaum und einen strukturgleichen Signalflussplan umrechenbar und umgekehrt. Dieser Zusammenhang spielt eine Schlüsselrolle bei der Spezifikation, Simulation und Optimierung kombinatorischer Schaltungen. Simuliert wird der Berechnungsfluss. Die Optimierung erfolgt zu einem großen Teil auf der Ebene der logischen Ausdrücke mit Hilfe der Regeln der Schaltalgebra. Das Entwurfsergebnis ist der strukturgleiche Signalflussplan. Zwei anschauliche Optimierungstechniken sind die Konstantenelimination und die Verschmelzung. Die Konstantenelimination ersetzt zweistellige Logikoperationen mit konstanten Operanden durch das Operationsergebnis – eine Konstante oder eine Operation mit nur einem Operanden (Abb. 1.21 a). Die Schaltung links in Abb. 1.21 b entspricht dem Ausdruck
28
1 Modellbildung und Simulation
((((x1 ∧ 0) ∧ x2 ) ∨ x3 ) ⊕ 1)
(1.5)
Nach einer schrittweisen Anwendung der Regeln aus Abb. 1.21 a bleibt am Ende nur noch ein Ausdruck mit einer invertierten Eingabegröße übrig: . . . = (((0 ∧ x2 ) ∨ x3 ) ⊕ 1) = ((0 ∨ x3 ) ⊕ 1) = (x3 ⊕ 1) = x ¯3 Die korrespondierende minimierte Schaltung ist ein Inverter. x∧0→0 x∧1→x
a) 0 x1 b)
& x2
0
& x3
x⊕0→x x⊕1→x ¯
x∨0→x x∨1→1 0
≥1 1
x3
vereinfachte Schaltung =1
x ¯3
y
⇒
x3
y
Abb. 1.21. Konstantenelimination a) Vereinfachungsregeln b) Vereinfachung der Schaltung für den Ausdruck in Gleichung 1.5
Die Verschmelzung fasst gleiche Teilberechnungen zusammen. Die beiden Ausdrücke y1 := x3 x2 x1 ∨ (x4 ⊕ x2 x1 ) y2 := x5 x4 ∨ x2 x1 enthalten zusammen dreimal den Teilausdruck x2 x1 . Zur Verschmelzung wird der Wert des gemeinsamen Teilausdrucks vorab berechnet und einer Variablen zugewiesen, die den Teilausdruck in den nachfolgenden Ausdrücken ersetzt: z := x2 x1 y1 := x3 z ∨ (x4 ⊕ z) y2 := x5 x4 ∨ z Jede eingesparte Operation im Berechnungsfluss bildet sich auch im Signalfluss auf eine eingesparte Operation ab. Die dreifache Verwendung des Wertes der Variablen z entspricht im Datenfluss einem Signal z mit drei Empfängern (Abb. 1.22). Durch die Verschmelzung werden in der Beispielschaltung die beiden grau unterlegten Gatter G1a und G1b eingespart. Klammernsetzung in Ausdrücken Ein Ausdruck setzt eine Berechnung aus ein- und zweistelligen Operationen zusammen. Die Abarbeitungsreihenfolge wird im einfachsten Fall mit Klammern festgelegt. In der Mathematik gilt, dass die Negation, falls keine Klammern gesetzt sind, zuerst ausgeführt wird und dass UND Vorrang vor ODER
1.2 Funktion, Struktur und Simulation x3 x1 x2
& & &
x4 x5
G1a
&
G1
G3 =1
G1b
G4 &
G2
G5 ≥1 G6 ≥1
x3 y1
y2
x1 x2
G1 z &
x4 x5
& =1 &
G2 G3 G4
G5 ≥1
y1
G6 ≥1
y2
29
eingesparte Gatter
Abb. 1.22. Verschmelzung
hat. Im Ausdruck in Gleichung 1.6 links wird zuerst die Invertierung, dann werden die UND-Verknüpfungen und abschließend die ODER-Verknüpfung ausgeführt. Die Ausführungsreihenfolge der beiden UND-Verknüpfungen ist vertauschbar. Im Ausdruck rechts ist dieselbe Ausführungsreihenfolge mit Klammern festgelegt: x1 ∨ x ¯2 x3 x4 = (x1 ∨ (((¯ x2 ) ∧ x3 ) ∧ x4 ))
(1.6)
In VHDL unterscheiden sich die Vorrangregelungen geringfügig von denen in der Mathematik. Die Invertierung wird auch hier, falls keine Klammern gesetzt sind, zuerst ausgeführt. Die zweistelligen logischen Operatoren sind jedoch alle gleichberechtigt. Wenn die Ausführungsreihenfolge weder mit Klammern festgelegt noch vertauschbar ist, gibt es eine Fehlermeldung. Der ungeklammerte Ausdruck in Anweisung A1 des nachfolgenden Programmausschnitts ist z.B. nicht zulässig, weil nicht festgelegt ist, ob die ODERVerknüpfung Vorrang hat oder ob die UND-Verknüpfungen Vorrang haben. In Anweisung A2 ist die unzulässige Mehrdeutigkeit mit einer Klammer um die beiden UND-Verknüpfungen beseitigt: variable x: std_logic_vector(4 downto 1);
... A1: ... x(1) or not x(2) and x(3) and x(4); –- Analysefehler A2: ... x(1) or (not x(2) and x(3) and x(4)); –- zulässig
Signalzuweisungen Ein Signal beschreibt einen zeitlichen Werteverlauf und dient als Beschreibungsmittel für die Kommunikation zwischen Prozessen und Teilschaltungen. Ein digitales Signal wird durch einen Anfangswert und eine zeitlich geordnete Folge von Wert-Zeit-Tupeln beschrieben8 . Der Anfangswert gilt bis zum Zeitwert des ersten Tupels und der Wert eines jeden Tupels bis zum Zeitwert des Folgetupels. Eine Signalzuweisung weist dem Signal jeweils ein oder mehrere 8
Diese Darstellung ist für Signale, deren Werte sich nur zu diskreten Zeitpunkten ändern und sonst konstant bleiben, wesentlich kompakter als Abtastfolgen, wie sie in der digitalen Signalverarbeitung und in der digitalen Mess-, Steuer- und Regelungstechnik verwendet werden.
30
1 Modellbildung und Simulation
Wert-Zeit-Tupel zu. Jedes Tupel besteht aus einem Ausdruck W zur Berechnung des neuen Signalwertes und einem Ausdruck td für die Verzögerungszeit: Signalname <= [VM ] W [after td ]{, W after td };
(VM – Verzögerungsmodell). Der Zeitwert, ab dem ein neu zugewiesener Signalwert gilt, ist jeweils die Summe aus der aktuellen Simulationszeit tsim und der Verzögerungszeit td . Verzögerungszeiten haben den Untertyp delay_length für nicht negative Zeitangaben (vgl. Abschnitt 1.2.1). Bei der Simulation läuft eine Art Uhr für die Simulationszeit mit, die nur erhöht, aber nicht zurückgestellt werden kann. Die berechneten Signalverläufe vom Simulationsbeginn bis zur aktuellen Simulationszeit können nicht mehr verändert werden. Zugewiesene Tupel, deren Zeitwerte noch nicht erreicht sind, werden als schwebend bezeichnet. Der Signalverlauf, den sie beschreiben, ist noch nicht endgültig. Eine neue Signalzuweisung löscht alle schwebenden Zuweisungen, deren Zeitwerte größer als die der neu zugewiesenen Tupel sind. Den Umgang mit schwebenden Zuweisungen mit kleineren Zeitwerten legt das Verzögerungsmodell fest. Vorerst werden die Beispiele so gewählt, dass früher zugewiesene Wert-Zeit-Tupel zu den Ausführungszeitpunkten der nachfolgenden Signalzuweisungen nicht mehr schwebend sind. Damit spielt das Verzögerungsmodell vorerst keine Rolle. Die meisten Signalzuweisungen weisen nur ein Wert-Zeit-Tupel nach dem Standardverzögerungsmodell zu: Signalname <= W [after td ];
Ohne die optionale After-Klausel ist die Verzögerungszeit null. Der Zeitwert, ab dem der neue Signalwert gilt, ist die aktuelle Simulationszeit. In Abb. 1.23 wird einem Signal y mit dem Anfangswert »0« innerhalb eines Prozesses eine Änderungsfolge »1-0-1« zugewiesen. Zum Ausprobieren sind die Signalvereinbarungen in den Vereinbarungsteil der Entwurfseinheit und die Signalzuweisungen in den Anweisungsteil des Testprozesses im Testrahmen in Abb. 1.18 einzufügen. Der komplette Testrahmen steht auf der Web-Seite [27] in der angegebenen Datei. y 10 signal y: std_logic := ’0’; 0 ... y <= ’1’ after 1 ns, ’0’ after 2 ns, ’1’ after 3 ns; wait;
1
2 tsim in ns
⇒Web-Projekt: P1.2/Test Sig1.vhdl
Abb. 1.23. Zuweisung einer Änderungsfolge »1-0-1«
Bei der Ausführung der Simulation ist zusätzlich im Kommandozeilenaufruf der Dateiname für die Abspeicherung der berechneten Signalverläufe anzugeben:
1.2 Funktion, Struktur und Simulation
31
ghdl -r Test_Sig1 --wave=Test_Sig1.ghw
Das Visualisierungsprogramm GTKWAVE wird anschließend mit dieser Datei und einer Sav-Datei mit den Visualisierungseinstellungen gestartet: gtkwave Test_Sig1.ghw [Test_Sig1.sav]
Visualisierungseinstellungen sind z.B. die Auswahl der darzustellenden Signale und das darzustellende Zeitfenster. Auf der Web-Seite [27] steht für jedes Projekt, in dem Signalverläufe ausgewertet werden, eine geeignete Sav-Datei. Die aktuelle Simulationszeit spielt für die Berechnung der Signalverläufe eine Schlüsselrolle. Sie kann innerhalb eines Prozesses mit der vordefinierten Funktion now abgefragt und mit Warteanweisungen, die eine Weckbedingung haben, weitergestellt werden. In VHDL wird zwischen folgenden Weckbedingungen unterschieden: •
Änderung eines Signals aus einer Weckliste
•
Weiterrücken der Simulationszeit um eine Verzögerungszeit
•
eine beliebige Bedingung, die Signalwerte oder die Simulationszeit auswertet
wait on Weckliste; wait for Verzögerungszeit;
wait until Bedingung;
Eine Warteanweisung legt den Prozess, in dem sie ausgeführt wird, schlafen. Während er schläft, werden andere Prozesse weiterbearbeitet, die Simulationszeit weitergestellt und die Signalwerte aktualisiert. Auch bei einer Zuweisung mit der Verzögerungszeit »0« (Delta-Delay-Modell) ist der neue Signalwert im Prozess erst nach der nächsten Warteanweisung sichtbar. Abbildung 1.24 zeigt eine typische Prozessbeschreibung für die Erzeugung eines Testeingabesignals für eine kombinatorische Schaltung. Das zu erzeugende Signal ist im Beispiel ein 5-Bit-Vektor mit dem bitweisen Anfangswert »nicht initialisiert«. Im Prozess wird für jeden Testschritt eine bestimmte Zeit gewartet und danach ein neuer Wert zugewiesen. Am Ende der Anweisungsfolge steht die obligatorische Warteanweisung ohne Weckbedingung, die den signal x: std_logic_vector(4 downto 0); ... Eingabe: process begin wait for 5 ns; x <= "10010"; wait for 5 ns; x <= "00011"; ... wait; end process;
x UUUUU 10010 10011 0
5
10
...
tsim in ns
U nicht initialisiert, impliziter Anfangswert der Signalvereinbarung ⇒Web-Projekt: P1.2/EingabeProc.vhdl
Abb. 1.24. Prozessbeschreibung zur Erzeugung eines Testeingabesignals
32
1 Modellbildung und Simulation
Prozess dauerhaft schlafen legt. Ohne diese würde der Prozess die Simulationszeit immer Weiterschalten und dem Signal einen periodischen Verlauf zuweisen. Die Simulation müsste von außen beendet werden, unter Linux z.B. mit »Ctrl-C«. Abbildung 1.25 a zeigt ein Beispielprogramm zur Signalerzeugung, das auch die Wait-On-Anweisung nutzt. Das erzeugte Signal n ist vom Typ natural und wird mit »0« initialisiert. Die erste Zuweisung weist ihm zum Simulationszeitpunkt null mit den Verzögerungen 1 ns und 2 ns die Werte »1« und »2« zu. Dann wird nacheinander auf beide Änderungen des Signals (d.h. bis zu den Simulationszeitpunkten 1 ns und 2 ns) gewartet. Daran schließt sich eine Wartezeit von 0,5 ns an. Die zweite Zuweisung erhöht den aktuellen Signalwert zum Simulationszeitpunkt von 2,5 ns, der dann »2« ist, wieder mit einer Verzögerung von 1 ns und 2 ns, d.h. zu den Zeitpunkten 3,5 ns und 4,5 ns, auf »4« und »6« und wartet dann wieder auf beide Signaländerungen. Um das Beispiel schrittweise testen zu können, enthält es nach jeder Zuweisung und nach jeder Warteanweisung eine Ausgabeanweisung. Der Ausgabetext besteht jeweils aus der Anweisungsbezeichnung, der aktuellen Simulationszeit und dem aktuellen Signalwert. Abbildung 1.25 b zeigt den berechneten Signalverlauf und veranschaulicht die Wirkungsweise der einzelnen Anweisungen. signal n: natural; ... Z1: n <= n+1 after 1 A1: wait on n; A2: wait on n; A3: wait for 0.5 ns; A4: Z2: n <= n+2 after 1 A5: wait on n; A6: wait on n; A7: a) wait; Signalverlauf: Signalzuweisungen: Ausgabeanweisungen:
b)
Ausgabetexte:
ns, n+2 after 2 ns; write("A1:" & str(now)&’:’&str(n)); write("A2:" & str(now)&’:’&str(n)); write("A3:" & str(now)&’:’&str(n)); write("A4:" & str(now)&’:’&str(n)); ns, n+4 after 2 ns; write("A5:" & str(now)&’:’&str(n)); write("A6:" & str(now)&’:’&str(n)); write("A7:" & str(now)&’:’&str(n)); ⇒Web-Projekt: P1.2/Test Wait.vhdl
n
0 0
1 1
2 2
Z1 A1 A1:0 fs:0
A2
A3 A4/5
4 3 Z2
6
4 A6
tsim A7
A3:2.00 ns:2 A6:3.50 ns:4 A7:4.50 ns:6 A2:1.00 ns:1 A4:2.50 ns:2 A5:2.50 ns:2
Abb. 1.25. a) Signalerzeugung mit Wait-On-Anweisungen b) erzeugter Signalverlauf und Wirkungsweise der einzelnen Anweisungen
1.2 Funktion, Struktur und Simulation
33
Signale gehören zu den komplizierteren Beschreibungsmitteln digitaler Schaltungen. Es sei deshalb empfohlen, die vorgegebenen Beispiele selbst mit dem Simulator auszuprobieren und zu verändern. 1.2.4 Ereignisgesteuerte Simulation Die Simulation einer digitalen Schaltung erfolgt ereignisgesteuert. Vorerst werden nur kombinatorische Schaltungen betrachtet. Das einfachste Funktionsmodell einer kombinatorischen Schaltung ist ein Prozess, der die Ausgabewerte aus den aktuellen Eingabewerten berechnet, die berechneten Werte den Ausgabesignalen zuweist, auf die nächste Eingabeänderung wartet und bei jeder Eingabeänderung den gesamten Ablauf wiederholt. Die Ausgabeänderungen sind wiederum Weckereignisse für Prozesse, die die Signale weiterverarbeiten. Abbildung 1.26 a zeigt eine kombinatorische Schaltung aus fünf Gattern und Abb. 1.26 b eine Funktionsbeschreibung der Schaltung durch einen Prozess. Die Anweisungsfolge innerhalb des Prozesses berechnet zuerst die Ausgabewerte der Gatter G1 bis G3 und speichert sie in Variablen. Aus den zwischengespeicherten Werten werden danach die Ausgabewerte der Schaltung berechnet und mit der Verzögerung td = 2 ns an die beiden Bits des Ausgabesignals zugewiesen. Abschließend wartet der Prozess auf eine Änderung des Signals x, d.h. auf eine Änderung von mindestens einem der Eingabebits. Die Anweisungsfolge in einem Prozess wird zur Simulation in eine Endlosschleife eingebettet, so dass nach dem Aufwachen des Prozesses wieder die gesamte Anweisungsfolge von vorn ausgeführt wird. signal x: std_logic_vector(4 downto 0); td signal y: std_logic_vector(1 downto 0); G1 x0 v0 & constant td: delay_length := 2 ns; x1 G4 ... y0 & G2 x Schaltungsprozess:process 2 v1 & x3 G5 variable v: std_logic_vector(2 downto 0); y1 & G3 begin v2 & x4 G1:v(0) := x(0) nand x(1); a) G2:v(1) := x(2) nand x(3); x UUUUU 10010 10011 ... G3:v(2) := x(0) nand x(4); G4:y(0) <= v(0) nand v(1) after td; y UU 00 11 . . . G5:y(1) <= v(1) nand v(2) after td; 0 5 10 tsim in ns wait on x; c) b) end process; ⇒Web-Projekt: P1.2/ProcSimG5.vhdl Abb. 1.26. a) Kombinatorische Schaltung b) Prozess zur Funktionsbeschreibung c) Simulationsausgabe (Eingabebereitstellung mit dem Prozess aus Abb. 1.24)
Zur Simulation der Beispielschaltung sind die einzelnen Beschreibungselemente an den richtigen Stellen in den Testrahmen in Abb. 1.18 einzusetzen: die
34
1 Modellbildung und Simulation
Signalvereinbarungen in den Vereinbarungsteil und der Prozess in den Anweisungsteil der Entwurfseinheit. Zur Simulation wird ein weiterer Prozess benötigt, der die Eingabesignale bereitstellt. Das ist im zugehörigen Web-Projekt der Prozess aus Abb. 1.24. Er erzeugt ein Eingabesignal x, das zum Simulationsbeginn mit »UUUUU« initialisiert wird, nach 5 ns den Wert »10010« und nach 10 ns den Wert »10011« zugewiesen bekommt. Das berechnete Ausgabesignal ist zu Beginn »UU« und übernimmt nach jeder Eingabeänderung mit einer Verzögerung von zwei Nanosekunden den Funktionswert für den neuen Eingabevektor, für den ersten »00« und für den zweiten »11«. Abbildung 1.26 c zeigt den berechneten Signalverlauf. Zur genaueren Untersuchung, wie die beiden Prozesse arbeiten, können in die Prozesse Anweisungen zur Textausgabe eingefügt werden. Der experimentierfreudige Leser wird merken, dass es gar nicht so einfach ist, sinnvolle und auswertbare Simulationsausgaben zu erhalten. Alternativ zu dem Modell in Abb. 1.26 b kann jedes Gatter einer kombinatorischen Schaltung auch durch einen eigenen Prozess beschrieben werden. Abbildung 1.27 a zeigt eine Beispielschaltung aus drei Gattern und Abb. 1.27 b das zugehörige Simulationsmodell. Der erste Prozess stellt die Testeingabesignale bereit. Der zweite und der dritte Prozess beschreiben je ein UND-Gatter und der vierte Prozess ein ODER-Gatter. Die Tabelle in Abb. 1.27 c veranschaulicht den Simulationsablauf. Zum Simulationsbeginn sind alle Signale mit »0« initialisiert. Der Simulator weckt nacheinander alle Prozesse und führt sie bis zur ersten Warteanweisung aus. Die Prozesse der Gatter G1 bis G3 weisen ihren Ausgabesignalen nach 1 ns bzw. 2 ns je den Wert »0« zu. Diese Zuweisungen bewirken jedoch keine Signaländerungen. Der Eingabeprozess legt sich sofort schlafen, wacht nach 1 ns wieder auf und weist für den Aufwachzeitpunkt tsim = 1 ns an x3 eine Änderung nach »1« zu. Diese Änderung weckt den Prozess G2, der z2 für den Simulationszeitpunkt 2 ns eine »0« (auch keine Änderung) zuweist. Das nächste Mal wacht der Eingabeprozess zum Simulationszeitpunkt tsim = 3 ns auf und weist den Eingabesignalen x1 und x4 eine Änderung nach »1« zu. Das weckt nacheinander die Prozesse G1 und G2. Die Signalzuweisung in G1 an z1 erzeugt keine Änderung, die in G2 an z2 erzeugt für tsim = 4 ns eine Änderung nach »1«. Diese Änderung weckt zum Simulationszeitpunkt tsim = 4 ns den Prozess G3. In G3 wird dem Ausgabesignal y für den Simulationszeitpunkt tsim = 6 ns der geänderte Wert »1« zugewiesen. Abbildung 1.27 d zeigt die berechneten Signalverläufe. Die Simulation aller Gatter durch einen eigenen Prozess bildet das Zeitverhalten einer Schaltung wesentlich genauer nach als ein einzelner Prozess. Der Preis dafür ist ein viel größerer Rechenaufwand für die Simulation. Für wichtige Arten von Prozessbeschreibungen gibt es in VHDL Kurzschreibweisen. Verhaltensmodelle für Schaltungen werden in der Regel – wie auch in den bisherigen Beispielen – durch einen Prozess mit genau einer Warteanweisung mit Weckliste am Ende der Anweisungsfolge beschrieben. Die Kurzschreibweise hierfür ist ein Prozess mit Weckliste. Bei diesem steht die
1.2 Funktion, Struktur und Simulation td = 1 ns x1 x2
x3 x4
a)
end process;
z1 & td = 2 ns G1 td = 1 ns &
z2 G2
≥1
–- Gatter G1 G1: process begin z1 <= x1 and x2 after 1 ns; wait on x1, x2; end process;
y
G3
entity G3_Proc is end entity;
–- Gatter G2 G2: process begin z2 <= x3 and x4 after 1 ns; wait on x3, x4; end process;
architecture Sim of G3_Proc is signal x1, x2, x3, x4, z1, z2, y: std_logic := ’0’; begin –- Eingabeprozess Eingabe: process begin wait for 1 ns; x3 <= ’1’; wait for 2 ns; x1<=’1’; x4 <= ’1’; ... wait;
b) c) tsim 0 0 0 0 1
Signalzuweisung
z1 ⇐ 0 ∧ 0 nach 1 ns ns G1 z2 ⇐ 0 ∧ 0 nach 1 ns ns G2 y ⇐ 0 ∨ 0 nach 2 ns ns G3 ns Eingabe E x3 ⇐ 1 ns
1 ns 3 ns 3 3 4 6
Prozess
ns ns ns ns
G2 E G1 G2 G3 ···
35
z2 ⇐ 1 ∧ 0 nach 1 ns x1 ⇐ 1 x4 ⇐ 1 z1 ⇐ 1 ∧ 0 nach 1 ns z2 ⇐ 1 ∧ 1 nach 1 ns y ⇐ 0 ∨ 1 nach 2 ns ···
–- Gatter G3 G3: process begin y <= z1 or z2 after 2 ns; wait on z1, z2; end process; end architecture; vorgemerkte Ereignisse
@1 ns: E @1 ns: x3 → 1 + @3 ns: E @3 ns: E @3 ns: x1 → 1 + @3 ns: x4 → 1 @3 ns: x4 → 1 @4 ns: z2 → 1 @6 ns: y → 1 ···
Signale x4 x3 x2 x1 z2 z1 y 0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0
0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 1 1 1
1 1 1 1 1
0 0 0 0 0
0 1 1 1 1
0 0 0 1 1
0 0 0 0 0
0 0 0 0 1
x1 x2 z1
1 0 1 0 1 0
x3 x4 z2 y
1 0 1 0 1 0 1 0
d)
Fortsetzung im Web-Projekt
0
5
10
15 t in ns
Initialisierung @ ... Simulationszeitpunkt → Signal¨anderung E Eingabeprozess wecken ⇒WEB-Projekt: P1.2/G3 Proc.vhdl
Abb. 1.27. Simulation einer kombinatorischen Schaltung mit einem Prozess je Gatter a) Beispielschaltung b) VHDL-Beschreibung c) Tabelle der Ereignisse und Aktionen während der Simulation d) berechnete Signalverläufe
Weckliste hinter dem Schlüsselwort »process« und die Warteanweisung entfällt (Abb. 1.28 b). In einem solchen Prozess sind keine weiteren Warteanweisungen erlaubt. Diese Spezialschreibweise dient vor allem zur Vermeidung des Fehlers »vergessene Warteanweisung«. Ein Prozess ohne Warteanweisung hat die unschöne Eigenschaft, dass er den Kontrollfluss nicht mehr abgibt, so dass die Simulation praktisch stehen bleibt.
36
1 Modellbildung und Simulation –- a) Prozess mit genau einer Warteanweisung mit Weckliste td = 1 ns process x1 begin y & x2 z1 <= x1 and x2 after 1 ns; wait on x1, x2; end process; –- b) funktionsgleiche Beschreibung durch einen Prozess mit Weckliste process (x1, x2) begin z1 <= x1 and x2 after 1 ns; end process; –- c) funktionsgleiche Beschreibung durch eine nebenläufige Signalzuweisung z1 <= x1 and x2 after 1 ns; Abb. 1.28. Kurzschreibweisen für kombinatorische Prozesse
Für einen Prozess mit Weckliste gibt es wiederum eine Kurzschreibweise. Wenn der Anweisungsteil nur aus einer Anweisung besteht und die Weckliste alle Eingabesignale enthält, kann der Prozessrahmen weggelassen werden. Die resultierende Kurzschreibweise ist eine nebenläufige Signalzuweisung (Abb. 1.28 c). Abbildung 1.29 zeigt als Beispiel eine verkürzte Beschreibung der drei Gatter aus Abb. 1.27 mit nebenläufigen Signalzuweisungen. Der Eingabeprozess muss in der Langform übernommen werden. Die Signalzuweisungen G1 bis G3 werden zur Simulation wieder durch ihre Prozesse ersetzt, so dass die Beschreibung in 1.29 in dasselbe ausführbare Simulationsprogramm wie die Beschreibung in Abb. 1.27 übersetzt wird. architecture Sim of G3_NLZ is signal x1, x2, x3, x4, z1, z2, y: std_logic := ’0’; begin –- nebenläufige Signalzuweisungen G1:z1 <= x1 and x2 after 1 ns; G2:z2 <= x3 and x4 after 1 ns; G3: y <= z1 or z2 after 2 ns; –- Eingabeprozess Eingabe: process –- weiter wie in Abb. 1.27 end architecture;
td = 1 ns x1 x2
z1 td = 2 ns G1 ≥1 y td = 1 ns z2 G3 x3 & x4 G2 &
⇒WEB-Projekt: P1.2/G3 NLZ.vhdl
Abb. 1.29. Beschreibung der Gatterprozesse aus Abb. 1.27 b durch nebenläufige Signalzuweisungen
1.2 Funktion, Struktur und Simulation
37
1.2.5 Strukturbeschreibung Eine Strukturbeschreibung beschreibt die Zusammensetzung einer Gesamtschaltung aus Teilschaltungen. Wie bereits in Abschnitt 1.2.2 eingeführt, ist das ein Graph mit den Bauteilen als Knoten und den Signalen als Kanten. Die rechnerinterne Darstellung besteht aus der Vereinbarung aller Bauteile und Signale und einer Verbindungsliste, die entweder den Bauteilanschlüssen Signale oder den Signalen Bauteilanschlüsse zuordnet. In VHDL erfolgt die Zuordnung bauteilorientiert. Schnittstellenvereinbarung Jede Entwurfseinheit – unabhängig davon ob sie eine Teilschaltung, eine Gesamtschaltung oder einen Testrahmen beschreibt – hat in VHDL eine Schnittstelle. Die Schnittstellenvereinbarung definiert die Anschlusssignale und für parametrisierte Schaltungen zusätzlich die Parameter. Sie hat die folgende Form: entity Bezeichner_Entwurfseinheit is [generic (Liste_der_Konfigurationsparameter );] [port (Liste_der_Anschlusssignale);] end entity;
Die Liste der Anschlusssignale legt für alle Anschlusssignale einen Bezeichner, die Signalflussrichtung, den Datentyp und optional einen Standardwert fest: Signalname {,Signalname}:Richtung Typ [:=Standardwert] {; Signalname {,Signalname}:Richtung Typ [:=Standardwert]}
Mögliche Signalflussrichtungen sind: in out inout buffer
––––-
Eingang Ausgang Ein- und Ausgang Ausgang mit rücklesbarem Wert
Die Liste der Konfigurationsparameter hat eine ähnliche Beschreibungsstruktur, nur dass die Parameter lokal statische (zum Übersetzungszeitpunkt bekannte) Konstanten sein müssen und keine Signalflussrichtung haben: Parametername {,Parametername}: Typ [:=Standardwert] {; Parametername {,Parametername}: Typ [:=Standardwert]}
Die Standardwerte haben nur für Eingabesignale und Parameter eine Bedeutung. Das sind die Werte dieser Objekte, wenn ihnen in der übergeordneten Beschreibung kein Signal bzw. kein Parameterwert zugeordnet wird. Abbildung 1.30 zeigt als Beispiel die Beschreibung eines UND-Gatters. Die Schnittstelle definiert die Verzögerungszeit als Konfigurationsparameter und legt die Typen und Signalflussrichtungen der drei Anschlusssignale fest. Die
38
1 Modellbildung und Simulation
Verzögerungszeit des Gatters hat den Typ delay_length und den Standardwert 0 ns. Die Anschlusssignale a und b sind Eingabesignale und c ist ein Ausgabesignal, alle mit dem Typ std_logic. Die Beschreibung besteht im Beispiel nur aus einer nebenläufigen Signalzuweisung. Wie im Vorabschnitt behandelt, ist das die Kurzschreibweise für einen Prozess mit den beiden Eingabesignalen in der Weckliste und der Signalzuweisung im Anweisungsteil.
a c
td
a 10
&
b 10
c
c 10
begin c <= a and b after td; end architecture; td
entity Und2 is generic(td: delay_length); port(a, b: in std_logic; c: out std_logic); end entity; architecture Vers1 of Und2 is
architecture Vers2 of Und2 is signal u, v: std_logic; begin u <= a after td; v <= b after td; c <= u and v; end architecture; ⇒WEB-Projekt: P1.2/Und2.vhdl
Abb. 1.30. UND-Gatter mit Schnittstelle und zwei Beschreibungen
In VHDL kann eine Entwurfseinheit auch wie in Abb. 1.30 mehrere Beschreibungen haben. In der ersten Beschreibungsversion wird im Beispiel die UND-Verknüpfung der beiden Eingabesignale verzögert an das Ausgabesignal zugewiesen und in der zweite Beschreibung werden die Eingabesignale zuerst verzögert und dann UND-verknüpft. Beschreibungen derselben Entwurfseinheit werden am Beschreibungsnamen unterschieden. Die zweite und alle weiteren Beschreibungen können in derselben oder in einer anderen Datei stehen. Die Schnittstellenbeschreibung darf auch bei einer Aufteilung auf mehrere Dateien nur einmal enthalten sein. Bei der Analyse muss immer zuerst die Schnittstellenbeschreibung vorübersetzt und in der Bibliothek abgelegt werden, bevor die Beschreibungen analysiert werden können. Teilschaltungsinstanziierung Eine Teilschaltungsinstanz besteht aus einem Verweis auf eine Entwurfseinheit, optional einen Verweis auf eine ihrer Beschreibungen und einer Zuordnung von Anschlusssignalen und Parameterwerten: Teilschaltungsname: entity Bibliothek .Entwurfseinheit[(Beschreibung)] [generic map(Zuordnungsliste_Parameter )] port map(Zuordnungsliste_Anschlüsse);
1.2 Funktion, Struktur und Simulation
39
Entwurfseinheiten desselben Projekts stehen in der Regel in der Arbeitsbibliothek work. Die Zuordnungslisten können namensbasiert oder positionsbasiert aufgebaut sein. Eine namensbasierte Zuordnungsliste besteht aus Zuordnungstupeln der Form: BS => BZ {, BS => BZ }
(BS – Bezeichner in der Schnittstellendefinition der Teilschaltung; BZ – Bezeichner des zugeordneten Datenobjekts in der Strukturbeschreibung). Eine positionsbasierte Zuordnungsliste ist eine kommaseparierte geordnete Liste der zugeordneten Objekte: BZ {, BZ }
Diese Objekte müssen in der durch die Schnittstelle vereinbarten Reihenfolge zugeordnet werden. Für beide Arten der Zuordnungsliste müssen die Typen der einander zugeordneten Objekte übereinstimmen und für die Zuordnungsliste der Anschlüsse müssen zusätzlich die Signalflussrichtungen zueinander passen. Passen heißt, dass Signale, außer in wenigen später behandelten Ausnahmen, genau eine Quelle und mindestens einen Empfänger haben. Nicht verwendeten Anschlüssen und Parametern wird entweder nichts oder das Schlüsselwort »open« zugeordnet. Beispiel sei die Strukturbeschreibung in Abb 1.31 für die Gatterschaltung aus Abb. 1.27. Die Schnittstellenbeschreibung definiert vier Eingabe- und ein Ausgabesignal, die alle vom Typ std_logic sind. Die internen Signale werden im Vereinbarungsteil der Beschreibung vereinbart. Die Instanziierung der drei enthaltenen Gatter folgt im Anweisungsteil. Die Gatter G1 und G2 sind Entwurfseinheiten vom Typ »Und2« mit der Beschreibung »Vers1« aus der
x1 x2 x3 x4
G1 z1 & G3 ≥1 G2 z2 &
y
library ieee; use ieee.std_logic_1164.all; entity G3E is port(x1,x2,x3,x4: in std_logic; y: out std_logic); end entity; architecture Struktur of G3E is signal z1, z2: std_logic; begin
–- Gatterinstanzen G1:entity WORK.Und2(Vers1) generic map(1 ns) port map(x1, x2, z1); G2:entity WORK.Und(Vers1) generic map(td => 1 ns) port map(a=>x3, b=>x4, c=>z2); G3:entity WORK.Oder(Vers1) generic map(td => 2 ns) port map(a=>z1, b=>z2, c=>y); end architecture; ⇒WEB-Projekt: P1.2/G3E.vhdl
Abb. 1.31. Beispiel einer Strukturbeschreibung
40
1 Modellbildung und Simulation
Arbeitsbibliothek work. Der Teilschaltungstyp »Oder2« für G3 sei genauso wie der Typ »Und2« in Abb. 1.30 definiert, nur mit »or« statt »and« in der Signalzuweisung. Der Bibliotheksbezeichner work ist vordefiniert und verlangt im Gegensatz zu den Bibliotheksbezeichnern ieee und »Tuc« keine Vereinbarung im Vorspann. Für G1 erfolgt die Zuordnung der Parameter und Anschlusssignale positionsbasiert und für die anderen beiden Gatter namensbasiert. Die positionsbasierte Zuordnung ist kürzer, die namensbasierte verständlicher und weniger fehleranfällig. 1.2.6 Testrahmen Die oberste Hierarchieebene in einem Simulationsmodell ist der Testrahmen. Das ist eine Entwurfseinheit ohne Anschlusssignale und Parameter. In der Regel wird das Testobjekt als Schaltungsinstanz, statt wie in Abb. 1.27 als System aus mehreren Prozessen, eingebunden. Das ist übersichtlicher, änderungsfreundlicher und weniger fehleranfällig. Abbildung 1.32 zeigt ein funktionsgleiches Simulationsmodell zu Abb. 1.27. Das Testobjekt ist eine Bauteilinstanz. Der Prozess für die Eingabebereitstellung ist übernommen. Zur Simulation und auch für andere Bearbeitungsschritte wird die Hierarchie der Strukturbeschreibung aufgelöst. Dazu werden von unten beginnend alle Teilschaltungsinstanzen durch die Prozesse, die ihre Funktion beschreiben, ersetzt. Im Beispiel entsteht praktisch wieder das Modell aus Abb. 1.27.
entity Test_G3E is end entity; architecture Test of Test_G3E is signal e1, e2, e3, e4, a: std_logic := ’0’; begin TObj: entity WORK.G3E(Struktur) port map(x1=>e1, x2=>e2, x3=>e3, x4=>e4, y=>a); Eingabe: process begin wait for 1 ns; e3 <= ’1’; wait for 2 ns; e1 <= ’1’; e4 <= ’1’; ... wait; end process; end architecture;
Testrahmen e1 x1 a Name: G1 e2 x2 b Typ: Und2 e3 e4
x3 x4
a Name: G2 b Typ: Und2
c
c
z1
z2
Name: TObj Typ: G3E a Name: G3 c b Typ: Oder2
y
a
Prozesse f¨ ur die Testeingabe und Testausgabe ⇒WEB-Projekt: P1.2/Test_G3E.vhdl
Abb. 1.32. Testrahmen mit dem Testobjekt als Schaltungsinstanz
1.2 Funktion, Struktur und Simulation
41
1.2.7 Instanziierung von Komponenten Eine ältere, umständlichere Art der Instanziierung von Teilschaltungen verweist statt auf Bibliotheksobjekte auf Komponenten: Teilschaltungsname: [component] Bezeichner_Komponente [generic map(Zuordnungsliste_Parameter )] port map(Zuordnungsliste_Anschlüsse);
Eine Komponente ist ein Platzhalter, der syntaktisch bis auf das Schlüsselwort genauso wie die Schnittstelle einer Entwurfseinheit vereinbart wird: component Bezeichner_Komponente is [generic (Liste_der_Konfigurationsparameter );] port (Liste_der_Anschlusssignale); end component;
Die Vereinbarungen der Komponenten können im Vereinbarungsteil der Entwurfseinheit stehen oder aus einem Package importiert werden. Die Zuordnung der Entwurfseinheit und der Entwurfsbeschreibung zu einer Komponente kann implizit oder explizit erfolgen. Implizit wird die gleichnamige Entwurfseinheit mit derselben Schnittstelle, die zuletzt analysiert wurde, und deren zuletzt analysierte Beschreibung zugeordnet. Die explizite Zuordnung erfordert eine spezielle Konfigurationsbeschreibung, die hier im Buch weder behandelt noch benutzt wird. Andere Bücher und auch das Entwurfssystem ISE von »Xilinx« bevorzugen jedoch diese Art der Instanziierung von Teilschaltungen. Deshalb ist es notwendig, wenigstens zu wissen, dass es sie gibt und dass es außer der zusätzlichen Zuordnungsebene keinen semantischen Unterschied zu der hier verwendeten Art der Instanzbildung gibt. 1.2.8 Zusammenfassung und Übungsaufgaben Eine digitale Schaltung ist ein System aus signalverarbeitenden Bausteinen, die über Signale miteinander kommunizieren. In einer formalen Beschreibung hat jedes Signal und jedes andere Datenobjekt einen Typ, der den Wertebereich festlegt. Eingeführt wurden die standardisierten VHDL-Typen für Bits, Bitvektoren, Zahlen, die Simulationszeit und für die Textverarbeitung. Das Funktionsmodell einer Teilschaltung ist ein Prozess. Von den imperativen innerhalb eines Prozesses nutzbaren Beschreibungselementen wurden Variablenzuweisungen, Signalzuweisungen, Ausdrücke und Warteanweisungen behandelt. Ausdrücke beschreiben einen baumartigen Berechnungsfluss, der sich durch einen strukturgleichen Signalfluss nachbilden lässt und umgekehrt. Sie bilden die Basis für viele Schaltungsoptimierungen, von denen die Konstantenelimination und die Verschmelzung behandelt wurden. Die Wirkung jeder Zuweisung lässt sich einzeln im Simulator ausprobieren. Die zeitlichen Abläufe werden mit Hilfe von Signalzuweisungen und Warteanweisungen nachgebildet. Das Funktionsmodell einer kombinatorischen
42
1 Modellbildung und Simulation
Schaltung ist ein Prozess, der die Ausgabewerte aus den aktuellen Eingabewerten berechnet, verzögert zuweist, sich bis zur nächsten Eingabeänderung schlafen legt und nach dem Aufwachen dieselbe Berechnungsfolge wiederholt. In einer Schaltung aus mehreren Teilschaltungen wird jede Teilschaltung durch einen solchen Prozess simuliert. Zusätzlich sind Prozesse zur Bereitstellung der Eingabesignale erforderlich, die im einfachsten Fall aus einer Folge von Signalzuweisungen und Warteanweisungen bestehen. Zur hierarchischen Strukturierung werden die Prozesse zu Entwurfseinheiten zusammengefasst, mit Schnittstellen versehen und in übergeordnete Entwurfseinheiten als Instanzen eingebunden. Die oberste Ebene in der Beschreibungshierarchie eines Simulationsmodells ist der Testrahmen, eine Entwurfseinheit ohne Anschlüsse. Bei der Schaltungsanalyse vor der Simulation wird die Hierarchie wieder in einzelne über Signale kommunizierende Prozesse – das eigentliche Simulationsmodell – aufgelöst. Aufgabe 1.5 Gegeben sind die Zeitverläufe der Spannungspotenziale auf den vier BitLeitungen eines Signals signal b: std_logic_vector(3 downto 0);
in Abb. 1.33. Kennzeichnen Sie die Zeitbereiche, in denen das Signal ungültig ist. Ordnen Sie den Zeitfenstern, in denen es gültig ist, die Signalwerte zu. ϕ(b0 ) ϕ(b1 ) ϕ(b2 ) ϕ(b3 ) b = b3 b 2 b 1 b 0
0001
– gesucht – t
Abb. 1.33. Signalverlauf zu Aufgabe 1.5
Aufgabe 1.6 a) Wie viele Werte lassen sich mit einem Byte (8 Bit) unterscheiden? b) Wie groß muss ein Bitvektor sein, damit er 25 Zustände unterscheiden kann?
1.2 Funktion, Struktur und Simulation
43
Aufgabe 1.7 Gegeben sind die nachfolgenden Vereinbarungen und Anweisungen: –- Vereinbarungen der Entwurfseinheit signal a: integer := 0; –- Vereinbarungen im Testprozess variable b: integer := 0; –- Anweisungsfolge im Testprozess A1: a <= a + 1 after 0.5 ns; b := b + 1; A2: wait for 1 ns; A3: a <= a + 1 after 0.5 ns; b := b + 1; A4: a <= a + 1 after 0.5 ns; b := b + 1; A5: wait for 1 ns;
Welche Werte haben das Signal a und die Variable b nach Abarbeitung jeder der Anweisungszeilen A1 bis A5? Aufgabe 1.8 Gegeben ist der folgende logische Ausdruck: x1 ∧ x2 ∧ x ¯3 ∧ x ¯1 ∧ x ¯2 ∧ x ¯3 a) Zeichnen Sie den Berechnungsbaum unter Verwendung der ein- und zweistelligen logischen Operationen Invertierung, UND und ODER. b) Zeichnen Sie den Signalflussplan unter Verwendung von Invertern sowie UND- und NAND-Gattern mit n Eingängen. c) Der Ausdruck sei die logische Funktion einer Schaltung. Das Eingabesignal sei vom Typ std_logic_vector, das Ausgabesignal vom Typ std_logic und die Verzögerungszeit 2 ns. Beschreiben Sie die Signalvereinbarungen und die Signalzuweisung in VHDL. Aufgabe 1.9 Gegeben sind die Funktionsbeschreibung des Teilschaltungstyps »Gxx« und der Signalflussplan einer Schaltung mit zwei Instanzen von diesem Typ: entity Gxx is x1 port(a, b, c: in std_logic; x2 d: out std_logic); x3 end entity Gxx; architecture fkt of Gxx is begin d <= (a and not c) or (b and c) after 1 ns; end architecture;
G1 Gxx
a b c
d x4
z
G2 Gxx
a b c
d
y
⇒WEB-Projekt: P1.2/Gxx.vhdl
44
1 Modellbildung und Simulation
a) Beschreiben Sie die Struktur der Gesamtschaltung in VHDL. Der Bezeichner der Gesamtschaltung sei »S2B«, der Beschreibungsname »stk« und der Datentyp der Anschlüsse der Gesamtschaltung std_logic. b) Entwickeln Sie einen Testrahmen »Test_S2B« mit dem Beschreibungsnamen »t1«, der die Strukturbeschreibung der Gesamtschaltung als Instanz und einen Prozess, der nacheinander im Abstand von 5 ns die Eingabewerte »0000«, »0001«, »0011« und »0111« den zu einem Vektor zusammenzufassenden Eingabesignalen x4 . . . x1 zuweist, enthält.
1.3 Laufzeittoleranz Definition 1.17 (Laufzeittoleranz) Eine Schaltung ist laufzeittolerant, wenn ihre Funktion nicht von den internen Signalverzögerungen abhängt, solange diese innerhalb ihrer zulässigen Toleranzbereiche liegen. Eine kombinatorische Schaltung bildet die Werte der Eingabesignale verzögert auf die Werte der Ausgabesignale ab. Die Abbildungsfunktion ist eindeutig. Im stationären Zustand, nach Abklingen aller Änderungsvorgänge, ist jedem Eingabewert genau ein Ausgabewert zugeordnet. Das Verzögerungsverhalten wird von dem bisherigen Modell jedoch nur grob angenähert. Die physikalischen Größen zur Darstellung der Signalwerte (Spannungen, Ströme etc.) ändern sich stetig. Sie springen nicht, wie in den bisherigen Simulationsmodellen angenommen, zwischen »0« und »1« hin und her, sondern sie sind bei jedem Signalwechsel kurzzeitig ungültig (vgl. Abschnitt 1.2.1). Bei manchen Eingabeänderungen wechseln die Ausgaben sogar mehrfach zwischen »0« und »1«, bevor der stationäre Wert erreicht ist. Darüber hinaus unterliegen die Zeitparameter erheblichen fertigungs- und temperaturbedingten und von anderen Einflussgrößen abhängigen Streuungen. Eine digitale Schaltung arbeitet nur dann zuverlässig, wenn ihre Funktion nicht von Zeitparametern abhängt, wenn diese innerhalb ihrer zulässigen Toleranzbereiche liegen. 1.3.1 Glitches Ein Glitch ist ein kurzer Impuls, der durch Verknüpfung von Signalen mit zeitversetzten Änderungen entsteht. Abbildung 1.34 zeigt eine kleine Gatterschaltung mit simulierten Beispielsignalverläufen. Bei einer steigenden Flanke an x2 und einer verzögerten fallenden Flanke an z entsteht ein kurzer Impuls am Ausgang y. Auftreten und Länge der Glitches hängen von den Flankenversätzen der Eingabesignale und von den Verzögerungen innerhalb der Schaltung ab und sind in der Regel nicht genau vorhersagbar. Glitches können in einer kombinatorischen Schaltung bereits dann entstehen, wenn sich nur ein einzelnes Eingabebit ändert, und zwar, wenn der Signalfluss rekonvergent ist. Rekonvergent bedeutet, dass der Signalfluss sich
1.3 Laufzeittoleranz td2
td1 z
x1 x2
&
y
Glitches ⇒Web Projekt: P1.3/SimGlitch1.vhdl
x1 x2
1 0 1 0
z
1 0
y
1 0
td1 td2
45
td1 td2
td2
td2
Abb. 1.34. Glitches verursacht durch geringfügig zeitversetzte Signaländerungen an unterschiedlichen Eingängen
verzweigt und später wieder zusammentrifft. Entlang der unterschiedlichen Signalpfade werden die Änderungen unterschiedlich stark verzögert, so dass an den Eingängen des Funktionsblocks, an dem der Signalfluss wieder zusammentrifft, Signale mit zeitversetzten Änderungen ankommen. In Abb. 1.35 a verzweigt das Signal x1 . Auf dem unteren Zweig wird es mit x2 verknüpft und verzögert. Auf dem oberen Pfad trifft es unverzögert auf das Gatter G2. Bei x2 = 1 und einer steigenden Flanke an x1 wechselt dadurch die Ausgabe kurzzeitig auf »0« (Abb. 1.35 b). td2
td1 a)
c)
x1 x2
x1 x2
&
z1
&
y1
vereinfacht td2 td1 z2
&
x1 x2
1 0 1 0
z1
1 0
y1
1 0
b) y2
⇒Web-Projekt:P1.3/SimGlitch2.vhdl
z2
1 0
y2
1 0
td1
td1
td1 td2
td2
td2
td2
td1 td2
Glitch td1 td2
td2
Abb. 1.35. a) Schaltung mit rekonvergentem Signalfluss b) simulierte Zeitverläufe mit einem Glitch c) funktionsgleiche Schaltung ohne rekonvergenten Signalfluss
Das Entstehen von Glitches hängt nicht von der Funktion, sondern von der Struktur einer Schaltung ab. Der Ausdruck, der die logische Funktion der Schaltung in Abb. 1.35 a beschreibt, kann z.B. wie folgt umgeformt werden: y = x2 x1 x1 = (¯ x2 ∨ x ¯ 1 ) x1 = x ¯ 2 x1 Die Schaltung für den vereinfachten Ausdruck enthält keine rekonvergenten Signalpfade. Das schließt aus, dass bei der Änderung eines einzelnen Eingabebits ein Glitch entsteht (Abb. 1.35 c). Zeitnahe Änderungen an mehreren Eingängen können natürlich weiterhin Glitches verursachen. Wenn die Schaltungsstruktur einer kombinatorischen Funktion unbekannt, noch zu entwerfen oder zu synthetisieren ist, kann nicht vorhergesagt werden, ob oder unter welchen Bedingungen Glitches im Einsatz auftreten werden. In einem synthesebasierten Entwurf gilt deshalb die Grundregel:
46
1 Modellbildung und Simulation
Eine Schaltung ist nur dann laufzeittolerant, wenn ihre Funktion nicht von möglicherweise auftretenden oder nicht auftretenden Glitches abhängt. 1.3.2 Das Verzögerungsmodell einer Signalzuweisung Glitches vervielfachen die Anzahl der Änderungsereignisse bei der Ausbreitung eines Signals in einer Schaltung. Das treibt den Rechenaufwand für die Simulation in die Höhe. Auf der anderen Seite benötigt jede physikalische Signaländerung eine gewisse Zeit, so dass zumindest die sehr kurzen Glitches, die sich nach dem bisherigen Simulationsmodell ergeben, in Wirklichkeit gar nicht auftreten. Auch um Rechenzeit zu sparen, sind kurz aufeinanderfolgende Signaländerungen in der Regel zu unterdrücken. Die Mindestlänge der Glitches, die die Simulation berechnet und weiterleitet, wird mit dem Verzögerungsmodell der Signalzuweisungen eingestellt. Ohne Angabe hat eine Signalzuweisung das Standardverzögerungsmodell. Mit diesem werden bei der Zuweisung eines neuen Wert-Zeit-Tupels alle schwebenden Zuweisungen gelöscht. Es gibt eine Ausnahme. Wenn die schwebenden Zuweisungen den neuen Wert bereits für einen früheren Simulationszeitpunkt vorgemerkt haben, wird der frühere Zeitpunkt beibehalten. Insgesamt werden mit dem Standardverzögerungsmodell alle Glitches bis zur Länge der Verzögerungszeit unterdrückt. In Abb. 1.36 a wird das Eingabesignal x mit einer Verzögerung von 10 ns an das Ausgabesignal y zugewiesen. Die erste Eingabeänderung wird wirksam, die zweite ist bei der Zuweisung der dritten Änderung noch schwebend und wird gelöscht. Die dritte Zuweisung erreicht zwar ihren Ausführungszeitpunkt, bewirkt aber keine Ausgabesignaländerung. Die vierte Zuweisung wird wieder vor ihrem Ausführungszeitpunkt gelöscht und die a) Standardverz¨ogerungsmodell y <= x after 10 ns;
b) Transportverz¨ogerungsmodell y <= transport x after 10 ns;
x
’1’ ’0’
x
’1’ ’0’
y
’1’ ’0’
y
’1’ ’0’
0 10 20 30 40 50 t in ns
0 10 20 30 40 50 t in ns
c) Verz¨ogerungsmodell mit R¨ uckweiszeit y <= reject 5 ns inertial x after 10 ns; x
’1’ ’0’
y
’1’ ’0’
0 10 20 30 40 50 t in ns
schwebende Zuweisung, die normal abgearbeitet wird durch eine sp¨ atere Zuweisung gel¨ oscht wird ¨ keine Anderung bewirkt
Abb. 1.36. Signalzuweisungen mit unterschiedlichen Verzögerungsmodellen
1.3 Laufzeittoleranz
47
fünfte bewirkt wieder keine Ausgabesignaländerung. Die Mindestdauer eines Eingabeimpulses, der auf einen Ausgabeimpuls abgebildet wird, ist 10 ns. Die anderen Verzögerungsmodelle sind in der Signalzuweisung anzugeben: Signalname <= VM W [after td ]{, W after td }; VM ⇒ transport|[reject tr ] inertial]
(VM – Verzögerungsmodell; W – Ausdruck zur Berechnung des neuen Wertes; td – Verzögerungszeit; tr – Rückweiszeit). Das Transportverzögerungsmodell (Schlüsselwort »transport«) deaktiviert die Glitch-Unterdrückung. Bei der Zuweisung eines neuen Wert-Zeit-Tupels bleiben alle schwebenden Zuweisungen, deren Zeitwerte vor dem der neuen Zuweisung liegen, erhalten. Schwebende Zuweisungen für spätere Zeitpunkte werden unabhängig vom Verzögerungsmodell immer gelöscht. Die Signalzuweisung in Abb. 1.36 b erzeugt eine exakte zeitversetzte Kopie des Eingabesignals auf der rechten Zuweisungsseite. Zwischen den beiden Grenzfällen – Löschen aller oder nur der späteren schwebenden Zuweisungen – gibt es ein weiteres Verzögerungsmodell mit einstellbarer Glitch-Unterdrückungsdauer. Das ist das Reject-Inertial-Modell: y <= reject tr inertial W after td {, W after td };
(td – Verzögerungszeit; tr < td – Rückweiszeit). Bei einer neuen Zuweisung werden bei diesem Verzögerungsmodell nur die schwebenden Zuweisungen für spätere Zeitpunkte als tsim + td − tr (tsim – aktuelle Simulationszeit der Signalzuweisung) gelöscht. Dabei gibt es wieder dieselbe Ausnahme wie bei dem Standardverzögerungsmodell. Wenn die schwebenden Zuweisungen den neuen Wert bereits für einen früheren Simulationszeitpunkt vorgemerkt haben, wird der frühere Zeitpunkt beibehalten. Eine Signalzuweisung mit diesem Verzögerungsmodell unterdrückt alle Glitches bis zur Länge der Rückweiszeit tr (Abb. 1.36 c). Abbildung 1.37 zeigt ein Beispielprogramm, um die Wirkung der unterschiedlichen Verzögerungsmodelle genauer zu untersuchen. Die Signale sind vom Typ natural und haben den Anfangswert null. Zum Simulationsbeginn werden allen drei Signalen fünf Änderungen im Abstand von 1 ns mit von »1« hochzählenden Werten zugewiesen. Nach einer Wartezeit von 2,5 ns werden zwei weitere Änderungen nach 3 ns und 4 ns zugewiesen, diesmal jedoch mit unterschiedlichen Verzögerungsmodellen. Mit dem Standardverzögerungsmodell löscht die zweite Zuweisung die schwebenden Änderungen für alle Zeitpunkte größer der aktuellen Simulationszeit von 2,5 ns. Von der ersten Zuweisung werden nur die Änderungen nach 1 ns und 2 ns ausgeführt. Mit dem Transportverzögerungsmodell bleiben die schwebenden Änderungen für alle Zeitpunkte größer der aktuellen Simulationszeit von 2,5 ns plus der Verzögerungszeit von 3 ns erhalten, so dass auch die Änderungszuweisungen für 3 ns, 4 ns und 5 ns ausgeführt werden. Bei der Zuweisung mit Rückweiszeit werden die schwebenden Änderungen für alle Zeitpunkte größer der aktuellen Simulationszeit von 2,5 ns plus der Verzögerungszeit von 3 ns
48
1 Modellbildung und Simulation signal y1, y2, y3: natural; ... y1 <= 1 after 1 ns, 2 after 2 ns, 3 after 3 ns, 4 after 4 ns, 5 after 5 ns; y2 <= ...; y3 <= ...; wait for 2.5 ns;
y1 0
1
2
8 9
y2 0
1
2
3
4 5 8 9
y3 0
1
2
3
8 9
y1 <= 8 after 3 ns, 9 after 4 ns; 0 1 2 3 4 5 6 y2 <= transport 8 after 3 ns, 9 after 4 ns; tsim in ns y3 <= reject 1,6 ns inertial 8 after 3 ns, 9 after 4 ns; wait; ⇒Web-Projekt: P1.3/Test VM.vhdl
Abb. 1.37. Test der Wirkung der Verzögerungsmodelle (... – Zuweisung wie an y1 )
abzüglich der Rückweiszeit von 1,6 ns gelöscht. Die schwebende Änderung für 3 ns wird ausgeführt und die schwebenden Änderungen für 4 ns und 5 ns werden gelöscht. 1.3.3 Simulation mit Halte- und Verzögerungszeiten Der Toleranzbereich für die Verzögerung einer kombinatorischen Schaltung – sei es ein einzelnes Gatter oder eine Schaltung aus vielen Gattern – lässt sich mit zwei Worst-Case-Parametern beschreiben: • •
der minimalen Haltezeit th (hold time) und der maximalen Verzögerungszeit td (delay time).
Die minimale Haltezeit th – kurz Haltezeit – ist die Zeit, die der alte gültige Ausgabewert nach einer Eingabeänderung garantiert noch erhalten bleibt. Die maximale Verzögerungszeit td – kurz die Verzögerungszeit – ist die Zeit nach Anliegen des neuen gültigen Eingabewertes, nach der spätestens der neue gültige Wert ausgegeben wird (Abb. 1.38). th , td x
f (x)
x wi y
Signalwert ung¨ ultig
th y f (wi )
wi+1 td f (wi+1 ) t
Abb. 1.38. Simulation mit Halte- und Verzögerungszeiten
Das VHDL-Modell für dieses Zeitverhalten ist ein Prozess, der bei jeder Eingabeänderung geweckt wird. Innerhalb des Prozesses wird abgefragt, ob der alte Eingabewert vor der Änderung gültig war und ob der neue Eingabewert gültig ist. Im ersten Fall wird dem Ausgabesignal nach der Haltezeit der Wert »ungültig« zugewiesen, im zweiten Fall nach der Verzögerungszeit der neue gültige Funktionswert:
1.3 Laufzeittoleranz
49
process(x) begin if vorherige_Eingabe_gültig then y <= ungültig after th; end if; if neuer_Eingabewert_gültig then y <= transport f (x) after td; end if; end process;
(f (x) – Vorschrift zur Berechnung der Ausgabewerte aus den Eingabewerten, vorerst ein Ausdruck, nach der Einführung von VHDL-Funktionen meist ein Funktionsaufruf). Die zweite Zuweisung muss mit dem Transportverzögerungsmodell erfolgen, damit die möglicherweise noch schwebende Zuweisung des Pseudo-Wertes »ungültig« nicht gelöscht wird. Dasselbe Verhalten kann verkürzt mit nur einer einzigen nebenläufigen Signalzuweisung beschrieben werden: y <= ungültig after th, f (x) after td;
Eine nebenläufige Signalzuweisung wird in einen Prozess übersetzt, der bei jeder Eingabeänderung geweckt wird (vgl. Abschnitt 1.2.4). Es sei unterstellt, dass der Funktionswert einer ungültigen Eingabe gleichfalls ungültig ist. Dann wird bei einer Eingabeänderung nach »ungültig« sowohl nach der Haltezeit als auch nach der Verzögerungszeit der Wert »ungültig« zugewiesen. Die zweite Zuweisung hat keine Wirkung. Bei der nachfolgenden Eingabeänderung nach »gültig« hat die Zuweisung von »ungültig« nach der Haltezeit auch keine Wirkung. Erst die Zuweisung des neuen gültigen Funktionswertes ändert die Ausgabe. Abbildung 1.39 demonstriert das am Beispiel eines Inverters. Die Signalzuweisung an x in Abb. 1.39 a stellt das Testeingabesignal bereit. Sie wird in
a)
signal x, y: std_logic; constant th: delay_length := 1 ns; constant td: delay_length := 2 ns; ... x <= ’0’ after 0.5 ns, ’X’ after 4 ns, ’1’ after 4.5 ns, ’X’ after 8 ns, ’0’ after 9.5 ns, ’1’ after 12 ns, ’0’ after 12.5 ns; y <= ’X’ after th, not x after td; ⇒Web-Projekt: P1.3/Test LT.vhdl th = 1 ns td = 2 ns
b)
x
x
1 0
y
1 0
y
nicht initialisiert (U) ung¨ ultig (X) c)
td
th 0
2
td
th 4
6
th td 8
10
th
td
tsim
Abb. 1.39. Simulation eines Inverters mit Halte- und Verzögerungszeit a) Simulationsmodell b) Schaltung c) Simulationsergebnis
50
1 Modellbildung und Simulation
einen Prozess mit einer leeren Weckliste übersetzt. Ein solcher Prozess wird nur einmal zum Simulationsbeginn ausgeführt. Er weist im konkreten Fall einmalig einen Signalverlauf zu und legt sich danach dauerhaft schlafen. Die zweite Zuweisung beschreibt den Inverter in Abb. 1.39 b mit einer Haltezeit th und einer Verzögerungszeit td . Der Prozess, der die Zuweisung im Simulationsmodell einrahmt, wird außer zum Simulationsbeginn auch bei jeder Änderung von x geweckt. Wenn das Eingabesignal ungültig wird, speichert der Ausgang für die Haltezeit noch den alten Wert und wird dann »ungültig«. Nach Anliegen eines neuen gültigen Eingabewertes übernimmt der Ausgang den invertierten Wert erst nach der Verzögerungszeit (Abb. 1.39 c). Abbildung 1.40 zeigt eine weitere Beispielsimulation mit Halte- und Verzögerungszeiten, diesmal von einer Schaltung aus drei Gattern. Die UND-Gatter reagieren im Beispiel auf Eingabeänderungen frühstens nach 0,5 ns und spätestens nach 1 ns. Für das ODER-Gatter liegt die Verzögerung im Bereich zwischen 0,5 ns und 2 ns. Das hat im Beispiel zur Folge, dass das Ausgabesignal die meiste Zeit ungültig ist. Die mehrfachen Wechsel der berechneten Signale zwischen »U« und »X« haben im Beispiel keine Bedeutung. Sie kommen dadurch zustande, dass bei der Simulation mit std_logic zwischen zwei Ursachen für ungültige Werte unterschieden wird. Wenn die Ursache eine fehlende Initialisierung ist, wird ungültig durch »U« und sonst durch »X« dargestellt. Diese Unterscheidung dient zur Aufdeckung von Initialisierungsfehlern in Schaltungen mit Speicherverhalten.
a)
signal x1, x2, x3, x4, z1, z2, y: STD_LOGIC; constant th: delay_length := 500 ps; constant td1: delay_length := 1 ns; constant td2: delay_length := 2 ns; ... G1:z1 <= ’X’ after th, x1 and x2 after td1; G2:z2 <= ’X’ after th, x3 and x4 after td1; G3: y <= ’X’ after th, z1 or z2 after td2; b)
x1 x2
&
z1
th , td2
G1
th , td1 x3 x4
&
≥1 z2
y
G3
G2
⇒Web-Projekt: P1.3/G3 LT.vhdl
x1 x2 x3 x4 z1 z2 y c)
th , td1
1 0 1 0 1 0 1 0 1 0 1 0 1 0
0
5
nicht initialisiert (U)
10 ung¨ ultig (X)
15
20
Simulation ohne th
tsim U ohne th
Abb. 1.40. Simulation einer 3-Gatter-Schaltung mit Halte- und Verzögerungszeiten a) VHDL-Beschreibung b) Schaltung c) Simulationsergebnis
1.3 Laufzeittoleranz
51
Die gestrichelten Linien an den berechneten Signalverläufen zeigen die Werteverläufe, die sich bei einer Simulation ohne Haltezeiten ergeben. Ohne Haltezeiten werden die Gültigkeitsfenster nicht mitberechnet. Ein ungültiges Signal hat nicht unbedingt den falschen Wert. In unserem Modell bedeutet ungültig lediglich, dass der Wert nicht garantiert richtig ist. Eine Schaltung, die ungültige Werte weiterverarbeitet, funktioniert möglicherweise, nur kann die Simulation dafür nicht garantieren. Der Nachweis, dass eine Schaltung laufzeittolerant ist, erfordert eine Simulation mit Halte- und Verzögerungszeiten, damit auch die Gültigkeitsfenster mitberechnet werden. 1.3.4 Laufzeitanalyse Die Laufzeitanalyse ist ein einfaches Verfahren zur Berechnung der Halteund der Verzögerungszeit einer Gesamtschaltung aus den Halte- und Verzögerungszeiten der Teilschaltungen, aus denen sie besteht. In einer Schaltung mit einem gerichteten Datenfluss addieren sich die Halte- und Verzögerungszeiten entlang aller Signalpfade. Die Gesamthaltezeit ist die Zeit, die sich die Ausgabe nach einer Eingabeänderung garantiert nicht ändert. Das ist die Haltezeit des Pfades mit der kleinsten Summe von Einzelhaltezeiten. Die Gesamtverzögerungszeit ist die Zeit, nach der das System spätestens den Funktionswert der neuen Eingabe ausgibt. Das ist entsprechend die Verzögerungszeit entlang des längsten Pfades mit der größten Verzögerung. Abbildung 1.41 zeigt eine Schaltung aus fünf Gattern. Die Tabelle daneben zeigt für alle Pfade durch die Schaltung die aufsummierten Halte- und Verzögerungszeiten. Die kürzeste Haltezeit hat der Pfad von x5 über G4 nach y2 . Seine Haltezeit ist die Gesamthaltezeit. Die längste Verzögerungszeit, die gleichzeitig die Gesamtverzögerungszeit ist, hat der Pfad über G2-G4-G5.
a)
td1 =2 ns t = 2 ns th1 =1 ns d3 th3 =1 ns td5 = 2 ns th5 = 1 ns x1 & & x2 G1 & y1 G3 td2 =3 ns G5 td4 =3 ns th2 =1 ns th4 =1 ns x3 & & y2 x4 G2 G4 x5
Pfad
P
th.i
G1-G3-G5 3 ns
b)
P
td.i
6 ns
G2-G3-G5 3 ns
7 ns
G2-G4-G5 3 ns
8 ns
G2-G4
2 ns
6 ns
G4-G5
2 ns
5 ns
G4
1 ns
3 ns
Abb. 1.41. Beispiel für die Laufzeitanalyse a) Schaltung b) Tabelle der Halteund Verzögerungszeiten aller Pfade
52
1 Modellbildung und Simulation
Für größere Schaltungen sind Algorithmen vom Typ »Wiederhole für jeden Pfad« ungünstig, weil die Pfadanzahl exponentiell mit der Schaltungsgröße zunehmen kann. Die Laufzeitanalyse lässt sich auch mit einem Algorithmus nach dem Schema »Wiederhole für alle Teilschaltungen« beschreiben, bei dem der Rechenaufwand nur linear mit der Schaltungsgröße zunimmt. Dazu werden die Eingänge der Gesamtschaltung, die Eingänge aller Teilschaltungen, die Ausgänge der Teilschaltungen und die Ausgänge der Gesamtschaltung je zu einer Signalgruppe zusammengefasst und für jede Signalgruppe die Halte- und die Verzögerungszeitdifferenz zur Signalgruppe S0 berechnet9 . Beginnend an den Schaltungseingängen werden dabei für jede Teilschaltung zwei Operationen ausgeführt: • •
Berechnung der Halte- und Verzögerungszeitdifferenz für die Gruppe der Eingabesignale durch Minimum- bzw. Maximumbildung und Berechnung der Halte- und Verzögerungszeitdifferenz für die Gruppe der Ausgabesignale durch Addition der Parameter der Teilschaltung.
Abbildung 1.42 a zeigt die Beispielschaltung aus Abb 1.41 mit den eingezeichneten Signalgruppen. Abbildung 1.42 b veranschaulicht den Rechenweg. Die Halte- und Verzögerungszeitdifferenzen der Gruppen S1 und S2 zur Gruppe S0 sind gleich den Halte- und Verzögerungszeiten der dazwischen liegenden Gatter. Gruppe S3 übernimmt für die Haltezeitdifferenz das Minimum und für die Verzögerungszeitdifferenz das Maximum der Gruppen S1 und S2. Für Gruppe S5 werden die Halte- und Verzögerungszeiten des Gatters dazwischen dazu addiert etc. Die grauen Balken zeigen, wie sich das Ungültigkeitsfenster der Signalgruppe S0 auf die Ungültigkeitsfenster der im Datenfluss nachfolgenden Signale fortpflanzt. Der Rechenaufwand verhält sich proportional zur Anzahl der Teilschaltungen. 1.3.5 Zusammenfassung und Übungsaufgaben Bei einer realen Schaltung ist die Verzögerung zwischen den Eingabe- und den Ausgabeänderungen kein exakt angebbarer Wert. Denn jedes Signal ist beim Übergang zwischen zwei gültigen Werten kurzzeitig ungültig. Bis zum Erreichen des stationären Wertes können mehrfache Wechsel (Glitches) auftreten. Diese und weitere Eigenarten digitaler Schaltungen lassen sich nur mit Toleranzbereichen für die Zeitparameter modellieren. Eine kombinatorische Schaltung hat eine (minimale) Haltezeit, nach der nach einer Eingabeänderung garantiert noch der alte, und eine (maximale) Verzögerungszeit, nach der spätestens der neue Wert am Ausgang anliegt. In der Zeit dazwischen kann für den Ausgabewert nicht garantiert werden. Das »nicht garantiert« wird durch den Pseudo-Wert »ungültig«, dargestellt. Die Halte- und die Verzögerungszeiten einer Gesamtschaltung lassen sich mit einer Laufzeitanalyse aus den Halte- und Verzögerungszeiten der Teilschaltungen berechnen. 9
Die Signalgruppe S0 ist die Gruppe der Eingabesignale der Gesamtschaltung.
1.3 Laufzeittoleranz
x1 x2
td1 th1
S 0
& td2 th2
x3 x4 x5
a)
S 1
S 2
&
S 3
S 4
td3 th3
S 5
& td4 th4 &
&
S 8
S 9
y1 y2 Signalgruppen
td1 th1
S1
td5 th5
S 6
Signalgruppe S0
S 7
53
thSi
tdSi
0 (Definition)
0 (Definition)
thS0 + th1
tdS0 + td1
td2 th2
S2 S3
th3
S5 S4
thS3 + th3 thS0
td4 th4
S6 S7 b)
thS0 + th2 tdS0 + td2 min(thS1 , thS2 ) max(tdS1 , tdS2 )
td3
td5 th5
S8 S9
tdS3 + td3 tdS2
thS0 + th4 tdS2 + td4 min(thS5 , thS6 ) max(tdS5 , tdS6 ) thS7 + th5 tdS7 + td5 min(thS6 , thS9 ) max(tdS6 , tdS9 )
Abb. 1.42. Laufzeitanalyse mit Signalgruppen a) Schaltung b) Berechnung
Aufgabe 1.10 a) Entwickeln Sie einen Testrahmen zur Simulation der Schaltung in Abb. 1.43 mit den vorgegebenen Eingabesignalverläufen. Die Gatter und Eingabesignalverläufe sollen durch nebenläufige Signalzuweisungen beschrieben und die Signale zi und y zum Simulationsbeginn die Anfangswerte haben, die sie hätten, wenn die Anfangseingabewerte vor dem Simulationsbeginn schon länger als die maximale Verzögerungszeit anliegen würden. b) Bestimmen Sie die Verläufe der internen Signale und des Ausgabesignals zuerst ohne Rechner und überprüfen Sie das Ergebnis durch Simulation.
x0 x1 x2
2 ns
1 0 1 0 1 0
x0 1 ns x1 0 2 4 6 8 10
tsim
x2
& G1 z0
4 ns ==
G2 z1 G3 z2
1,5 ns &
Abb. 1.43. Schaltung und Eingabesignale zu Aufgabe 1.10
G4
y
54
1 Modellbildung und Simulation
c) Einer der Eingabesignalwechsel verursacht zwei Signalwechsel am Ausgang. Welcher Signalwechsel ist das und unter welcher Bedingung kommt der Mehrfachsignalwechsel zustande? Aufgabe 1.11 Innerhalb eines Prozesses werden folgende Signalzuweisungen und Warteanweisungen abgearbeitet: y <= ’0’, ’X’ after 3 ns, ’1’ after 7 ns, ’X’ after 8 ns, ’1’ after 10 ns, ’0’ after 11 ns, ’1’ after 13 ns, ’1’ after 15 ns, ’X’ after 18 ns, ’0’ after 20 ns; wait for 5 ns; y <= ’1’ after 12 ns; -- y <= transport ’1’ after 12 ns; -- y <= reject 8 ns inertial ’1’ after 12 ns;
Welche der schwebenden Änderungen werden gelöscht, wenn die zweite Signalzuweisung a) mit dem Standardverzögerungsmodell b) mit dem Verzögerungsmodell »transport« c) mit dem Verzögerungsmodell »reject 8 ns inertial« erfolgt und welchen Signalverlauf erzeugen die drei Zuweisungen, wenn die erste Signalzuweisung zum Zeitpunkt tsim = 0 ausgeführt wird? Überprüfen Sie das Ergebnis mit dem Simulator. Aufgabe 1.12 Bestimmen Sie für die Schaltung in Abb. 1.44 die Signalverläufe der internen Signale z0 bis z2 und des Ausgabesignals y für den Eingabesignalverlauf x <= ’0’, ’1’ after 10 ns, ’0’ after 20 ns;
Alle Signale seien vom Typ std_logic und haben zum Simulationsbeginn den Pseudo-Wert ’U’ (uninitialised, nicht initialisiert). 1 ns 1 ns x
z0
1 ns 1 ns 1 ns 1 ns
1 ns =1
z1
z2
Abb. 1.44. Schaltung zu Aufgabe 1.12
1 ns =1
y
1.4 Register
55
Aufgabe 1.13 Gegeben ist die nachfolgende Schaltungsbeschreibung: –- Vereinbarungen der Entwurfseinheit signal x: std_logic_vector(4 downto 0) := "01011"; signal z: std_logic_vector(4 downto 0); signal y: std_logic; –- nebenläufige Zuweisungen im Beschreibungsteil der Entwurfseinheit G1: z(0) <= ’X’ after 4 ns, x(0) and x(1) after 8 ns; G2: z(1) <= ’X’ after 5 ns, x(2) or x(3) after 6 ns; G3: z(2) <= ’X’ after 3 ns, z(0) or z(1) after 6 ns; G4: z(3) <= ’X’ after 4 ns, z(1) and x(3) after 7 ns; G5: z(4) <= ’X’ after 3 ns, z(3) or x(4) after 5 ns; G6: y <= ’X’ after 5 ns, z(2) or z(4) after 7 ns;
a) Zeichnen Sie den Signalflussplan und lesen Sie für alle Teilschaltungen die Halte- und die Verzögerungszeiten ab. b) Bestimmen Sie die Halte- und die Verzögerungszeit der Gesamtschaltung.
1.4 Register Nach einer Eingabeänderung sind die Signalwerte an den Ausgängen einer kombinatorischen Schaltung für eine gewisse Zeit ungültig. Ungültig kann bedeuten, dass die Spannungswerte zur Signaldarstellung im verbotenen Bereich liegen oder dass zu nicht genau bekannten Zeitpunkten Signaländerungen auftreten können. Bei der Simulation pflanzen sich die Zeitfenster mit gültigen und ungültigen Werten entlang der Signalpfade fort. Da die Haltezeiten stets kleiner als die Verzögerungszeiten sind, nimmt die Breite der Gültigkeitsfenster entlang der Signalpfade ab. Wie aus Abb. 1.45 ablesbar, ist die Breite tGE des Gültigkeitsfensters am Ende eines Signalpfads gleich der Breite tGA des Gültigkeitsfensters am Anfang abzüglich der Differenz aus den Verzögerungszeiten und den Haltezeiten entlang des Pfades. Für den Pfad in der Abbildung gilt z.B.
0 z1
≥1 th1 , td1
0 z2
≥1 th2 , td2
1 z3
td1 &
z4
th3 , td3
tGA G¨ ultigkeitsdauer am Pfadanfang tGE G¨ ultigkeitsdauer am Pfadende
z1 z2 z3 z4
td2
td3
tGE
1 0 1 0 1 0 1 0
tGA
th1 th2 th3
Abb. 1.45. Abnahme der Breite des Gültigkeitsfensters entlang eines Signalpfades
56
1 Modellbildung und Simulation
tGE = tGA +
3 X
thi −
i=1
3 X
tdi
i=1
Damit die Daten an den Pfadenden weiterverarbeitet werden können, müssen sie innerhalb ihrer Gültigkeitsfenster abgetastet werden. Abtasten bedeutet, dass die Daten in einem schmalen Zeitfenster übernommen und die übrige Zeit gespeichert werden. Die Abtastung verbreitert die Gültigkeitsfenster, in dem die Daten weiterverarbeitet werden können. 1.4.1 Register als Abtastelemente Das Funktionselement für die Signalabtastung heißt Register. Es hat das abzutastende Signal x als Eingang, ein typgleiches abgetastetes Signal x’ als Ausgang und einen Takteingang T (Abb. 1.46). Der Takt ist ein Spezialsignal, das mit hoher Zeitgenauigkeit periodisch zwischen »0« und »1« umschaltet und dessen Flanken die Abtastzeitpunkte festlegen. Die Abtastflanken – das können die steigenden, die fallenden oder beide Taktflanken sein – werden als aktive Taktflanken bezeichnet. T
ts , tn thr , tdr x T
n
n
x’
b)
tn
ts
w1
x x’
a)
ts w0
tn w2
w1 thr tdr
w2
T x x’ wi
Takt abzutastendes Signal abgetastetes Signal Signalwert Abtastfenster Wert ung¨ ultig
Abb. 1.46. Register a) Symbol b) Zeitverhalten
Das Abtastverhalten eines Registers wird durch die folgenden vier Zeitparameter beschrieben: • • • •
die die die die
Vorhaltezeit ts , Nachhaltezeit tn , Registerhaltezeit thr und Registerverzögerungszeit tdr .
Die allgemeine Funktion eines Registers ist anhand der Signalverläufe in Abb. 1.46 b dargestellt. Bei jeder aktiven Taktflanke – in der Abbildung der steigenden Flanke – wird das Ausgabesignal nach der Registerhaltezeit thr ungültig und übernimmt nach der Registerverzögerungszeit tdr den abgetasteten Wert. Voraussetzung für eine korrekte Datenübernahme ist, dass der Eingabewert im gesamten Abtastfenster stabil und gültig ist. Das Abtastfenster beginnt dabei um die gegebene Vorhaltezeit vor und endet nach der gegebenen Nachhaltezeit nach der aktiven Taktflanke. Meist kann entweder die Vorhaltezeit oder die Nachhaltezeit gleich null gesetzt werden.
1.4 Register
57
Aus der Spezialaufgabe eines Taktes, die Abtastzeitpunkte festzulegen, resultieren folgende Besonderheiten. Taktsignale werden im Gegensatz zu anderen Signalen ohne Zeittoleranzen und unbestimmte Zwischenzustände modelliert. Die physikalisch bedingten zeitlichen Unsicherheiten der Taktflanken stecken mit in den Zeitparametern der Register. Takte müssen Glitch-frei sein und mit geringen Verzögerungsunterschieden an die Takteingänge der einzelnen Register und Registerzellen geführt werden. Das erfordert spezielle Taktversorgungsschaltungen, die in der Regel manuell zu entwerfen sind (siehe später Abschnitt 4.3.6). Das Abtasten ist eine Grundfunktion für alle sequenziellen Abläufe in einer digitalen Schaltung. Deshalb wird eine spezielle Notation eingeführt. Das Signal, aus dem durch Abtastung ein Signal x gebildet wird, wird mit x+ bezeichnet, das einmal abgetastete Signal von x mit x’, das zweimal abgetastete Signal mit x” etc. In VHDL werden wir für x+ »x_next«, für x’ »x_del«, für x” »x_del2« etc. schreiben. 1.4.2 VHDL-Abtastprozesse Das Verhaltensmodell für ein Register ist ein Abtastprozess. Ein Abtastprozess ist ein Prozess, der nur von Taktänderungen geweckt wird, nur bei einer aktiven Taktflanke Signalzuweisungen ausführt und der optional die Vor- und Nachhaltebedingungen kontrolliert. Dafür werden zusätzliche, noch nicht eingeführte VHDL-Beschreibungsmittel benötigt: • • •
Signalattribute, Fallunterscheidungen im Kontrollfluss und Anweisungen zur Kontrolle zuzusichernder Bedingungen.
Attribute sind in VHDL Beschreibungsmittel, um zusätzliche Eigenschaften von Objekten abzufragen. Mit Signalattributen kann z.B. abgefragt werden, ob ein bestimmtes Signal den Prozess geweckt hat, ob ein anderes Signal zum Weckzeitpunkt bereits hinreichend lange den aktuellen Wert führt und wann ein Signal das letzte Mal seinen Wert geändert hat (Tabelle 1.1). Mit den Attributen ’stable(...) und ’last_event können die Vor- und Nachhaltebedingungen kontrolliert werden. Die Abfrage, ob ein Prozess von der steigenden oder fallenden Flanke geweckt wurde, erfolgt vorzugsweise mit den in dem standardisierten Package ieee.std_logic_1164 definierten Funktionen: function rising_edge(signal s: std_logic) return boolean is begin return s’event and s=’1’ and last_value(s)=’0’;10 end function; 10
vereinfachte Beschreibung ohne Berücksichtigung schwacher Signalwerte (schwache Signalwerte siehe später Abschnitt 4.1.6)
58
1 Modellbildung und Simulation Tabelle 1.1. Signalattribute
Attribut
Ergebnistyp
Ergebnis
s’event
boolean
true, wenn der Prozess durch eine Änderung von s geweckt wurde
s’stable(t)
boolean
true, wenn seit einer Zeit t keine Signaländerung stattgefunden hat
s’last_event
delay_length
Zeit seit der letzten Änderung von s
s’last_value
tTyp
Wert vor der letzten Änderung von s
s’delayed(t)
tTyp
das um t verzögerte Signal zu s
...
...
(s – Signal mit dem Datentyp tTyp; t – Variable oder Konstante vom Typ delay_length) function falling_edge(signal s: std_logic) return boolean is begin return s’event and s=’0’ and last_value(s)=’1’; end function;
Bei einer steigenden Flanke ist der Wert des Signals s, das den Prozess geweckt hat, vor der Änderung »0« und nach der Änderung »1«. Bei einer fallenden Flanke sind die beiden Werte vertauscht. Ein Prozess, der nur den Takt in der Weckliste hat, wird bei der Simulation immer genau einmal geweckt, ohne dass eine Taktflanke aufgetreten ist, und zwar zum Simulationsbeginn. Damit beim Simulationsbeginn keine Datenübernahme simuliert wird, sind die Flankenbedingungen zusätzlich mit »T’event« UND-verknüpft. Binäre Fallunterscheidungen im Kontrollfluss werden mit der If-Anweisung beschrieben: if b then Anweisung {Anweisung} end if;
Die Bedingung b ist dabei ein Ausdruck oder eine Funktion vom Typ boolean, z.B. der Rückgabewert der Funktion rising_edge(...) für »steigende Flanke«. Die Kontrolle zugesicherter Bedingungen erfolgt vorzugsweise mit der Assert-Anweisung [assert b] report m [severity sl ];
(b – zugesicherte Bedingung; m – Text für die Fehlermeldung; sl – Fehlerschwere). Die Fehlerschwere klassifiziert die Bildschirmausgabe nach ihrer Bedeutung für die Simulation und hat den im Package std.standard vordefinierten Typ type severity_level is ( note, –- Kontrollausgabe
1.4 Register warning, error, failure);
59
–- Warnung –- Fehler –- schwerer Fehler/Simulationsabbruch
Ohne Assert-Klausel wird die Report-Anweisung immer ausgeführt, ohne Severity-Klausel erhält die Ausgabe die Fehlerschwere note. Abbildung 1.47 zeigt eine Verhaltensbeschreibung für einen RegisterAbtastprozess. Die Typangabe tT yp steht für einen beliebigen Typ mit einem Pseudo-Wert für ungültig, z.B. std_logic oder std_logic_vector. In der Weckliste darf nur der Takt stehen (Zeile 6). Bei einer steigenden Taktflanke (Zeile 8) wird, wenn die Vorhaltebedingung erfüllt ist (Zeile 9), an die Ausgabe nach der Haltezeit der Pseudo-Wert »ungültig« und nach der Verzögerungszeit der aktuelle Eingabewert zugewiesen (Zeile 10). Die Nachhaltebedingung kann nur rückwirkend kontrolliert werden. Der Prozess wird für die Nachhaltezeit schlafen gelegt (Zeile 11). Wenn sich während dieser Zeit die Eingabe verändert hat, wird die Ausgabe auf ungültig gesetzt und eine Warnung ausgegeben (Zeile 12 bis 14). Wenn bereits die Vorhaltebedingung verletzt war, wird gleichfalls die Ausgabe auf ungültig gesetzt und eine Warnung ausgegeben (Zeile 16 bis 19). 1: entity Register is 2: generic(ts, tn, thr, tdr: delay_length); 3: port( T: in std_logic; 4: x: in tT yp; y: out tT yp); 5: end entity; ... 6: process(T) 7: begin 8: if rising_edge(T) then 9: if x’last_event>ts then 10: x_del <= ungültig after thr, x after tdr; 11: wait for tn; 12: if x’last_event
ts , tn thr , tdr x T T x x’
tTyp
ts
tTyp
tn
ts
w1 w0
x’
tn w2
w1 thr tdr
w2
Signale: T Takt x abzutastendes Signal x’ (x del) abgetastetes Signal Registerparameter: ts Vorhaltezeit tn Nachhaltezeit thr Haltezeit tdr Verz¨ogerungszeit tTyp Typ des Datensignal wi Signalwert Abtastfenster Wert ung¨ ultig
Abb. 1.47. Verhaltensmodell für ein Register
60
1 Modellbildung und Simulation
In den nachfolgenden Beispielen werden in den Abtastprozessen die Meldungen weggelassen und für die Nachhaltezeit tn = 0 unterstellt. Das vereinfacht die Registerbeschreibung auf folgende Beschreibungsschablone: 6: process(T) 7: begin 8: if rising_edge(T) then 9: if x’last_event>ts then 10: x_del <= ungültig after thr, x after tdr; 11: else 12: x_del <= ungültig after thr; 13: end if; 14: end if; 15: end process;
1.4.3 Verarbeitung plus Abtastung Mit Abtastprozessen werden nicht nur Register, sondern auch kombinatorische Schaltungen, deren Ausgaben abgetastet oder kombinatorische Schaltungen, die abgetastete Signale verarbeiten, modelliert. Abbildung 1.48 zeigt eine kombinatorische Schaltung mit einem Abtastregister am Ausgang. ts thr , tdr
thf , tdf y
+
T
thf t +t
df s y x f (x) process(T) w x i begin T ts if rising_edge(T) then y+ f (wi ) if x’delayed(thf)’last_event>tdf+ts-thf then tdr y <= ungültig after thr, f (x) after tdr; thr else f (w ) f (wi ) y i−1 y <= ungültig after thr; end if; notwendiges G¨ ultigkeitsfenster end if; f (x) Beschreibung der kombinatorischen end process; Funktion, z.B. durch einen Ausdruck
Abb. 1.48. Verarbeitung plus Ausgabeabtastung
Der Prozess wird wie der eines Registers bei jeder Taktänderung geweckt. Zur Kontrolle der Vorhaltebedingung wird das Eingabesignal x mit dem Attribut ’delayed(...) um die Haltezeit der vorgelagerten kombinatorischen Schaltung verzögert und für das verzögerte Signal kontrolliert, dass seine letzte Änderung mindestens eine Zeit tdf + ts − thf zurückliegt. Wenn die Vorhaltebedingung erfüllt ist, übernimmt der Registerausgang nach einer Haltezeit den Pseudo-Wert »ungültig« und nach der Verzögerungszeit den abgetasteten Funktionswert. Sonst übernimmt der Registerausgang nur den Wert »ungültig« und behält ihn bis zur nächsten aktiven Taktflanke. Die kombinatorische
1.4 Register
61
Funktion kann dabei mit einem Ausdruck, einem Funktionsaufruf oder einer Folge von Anweisungen nachgebildet werden. Bei einer Nachbildung durch mehrere Anweisungen sind die Zwischenergebnisse in Variablen weiterzureichen. Signale sind als Zwischenspeicher ungeeignet. Denn sie übernehmen die ihnen zugewiesenen Werte erst, nachdem der Prozess sich bis zur nächsten Taktänderung schlafen gelegt hat. Wenn eine Verarbeitungseinheit abgetastete Signale verarbeitet, ist das Verhaltensmodell ähnlich (Abb. 1.49). Die Eingabesignale der kombinatorischen Schaltung ändern sich nur nach einer aktiven Taktflanke. Das Verhaltensmodell ist wieder ein Abtastprozess, der bei jeder aktiven Taktflanke geweckt wird, die Vor- und die Nachhaltebedingungen überprüft, das Ausgabesignal nach einer Haltezeit invalidiert und, wenn die Vor- und die Nachhaltebedingungen eingehalten sind, nach einer Verzögerungszeit den neuen gültigen Ausgabewert zuweist. Nur die Zeitparameter errechnen sich etwas anders. Die Vor- und die Nachhaltezeit der Gesamtschaltung sind die des Abtastregisters und die Halte- und die Verzögerungszeit sind jeweils die Summe der des Registers und der der Verarbeitungsschaltung. ts thr , tdr thf , tdf process(T) T begin x’ x f (x) y x wi if rising_edge(T) then if x’last_event>ts then tdr ts T thr y <= ungültig after thr + thf, f (x) after tdr + tdf; x’ wi−1 wi else tdf thf y <= ungültig after thr + thf; end if; f (wi ) y f (wi−1 ) end if; end process; notwendiges G¨ ultigkeitsfenster
Abb. 1.49. Abtastung plus Verarbeitung
1.4.4 Register-Transfer-Funktion Eine Register-Transfer-Funktion ist die Funktion einer kombinatorischen Schaltung, die zwischen Eingabe- und Ausgaberegistern eingebettet ist (Abb. 1.50 a). Die Eingaberegister übernehmen mit jeder aktiven Taktflanke neue Eingabewerte und die Ausgaberegister die neuen Ergebnisse. Für die Verarbeitung von der Eingabeübernahme bis zur Ergebnisübernahme steht genau die Dauer einer Taktperiode zur Verfügung (Abb. 1.50 b). Damit die Vorhalteund die Nachhaltebedingungen des Ausgaberegisters erfüllt sind, muss gelten TP ≥ TPmin = tdr + tdf + ts tn ≤ tnmax = thr + thf
(1.7) (1.8)
62
1 Modellbildung und Simulation
process(T): TP begin t s , tn thr , tdr thr , tdr thf , tdf t s , tn if rising_edge(T) then if x’last_event>ts then x y’ y = f (x’) y x_del <= x; x’ else T a) x_del <= ungültig; TP end if; T 10 y_del <= ungültig after th, f (x_del) after td; tdr tdr thr thr end if; x’ w0 w2 w1 c) end process; Abtastfenster x’, y’ (x del, y del) Abtastsignale Modellierung nicht erforderlich
thf b)
y f (w0 )
tdf
ts tn f (w1 )
thf
Abb. 1.50. Register-Transfer-Funktion a) Schaltungsstruktur b) Signalverläufe c) VHDL-Beschreibung
(TP – Dauer einer Taktperiode; tdr – Verzögerung des Eingaberegisters; tdf – Verzögerung der Verarbeitungsschaltung; ts – Vorhaltezeit des Ergebnisregisters; tn – Nachhaltezeit des Ergebnisregisters; thr – Haltezeit des Eingaberegisters; thf – Haltezeit der Verarbeitungsschaltung). Die Dauer einer Taktperiode darf nicht kleiner als die Summe der Verzögerungszeiten und der Vorhaltezeit des Ausgaberegisters sein und die Nachhaltezeit des Ausgaberegisters darf nicht größer als die Summe der Haltezeiten sein. Die maximal zulässige Taktfrequenz ist der Kehrwert der minimalen Taktperiode: fT ≤
1 1 = TPmin tdr + tdf + ts
(1.9)
Die minimale Taktperiode und die maximale Nachhaltezeit lassen sich mit einer Laufzeitanalyse berechnen. Wenn die Gleichungen 1.7 und 1.8 erfüllt sind, sind auch die Vorhalte- und Nachhaltebedingungen für die Ausgabeabtastung immer erfüllt. Ohne dass sich die fehlererkennenden Eigenschaften verschlechtern, kann die Simulation auf ihre Kontrolle verzichten. Für die Register-Transfer-Funktion genügt eine verzögerungsfreie Simulation ohne Berechnung der Signalgültigkeit. In einer synchronen Schaltung braucht die Simulation praktisch nur für Eingaberegister die Vorhalte- und die Nachhaltebedingungen zu kontrollieren und nur für Ausgaberegister und Ausgabefunktionen Halte- und Verzögerungszeiten zu berücksichtigen. Für die restlichen Schaltungsteile, die von Registern eingerahmt sind, genügt das viel einfachere Verhaltensmodell ohne Kontrollen und ohne Verzögerungen (Abb. 1.50 c). 1.4.5 Taktversatz In der Schaltung in Abb. 1.51 besteht zwischen der Übernahmeflanke für das Eingaberegister und der Übernahmeflanke für das Ergebnisregister ein
1.4 Register
63
konstanter Taktversatz. Der Taktversatz kann entweder vorsätzlich eingebaut oder von unbeabsichtigten Laufzeitunterschieden entlang der Taktleitungen verursacht sein. thr , tdr x′
x
thf , tdf
TP
ts , tn y
f (x’)
T1 T2
y′
a)
T1
thr
tdr
t∆T12 Verz¨ogerung
x
T2
¨ Ubernahmeflanke Eingaberegister ¨ Ubernahmeflanke Ausgaberegister Abtastfenster
t∆T12
′
tdf
ts
y b)
0
thf tn
t
Abb. 1.51. Register-Transfer-Funktion mit Taktversatz a) Schaltung b) Signalverläufe
Damit die Vor- und die Nachhaltebedingungen in dieser Schaltung erfüllt sind, muss gelten: •
Die Summe der Verzögerungszeiten und der Vorhaltezeit muss kleiner als die Summe aus der Taktperiode und dem Taktversatz sein tdr + tdf + ts ≤ TP + t∆T12
•
(1.10)
Die Summe der Haltezeiten muss größer als die Summe aus dem Taktversatz und der Nachhaltezeit sein thr + thf ≥ tn + t∆T12
(1.11)
(t∆T12 – Taktversatz zwischen den Takten T1 und T2 ). Aus Ungleichung 1.11 ergibt sich der maximal zulässige Taktversatz: t∆T12 ≤ thr + thf − tn Mit dem maximal zulässigen Taktversatz beträgt die minimale Taktperiode TPmin = tdr + tdf + ts − (thr + thf − tn ) und die maximal zulässige Taktfrequenz fT ≤
1 tdr + tdf + ts − (thr + thf − tn )
(1.12)
Sie ist der Kehrwert der Summe der Verzögerungszeiten, der Vorhaltezeit und der Nachhaltezeit des Ergebnisregisters abzüglich der Summe der Haltezeiten. Die maximal erzielbare Taktfrequenz für eine Register-Transfer-Funktion
64
1 Modellbildung und Simulation
hängt folglich nicht nur von den Verzögerungszeiten, sondern in gleichem Maße auch von den Haltezeiten ab. Bei einer Übertragung über eine reflexionsfreie Leitung ist z.B. die Haltezeit fast so groß wie die Verzögerungszeit. Modelliert als Register-Transfer-Funktion darf das Senderegister einer Leitungsübertragung das nächste Datenpaket lange, bevor das vorherige Datenpaket das Empfangsregister erreicht hat, losschicken. Bei einer typischen Ausbreitungsgeschwindigkeit von 10 cm ns – das ist etwa ein Drittel der Lichtgeschwindigkeit – und einer Übertragungsrate von 109 Bit s sind auf einer 10 m langen Datenleitung gleichzeitig bis zu 100 Datenbits unterwegs. Das Prinzip, gleichzeitig auf diese Weise mehrere Datensätze bearbeiten oder übertragen zu lassen, wird auch als Wellen-Pipeline bezeichnet. 1.4.6 Zusammenfassung und Übungsaufgaben Eine kombinatorische Schaltung arbeitet nur dann zuverlässig, wenn ihre Ausgabesignale innerhalb ihrer Gültigkeitsfenster mit Registern abgetastet werden. Ein Register übernimmt seine Eingabewerte mit der aktiven Taktflanke und speichert sonst seinen Zustand. Ein Takt ist ein Spezialsignal, das periodisch, Glitch-frei und mit hoher Zeitgenauigkeit zwischen »0« und »1« wechselt. Das Verhaltensmodell eines Registers ist ein Abtastprozess, der nur von einer Taktänderung geweckt wird und in dem bei einer aktiven Taktflanke, wenn das Eingabesignal im Abtastfenster stabil ist, der Wert des abzutastenden Signals übernommen, gespeichert und für die nachfolgenden Schaltungsteile bereitgestellt wird. Bei einer Verletzung der Vor- oder Nachhaltebedingung ist der gespeicherte Wert ungültig und darf nicht weiterverarbeitet werden. Auch kombinatorische Funktionseinheiten mit Eingabeabtastung oder Ausgabeabtastung können, um Simulationsaufwand zu sparen, als Abtastprozesse modelliert werden. Eine eingangs- und ausgangsseitig mit Registern eingerahmte kombinatorische Funktionseinheit wird als Register-Transfer-Funktion bezeichnet. Das Besondere an einer Register-Transfer-Funktion ist, dass sich die Zeitbedingungen für eine zuverlässige Datenübernahme auch ohne Simulation kontrollieren lassen, so dass ein stark vereinfachtes Verhaltensmodell genügt. Aufgabe 1.14 Beschreiben Sie die Schaltung in Abb. 1.52 a mit dem Zeitverhalten in Abb. 1.52 b durch einen Abtastprozess. Die Nachhaltezeit sei tn = 0 und alle Signale sollen den Typ std_logic haben. Bei Verletzung der Vorhaltebedingungen ist der gespeicherte Wert zu invalidieren. a) Bestimmen Sie die Werte von t1 bis t7 des Zeittoleranzschemas in Abb. 1.52 b aus den Zeitparametern der Schaltung.
1.4 Register
65
b) Entwickeln Sie die VHDL-Beschreibung für den Prozess unter Nutzung der Ergebnisse aus Aufgabenteil a.
th1 , td1 x0 x1
=1
&
G1
a)
ts
th2 , td2
G2
x2
y+ T
T
thr , tdr
t1 t2
x1 x0
y Reg
t3 t4
x2
t5
y+ Abtastfenster ung¨ ultig
t6 t7
y
b)
Abb. 1.52. a) Schaltung b) Zeitverhalten zu Aufgabe 1.14
Aufgabe 1.15 Abbildung 1.53 zeigt den Signalflussplan einer Register-Transfer-Funktion, die, wenn das höchste Operandenbit xn−1 = 1 ist, den Operanden x bitweise invertiert und eine »1« addiert. a) Welcher funktionale Zusammenhang besteht zwischen der Operandenbitbreite n und der maximalen Taktfrequenz der Schaltung? b) Mit welcher Taktfrequenz darf die Schaltung bei einer Operandenbreite n = 16 maximal betrieben werden?
tdr = 1 ns
td1 = 0,5 ns td2 = n · 1 ns td3 = 2 ns
x n
T
n
xn−1
x+1
n n
1 0
ts = 0,5 ns y+ n
Abb. 1.53. Schaltung zu Aufgabe 1.15
Aufgabe 1.16 In welchem Zeitbereich muss der Taktversatz t∆T12 in der Schaltung in Abb. 1.54 liegen, damit die Vorhalte- und die Nachhaltebedingung des Ergebnisregisters auch noch bei einer Taktfrequenz von fT = 100 MHz erfüllt sind?
66
1 Modellbildung und Simulation thr = 0 ns tdr = 1 ns
thf = 5 ns tdf = 11 ns y = f (x)
x
tn = 0 ts = 2 ns y
Takt 1
Takt 2
Abb. 1.54. Schaltung zu Ausgabe 1.16
1.5 Asynchrone Eingabe Asynchron bedeutet »ohne zeitliche Abstimmung«. Eine asynchrone Eingabe ist ein Signal, das sich ohne zeitliche Ausrichtung zum Systemtakt ändert, z.B. das Signal, das eine manuell betätigte Taste erzeugt. Der Zeitversatz einer Signaländerung eines asynchronen Eingabesignals zur Abtastflanke eines Taktes ist praktisch eine Zufallsgröße. Auch hier gilt, dass die Werte nur innerhalb ihrer Gültigkeitsfenster ausgewertet werden dürfen. Das verlangt spezielle Schaltungslösungen und Beschreibungsschablonen. 1.5.1 Abtasten asynchroner Signale Der Abtastwert eines einzelnen Bits ist »0« oder »1«. Denn andere Werte kann ein einzelnes gespeichertes Bit nicht annehmen, auch nicht, wenn der abzutastende Wert im Abtastmoment ungültig ist (Abb. 1.55).
ts
thr , tdr
T
x’
x
x T
x’
txT ts
ts tdr thr
TP ts
tdr thr
txT ts
tdr thr
tdr thr
txT zuf¨alliger Zeitversatz x asynchrones Signal x’ abgetastetes Signal unwahrscheinlich gleichwahrscheinlich
Abb. 1.55. Abtasten eines asynchronen bitorientierten Signals
Sehr tückisch ist der Entwurfsfehler, ein asynchrones Signal vor der Abtastung zu verarbeiten. In Abb. 1.56 werden ein asynchrones bitorientiertes Eingabesignal a und ein abgetasteter Bitvektor x von einer kombinatorischen Schaltung mit einer Funktion f (. . .) auf einen Bitvektor y abgebildet, der abgetastet wird. Das Signal y ist nach Änderungen von x und a kurzzeitig ungültig. Die von x verursachten Änderungen sind wie bei einer RegisterTransfer-Funktion so zum Takt ausgerichtet, dass sie nicht im Abtastfenster liegen. Das gilt aber nicht für die von dem asynchronen Eingabesignal verursachten Änderungen. Diese haben einen zufälligen Zeitversatz zur aktiven Taktflanke, so dass mit einer gewissen Wahrscheinlichkeit ein ungültiger Wert abgetastet wird. Der Abtastwert eines ungültigen Bitvektors ist eine nicht
1.5 Asynchrone Eingabe
67
vorhersagbare Kombination aus Nullen und Einsen. Das kann auch ein unzulässiger, überhaupt nicht vorgesehener Bitvektorwert sein, der die Schaltung in einen Zustand versetzt, in dem sie nicht mehr funktioniert (Systemabsturz, siehe später Abschnitt 1.6.3). Der mittlere zeitliche Abstand zwischen den Fehlfunktionen, die die fehlende Abtastung verursacht, kann Sekunden, aber auch Tage oder Wochen betragen. Die ausgelösten Fehlfunktionen haben Zufallscharakter. Fehler, die sehr selten Fehlfunktionen verursachen, bleiben oft beim Test unerkannt und beeinträchtigen später im Einsatz die Zuverlässigkeit [29]. Die korrekte laufzeitrobuste Schaltung hat zusätzlich das in Abb. 1.56 grau unterlegt gezeichnete Abtastregister, das das asynchrone Eingabesignal vor der Weiterverarbeitung zum Takt ausrichtet. Nur so kann sichergestellt werden, dass auch y zu jedem Abtastzeitpunkt gültig ist. T
′
a
a
x
T
a
t h , td
ts
a x T
f (..)
1 0
y
w1 1 0
th y’
y
ts y’
w2
td
th
f (w1 , 0)
ts
f (w0 , 0)
w3
td
f (w1 , 1)
th f (w2 , 1)
ts
ung¨ ultig
td f (w3 , 0)
f (w2 , 1)
Abb. 1.56. Verarbeitung eines nicht abgetasteten Signals und Fehlerbeseitigung
1.5.2 Entprellen von Tasten Manuell betätigte Tasten und Schalter liefern nicht nur asynchrone Eingabesignale. Sie prellen auch noch. Prellen bedeutet, dass die Schaltkontakte – ein Masse-Feder-System – bei Betätigung mechanisch schwingen und möglicherweise mehrfach öffnen und schließen. Die Prelldauer ist deutlich kürzer als die minimale Betätigungsdauer und liegt im Millisekundenbereich. Als asynchrone Signale müssen Tastensignale vor ihrer Weiterverarbeitung abgetastet werden. Die Entprellung erfolgt im einfachsten Fall mit einer Abtastperiode, die größer als die maximale Prelldauer und kürzer als die minimale Betätigungsdauer ist (Abb. 1.57 a). Mit dieser Abtastperiode ist garantiert, dass bei jedem Ein-Aus-Wechsel das Tastensignal höchstens einmal während der Zeit, in der die Taste möglicherweise prellt, abgetastet wird. Der Abtastwert einer prellenden Taste kann »0« oder »1« sein. Vorher hat er garantiert den einen und nachher garantiert den anderen Wert, so dass das abgetastete Signal nach jeder Tastenbetätigung garantiert nur eine Schaltflanke hat. In Abb. 1.57 b wird das abgetastete Eingabesignal nochmal abgetastet, um ein Signal y zu bilden, das nach jeder fallenden Flanke des abgetasteten Eingabesignals für die Dauer eines Taktes »1« und sonst »0« ist.
68
1 Modellbildung und Simulation T 10 signal T, x, x_del, x_del2, y: std_logic; tP ... x 10 tPr process(T) x’ 10 begin x” 10 if rising_edge(T) then if x=’1’ then x”x’ 11 10 00 x_del <= ’1’; th td y 10 else a) x_del <= ’0’; t h , td end if; x_del2 <= x_del; end if; x’ x” x end process;
–- nebenläufige Signalzuweisung y <= ’X’ after th, not x_del and x_del2 after td; c)
⇒Web-Projekt: P1.5/Entprellung.vhdl
b)
01
&
11
y
T
tPr maximale Prelldauer TP Abtastperiode th , td Halte- und Verz¨ogerungszeit inkl. der der Register
Abb. 1.57. Entprellen und Flankenerkennung für eine Tasteneingabe a) Schaltung b) Signalverläufe c) VHDL-Beschreibung
In der VHDL-Beschreibung in Abb 1.57 c werden die beiden Register im selben Abtastprozess durch je eine Signalzuweisung beschrieben. Bei der Abtastung eines binären asynchronen Signals ist der Abtastwert immer »0« oder »1«, aber nicht »X«. Im Programmbeispiel wird Abtastwerten ungleich »1« eine »0« zugeordnet. Zutreffender wäre eine zufällige Auswahl von »0« oder »1. Die zweite Abtastung ist eine Register-Transfer-Funktion und wird als solche ohne Zeitverhalten modelliert, da sich die Einhaltung der Zeitbedingungen auch ohne Simulation überprüfen lassen. Die bei den Signalzuweisungen im Abtastprozess unberücksichtigten Registerhalte- und Registerverzögerungszeiten seien in der Halte- und der Verzögerungszeit der kombinatorischen Ausgabefunktion, die als nebenläufige Signalzuweisung beschrieben ist, mit eingerechnet. 1.5.3 Asynchrone Initialisierung Die einzelnen Bits eines Registers haben nach Zuschalten der Versorgungsspannung und beim Start einer Simulation einen unbestimmten Anfangswert. Bei der Modellierung mit dem vom VHDL-Standard empfohlenen Bittyp std_logic ist das der symbolische Wert für nicht initialisiert »U« (uninitialized, vgl. Abschnitt 1.2.1). Bevor der Registerinhalt ausgewertet werden darf, muss ein definierter Wert in das Register geschrieben werden. Dazu haben Register oft einen zusätzlichen Initialisierungseingang, der, wenn er aktiviert wird, für jedes Bit einen definierten Wert einstellt. Abbildung 1.58 zeigt das erweiterte Datenflusssymbol und ein vereinfachtes Verhaltensmodell
1.5 Asynchrone Eingabe
69
ohne Berücksichtigung von Verzögerungen. Der Registerprozess hat, damit er auch zur Initialisierung geweckt wird, zusätzlich das Initialisierungssignal in der Weckliste. Bei einer Aktivierung des Initialisierungssignals wird der Anfangswert – eine vordefinierte Konstante aus Nullen und Einsen – und sonst bei jeder aktiven Taktflanke der Eingabewert zugewiesen. x I
x I
process(T, I) begin if I=’1’ then y <= aw; elsif rising_edge(T) then y <= x; end if; end process;
y T
signal x, y: tT yp; signal T, I: std_logic; constant aw: tT yp :=...; ...
Abb. 1.58. Register mit Initialisierungseingang (tT yp – beliebiger in Hardware darstellbarer Datentyp)
Die Initialisierung eines Registers ist laufzeitkritisch. Abbildung 1.59 a zeigt das am Beispiel einer Schaltung mit einem initialisierbaren Eingaberegister und einem einfachen Ausgaberegister. Wenn das Initialisierungssignal zu kurze Zeit aktiv ist, liegt, selbst wenn das Eingaberegister seinen Anfangswert korrekt übernimmt, der Initialwert am Registerausgang möglicherweise zu kurze Zeit stabil an, so dass das nachfolgende Register im ersten Schritt einen ungültigen Wert übernimmt (Abb. 1.59 b). Die in Abb. 1.59 c und 1.59 d gezeigten Fehlersituationen unterstellen, dass das Register aus Master-SlaveFlipflops besteht (siehe später Abschnitt 4.3.5). Ein Master-Slave-Flipflop besteht aus einer Master- und einer Slave-Speicherzelle. Der Master übernimmt
thr , tdr thf , tdf
a)
w0 wi
x I T
x I
x’
Wert unbestimmt Anfangswert Wert Verarbeitungsschritt i c)
f ()
I T
I T
ts y’
y
x’ b) ≪ TP I T
x
w1 w0
w1
x
1 0 1 0
x’
1 0 1 0
w2
d)
w1
w2
1 0 1 0
x
w2
w0
w2
x’
w1 w0 ≪ TP
w2 w1
w2
Abb. 1.59. Registerinitialisierung a) Schaltung mit initialisierbarem Eingaberegister b-d) mögliche Fehlfunktionen ohne Abtastung
70
1 Modellbildung und Simulation
den Eingabewert in der Taktphase vor der aktiven Taktflanke und gibt ihn in der Taktphase nach der aktiven Flanke an den Slave weiter. Wenn das Initialisierungssignal, das im Slave den Anfangswert einstellt, in der Übernahmephase des Slaves deaktiviert wird, übernimmt der Registerausgang in diesem Moment den Wert, der vor der letzten aktiven Flanke in den Master übernommen wurde. Dieser Wert liegt dann am Registerausgang kürzer als eine Taktperiode an (Abb. 1.59 c). Dadurch ist es möglich, dass das nachfolgende Register im zweiten Arbeitsschritt einen ungültigen Wert übernimmt. Bei einer etwa zeitgleichen Deaktivierung des Initialisierungssignals mit der Deaktivierung des Slave-Übernahmesignals entscheiden geringfügige Laufzeitunterschiede darüber, welche Slaves eines Registers den Initialisierungswert beibehalten und welche den Wert aus ihrem Master übernehmen. In diesem Fall ist der Ausgabewert des Registers bis zur nächsten aktiven Taktflanke unbestimmt und damit ungültig (Abb. 1.59 d). Zur Vermeidung von Initialisierungsfehlern ist das Initialisierungssignal abzutasten. Abbildung 1.60 zeigt eine typische Initialisierungsschaltung mit einer Reset-Taste und einer Power-On-Reset-Logik. Die Reset-Taste liefert ein asynchrones low-aktives Signal. Die Power-On-Reset-Logik ist hier eine Schaltung, die nach Zuschalten der Versorgungsspannung ein low-aktives Initialisierungssignal mit einem Kondensator noch für eine gewisse Zeit auf »0« hält. Auch dieses Signal ist asynchron. Beide Signale werden invertiert, ODER-verknüpft, abgetastet und auf die Initialisierungseingänge der internen Register geführt. Die abgetastete ODER-Verknüpfung ist ein Bitsignal, das nur »0« oder »1« sein kann. Sein Wert ändert sich nur mit der aktiven Taktflanke. Damit steht den Register-Transfer-Funktionen hinter den initialisierbaren Registern auch während der Initialisierung und im ersten Schritt nach der Initialisierung die komplette Taktperiode zur Verfügung. ResetTaste
Power-on-ResetSchaltung UV
Abtastung des Initialisierungssignals ≥1
I+
I
initialisierbare Register x I
x I
T
Abb. 1.60. Erzeugung eines abgetasteten Initialisierungssignals
1.5.4 Asynchrone parallele Schnittstelle Asynchrone parallele Eingabedaten haben immer einen zufälligen Zeitversatz zum Empfängertakt. Um sie verarbeiten zu können, muss ihr Gültigkeitsfenster mitübertragen oder aus den empfangenen Signalverläufen bestimmt
1.5 Asynchrone Eingabe
71
werden. Im einfachsten Fall wird der Takt der Signalquelle, an dem die Daten ausgerichtet sind, mitgesendet. In Abb. 1.61 liefert die Signalquelle einen n-Bit-Datenvektor x und einen Sendetakt Tx mit der halben Frequenz, mit der der Sender die Daten bereitstellt. Durch die Halbierung werden mit beiden Taktflanken Daten übertragen. Das Gültigkeitsfenster soll immer mit der Taktflanke beginnen und mindestens eine Länge tg haben. Der Empfänger muss alle empfangenen Signale, d.h. sowohl die Datensignale als auch den Übertragungstakt, abtasten, um sie zeitlich an seinem eigenen Takt auszurichten. Der Zeitversatz zwischen den Flanken des Abtasttaktes T und den Taktflanken von Tx ist eine Zufallsgröße, die zwischen null und der Periodendauer TP des Abtasttaktes liegt. Um sicherzustellen, dass jeder übertragene Datenwert mindestens einmal in seinem Gültigkeitsfenster abgetastet wird, darf die Periode des Abtasttaktes nicht kürzer als die Länge des Gültigkeitsfensters der empfangenen Daten sein: TP < tg
x’
x Tx T x Tx T x’ G
Tx’
=1 Tx”
G
asynchrones Eingabesignal Sendetakt Systemtakt abgetastetes Eingabesignal G¨ ultigkeitssignal f¨ ur x’
(1.13)
tg x w0 Tx T x’ Tx’ Tx” G
w1
w0 w1
TP
w2
w3
w2
w3
w4
w4
w5
w5
Abb. 1.61. Schnittstellenschaltung für die asynchrone parallele Eingabe
Nach der Abtastung wird anhand des Abtastwertes von Tx entschieden, ob der abgetastete Datenwert neu und gültig war. Der Abtastwert von Tx ist immer »0« oder »1«. Gültigkeitsbedingung für die abgetasteten Datenwerte ist, dass sich der aktuelle Abtastwert von Tx vom vorherigen, d.h. von dem doppelt abgetasteten Wert unterscheidet. Die Funktion zur Bildung des Gültigkeitssignal G ist eine EXOR-Verknüpfung. Abbildung 1.62 a zeigt eine VHDL-Beschreibung für die Schnittstellenschaltung aus Abb. 1.61. Die Eingabedaten und der Sendetakt werden ohne Kontrolle der Vorhalte- und der Nachhaltebedingungen abgetastet. Für den Sendetakt wird der Wertebereich der Abtastwerte wie in Abb. 1.57 b auf »0« und »1« begrenzt. Die EXOR-Verknüpfung zur Bildung des Gültigkeitssignals ist durch eine nebenläufige Signalzuweisung beschrieben. Nach einer Schnittstellenschaltung folgt üblicherweise eine kombinatorische Verarbeitungsschaltung mit Ausgabeabtastung, die zusammen mit der
72
1 Modellbildung und Simulation
signal T, Tx, Tx_del, Tx_del2 : std_logic; signal x, x_del: std_logic_vector(...); ... nachfolgende process(T) b) Schaltung begin Schnittstellenschaltung x’ if rising_edge(T) then x G f (...) x_del <= x; Tx’ =1 T x if Tx=’1’ then Tx_del <= ’1’; Tx” T else Tx_del <= ’0’; end if; Tx_del2 <= Tx_del; end if; end process; a) G <= ’X’ after th, Tx_del xor Tx_del2 after td ; Abb. 1.62. a) VHDL-Beschreibung der asynchronen parallelen Schnittstellenschaltung aus Abb. 1.61 b) typische Schaltungseinbindung
EXOR-Verknüpfung eine Register-Transfer-Funktion bildet (Abb. 1.62 b). In diesem Fall kann das Gültigkeitssignal G auch ohne die in Abb. 1.62 a grau unterlegten Beschreibungsbestandteile für das Zeitverhalten simuliert werden.
1.5.5 Zusammenfassung und Übungsaufgaben Die Eingabedaten einer digitalen Schaltung verhalten sich asynchron zum Schaltungstakt. Sie müssen vor ihrer Verarbeitung mit Registern abgetastet und bei mechanischen Eingabeelementen zusätzlich entprellt werden. Zum Entprellen muss die Abtastperiode größer als die maximale Prelldauer und kleiner als die minimale Betätigungsdauer sein. Auch asynchrone Initialisierungssignale sind, bevor sie auf die Initialisierungseingänge der Register geführt werden dürfen, abzutasten. Zur Übernahme asynchroner Daten von einer anderen Schaltung sind die empfangenen Daten gleichfalls zuerst abzutasten. Die Gültigkeit der empfangenen Daten wird rückwirkend aus den Abtastwerten bestimmt. Aufgabe 1.17 Für ein Gerät mit zwei mechanischen Tasten mit einer maximalen Prelldauer von tPr = 20 ms und einer Betätigungsdauer größer 0,2 s ist eine Eingabeschaltung zu entwickeln, die beim Drücken von genau einer Taste i ein Ausgabesignal yi erzeugt, das an der aktiven Taktflanke ausgerichtet und bei jeder Tastenbetätigung genau einen Takt lang »1« ist. Die Tasten-Eingabesignale seien low-aktiv (gedrückt »0«, nicht gedrückt »1«). a) In welchem Bereich muss die Taktfrequenz der Schaltung liegen?
1.5 Asynchrone Eingabe
73
b) Entwerfen Sie eine Schaltung mit Tasten, Pull-Up-Widerständen11 , Gattern und Registern. c) Beschreiben Sie den Abtastprozess und die Ausdrücke zur Bildung der Ausgabewerte in VHDL. Aufgabe 1.18 Im nachfolgenden Schaltungsausschnitt wird zum Inhalt eines Registers eine über mehrere Schalter einstellbare Konstante addiert. UV
x0 x1
+
xn−1
···
+
Addiererschaltung
UV
Versorgungsspannung (Signalwert Eins) Bezugspotenzial (Signalwert Null)
T
a) Unter welcher Bedingung kann es zu einem unerwarteten Fehlverhalten kommen? b) Wie ist die Schaltung zu verändern, damit sie zuverlässig arbeitet? Aufgabe 1.19 Bei einer asynchronen Übertragung seien die übertragenen Datenworte des Signals x immer dann gültig, wenn das mitübertragene Gültigkeitssignal g 6= 0 ist (Abb. 1.63). a) Mit welcher Taktfrequenz muss die Empfangsschaltung die beiden Signale abtasten? b) Wie ist das Gültigkeitssignal für die abgetasteten Datenworte zu bilden, damit es für jedes abgetastete gültige Datenwort genau einen Takt lang aktiv ist?
x g
wi 1 0
tg1
wi+1 tg0 TPg
tgx
tg0 tg1 tgx TPg
Mindestdauer g = 0 Mindestdauer g = 1 Maximaldauer g = X Periodendauer von g
Abb. 1.63. Signalverlauf zu Aufgabe 1.19
11
Ein Pull-Up-Widerstand verbindet das Signal mit der Versorgungspannung und zieht den Signalwert bei nicht betätigter Taste auf »1« (siehe später Abschnitt 4.1.6).
74
1 Modellbildung und Simulation
1.6 Sequenzielle Schaltungen Definition 1.18 (Sequenzielle Schaltung) Eine sequenzielle Schaltung (engl. sequential circuit, im deutschen Sprachraum auch Schaltwerk) ist eine digitale Schaltung mit Speicherverhalten, die in diskreten Zeitschritten aus aktuellen Zuständen und Eingaben Folgezustände und Ausgaben bildet. Definition 1.19 (Zustandsgraph) Ein Zustandsgraph ist eine Verhaltensbeschreibung für eine sequenzielle Schaltung, die den gespeicherten Zustand durch Knoten und die Zustandsübergänge durch Kanten darstellt. In der Grundform sind die Übergangsbedingungen Mengen von Eingabewerten und die gesteuerten Aktionen Ausgabewerte. Definition 1.20 (Operationsablaufgraph) Ein Operationsablaufgraph ist ein erweiterter Zustandsgraph, der zusätzlich Operationen steuert und in den Übergangsbedingungen der Kanten Operationsergebnisse auswertet. Definition 1.21 (Zustandscodierung) Die Zustandscodierung ist die Festlegung der Bitvektorkonstanten, mit denen die Zustände eines Zustandsgraphen oder eines Operationsablaufgraphen in der Schaltung dargestellt werden. Definition 1.22 (Software-orientierte Ablaufbeschreibung) Eine Software-orientierte Ablaufbeschreibung beschreibt Operationsabläufe mit den in höheren Programmiersprachen gebräuchlichen Kontrollflusselementen Sequenz, Fallunterscheidung und Schleife sowie mit Warteanweisungen zum Weiterschalten der Simulationszeit. Sequenziell bedeutet zeitlich nacheinander. Eine sequenzielle Schaltung berechnet ihre Ausgaben in mehreren Zeitschritten. Sie besteht aus kombinatorischen Schaltungen für die eigentliche Verarbeitung und aus Registern für die Abtastung und Aufbewahrung der Zwischenergebnisse für die nachfolgenden Berechnungsschritte. 1.6.1 Endlicher Automat Ein endlicher Automat ist ein allgemeines Funktionsmodell zur Beschreibung sequenzieller Abläufe. Er definiert einen sequenziellen Ablauf durch • • • •
endliche Wertebereiche für die Eingabe, den Zustand und die Ausgabe, einen Startzustand, eine Übergangsfunktion und eine Ausgabefunktion.
Die Wertebereiche sind Symbolmengen: • •
Eingabemenge Σ = {E1 , E2 , . . .} Zustandsmenge S = {Z1 , Z2 , . . .}
1.6 Sequenzielle Schaltungen
•
75
Ausgabemenge Π = {A1 , A2 , . . .}
Die Übergangsfunktion beschreibt die Abbildung der Eingabemenge und der Zustandsmenge auf die Zustandsmenge fs : Σ × S → S und die Ausgabefunktion die Abbildung der Eingabemenge und der Zustandsmenge auf die Ausgabemenge fa : Σ × S → Π. In einer digitalen Schaltung werden die Eingabe-, die Zustands- und die Ausgabemenge durch Bitvektoren dargestellt. Die Anzahl der mit einem Bitvektor darstellbaren Werte gehorcht Ungleichung 1.1: N ≤ 2n (N – Anzahl der darstellbaren Werte; n – Bitanzahl). Sie ist eine Zweierpotenz. Die Anzahl der darstellbaren Werte ist dabei in der Regel größer als die Anzahl der darzustellenden Werte. Die ungenutzten Bitvektorwerte dürfen im fehlerfreien Betrieb nicht auftreten. Anderenfalls ist das Verhalten nicht festgelegt. Die Übergangs- und die Ausgabefunktion werden durch kombinatorische Schaltungen nachgebildet. Der Zustand benötigt ein Register zur Speicherung. Der Startzustand ist der Initialisierungswert des Zustandsregisters. Das Zustandsregister richtet alle Zustandsübergänge an der aktiven Taktflanke aus. Zustandsgraph Die Funktion eines Automaten wird gern in der Form eines Zustandsgraphen spezifiziert und beschrieben. Ein Zustandsgraph stellt die Zustände als Knoten und die Zustandsübergänge als Kanten dar (Abb. 1.64). Jeder Zustand hat eine Bezeichnung – eine Nummer, eine Bitvektorkonstante oder einen
Z1 Y1
X1.1 Y1.1
X1.2 Y1.2 Z3 Y3
Z2 Y2 X2.2 Y2.2
Y3.1 Y3.1
X2.1 Y2.1
Xi Eingabewerte, bei denen der Zustands¨ ubergang erfolgt Zi Zustand Yi Ausgabewert Kennzeichen f¨ ur den Anfangszustand nach der Initialisierung
Abb. 1.64. Zustandsgraph
76
1 Modellbildung und Simulation
symbolischen Namen. Der Startzustand wird mit einem kleinen Dreieck gekennzeichnet. Die Kanten sind gerichtet und mit den Übergangsbedingungen beschriftet. In der Grundform ist die Übergangsbedingung die Menge der Eingabewerte, bei denen der Übergang erfolgen soll. Keine Angabe bedeutet, dass der Übergang bei jeder Eingabe erfolgen soll. In erweiterten Modellen kann die Übergangsbedingung ein beliebiger logischer Ausdruck sein. Die Ausgabewerte werden entweder den Knoten oder den Kanten zugeordnet. Die Ausgabe in einem Knoten gilt für alle Kanten, die den Knoten verlassen. Wenn einem Knoten eine Ausgabe zugeordnet ist, dann darf den abgehenden Kanten keine Ausgabe zugeordnet sein. Endliche Automaten werden unterteilt in Mealy-Automaten, Moore-Automaten und autonome Automaten. Ein Mealy-Automat ist ein allgemeiner Automat mit mehreren abgehenden Kanten je Zustand, denen zumindest zum Teil unterschiedliche Ausgabewerte zugeordnet sind. Die Ausgabe ist damit von der Eingabe abhängig. Abbildung 1.65 a zeigt als Beispiel den Zustandsgraphen für einen Vorwärts-Rückwärts-Zähler, der bei der Eingabe »V« zyklisch die Zustandsfolge »A-B-C-D« und bei der Eingabe »R« die umgekehrte Zustandsfolge »A-D-C-B« durchläuft. Bei dem dritten Wert aus der Eingabemenge »H« behält der Automat seinen aktuellen Zustand bei. Jedem Zustandsübergang ist ein Ausgabewert zugeordnet. a)
H/K A R/N
V/K
H/L
V/L R/K
B R/L
R/M
D
c)
s A B C D
V 00 H 01 R 10
H A B C D
R D A B C
V L M N K
H K L M N
Σ = {H, V, R} S = {A, B, C, D} Π = {K, L, M, N}
H/M
b) s+ = fs (x, s) y = fa (x, s) x :V B C D A
Zustand Eingabewert Ausgabewert
x/y V/M
C
V/N
H/N
s
R N K L M
C 10 D 11
K L A 00 M B 01 N
00 01 10 11
d)
Eingabemenge Zustandsmenge Ausgabemenge Anfangszustand
s+ = fs (x, s) y = fa (x, s)
s x: 00 01 10 11
00 01 10 11 00
01 00 01 10 11
10 11 00 01 10
00 01 10 11 00
01 00 01 10 11
10 11 00 01 10
Abb. 1.65. Vorwärts-Rückwärts-Zähler als Mealy-Automat a) Zustandsgraph b) tabellarische Übergangs- und Ausgabefunktion mit symbolischen Werten c) Codierung d) bitorientierte Übergangs- und Ausgabefunktion
Die Übergangsfunktion und die Ausgabefunktion lassen sich als Tabellen darstellen. Die Zeilen repräsentieren die Ist-Zustände, die Spalten die Eingabewerte und die Einträge die zugeordneten Folgezustände und Ausgabewerte (Abb. 1.65 b). Die Werte sind wie im Zustandsgraph Symbole, die im nächs-
1.6 Sequenzielle Schaltungen
77
ten Entwurfsschritt durch Bitvektorkonstanten ersetzt werden (Zustandscodierung). Die Unterscheidung von drei bzw. vier Werten verlangt nach Gleichung 1.2 mindestens 2 Bit. Abbildung 1.65 c zeigt eine Beispielcodierung und Abb. 1.65 d die Tabellen mit der Übergangs- und der Ausgabefunktion, in denen die Symbole durch die zugeordneten Bitvektoren ersetzt sind. In Abschnitt 2.2.4 wird der hier begonnene Entwurf bis zu einer Schaltung aus Logikgattern und Speicherzellen weitergeführt. Ein Moore-Automat hat eine einfachere Ausgabefunktion. Die Ausgabewerte sind den Knoten zugeordnet und damit nicht von der Eingabe abhängig. Der Moore-Automat in Abb. 1.66 hat dieselbe Übergangsfunktion und dieselbe Ausgabemenge wie der Mealy-Automat in Abb. 1.65. Nur sind die Ausgabewerte den Zuständen zugeordnet, so dass die Tabelle der Ausgabewerte mit einer Spalte auskommt. a)
H V
A K V
H
R D N
R R V
B L R
V C M
H
H
b) s+ = fs (x, s) y = fy (s) s x :V H R A B A D B C B A C D C B D A D C
K L M N
{H, V, R} Eingabemenge {A, B, C, D} Zustandsmenge {K, L, M, N} Ausgabemenge
s y
Zustand Ausgabewert
x
Eingabewert
x s s+ y fs (...) fa (...)
Eingabe Ist-Zustand Folgezustand Ausgabe ¨ Ubergangsfunktion Ausgabefunktion
Abb. 1.66. Moore-Automat a) Zustandsgraph b) tabellarische Übergangs- und Ausgabefunktion
Ein weiterer Sonderfall ist ein autonomer Automat. Das ist ein Automat ohne Eingabe. Bei einem autonomen Automaten hat jeder Zustand genau einen Nachfolger. Die Zustände werden entweder zyklisch oder einmalig durchlaufen (Abb. 1.67). Die Ausgabe ist den Zuständen zugeordnet. Wenn im Zustandsgraphen eines Moore-Automaten oder eines autonomen Automaten den Zuständen keine Ausgaben zugeordnet sind, gilt der Zustand gleichzeitig als Ausgabe.
a)
A
B
C
D
H
G
F
E
b)
A
B
C
D
E
F
G
H
Abb. 1.67. Zustandsgraphen autonomer Automaten a) zyklisch b) zyklenfrei
78
1 Modellbildung und Simulation
1.6.2 Vom Zustandsgraph zur VHDL-Beschreibung In einer digitalen Schaltung wird die Übergangsfunktion und die Ausgabefunktion je durch eine kombinatorische Schaltung und der Zustand durch ein Register nachgebildet (Abb. 1.68). Der Startzustand ist der Anfangswert des Zustandsregisters. Er wird über ein spezielles Initialisierungssignal eingestellt (vgl. Abschnitt 1.5.3). Damit die Funktion der Schaltung nicht von Laufzeiten abhängt, müssen das Eingabe- und das Initialisierungssignal zeitlich an der aktiven Taktflanke ausgerichtet sein. Die Register hierfür zählen nicht zur Schaltung des Automaten. Die kombinatorischen Schaltungen für die Übergangsund die Ausgabefunktion bilden ständig mit einer gewissen Zeitverzögerung aus dem Eingabesignal x und dem Ist-Zustand s den Folgezustand s+ und den Wert des Ausgabesignals y. Der Folgezustand wird mit der nächsten aktiven Taktflanke in das Zustandsregister als Ist-Zustand übernommen. Das Ausgabesignal y wird entweder direkt ausgegeben oder zur Verbreiterung des Gültigkeitsfensters mit einem Ausgaberegister abgetastet. Auch das Ausgaberegister wird nicht zum Funktionsmodell des Automaten gezählt. th , td
x+ I+ a)
x
fs (x, s) fy (x, s)
s+
x I I y
s
1 0
T
1 0
x X0 s+ S0
Zustandsregister optionale Abtastregister Abtastzeitpunkt Wert ung¨ ultig Signalwerte
X1
y’
T
Xi , Si , Yi
I
s
S1 S0
y Y0 b)
y’
S2 S1
Y1 Y0
X2 td th
Y2 Y1
Abb. 1.68. Nachbildung eines Automaten durch eine digitale Schaltung a) Schaltungsstruktur b) Zeitverhalten
Die VHDL-Beschreibungsmittel für dieses Modell sind Abtastprozesse und kombinatorische Prozesse. Die Übergangsfunktion ist praktisch eine RegisterTransfer-Funktion. Nach den Gleichungen 1.7 und 1.8 muss die Taktperiode größer als die Summe des Maximums der Verzögerung des Zustands- und des Eingaberegisters plus der Verzögerung der Schaltung für die Übergangsfunktion plus der Vorhaltezeit des Zustandsregisters sein. Die Summe der Haltezeiten muss größer als die Nachhaltezeit des Zustandsregisters sein (vgl. Abschnitt 1.4.4). Für die Ausgabefunktion zwischen dem Eingabe- und dem Zustandsregister am Eingang und dem Ausgaberegister am Ausgang gelten analoge Beziehungen. Das Eingabe-Ausgabe-Verhalten der beiden kombina-
1.6 Sequenzielle Schaltungen
79
torischen Schaltungen wird vorzugsweise mit VHDL-Auswahlanweisungen beschrieben. Die VHDL-Auswahlanweisung Eine Auswahlanweisung wertet einen Ausdruck aus. In Abhängigkeit von seinem Wert werden unterschiedliche Anweisungsfolgen abgearbeitet (Abb. 1.69). Auswahlwerte, für die dieselben Anweisungen abzuarbeiten sind, können mit dem Symbol »|« zusammengefasst werden. Der Auswahlwert »others« steht für alle anderen Auswahlwerte.
case Ausdruck is when Wert { |W ert } => Anweisung {Anweisung} {when Wert { |W ert } => Anweisung {Anweisung}} [[when others => Anweisung {Anweisung}]] end case ;
s w0 A0 w1 A1 .. . An
s w0 A0
w1 A1
sonst ... ...
An
s Auswahlausdruck wi Auswahlwert(e) i Ai Anweisungsfolge i
Abb. 1.69. Auswahlanweisung
Zur Nachbildung eines Automaten ist der Ausdruck für die Auswahl entweder das Zustandssignal oder das Eingabesignal oder eine Verknüpfung beider Signale. Die auszuführenden Anweisungen für die einzelnen Auswahlfälle sind entweder eine weitere Fallunterscheidung oder die Zuweisung eines konstanten Wertes an das Zustands- oder das Ausgabesignal. Eine Beispielbeschreibung für einen Mealy-Automaten Abbildung 1.70 zeigt den Zustandsgraph und die Zielstruktur des Beispielautomaten. Die Eingabemenge umfasst nur die Werte »0« und »1« und die Zustandsmenge die Bitvektorwerte »00«, »01« und »10«. Das Zustandsregister besitzt einen Initialisierungseingang zur Einstellung des Startzustands, der, wie aus dem Zustandsgraphen ersichtlich ist, »00« sein soll. Für das Eingabe- und das Initialisierungssignal ist unterstellt, dass sie vor dem Automaten mit dem Takt des Automaten abgetastet werden. Die Übergangsfunktion ist entsprechend eine Register-Transfer-Funktion, die bis zu ihrer maximalen Taktfrequenz nach Gleichung 1.9 laufzeitrobust ist. Eine Kontrolle des Zeitverhaltens ist im Simulationsmodell nicht erforderlich. Die Standardbeschreibungsschablone ist ein Abtastprozess mit dem Takt und dem Initialisierungssignal in der Weckliste (Abb. 1.71). Wenn der Prozess aufwacht und das Initialisierungssignal aktiv ist, wird dem Zustandssignal der Anfangswert »00« zugewiesen, sonst bei einer aktiven Taktflanke der Folgezustand.
80
1 Modellbildung und Simulation Ablaufgraph 0/1 00 1/0 0/0
1/1 10 a)
Zielstruktur 0/0
x
s+ I
01 1/1
fs
b)
T
y
fy
s
x I T
signal x, y, T, I: std_logic; signal s: std_logic_vector(1 downto 0); Abb. 1.70. Mealy-Automat a) Zustandsgraph b) Zielstruktur
Der Folgezustand ergibt sich dabei jeweils aus dem aktuellen Zustand und der Eingabe. In der Prozessbeschreibung in Abb. 1.71 ist der Auswahlausdruck eine Konkatenation (Verkettung) des Zustands- und des Eingabesignals zu einem gemeinsamen Bitvektor. So genügt eine Fallunterscheidung. Alle Auswahlwerte mit demselben Folgezustand sind zu einem Auswahlfall zusammengefasst. Alle anderen im Zustandsgraph nicht berücksichtigten Fälle – ungültiger Ist-Zustand, ungültige Eingabe etc. – erfasst der Sonst-Zweig, der dem Folgezustand den Pseudo-Wert »ungültig« zuweist. Ein ungültiger Zustand darf nicht weiterverarbeitet werden bzw. signalisiert eine Fehlfunktion. process(I, T) variable v: std_logic_vector(2 downto 0); begin if I=’1’ then s <= "00"; elsif rising_edge(T) then v := s & x; case v is when "00"&’0’ | "10"&’0’ => s <= "00"; when "01"&’0’ | "00"&’1’ => s <= "01"; when "10"&’1’ | "01"&’1’ => s <= "10"; when others => s <= "XX"; end case; end if; end process;
0/1 00
0/0 1/1
x
1/0
10
01 1/1
fs
s+ I
0/0
s
x I T
⇒Web-Projekt: P1.6/BspMealy.vhdl
Abb. 1.71. Abtastprozess zur Beschreibung der Übergangsfunktion und des Zustandsregisters für den Mealy-Automaten in Abb. 1.70
Die Ausgabefunktion ist als kombinatorischer Prozess beschrieben (Abb. 1.72). Ihre Eingabesignale – das sind der Ist-Zustand s und das Eingabesignal des Automaten x – stehen in der Weckliste. Der Ausgabewert wird auch hier mit einer Auswahlanweisung mit einer Konkatenation des Eingabewertes und des Zustands als Auswahlausdruck gebildet. Alle Kanten mit dem
1.6 Sequenzielle Schaltungen
81
Ausgabewert »0« und alle Kanten mit dem Ausgabewert »1« sind zu einem Auswahlfall zusammengefasst. Der Sonst-Fall erfasst alle unzulässigen Eingabewerte und Zustände und weist der Ausgabe den Pseudo-Wert »ungültig« zu. process(x, s)
0/1
variable v: std_logic_vector(2 downto 0); 00 begin 1/0 v := s & x; 0/0 case v is 0/0 01 when "00"&’1’ | "01"&’0’ | "10"&’0’ => y <= ’0’; 1/1 when "00"&’0’ | "01"&’1’ | "10"&’1’ => y <= ’1’; when others => y <= ’X’; 10 1/1 end case; end process; ⇒Web-Projekt: P1.6/BspMealy.vhdl Abb. 1.72. Beschreibung der Ausgabefunktion zu Abb. 1.70
Moore-Automat Bei einem Moore-Automaten sind die Ausgabewerte den Zuständen zugeordnet. Der Beispielautomat in Abb. 1.73 hat dieselbe Zyklusstruktur wie der Mealy-Automat in Abb. 1.70. Die Übergangsfunktion ist somit identisch. Die Ausgabefunktion wertet nur den Zustand des Automaten aus. In der Weckliste des beschreibenden Prozesses steht entsprechend nur das Zustandssignal. Für den Zustand »01« wird eine »0«, für die Zustände »00« und »10« eine »1« und sonst, wenn der Zustand keinen zulässigen Wert hat, wird der Pseudo-Wert »ungültig« an das Ausgabesignal zugewiesen. signal x, y, T, I: std_logic; signal s: std_logic_vector(1 downto 0); 00 ... 0 1 1 10 –- kombinatorischer Ausgabeprozess 1 process(s) 1 a) begin case s is when "01" => y <= ’0’; s+ when "00"|"10" => y <= ’1’; x fs x when others => s <= ’X’; I I end case; T b) end process; c) T
0 1
s
01 0
fy
0
y
Abb. 1.73. Moore-Automat mit der Übergangsfunktion des Mealy-Automaten in Abb. 1.70 a) Zustandsgraph b) VHDL-Beschreibung der Ausgabefunktion c) Zielstruktur
82
1 Modellbildung und Simulation
Autonomer Automat Das Beispiel für einen autonomen Automaten sei der 4-Bit-Johnson-Zähler in Abb. 1.74. Ein Johnson-Zähler ist ein rückgekoppeltes Schieberegister, bei dem der Ausgang der letzten Speicherzelle invertiert auf den Eingang der ersten Speicherzelle zurückgeführt ist. Er wird mit »alles Null« initialisiert, läuft zyklisch mit Einsen voll, bis die erste Eins hinten ankommt. Dann läuft er mit Nullen voll, bis die erste Null hinten ankommt. Die Zykluslänge ist doppelt so groß wie die Registerlänge.
0000 a)
1000
0001 1100
0011 1110
0111 1111
signal T, I: std_logic; signal s: std_logic_vector(3 downto 0); ... process(I, T) begin c) if I=’1’ then
I+ b) T
x I
s0
x I
s1
x I
s2
x I
s3
s <= "0000"; elsif rising_edge(T) then s <= s(2 downto 0) & (not s(3)); end if; end process; ⇒Web-Projekt: P1.6/Johnson4.vhdl
Abb. 1.74. 4-Bit-Johnson-Zähler a) Zustandsgraph b) Schaltungsstruktur c) VHDL-Funktionsbeschreibung
Das Zustandssignal ist im allgemeinen Fall ein Bitvektor der Länge n. Die gesamte Funktion lässt sich in einem einzelnen Abtastprozess beschreiben, bei dem der Takt und das Initialisierungssignal in der Weckliste stehen. Bei aktivem Initialisierungssignal wird das Zustandssignal zurückgesetzt. Sonst wird bei jeder aktiven Taktflanke den Zustandsbits i = 1 bis n − 1 der IstWert des Vorgängerbits und dem niederwertigsten Bit der negierte Ist-Wert des höchstwertigen Bits zugewiesen. 1.6.3 Unzulässige Zustände, Systemabsturz und Watchdog Ein Automat mit n Speicherzellen hat 2n Zustände. Davon wird meist nur ein geringer Teil für das Soll-Verhalten genutzt. Die übrigen Zustände sind unzulässig und sollten im normalen Betrieb nie erreicht werden. Größere digitale Systeme sind statisch gesehen nicht fehlerfrei, unterliegen Störungen und werden nicht immer so bedient, wie es die Zielfunktion vorsieht. Im Fehlerfall können Automaten auch in unzulässige Zustände übergehen. Ein Teil der unzulässigen Zustände kann der Automat nicht selbstständig wieder verlassen.
1.6 Sequenzielle Schaltungen
83
Er liefert dann bis zur Neuinitialisierung keine oder keine sinnvollen Ausgaben mehr. Ein solcher Systemzustand wird als Systemabsturz bezeichnet. Abbildung 1.75 a zeigt die komplette Zyklusstruktur des Johnson-Zählers aus Abb. 1.74. Außer den acht Zuständen, die er im fehlerfreien Fall zyklisch durchläuft, besitzt er acht unzulässige Zustände, die auch einen Zyklus bilden. Wenn der Johnson-Zähler aufgrund einer Fehlfunktion in einen der unzulässigen Zustände übergeht, gibt es zumindest im bisherigen Funktionsmodell keinen Weg zurück. Er bleibt bis zur Neuinitialisierung im Zyklus der unzulässigen Zustände.
a)
0000
1000
1100
0001
s3 s2 s1 s0
1110
0011
0111
1111
b) & I+
Ir+
≥1
T
Schaltung zur automatischen Neuinitialisierung
x I
Fehlfunktion
0100
kein R¨ uckweg
s0
x I
s1
x I
s2
1010
1001
0110
0010 s3 x I
1101
0101 I
+
Ir+
1011
externes Initialisierungssignal Signal zur automatischen Neuinitialisierung Zust¨ ande, die eine Neuinitialisierung ausl¨ osen
Abb. 1.75. a) Zyklusstruktur des 4-Bit-Johnson-Zählers mit unzulässigen Zuständen b) Schaltungserweiterung mit automatischer Neuinitialisierung
Ein Automat lässt sich theoretisch auch so konstruieren, dass auf einen unzulässigen Zustand im Zustandsgraph nach einer begrenzten Anzahl von Zustandsübergängen immer ein zulässiger Zustand folgt. In Abb. 1.75 b ist der Johnson-Zähler um eine Schaltung zur automatischen Neuinitialisierung erweitert, die, wenn er in einen der unzulässigen Zustände »0101« oder »1101« übergeht, das Initialisierungssignal aktiviert. Mit der nächsten aktiven Taktflanke startet eine Neuinitialisierung, nach der der Zähler wieder korrekt arbeitet. Große digitale Systeme, z.B. Rechner, haben Tausende von Zustandsbits und eine unüberschaubar große Anzahl unzulässiger Zustände. Bei solchen Systemen erfolgt die Detektierung eines Systemabsturzes durch Zeitüberwachung und der automatische Neustart in der Regel durch einen Watchdog. Ein Watchdog ist ein Zähler für Zeitimpulse, der vom zu überwachenden System in regelmäßigen Zeitabständen zurückgesetzt wird. Wenn das System abstürzt, bleiben die Rücksetzimpulse aus. Der Zähler läuft über und aktiviert das Initialisierungssignal des überwachten Systems (Abb. 1.76).
84
1 Modellbildung und Simulation u ¨berwachtes System
Watchdog RW RA Signal zur Neuinitialisierung des Systems RW Signal zum R¨ ucksetzen des Watchdogs
RA
Abb. 1.76. Zeitüberwachung mit Watchdog
1.6.4 Entwurf eines Zahlenschlosses In diesem Abschnitt wird eine Steuerung für ein Zahlenschloss mit mehreren Zifferntasten, einer Rücksetztaste und einem Ausgabesignal, das zum Testen eine Leuchtdiode ansteuert, entworfen. Wenn nach der Betätigung der Rücksetztaste die richtige Ziffernfolge eingetippt wird, soll die Leuchtdiode leuchten und sonst ausgeschaltet bleiben. Alle Anschlusssignale sind low-aktiv. Bei einer Tastenbetätigung ist das zugehörige Eingabesignal »0« und sonst »1«. Die Leuchtdiode leuchtet bei einer »0« am Ausgang. Mechanische Tasten liefern asynchrone Eingabesignale und prellen. Jedes Tastensignal muss mit einer Periodendauer, die länger als die maximale Prellzeit und kürzer als die minimale Betätigungsdauer ist, z.B. mit TP = 10 ms abgetastet werden (siehe Abschnitt 1.5.2). Das abgetastete Rücksetzsignal ist das Initialisierungssignal des Zustandsregisters. Für die Zifferntasten wird ein zweites Abtastsignal benötigt, um Eingabeänderungen zu erkennen. Es genügt jedoch, wenn aus der zweiten Abtastung zu erkennen ist, dass im Zeitschritt zuvor keine Taste gedrückt war, d.h. wenn nur die UND-Verknüpfung xU der Abtastwerte aller abgetasteten Signale von Zifferntasten noch einmal abgetastet wird. Der eigentliche Automat besteht aus je einer kombinatorischen Schaltung für die Übergangs- und für die Ausgabefunktion und dem Zustandsregister. Die Übergangsfunktion wertet den Ist-Zustand, die abgetasteten Ziffernsignale, deren UND-Verknüpfung xU und die abgetastete UNDVerknüpfung xU’ aus. Das Ausgabesignal soll aus den Zuständen gebildet werden (Moore-Automat). Abbildung 1.77 zeigt die beschriebene Schaltung, in der die Übergangsfunktion und die Ausgabefunktion des eingebetteten Automaten noch zu entwerfen sind. UV x+ + x+ 0 x1
x+ m−1 ···
I+
x I
&
xU
xU ’
fs
s+
s
x I
fy
T
Abb. 1.77. Gesamtschaltung der Steuerung für das Zahlenschloss
y
1.6 Sequenzielle Schaltungen
85
Im nächsten Schritt wird der Zustandsgraph entwickelt (Abb. 1.78 a). In seinem Kern ist ein Zahlenschloss ein Akzeptorautomat. Die Eingabe einer Ziffernfolge beginnt mit der Initialisierung, die den Automaten in den Startzustand Z0 versetzt. Von dort aus führt eine Zustandsfolge Zi zum Endzustand Zn (Eingabe akzeptiert). Der Index i ist dabei jeweils die Anzahl der bisher richtig eingegebenen Ziffern. Im Endzustand Zn soll die Leuchtdiode leuchten (y = 0) und in allen andern Zuständen soll sie ausgeschaltet sein (y = 1). signal T, I, I_next, xu, xu_del, y: std_logic; signal s: std_logic_vector(2 downto 0); signal x, x_next: std_logic_vector(3 downto 0); ... Z0 sonst∗ –- c) Beschreibung der Abtastregister LED aus process(T) 1110∗ begin Z1 sonst∗ if rising_edge(T) then LED aus x <= x_next; 0111∗ I <= I_next; Z2 sonst∗ xu_del <= xu; LED aus end if; 1101∗ end process; –- d) nebenläufige Bildung von xu xu <= x(0) and x(1) and x(2) and x(3);
Z3 LED ein
a)
∗
F LED aus
∧(xU = 0) ∧ (xU’= 1)
–- e) Abtastprozess der Uebergangsfunktion process(I, T) Zustand Codierung variable v: std_logic_vector(6 downto 0); 000 Z0 begin 001 Z1 if I = ’0’ then 010 Z2 100 Z3 s <= "000"; b) F 111 elsif rising_edge(T) and xu = ’0’ and xu_del = ’1’ and s(2) = ’0’ then v := s & x; case v is when "000" & "1110" => s <= "001"; –- 1. Geheimzahl: Taste 0 when "001" & "0111" => s <= "010"; –- 2. Geheimzahl: Taste 3 when "010" & "1101" => s <= "100"; –- 3. Geheimzahl: Taste 1 when others => s <= "111"; end case; end if; end process; –- f) nebenläufige Anweisung zur Bildung von y y <= not s(2) or s(1) or s(0); ⇒Web-Projekt:
P1.6/Zahlenschloss.vhdl
Abb. 1.78. Fortsetzung des Entwurfs der Zahlenschlosssteuerung a) Zustandsgraph b) Zustandscodierung c) Abtastregister d) UND-Gatter e) Übergangsfunktion und Zustandsregister f) Ausgabefunktion
86
1 Modellbildung und Simulation
Ein Zustandsübergang soll nur erfolgen, wenn im vorherigen Abtastschritt keine Taste betätigt war (xU’ = 1), im aktuellen Abtastschritt eine Taste betätigt ist (xU = 0) und keiner der Endzustände erreicht ist. Wenn die richtige Taste betätigt wird, ist der Abtastwert dieser Taste »0« und der der anderen Tasten »1«. Für ein Zahlenschloss mit m = 4 Zifferntasten sei die korrekte Ziffernfolge null-drei-eins. Bei Betätigung der nullten Taste ist der Eingabevektor x = 1110, bei Betätigung der dritten Taste x = 0111 und bei Betätigung der ersten Taste x = 1101. Die Betätigung einer falschen Taste oder von mehreren Tasten soll den Automaten in einen Fehlerzustand F versetzen, der gleichfalls ein Endzustand ist. Bei einem Automatenentwurf mit den bisher eingeführten VHDL-Beschreibungsmitteln folgt nach der Aufstellung des Zustandsgraphen die Zustandscodierung. Für jeden symbolischen Zustand ist eine Bitvektorkonstante festzulegen, mit der er in der Schaltung dargestellt wird. Die Zustandscodierung ist in Abb. 1.78 b so gewählt, dass die beiden Endzustände Z3 und F an s2 = 1 erkannt werden. Der erste Abtastprozess (Abb. 1.78 c) beschreibt die Abtastregister für das Eingabesignal, das Initialisierungssignal und das Signal xU , das in einer nebenläufigen Anweisung aus dem abgetasteten Zifferntastenvektor gebildet wird. Der zweite Abtastprozess (Abb. 1.78 e) beschreibt die Übergangsfunktion und das Zustandsregister des Automaten. Bei einem aktiven Abtastwert des Initialisierungssignals wird dem Zustandssignal der Wert für den Zustand Z0 zugewiesen, sonst bei einer aktiven Taktflanke, wenn ein Tastendruck erkannt wird und sich der Automat in keinem Endzustand befindet, erfolgt eine Auswahl nach dem Ist-Zustand und dem Abtastwert der Eingabe. Im Zustand Z0 erfolgt bei Eingabe des Wertes »1110« für die erste Geheimzahl der Übergang nach Z1 , von dort, wenn die zweite Eingabe richtig ist, nach Z2 etc. In allen anderen Fällen wechselt der Automat in den Fehlerzustand F mit dem Zustandswert s = 111. Das Ausgabesignal y wird in einer nebenläufigen Anweisung gebildet (Abb. 1.78 f). Die Zurückführung einer Aufgabe auf einen Automatenentwurf teilt den Entwurf in einen kreativen Teil zur Entwicklung der Schaltung mit dem Automaten als Black-Box und einen eher rezeptartigen Teil, in dem die Automatenbeschreibung selbst durch eine Schaltung nachgebildet wird. Der vorgelagerte kreative Teil bestimmt dabei die Struktur und die Güte der Lösung. Ohne den Trick mit dem Signal xU für »keine Taste gedrückt« hätte man zwischen den Zuständen für »Warte auf richtige Tasteneingabe« zusätzlich Zustände für »keine Taste gedrückt« einfügen müssen, was die Zustandsanzahl fast verdoppelt hätte. Mit dem Trick, Zustandsübergänge an weitere Bedingungen als nur die steigende Taktflanke zu binden, entfallen zahlreiche Kanten. 1.6.5 Operationsablaufgraph Die bisherige Beschreibung einer sequenziellen Schaltung durch einen Zustandsgraphen hat einen entscheidenden Schwachpunkt. Die aus den Werten aller Eingabesignale gebildeten Eingabemengen und die aus den Zuständen
1.6 Sequenzielle Schaltungen
87
aller Speicherelemente gebildeten Zustandsmengen wachsen beide exponentiell mit der Bitanzahl. Ab etwa zehn Zuständen und 20 Kanten, d.h. ab etwa zusammen vier Eingabe- und Zustandsbits, wird ein Zustandsgraph unübersichtlich. Wie lassen sich die zu verarbeitenden und die gespeicherten Informationen besser strukturieren? Das Vorbild ist ein Programm auf einem Rechner. Ein Programm hat einen Kontrollfluss, der Operationen steuert und zum Teil von Operationsergebnissen gesteuert wird. Die möglichen Operationen für Daten sind eingeschränkt (Zählen, Addieren, bitweise Logikoperationen etc.). Aus der Programmierung ist bekannt, dass sich so jeder Algorithmus beschreiben lässt, ohne dass die Beschreibungsgröße exponentiell mit der Anzahl der zu verarbeitenden und zu speichernden Bits zunimmt. Ein Operationsablaufgraph ist ein erweiterter Zustandsgraph, der zusätzlich Operationen steuert und in den Übergangsbedingungen der Kanten Operationsergebnisse auswertet. Für die Spezifikation einer sequenziellen Schaltung mit einem Operationsablaufgraphen sind zuerst die Operationen und die Operanden, die für die Nachbildung der Zielfunktion benötigt werden, als Register-Transfer-Funktionen zusammenzustellen. Im zweiten Schritt wird ein Ablaufgraph konstruiert, der die Abfolge der Operationen unter teilweiser Auswertung der Berechnungsergebnisse beschreibt. Die folgenden Beispiele zeigen, dass bereits eine einfache Zählfunktion und die Auswertung des Zählstandes die Beschreibung von Abläufen und deren Nachbildung durch sequenzielle Schaltungen drastisch vereinfachen kann. Größere Beispiele folgen später. Beispiel Dreiecksignalgenerator Der zu entwerfende Dreiecksignalgenerator soll seinen Ausgabewert immer alternierend aufwärts und abwärts zählen. Abbildung 1.79 a zeigtdas zu generierende 4-Bit-Dreiecksignal. Die Zykluslänge beträgt 2 · 24 − 1 = 30. Ein Automat nach dem bisherigen Schema hätte 30 Zustände, die zyklisch durchlaufen werden. In einem Programm würde man die gegebene Zielfunktion mit einem Zähler beschreiben, der in einer Schleife immer bis zu seinem Maximalwert aufwärts und anschließend bis zu seinem Minimalwert abwärts zählt. Bevor der Operationsablauf festgelegt werden kann, sind die zu manipulierenden Datenobjekte und die auf sie anzuwendenden Operationen zu spezifizieren. Der Dreiecksignalgenerator benötigt ein Zählregister und die drei auf das Zählregister anwendbaren Operationen: »clr« (Zählstand auf null setzten), »inc« (Inkrement, Aufwärtszählen) und »dec« (Dekrement, Abwärtszählen, Abb. 1.79 b). Der Operationsablaufgraph kommt mit zwei Zuständen aus. Bei der Initialisierung des Automaten ist auch der Zähler zu initialisieren. Im Zustand »0«
88
1 Modellbildung und Simulation
library Tuc; use Tuc.Numeric_Sim.all; ... signal T, I, s: std_logic; signal y: tUnsigned(3 downto 0); ... process(I, T) begin if I=’1’ then s <= ’0’; y <= "0000"; elsif rising_edge(T) then case s is when ’0’ => y <= y +"1"; if y="1110" then s <= ’1’; end if; when ’1’ => y <= y -"1"; if y="0001" then s <= ’0’; end if; when others => y <= "XXXX"; end case; end if; d) end process;
1111 1110 1101
y
0010 0001 0000
0
15
30 Schritt
a)
b)
inc
+1
dec
−1
clr 0000
I=1 clr
0 inc
sonst
y y y
ct=1110
1 dec
ct=0001 sonst
c) ⇒Web-Projekt: P1.6/DreieckGen.vhdl
Abb. 1.79. 4-Bit-Dreiecksignalgenerator a) zu generierendes Dreiecksignal b) Operationen des Zählregisters c) Operationsablaufgraph d) VHDL-Beschreibung
zählt der Zähler bis zum Maximalwert. Dann wechselt der Automat in den Zustand »1« und lässt den Zähler bis zum Minimalwert abwärts zählen. Dann wechselt er zurück zum Anfangszustand »0« und beginnt den Zyklus von vorn. In der VHDL-Beschreibung sind sowohl die Zähloperationen als auch die Übergangsfunktion des Kontrollflussautomaten Register-Transfer-Funktionen, die in einem gemeinsamen Abtastprozess beschrieben werden können. Bei aktivem Initialisierungssignal werden der Zählerstand und der Automatenzustand auf null gesetzt. Im anderen Fall, bei einer aktiven Taktflanke, erfolgt eine Fallunterscheidung nach dem Zustand. Im Zustand s = 0 wird der Zählstand um »1« erhöht und abgefragt, ob der vorletzte Zählstand erreicht ist. Wenn ja, wird dem Automatenzustand der Wert für »Abwärtszählen« zugewiesen. Man beachte, dass y und s Signale in einem Abtastprozess sind, die ihre zugewiesenen Werte erst mit der nächsten aktiven Taktflanke übernehmen. Deshalb muss der Folgezustand immer einen Zählschritt vor dem Endzustand zugewiesen werden. Im Zustand s = 1 wird bis »0000« abwärtsgezählt und einen Schritt zuvor wieder der Automatenzustand für »Aufwärtszählen« zugewiesen. Das Simulationsmodell enthält eine Addition und eine Subtraktion. Dafür wird ein Bitvektortyp benötigt, für den diese beiden arithmetischen Operationen definiert sind. Im Weiteren werden, wie auch hier im Beispiel, vorzeichenfreie Zahlen mit dem Typ »tUnsigned« dargestellt. Für vorzeichenbehaftete
1.6 Sequenzielle Schaltungen
89
Zahlen wird der Typ »tSigned« benutzt. Beide Typen sind in dem eigenen Package »Tuc.Numeric_Sim«, das mit auf der Web-Seite steht, definiert und sind mit dem Typ std_logic_vector eng verwandt (siehe später Abschnitt 3.2.7). 1.6.6 Beispiel Wegemessung mit Quadratur-Encoder Ein Quadratur-Encoder ist ein Signalgeber für eine inkrementelle Wegemessung. Er besteht aus einer rotierenden Scheibe mit Löchern und einer Doppellichtschranke (Abb. 1.80 a). Die Löcher in der Scheibe und die beiden Lichtschranken sind dabei so angeordnet, dass bei einer Rotation der Scheibe an den Lichtschrankenausgängen Impulse mit einem Tastverhältnis 1:1 erzeugt werden, die um eine viertel Periode zueinander versetzt sind. Bei einer Vorwärtsbewegung wechselt das Ausgabesignal zuerst am Ausgang a und bei einer Rückwärtsbewegung zuerst am Ausgang b (Abb. 1.80 b). a
a’
a”
b
b’
b”
T
a’ 10 b’ 10
vorw¨ arts r¨ uckw¨ arts a)
b)
ct
vorw¨ arts
012345
r¨ uckw¨ arts
43210
c)
a” b” 0 0 0 0 0 0 0 1 0 1 0 1 1 0 1 0 1 0 1 1 1 1 1 1
a’ 0 0 1 0 0 1 1 1 0 1 1 0
b’ 0 1 0 1 0 1 0 1 0 1 0 1
ct −1 +1 +1 −1 +1 −1 −1 +1
Abb. 1.80. Wegemessung mit einem Quadratur-Encoder a) Messanordnung mit Abtast- und Zustandsregister b) abgetastete Encoder-Ausgaben und Zählverhalten c) tabellarische Ausgabefunktion
Ein digitales Wegemesssystem mit einem Quadratur-Encoder besteht aus dem Abtastregister für die beiden asynchronen Eingabesignale, einem Vorwärts-Rückwärts-Zähler und einem Steuerautomaten. Der Steuerautomat hat vier Zustände. Das sind in diesem speziellen Fall die vier Variationen der Abtastwerte a”b” des Zeitschritts zuvor. Der Folgezustand ist praktisch gleich der abgetasteten Eingabe. Abbildung 1.80 c zeigt die den Zustandsübergängen zugeordneten Operationen. Bei einer Vorwärtsdrehung schaltet zuerst a ein (a”b”a’b’ = 0010), dann schaltet b ein (a”b”a’b’ = 1011), dann a aus (a”b”a’b’ = 1101) und zum Schluss b aus (a”b”a’b’ = 0100). Bei jedem dieser Übergänge ist der Zählstand zu erhöhen. Wenn zuerst b einschaltet (a”b”a’b’ = 0001), dann a einschaltet (a”b”a’b’ = 0111), dann b ausschaltet (a”b”a’b’ = 1110) und zum Schluss a ausschaltet (a”b”a’b’ = 1000), hat sich das Encoder-Rad je einen Schritt zurückgedreht. Der Zählstand ist um »1« zu verringern.
90
1 Modellbildung und Simulation
In der nachfolgenden VHDL-Beschreibung sind die Encoder-Signale zu einem 2-Bit-Vektor zusammengefasst. Die einfach und die zweifach abgetasteten Eingabewerte bilden zusammen einem 4-Bit-Vektor wie in der Tabelle in Abb. 1.80 c: signal T, I, I_del: std_logic; signal ab: std_logic_vector(1 downto 0); signal ab_del: std_logic_vector(3 downto 0); signal ct: tSigned(15 downto 0); ... –- Abtastprozess process(T) begin if rising_edge(T) then I_del <= I; ab_del(1 downto 0) <= ab; –- a’b’<=ab ab_del(3 downto 2) <= ab_del(1 downto 0); –- a”b”<=a’b’ end if; end process; –- Uebergangsfunktion process(I_del, T) begin if I_del=’1’ then ct <= "0000000000000000"; elsif rising_edge(T) then case ab_del is when "0010" | "0100" | "1011" | "1101" => ct <= ct+"01";12 when "0001" | "0111" | "1000" | "1110" => ct <= ct-"01"; when "0000" | "0101" | "1010" | "1111" => null; when others => ct <= "XXXXXXXXXXXXXXXX"; –- 16 × ’X’ end case; end if; end process; ⇒WEB-Projekt: P1.6/QuadEnc.vhdl
Die Gesamtbeschreibung ist in zwei Abtastprozesse aufgeteilt, einen mit und einen ohne Initialisierungssignal in der Weckliste. Außer dem Abtastregister benötigt auch das Zustandsregister keine Initialisierung, weil nach zwei Takten mit gültigen Eingaben automatisch ein gültiger Zustand übernommen wird. Das Initialisierungssignal für den Zähler darf dann aber erst nach den beiden ersten aktiven Taktflanken, wenn das Zustandsregister einen gültigen Wert hat, deaktiviert werden. Der Zähler im zweiten Prozess wird durch ein 16-Bit-Signal vom Typ »tSigned« nachgebildet. Das ist der für arithmetische Operationen für vorzeichenbehaftete Zahlen definierte Typ im Package »Tuc.Numeric_Sim« (siehe Anhang A.2.4). Der Zählprozess setzt den Zähler bei aktivem Initialisierungssignal zurück und wählt sonst bei einer steigenden Taktflanke die Zähloperation entsprechend der Tabelle in Abb. 1.80 c aus. Für die vier Bitvektorwerte, die bei der Vorwärts- bzw. Rückwärtsbewegung auf12
Die führende »0« repräsentiert das Vorzeichen (siehe später Abschnitt 2.4.2).
1.6 Sequenzielle Schaltungen
91
treten, wird der Zähler um den Wert eins erhöht bzw. verringert. Den vier Bitvektorwerten, bei denen der erste Abtastwert mit dem zweiten Abtastwert übereinstimmt, ist die Null-Anweisung (keine Operation) zugeordnet13 . Alle anderen Fälle – gleichzeitige Änderung beider Encoder-Werte, ungültige Eingaben, etc. – dürfen im korrekten Betrieb und bei der Simulation nicht auftreten. Um potenzielle fehlerhafte Zustände und Eingaben aufzudecken, wird bei ihrem Auftreten der Zählwert invalidiert. 1.6.7 Software-orientierte Ablaufbeschreibung Für komplexere Schaltungen ist die Aufstellung der Zielfunktion bereits ein arbeitsaufwändiger Prozess, der in Teilschritte untergliedert werden muss. Ein bewährter Zwischenschritt ist die Beschreibung der geplanten Operationsabläufe als normales Programm mit Fallunterscheidungen und Schleifen. Ein solches Programm ist übersichtlicher und änderungsfreundlicher als eine Funktionsbeschreibung mit einem Zustands- oder einem Operationsablaufgraphen. Die Aufteilung in Zeitschritte erfolgt mit Warteanweisungen. Abbildung 1.81 a zeigt den Signalverlauf des Dreiecksignalgenerator aus Abb. 1.79 und Abb. 1.81 b eine Software-orientierte Ablaufbeschreibung mit einem Struktogramm. Das Verhalten wird mit einer Endlosschleife, in der in einer ersten eingebetteten Schleife der Ausgabewert aufwärts und in einer zweiten eingebetteten Schleife abwärts gezählt wird, beschrieben. In jedem Schleifendurchlauf wird für eine Taktperiode gewartet. Ein Modell dieser Art ist auch in VHDL schnell geschrieben und verhält sich bei der Simulation wie die Zielfunktion. Die Nachbildung eines solchen Programmes durch einen Zustands- oder Operationsablaufgraphen erfolgt nach einem einfachen Rezept. Jeder Wartezustand wird zu einem Ablaufzustand. Die Operationen und Ausgaben werden jeweils dem nächsten Wartezustand zugeordnet. Komplexere Beispiele folgen später.
y
0 y := y − 1
wiederhole f¨ ur y = 0 bis 14
2 0
warte einen Zeitschritt 0
a)
y := 0
wiederhole immer
14
15
wiederhole f¨ ur y = 15 bis 0
30 Schritt b)
warte einen Zeitschritt
y=1 c)
y = 14 1 y := y + 1
Abb. 1.81. Erzeugung eines Dreiecksignals a) zu erzeugender Signalverlauf b) Software-orientierte Ablaufbeschreibung mit einem Struktogramm c) Nachbildung des Struktogramms durch einen Operationsablaufgraphen
13
Eine VHDL-Auswahlanweisung ordnet jedem Auswahlwert mindestens eine Anweisung zu. Die Dummy-Anweisung für »tue nichts« ist in VHDL die NullAnweisung.
92
1 Modellbildung und Simulation
1.6.8 Zusammenfassung und Übungsaufgaben Eine sequenzielle Schaltung berechnet ihre Ausgabewerte in mehreren Schritten. Ein universelles Funktionsmodell dafür ist ein Zustandsgraph, der auf dem Modell des endlichen Automaten basiert und das Verhalten durch Zustände und Zustandsübergänge beschreibt. In der Schaltungsnachbildung des Automaten werden die Zustände durch ein Zustandsregister, die Abbildung der Eingabe und des Ist-Zustands auf den Folgezustand durch eine kombinatorische Schaltung mit der Übergangsfunktion und die Abbildung der Eingabe und des Zustands auf die Ausgabe durch eine kombinatorische Schaltung mit der Ausgabefunktion beschrieben. Der Anfangszustand ist der Initialwert des Zustandsregisters. Die Nachbildung eines Zustandsgraphen in VHDL erfolgt nach einfachen Schablonen, in der Regel mit Fallunterscheidungen. Die Übergangsfunktion kann immer mit im Abtastprozess des Zustandsregisters beschrieben werden. Die Ausgabesignale werden vorzugsweise in einem kombinatorischen Prozess oder mit nebenläufigen Signalzuweisungen erzeugt. Eine sequenzielle Schaltung besitzt in der Regel unzulässige Zustände, die zwar nicht im normalen Betrieb, aber durch Fehlfunktionen erreicht werden und zu einem Systemabsturz führen können. Die übliche Fehlerbehandlung bei einem Systemabsturz ist die Neuinitialisierung, entweder manuell über eine Reset-Taste oder automatisch mit einem Watchdog. Zustandsgraphen sind nur für Funktionen mit wenigen zu unterscheidenden Zuständen und Eingabewerten geeignet. Zielfunktionen mit großen Zustands- und Eingabemengen lassen sich genau wie ein Programm auf einem Rechner durch eine Abfolge von Operationen modellieren. Dazu werden Operanden als in Registern gespeicherte Signale und die Operationen als RegisterTransfer-Funktionen definiert. Der Ablauf wird durch einen Operationsablaufgraphen beschrieben. Allein mit einer Zählfunktion als gesteuerte Operation lassen sich auf diese Weise recht komplexe Abläufe in einer anschaulichen Form darstellen und in VHDL beschreiben. In praktischen Entwürfen ist meist vor dem eigentlichen Automatenentwurf eine Skizze der Gesamtschaltung mit der Übergangs- und der Ausgabefunktion als Black-Box zu entwerfen. Ein anderer nützlicher Entwurfszwischenschritt ist die Beschreibung und Simulation der geplanten Operationsabläufe im Stile eines normalen imperativen Programms mit Warteanweisungen zum Weiterschalten der Simulationszeit. Weiterführende und ergänzende Literatur siehe [14, 24, 33, 36, 41, 45]. Aufgabe 1.20 Gegeben sind die Schaltung und die Zeitverläufe in Abb. 1.82. a) Bestimmen Sie für alle Werte des Ist-Zustands s1 s0 und des Eingabesi+ gnals x den Folgezustand s+ 1 s0 und den Ausgabewert y. b) Zeichnen Sie den Zustandsgraphen des Automaten. c) Ergänzen Sie die fehlenden Signalverläufe für den Zustand und die Ausgabe in Abb. 1.82 b.
1.6 Sequenzielle Schaltungen
=1 x a)
T I x
y =1
x I
I T
s0
x I
s1
93
1 0 1 0 1 0
00
s1 s0 b) y
1 0
Abb. 1.82. Schaltung und Signalverläufe zu Aufgabe 1.20
Aufgabe 1.21 Zur Funkübertragung von Daten werden balancierte Codes eingesetzt. Das sind Codes, bei denen innerhalb eines bestimmten Zeitfensters die Anzahl der übertragenen Nullen gleich der Anzahl der übertragenen Einsen ist. Für einen so codierten Datenstrom ist eine Überwachungsschaltung zu konstruieren, die die Differenz zwischen der Anzahl der empfangenen Einsen und der Anzahl der empfangenen Nullen N1 − N0 überwacht und, wenn diese den Wert −3 unterschreitet oder den Wert drei überschreitet, das Fehlersignal y aktiviert. Die nachfolgende Tabelle zeigt einen Beispielablauf: Eingabe x
1
0
0
0
0
0
1
0
1
1
1
1
1
1
1 ...
N1 − N0 Ausgabe y
0 0
1 0
0 0
-1 -2 -3 -3 -2 -3 -2 -1 0 0 1 0 0 0 0 0
0 0
1 0
2 0
3 3 1 ...
Entwerfen Sie einen Zustandsgraphen zur Nachbildung dieser Funktion. Aufgabe 1.22 Morsezeichen bestehen aus Punkten und Strichen. Punkte sind kurze Impulse. Ihre Länge soll in einem Bereich von tP = 200...300 ms liegen. Striche sind lange Impulse, die eine Zeitdauer zwischen tS = 600...900 ms haben sollen. Die Taktfrequenz der zu entwerfenden Schaltung sei fT = 20 Hz und die Prellzeit der Eingabesignale viel kleiner als die Taktperiode. Entwerfen Sie eine Schaltung mit einer Eingabetaste für das Morsesignal, einer Eingabetaste zum Initialisieren, einem bitorientierten Ausgabesignal p, das nach Empfang eines Punktes, einem bitorientierten Ausgabesignal s, das nach Empfang eines Strichs und einem bitorientierten Ausgabesignal err, das nach Empfang eines Impulses unzulässiger Breite für einen Takt aktiviert (auf »1« gesetzt) wird. a) Skizzieren Sie die Gesamtschaltung mit einem Automaten, dessen Übergangs- und Ausgabefunktion als Black-Box dargestellt ist. b) Entwerfen Sie den Operationsablaufgraphen unter Verwendung eines Zählers. c) Beschreiben Sie die Gesamtfunktion in VHDL durch Abtastprozesse und kombinatorische Prozesse.
94
1 Modellbildung und Simulation
d) Entwerfen Sie einen VHDL-Testprozess, der den Morse-Code für das Wort »Paris« als Testeingabesignal bereitstellt. P x
A
R
I
S
1 0
0
2
4
6
8
10
t in s
2 Synthese und Logikoptimierung
Simulationsmodelle digitaler Schaltungen dienen nicht nur zur Analyse und zur Simulation, sondern auch als Funktionsvorgabe für die Synthese. Die Synthese sucht für eine vorgegebene Hardware-Beschreibung eine funktionsgleiche Schaltung. Ein Problem ist das Zeitverhalten. Im Grunde sind nur laufzeitrobuste Schaltungen, d.h. Schaltungen, bei denen die Zeitabläufe durch Takte und nicht durch Verzögerungszeiten festgelegt werden, synthetisierbar. Denn sämtliche automatisierbaren Verfahren der Logikoptimierung, z.B. die Konstantenelimination und die Verschmelzung in Abschnitt 1.2.3, setzen voraus, dass das Zeitverhalten der Gesamtschaltung in weiten Bereichen verändert werden darf. Keine Vorgabe für das Zeitverhalten bedeutet im Simulationsmodell »verzögerungsfrei« (Delta-Delay-Modell) und Funktionsgleichheit, dass die Simulation mit der Synthesebeschreibung nach jeder aktiven Taktflanke dieselben Signalwerte berechnet, wie ein Simulationsmodell mit Zeitverhalten nach Abarbeitung aller schwebenden Signaländerungen. Synthesebeschreibungen sind entsprechend vereinfachte Simulationsmodelle ohne Beschreibungsmittel für Verzögerungszeiten (keine After-Klauseln, keine Wait-On-Anweisung etc.) und auch ohne Gültigkeitskontrollen und Textausgaben. Sie bestehen nach Auflösung der Hierarchie aus • • •
vorentworfenen Schaltungen, die die Synthese unverändert in die Gesamtschaltung übernimmt, kombinatorischen Prozessen mit unverzögerten Signalzuweisungen und verzögerungsfreien Abtastprozessen ohne Kontrolle von Vor- und Nachhaltebedingungen.
Die Synthese ist in mehrere Teilschritte untergliedert [16] (Abb. 2.1): • • • •
Register-Transfer-Synthese, Schaltungsgenerierung für parametrisierte Teilschaltungen, Logikoptimierung und Technologieabbildung.
G. Kemnitz, Technische Informatik, eXamen.press, DOI 10.1007/978-3-642-17447-6_2, © Springer-Verlag Berlin Heidelberg 2011
96
2 Synthese und Logikoptimierung Register-Transfer-Synthese Schaltungsgeneratoren Logikoptimierung Technologieabbildung geometrischer Entwurf
Abb. 2.1. Synthese
Die Register-Transfer-Synthese arbeitet ähnlich wie ein Software-Compiler. Sie analysiert das Simulationsmodell und löst die Hierarchie auf. Statt eines Berechnungsflusses wird jedoch ein Datenfluss extrahiert. Das erste Zwischenergebnis ist eine Schaltungsstruktur aus vorentworfenen Teilschaltungen und Prozessen. Die vorentworfenen Teilschaltungen werden in die Gesamtschaltung übernommen. Die Prozesse müssen noch durch Schaltungen nachgebildet werden. Das ist der komplizierte Teil, der im Weiteren genauer behandelt wird. Die extrahierten Grundschaltungen – Gatter, Register, Rechenwerke etc. – sind zum Teil parametrisierte Modelle, die nach der Register-TransferSynthese an Schaltungsgeneratoren weitergereicht werden. Die Generierung schließt eine lokale Schaltungsoptimierung mit ein. Die verbleibenden Teilschaltungen aus parameterfreien logischen Grundschaltungen werden anschließend optimiert, d.h., sie werden in weniger aufwändige und schnellere funktionsgleiche Schaltungen umgerechnet. Die nach der Optimierung folgende Technologieabbildung und der geometrische Entwurf werden in Kapitel 4 angeschnitten.
2.1 Register-Transfer-Synthese Die Register-Transfer-Synthese bildet imperative Prozessbeschreibungen durch Register und Register-Transfer-Funktionen nach. Das ist selbst ohne Zeitvorgaben ein schlecht gestelltes Problem. Die meisten imperativen Beschreibungen sind nicht durch Schaltungen nachbildbar. Es gibt unzählige Beschreibungsmöglichkeiten derselben Funktion. Geringe Änderungen in der Beschreibung eröffnen sprunghaft neue Interpretationsmöglichkeiten. Deshalb ist die Zielstellung umzuformulieren: Wie muss die Beschreibung aussehen, damit die Synthese eine funktionsgleiche Schaltung findet? Benötigt werden Beschreibungsschablonen mit dem gewünschten Verhalten, die vom Syntheseprogramm als zulässig erkannt und korrekt nachgebildet werden. Der Ball »automatische Schaltungserzeugung« wird an den Entwickler
2.1 Register-Transfer-Synthese
97
zurückgespielt, und zwar in der Form »beschreibe die Zielfunktion in einer synthetisierbaren Weise«. Die nachfolgenden Beispiele für synthesefähige Beschreibungen wurden mit dem Entwurfssystem ISE von Xilinx [5] und einem Spartan-3-Board [2] getestet. Im Buch werden die wesentlichen Merkmale und Besonderheiten erläutert. Die kompletten Entwurfsbeschreibungen stehen im Web [27]. 2.1.1 Beschreibung und Extraktion von Registern Die typische Simulationsbeschreibung für ein Register ist der Abtastprozess in Abb. 1.47 (Abschnitt 1.4.2). Hinzu kann noch die asynchrone Initialisierung aus Abb. 1.58 (Abschnitt 1.5.3) kommen. Für die Synthese werden diese Beschreibungen stark vereinfacht. Aus dem Simulationsmodell entfallen • • • •
alle Beschreibungselemente mit Zeitvorgaben, die Kontrolle der Vor- und Nachhaltebedingungen, die Berechnung der Signalgültigkeit und die Textausgaben.
Übrig bleibt eine Beschreibung mit einer der beiden Strukturen aus Abb. 2.2, ein Prozess mit dem Takt und optional dem Initialisierungssignal in der Weckliste, der dem Ausgabesignal bei Aktivierung des Initialisierungssignals einen signal x, y: tTyp; signal T, I: std_logic; tTyp tTyp ... x x y I/I¯ I –- Abtastprozess mit Initialisierung process(I, T) T /T¯ aktive Taktflanke begin fallende Flanke if I=’1’ then steigende Flanke –- oder if I=’0’ then Initialisierungssignal y <= Anfangswert; low-aktiv elsif rising_edge(T) then high-aktiv –- oder elsif falling_edge(T) then y <= x; end if; tTyp tTyp x y end process; ¯ –- Abtastprozess ohne Initialisierung T /T process(T) aktive Taktflanke fallende Flanke begin steigende Flanke if rising_edge(T) then –- oder if falling_edge(T) then y <= x; tTyp schaltungstechnisch darstellbaend if; rer Datentyp, z.B. STD LOGIC , end process; STD LOGIC VECTOR, ... Abb. 2.2. Beschreibungsschablonen für Register
98
2 Synthese und Logikoptimierung
konstanten Anfangswert und sonst bei einer aktiven Taktflanke den Eingabewert zuweist. Das Initialisierungssignal darf low- oder high-aktiv sein. Die aktive Taktflanke darf die steigende oder die fallende Taktflanke sein. Falls die Bauteilbibliothek Register für eine Datenübernahme mit beiden Taktflanken kennt, dürfen es auch beide Taktflanken sein. Zusammenfassung mehrerer Register zu einem Prozess Die Prozessbeschreibungen für Register mit denselben Signalen in der Weckliste und gleichem Kontrollfluss können zu einem Prozess zusammengefasst werden. Der registerspezifische Beschreibungsteil ist dann nur noch die an die aktive Taktflanke gebundene Zuweisung des Eingabewertes an das Ausgabesignal und die an das Initialisierungssignal gebundene Anfangswertzuweisung. Register mit unterschiedlichen Initialisierungs- oder Abtastbedingungen verlangen getrennte Prozesse. Die Synthese sucht nach der Beschreibungsschablone »Prozess mit Weckliste, ein oder zwei bitorientierte Signale in der Weckliste, die im Prozess nur als Übernahmebedingungen dienen etc.«. Wenn die Synthese einen Prozess nach dieser Schablone findet, klassifiziert sie das Signal aus der Weckliste, an dessen Flanke die Übernahme gebunden ist, als Takt und das Signal, bei dessen Aktivierung eine Konstante zugewiesen wird, als Initialisierungssignal. Die Signale, denen etwas zugewiesen wird, werden zu Registerzuständen. Aus dem Kontrollfluss ergibt sich weiter, ob das Initialisierungssignal high- oder low-aktiv, ob die aktive Taktflanke steigend oder fallend ist und bei initialisierbaren Registern der Initialisierungswert. Abbildung 2.3 zeigt eine Beschreibung von fünf Registern mit drei Prozessen. Der erste Prozess beschreibt die Signale K(0) bis K(2) als Registerzustände. Der Übernahmetakt ist das Signal a. Die Übernahme erfolgt mit der steigenden Flanke. Die Registereingänge sind die Signale d, K(0) und K(1). Es handelt sich offensichtlich um ein 3-Bit-Schieberegister. Der zweite Abtastprozess beschreibt ein 3-Bit-Register mit dem Zustandssignal L und dem Eingabesignal K mit dem high-aktiven Initialisierungssignal c, dem Takt b und einer Datenübernahme mit der fallenden Taktflanke. Der dritte Prozess beschreibt gleichfalls ein 3-Bit-Register mit dem high-aktiven Initialisierungssignal c, das aber einen anderen Takt und eine andere Übernahmebedingung hat und deshalb nicht im selben Prozess beschrieben werden kann. Es ist denkbar, dieselben Registerfunktionen auch in anderer Weise zu beschreiben, z.B. in einem gemeinsamen Prozess mit den Signalen a, b und c in der Weckliste. Für die Simulation ist das egal. Die Synthese analysiert jedoch nicht die Funktion, sondern die Beschreibungsstruktur. Sie reagiert empfindlich auf ihr unbekannte Kontrollflüsse im Simulationsmodell. Im günstigen Fall liefert sie die Fehlermeldung, dass sie keine geeignete Schaltung zuordnen kann. Im ungünstigen Fall extrahiert sie eine falsche Schaltung. In Zweifels-
2.1 Register-Transfer-Synthese signal a, b, c, d: std_logic; signal K, L, M: std_logic_vector( 2 downto 0); ... process(a) begin if rising_edge(a) then K(0) <= d; K(1) <= K(0); K(2) <= K(1); end if; end process;
if c=’1’ then L <= "000"; elsif falling_edge(b) then L <= K; end if; end process; process(a, c) begin if c=’1’ then M <= "010"; elsif rising_edge(a) then M <= K; end if; end process;
process(b, c) begin Registereingänge
d
K(0)
K(1)
K
K
Registerzustand
K(0)
K(1)
K(2)
L
M
1
1
1
3
3
a↑
a↑
a↑
b↓
a↑
Initialisierungssignal
–
–
–
c (H)
c (H)
Initialisierungswert
–
–
–
000
010
Bitbreite Taktsignal
99
Abb. 2.3. VHDL-Registerbeschreibung und extrahierte Registerparameter (↑ – steigende Taktflanke; ↓ – fallende Taktflanke; (H) – high-aktiv; (L) – low-aktiv)
fällen hilft eine vergleichende Simulation zwischen der Synthesevorgabe und dem Syntheseergebnis1 . 2.1.2 Kombinatorische Schaltungen Eine kombinatorische Schaltung hat kein Gedächtnis. Der imperative Beschreibungsrahmen ist ein kombinatorischer Prozess, also ein Prozess mit Weckliste, in der alle Eingabesignale stehen (vgl. Abschnitt 1.2.4). Innerhalb des Prozesses wird die Abbildung der Eingabewerte auf die Ausgabewerte durch einen gerichteten Berechnungsfluss beschrieben (vgl. Abschnitt 1.2.3). Die Zwischenergebnisse sind in Variablen zu speichern. Die Ergebnisse werden den Ausgabesignalen zugewiesen. Es dürfen keine Variablen ausgewertet werden, die möglicherweise Werte früherer Weckzeitpunkte enthalten und es darf keine Alternativen im Berechnungsfluss geben, in denen einem Ausgabesignal kein aktualisierter Wert zugewiesen wird. Für die Synthese erfolgt die Zuweisung an die Ausgabesignale verzögerungsfrei, d.h. ohne After-Klausel. Abbildung 2.4 zeigt Beispiele für Signalzuweisungen, die durch Grundgatter nachgebildet werden. Das sind zum einen logische Bit-Operationen und zum 1
Beides sind simulierbare Schaltungsbeschreibungen.
100
2 Synthese und Logikoptimierung
anderen bitorientierte Beschreibungen von Multiplexern durch Fallunterscheidungen. Die Operanden dürfen jeden der bisher eingeführten Bit-Typen haben. Das Ergebnis hat denselben Typ wie die Operanden. Die Synthese ersetzt den boolean-Wert true in der Regel durch »1« und false durch »0«. Bei Datenobjekten vom Typ std_logic bleiben die Pseudo-Werte ungenutzt. signal a, b, s, y: tBit; y <= a and b a & y b y <= a nand b a & y b
y <= a or b a ≥1 y b y <= a nor b a ≥1 y b
y <= not a a y <= a xor b a =1 y b y <= a xnor b a =1 y b
if s=1 then y <= a; else y <= b; end if ; a
1 0
b s
y
Abb. 2.4. Operatoraufrufe und Fallunterscheidungen, die durch Grundgatter nachgebildet werden (tBit – Bittyp: bit, boolean oder std_logic)
Abbildung 2.5 zeigt eine Beispielbeschreibung einer Gatterschaltung und die extrahierte Schaltung. Die Operatoren werden durch Gatter nachgebildet. Jeder Ausdruck bildet eine baumartige Teilschaltung. Aus den Variablen werden Signale, die die Teilbäume miteinander verbinden. signal x0, x1, x2, x3, x4: std_logic; signal y0, y1: std_logic; ... process(x0, x1, x2, x3, x4) variable v0, v1: std_logic; begin A1: v0 := x0 and x1; A2: v1 := v0 nor x3; A3: y0 <= (v0 and x2) or v1; A4: y1 <= ((not x4) or x3) nand v1; a) end process;
x0 x1 x2
A1
&
A4
x4
A3
& A2
x3 b)
v0
≥1
v1
≥1
y0
&
y1
≥1
⇒Web-Projekt: P2.1/Extr LS.vhdl
Abb. 2.5. a) Beschreibung einer Gatterschaltung b) extrahierte Schaltung
In synthesefähigen kombinatorischen Prozessen sind auch Fallunterscheidungen zulässig. Diese werden durch Multiplexer nachgebildet. In einem Multiplexermodell braucht jede bedingte Zuweisung eine alternative Zuweisung für den Fall, dass die Bedingung nicht erfüllt ist. Diese kann im Else-Zweig stehen oder sie erfolgt vor der Fallunterscheidung und wird bei erfüllter Bedingung überschrieben. Auch zugewiesene Signalwerte können innerhalb eines kombi-
2.1 Register-Transfer-Synthese
101
natorischen Prozesses von einer nachfolgenden Signalzuweisung überschrieben werden. Eine Fortsetzung einer If-Anweisung mit einem Elsif-Zweig wird in einen zweiten Multiplexer übersetzt, der wieder zwischen zwei Werten auswählt. Mehrere Elsif-Zweige werden in eine Multiplexerkette übersetzt (Abb. 2.6). signal a, b, c, d, p, q, r, y: std_logic; ... process(a, b, c, d, p, q, r) begin if p=’1’ then y <= a; elsif q=’1’ then y <= b; elsif r=’1’ then y <= c; else y <= d; end if; end process;
a
b c d
1 0 r
1 0 q
1 0
y
p
⇒Web-Projekt: P2.1/SynthMux2.vhdl
Abb. 2.6. Nachbildung einer If-Elsif-Anweisung durch Multiplexer
Operatoren mit Bitvektoren als Operanden und komplexe Fallunterscheidungen werden als parametrisierte Funktionsblöcke extrahiert und an Schaltungsgeneratoren weitergereicht. Das erste Beispiel sei die Auswahlanweisung in Abb. 2.7. Die Auswahlgröße ist ein 2-Bit-Vektor mit vier zulässigen Werten. Die Synthese bildet diese Auswahlanweisung durch einen Multiplexer mit vier Dateneingängen nach. Im Gegensatz zur Beschreibung mit If-ElsifAnweisungen, die durch mehrere verkettete Multiplexer mit je zwei Dateneingängen nachgebildet werden, überlässt die Auswahlanweisung die Strukturentscheidung dem Schaltungsgenerator, der auf diese Weise mehr Optimierungsmöglichkeiten erhält. signal s: std_logic_vector(1 downto 0); signal x1, x2, x3, x4, y: std_logic_vector(3 downto 0); ... process(s, x1, x2, x3, x4) 4 x1 00 begin 4 case s is x2 01 4 y 4 when "00" => y <= x1; x3 10 when "01" => y <= x2; 4 x4 11 when "10" => y <= x3; 2 when others => y <= x4; s end case; end process; ⇒Web-Projekt: P2.1/SynthCase.vhdl Abb. 2.7. Multiplexerbeschreibung mit einer Auswahlanweisung
102
2 Synthese und Logikoptimierung
Bei einem Auswahlwert vom Typ std_logic_vector gibt es eine Besonderheit. Die VHDL-Sprachdefinition verlangt, dass jeder Auswahlwert, den der Auswahlausdruck annehmen kann, in genau einem Auswahlfall berücksichtigt wird, selbst wenn er in der Schaltungsbeschreibung gar nicht auftreten kann. Das gilt auch für die schaltungstechnisch nicht darstellbaren Pseudo-Werte von std_logic. Auch diese sind als Auswahlfälle zu berücksichtigen. Sie dürfen aber in einer Synthesebeschreibung nicht explizit aufgelistet werden. Der Workaround ist der Auswahlwert »others«. Er erfasst alle nicht explizit aufgezählten Werte inkl. der Pseudo-Werte. Der letzte Auswahlwert in einer Multiplexerbeschreibung mit einem Auswahlausdruck vom Typ std_logic_vector muss deshalb immer wie in Abb. 2.7 »others« sein. Logische Bedingungen für die Kontrollflusssteuerung mit If-Elsif-Anweisungen werden mit Vergleichsoperatoren gebildet. Bei der Übersetzung von true in »1« und false in »0« werden Bitvergleiche mit »1« in eine Identität, Bitvergleiche mit »0« in einen Inverter und der Test zweier Bits auf Ungleichheit in ein EXOR-Gatter übersetzt (Abb. 2.8). x1 x0 0 0 1 1
0 1 0 1
x0 = 0
x0 6= 0
x0 = 1
x0 6= 1
x1 = x0
x1 6= x0
TRUE (1) FALSE (0)
FALSE (0) TRUE (1)
FALSE (0) TRUE (1)
TRUE (1) FALSE (0)
TRUE (1) FALSE (0) FALSE (0) TRUE (1)
FALSE (0) TRUE (1) TRUE (1) FALSE (0)
x0 x1
x0 x1
x0
x0
x0
x0
==
=1
Abb. 2.8. Schaltungsnachbildung von Bitvergleichen
Der Test auf größer, größer gleich etc. ist standardmäßig nur für Zahlentypen und für Bitvektortypen zur Zahlendarstellung definiert. Es gibt unterschiedliche Zahlentypen (ganzzahlige oder solche mit Nachkommastellen, vorzeichenfreie oder vorzeichenbehaftete). Diese unterscheiden sich in der Codierung der Werte und verlangen unterschiedliche Schaltungen für dieselbe Operation (siehe später Abschnitt 2.4.5). Die Synthese extrahiert für diese Operatoren außer der Operandenbitbreite auch die Art der Zahlendarstellung als Parameter. Wir werden im Weiteren in synthesefähigen Beschreibungen nur zwei Bitvektortypen für die Zahlendarstellung verwenden: –- für ganzzahlige vorzeichenfreie Zahlen tUnsigned is array (natural range <>) of std_logic; –- für ganzzahlige vorzeichenbehaftete Zahlen tSigned is array (natural range <>) of std_logic;
Beide sind gemeinsam mit den arithmetischen und logischen Operatoren im Package »Tuc.Numeric_Synth« so definiert, dass die Synthese sie unterstützt (siehe später Abschnitt 3.2.7). Die extrahierten Vergleicherschaltungen
2.1 Register-Transfer-Synthese
103
sind parametrisierte Funktionsblöcke mit typgleichen Operanden, einem bitorientierten Ausgang, der true als »1« und false als »0« darstellt. Wir werden im Weiteren dafür die Symbole in Abb. 2.9 verwenden. signal a, b: t[Un]signed(n-1 downto 0); n
a b
a
==
n
1 wenn a = b 0 sonst
n
a b
a
/=
n
b
1 wenn a 6= b 0 sonst
b
n
a a
n n
a a≤b b
n
a 1 wenn a < b 0 sonst
b a
1 wenn a ≤ b 0 sonst
b
n
a a>b b
n n
a a≥b b
n
1 wenn a > b 0 sonst 1 wenn a ≥ b 0 sonst
Abb. 2.9. Datenflusssymbole für Vergleicherschaltungen
Arithmetische Operationen werden durch Rechenwerke nachgebildet. Abbildung 2.10 zeigt die im Weiteren verwendeten Datenflusssymbole für Addierer, Subtrahierer und Multiplizierer. Die Division und die Potenzbildung, für die in der Abbildung keine Symbole eingeführt sind, werden durch sequenzielle Schaltungen nachgebildet (siehe später Abschnitt 2.6.5). Ihre Operatoren sind in einer Synthesebeschreibung nicht erlaubt. Für die zulässigen arithmetischen Operatoren extrahiert die Register-Transfer-Synthese zusätzlich die Bitanzahl der Operanden und deren Art der Zahlendarstellung. Die Operandentypen müssen übereinstimmen, die Bitbreiten nicht. Das Ergebnis hat den Operandentyp. Die Ergebnisbitbreite der Addition und der Subtraktion ist gleich der Bitbreite des größten Operanden, die der Multiplikation gleich der Summe der Bitbreiten der Operanden. Regelverstöße verursachen Übersetzungsfehler. Zulässige Beschreibungen für Rechenwerke gibt die RegisterTransfer-Synthese an Schaltungsgeneratoren weiter. signal a: t[Un]signed(n - 1 downto 0); signal b: t[Un]signed(m - 1 downto 0); constant c: T[Un]signed(m - 1 downto 0) := ...;
a b a
+ m n
+c
Multiplizierer
Subtrahierer
Addierer n
max(n, m)
a+b
max(n, m)
a+c
a b a
n m n
a a−b b −c
max(n, m)
a−b
max(n, m)
a−c
a b a
n m n
* ∗c
n+m
n+m
a*b a*c
Abb. 2.10. Datenflusssymbole für Addierer, Subtrahierer und Multiplizierer
Abbildung 2.11 zeigt eine synthesefähige Beschreibung für ein Spezialrechenwerk. Das Eingabesignal a, ein 3-Bit-Vektor für die Darstellung einer vorzeichenfreien Zahl, wird mit der vorzeichenfreien 4-Bit-Konstante »1010«
104
2 Synthese und Logikoptimierung
(Wert zehn) multipliziert. An das Zwischenergebnis wird eine führende »0« konkateniert, um es auf acht Bit zu verlängern. Anschließend wird der zweite 3-Bit-Operand addiert. Die 8-Bit-Summe wird an das Ausgabesignal zugewiesen. In der extrahierten Schaltung wird die Multiplikation mit der Konstanten durch einen Multiplizierer, die Konkatenation durch eine Zusammenführung mit einem Einzelbit und die Addition durch einen Addierer nachgebildet. signal a, b: tUnsigned(2 downto 0); signal y: tUnsigned(7 downto 0); ... process(a, b) variable v: tUnsigned(6 downto 0); begin A1: v := a * "1010"; A2: y <= (’0’ & v) + b; end process;
0 3
a
7 8
∗ 10
b
+
3
8
y
⇒Web-Prolekt: P2.1/SynthRW1.vhdl
Abb. 2.11. Beschreibung eines Spezialrechenwerks mit arithmetischen Operationen
Das Beispiel in Abb. 2.12 beschreibt eine Schaltung mit einer arithmetischen Operation, einer Vergleichsoperation, einer Fallunterscheidung und logischen Operationen. Wenn der Wert des 4-Bit-Vektors a größer 3 (binär »0011«) und eines der binären Signale e oder f »1« ist, wird dem Ausgabesignal y die Summe von a und b und sonst der Wert von b zugewiesen. Die Register-Transfer-Synthese extrahiert die Teilschaltungen für die beiden Auswahlalternativen, einen Addierer für die Summe von a und b und eine Verbindung für »nur b«. Dann extrahiert sie die Schaltung für die Bildung des Auswahlwertes, einen Vergleicher von a mit »3« und die logische Verknüpfung des Vergleichsergebnisses mit den bitorientierten Signalen e und f . Die Fallsignal a, b, y: tUnsigned(3 downto 0); signal e, f: std_logic; ... process(a, b, e, f) begin if (a>"0011") and (e or f)=’1’ then y <= a + b; else y <= b; end if; end process;
a b
4
+
4
0011 e f
a a>b b ≥1
4
1 0
4
y
&
⇒Web-Projekt: P2.1/SynthRW2.vhdl
Abb. 2.12. Beschreibung einer Schaltung mit einem Addierer, einem Vergleicher, logischen Verknüpfungen und einem Multiplexer
2.1 Register-Transfer-Synthese
105
unterscheidung wird durch einen 4-Bit-Multiplexer am Ende des Datenflusses nachgebildet. Zuweisungen an unterschiedliche Variablen oder Signale in einer Auswahlanweisung beschreiben mehrere Multiplexer. Abbildung 2.13 zeigt eine Auswahlanweisung mit Zuweisungen an zwei Variablen. Beiden wird, damit die Auswahlanweisung kein Speicherverhalten beschreibt, vor der Auswahlanweisung ein Standardwert zugewiesen. Mit »|« werden mehrere Auswahlwerte zusammengefasst. Die Synthese findet für die Variable u einen Multiplexer, der zwischen den Eingabegrößen a, b und usonst umschaltet, und für v einen Multiplexer, der zwischen den Eingabegrößen c, d und vsonst umschaltet.
case s is when w1 w2 => when w3 => when w4 => end case; a b usonst s
t1
v := vsonst ;
u := usonst ; u := a; u := b;
v := c; v := d;
Mux1
w1 w2 t1 u w3 t1 sonst t1
{w1, w2, w3, ...}
c d vsonst
t2 t2 t2
Mux2 w3 w4 sonst
t2
v
t1, t2 – beliebige Bit- oder Bitvektortypen
Abb. 2.13. Auswahlanweisung zur Beschreibung mehrerer Multiplexer
Abbildung 2.14 zeigt als abschließendes Beispiel die Beschreibung einer Wertetabelle mit einer Auswahlanweisung. Der Auswahlausdruck ist ein Bitvektor. Alle Eingabewerte, denen der Ausgabewert »1« zugeordnet ist, stehen im ersten Auswahlfall. Alle anderen Eingabewerte, für die der Ausgabewert »0« ist, erfasst der Sonst-Fall. signal x: std_logic_vector(3 downto 0); signal y: std_logic; ... process(x) begin case x is when "0001"|"0010"|"0100"|"1000" => y <= ’1’; when others => y <= ’0’; end case; end process; ⇒Web-Projekt:
x3 x2 x1 x0 y 0 0 0 1
0 0 0 1 1 0 0 0 sonst
1 0 0 0
1 1 1 1 0
P2.1/SynthWTab.vhdl
Abb. 2.14. Beschreibung einer Wertetabelle mit einer Auswahlanweisung
106
2 Synthese und Logikoptimierung
Zusammenfassend ist die Beschreibungsschablone für die Synthese einer kombinatorischen Schaltung ein kombinatorischer Prozess mit allen Eingabesignalen in der Weckliste, der bei jeder Änderung eines Eingabesignals die Werte aller Ausgabesignale neu aus den aktuellen Eingabewerten berechnet. Die Anweisungsfolge im Prozess ergibt sich durch Nachbildung des Datenflusses der Zielschaltung durch einen Berechnungsfluss. Gatter werden zu logischen Operatoren, Rechenwerke zu arithmetischen Operatoren, Vergleichsschaltungen zu Vergleichsoperatoren und Multiplexer zu Fallunterscheidungen. Die Synthese muss zuerst einen Prozess als kombinatorischen Prozess erkennen und versucht im Erfolgsfall, den Berechnungsfluss in umgekehrter Weise durch einen Datenfluss nachzubilden. Eine Beschränkung auf bewährte Beschreibungsschablonen erhöht die Erfolgschancen. 2.1.3 Kombinatorische Schaltungen mit Abtastregistern In einem Simulationsmodell kann die Beschreibung einer Verarbeitungsfunktion mit ihren Abtastregistern zu einem Abtastprozess zusammengefasst werden. Das mindert den Simulationsaufwand. Denn ein kombinatorischer Prozess wird bei jeder Eingabeänderung geweckt und berechnet dann seine Ausgabewerte neu. Abtastprozesse werden nur von Taktänderungen geweckt und berechnen nur bei einer aktiven Taktflanke ihre Ausgabewerte neu (vgl. Abschnitt 1.4.1). Diese Art der Zusammenfassung von Verarbeitungsfunktionen und Registern ist auch in Synthesebeschreibungen üblich. Die Umwandlung eines kombinatorischen Prozesses in einen Abtastprozess, der die Ausgabe zusätzlich bis zur nächsten aktiven Taktflanke verzögert, ist ein einfacher Formalismus. Die Eingabesignale in der Weckliste werden durch den Takt ersetzt und die Berechnungen werden in eine zusätzliche Fallunterscheidung vom Typ »wenn aktive Taktflanke, dann« eingebettet. Ein initialisierbares Abtastregister verlangt zusätzlich, dass auch das Initialisierungssignal in der Weckliste steht, und dass der Prozess einen Kontrollfluss nach folgendem Schema hat: process(I, T) if I_aktiv then y <= Anfangswert; elsif aktive_Flanke_von_T then Verarbeitungsergebnis_berechnen; y <= Verarbeitungsergebnis; end if; end process;
Das ist die Registerbeschreibungsschablone aus Abschnitt 2.1.1 erweitert um die Berechnungsschritte für die Nachbildung der Verarbeitungsfunktion. Die Synthese kann das Abtastregister entweder an den Eingängen oder den Ausgängen einfügen. Denn beide Schaltungsstrukturen unterscheiden sich nur im Zeitverhalten, und das gibt eine Synthesebeschreibung nicht vor.
2.1 Register-Transfer-Synthese
107
Abbildung 2.15 zeigt die um eine Abtastfunktion erweiterte Beschreibung der Schaltung aus Abb. 2.12. Es wird zusätzlich ein Takt vereinbart, der die Eingabesignale in der Weckliste ersetzt. Die Anweisungsfolge ist in eine IfAnweisung mit rising_edge(T) als Ausführungsbedingung eingeschlossen. Der Rest der Beschreibung ist unverändert übernommen. signal a, b, y: std_logic_vector(3 downto 0); signal T, e, f: std_logic; 4 4 a ... + 4 1 4 4 b process(T) y’ 0 y begin T a if rising_edge(T) then a>b 0011 b if (a>"0011") and (e or f)=’1’ then & e ≥1 y <= a + b; f Abtastelse register y <= b; Schaltung end if; end if; end process; ⇒Web-Projekt: P2.1/SynthRW2Reg.vhdl
Abb. 2.15. Beschreibungserweiterung der Schaltung aus Abb. 2.12 um eine Abtastfunktion
In einer Register-Transfer-Beschreibung können abgetastete Signale Werte speichern (vgl. Abschnitt 1.6). Ohne Zuweisung behält ein Register seinen Wert. Eine fehlende Zuweisung hat dieselbe Wirkung wie die Zuweisung des aktuellen Wertes und kann durch einen Multiplexer, der zwischen dem neuen und dem aktuellen Wert umschaltet, nachgebildet werden (Abb. 2.16 a). Alternativ lässt sich die bedingte Übernahme schaltungstechnisch auch in das signal x, y: std_logic_vector(n-1 downto 0); signal T, E: std_logic; ... process(T) begin if rising_edge(T) then if E=’1’ then y <= x; end if; end if; end process;
n
x a)
n
n
n
y
T
E x E
b)
0 1
n
n
x E
y
T
Abb. 2.16. Register mit bedingter Wertübernahme a) Nachbildung mit einem Multiplexer b) Registersymbol mit zusätzlichem Freigabeeingang
108
2 Synthese und Logikoptimierung
Registermodell einbetten. Das Register erhält dazu zusätzlich einen Freigabeeingang E und übernimmt seine Eingabewerte nur noch dann bei einer aktiven Taktflanke, wenn das Freigabesignal aktiv ist (Abb. 2.16 b). Die Einbettung der Übernahmesteuerung in das Registermodell eröffnet zusätzlichen Gestaltungsspielraum für die Schaltungsoptimierung, die sich an die RegisterTransfer-Synthese anschließt (siehe später Abschnitt 4.3.5). Abgetastete Signale können im Folgetakt weiterverarbeitet werden. Abbildung 2.17 zeigt das am Beispiel eines 4-Bit-Zählers. Der Zähler hat einen Takteingang, einen Initialisierungseingang, einen Steuereingang V zum Vorwärtszählen und einen Steuereingang R zum Rückwärtszählen. Der Zählzustand ist ein 4-Bit-Vektor zur Darstellung vorzeichenfreier ganzer Zahlen. Wenn das Initialisierungssignal aktiv ist, wird der Zählzustand mit »0000« initialisiert. Sonst wird bei einer aktiven Taktflanke, wenn V aktiv ist, der Zählstand um »1« erhöht und, wenn V inaktiv und R aktiv ist, der Zählstand um »1« verringert. Die Auswertung und Veränderung des Zählsignals im selben Prozess ist nur deswegen zulässig, weil die Zuweisung an die aktive Taktflanke gebunden ist. In einem kombinatorischen Prozess, der bei jeder Eingabeänderung geweckt wird, würde eine solche Signalzuweisung ein unsinniges, schaltungstechnisch nicht nachbildbares Verhalten beschreiben, nämlich ein Signal, das seinen Wert ständig weiterzählt, ohne dass dazwischen Simulationszeit vergeht. signal y: tUnsigned(3 downto 0); signal T, I, V, R: std_logic; ... process(T, I) begin if I=’1’ then y <= "0000"; elsif rising_edge(T) then if V=’1’ then y <= y + "1"; elsif R=’1’ then y <= y -"1"; end if; end if; end process;
+1 −1
4 4 4
1 0 R
4
1 0
4
V I
4
x I
y
T
⇒Web-Projekt: P2.1/VRZaehler1.vhdl
Abb. 2.17. Beschreibung und extrahierte Schaltung eines Vorwärts-RückwärtsZählers
In Abb. 2.17 findet die Register-Transfer-Synthese zwei arithmetische Operationen, von denen entweder die eine oder die andere oder keine auszuführen ist. Die If-Anweisung wird durch einen Multiplexer nachgebildet, der zwischen dem um »1« erhöhten Zählwert und dem Ausgabewert des Multiplexers für den Else-Zweig auswählt. Der Multiplexer für den Else-Zweig wählt zwischen dem um »1« verringerten und dem aktuellen Zählwert aus. Die Fallunterschei-
2.1 Register-Transfer-Synthese
109
dungen lassen sich auch so abändern, dass die Register-Transfer-Synthese die Beschreibungsschablone für ein Register mit Freigabeeingang findet. In Abb. 2.18 berechnet die erste Fallunterscheidung den um »1« erhöhten oder verringerten Zählwert und speichert ihn in einer Variablen2 . Die zweite Fallunterscheidung weist den berechneten, um eins verringerten oder vergrößerten Wert aber nur dann als Folgezustand an das Zählsignal zu, wenn entweder das Steuersignal zum Vorwärtszählen oder das Steuersignal zum Rückwärtszählen aktiv ist. Das ist das Verhalten eines Registers mit Freigabeeingang3 . variable tmp: tUnsigned(3 downto 0); ... if V=’1’ then tmp := y +"1"; else tmp := y -"1"; end if; if V=’1’ or R=’1’ then y <= tmp; end if;
+1 −1 V R
4 4
≥1
0 1
4
I
4
x E I
y
T
⇒Web-Projekt: P2.1/VRZaehler2.vhdl
Abb. 2.18. Änderung der Fallunterscheidungen des Vorwärts-Rückwärts-Zählers so, dass die Synthese garantiert ein Register mit Freigabeeingang findet
In den bisherigen Modellen dienten Variablen ausschließlich zur Weitergabe von Zwischenergebnissen zum selben Weckzeitpunkt. So genutzte Variablen übersetzt die Synthese in Signale, die kombinatorische Teilschaltungen verbinden. In einem Abtastprozess kann eine Variable jedoch auch ein Speicherverhalten beschreiben. Das passiert genau dann, wenn der zugewiesene Wert erst ausgewertet wird, wenn der Prozess von einer späteren aktiven Taktflanke aufgeweckt wird. Variablen können jedoch nur eingebettete, von außen nicht direkt zugängliche Register beschreiben, weil sie nur innerhalb des Prozesses, in dem sie definiert sind, gelesen und verändert werden können. Abbildung 2.19 zeigt die Beschreibung eines Schieberegisters mit Variablen. Die Variable z0 übernimmt den Wert des Eingabesignals x mit der aktiven Taktflanke. Ihr Wert wird von der Variablen z1 erst nach der nächsten aktiven Taktflanke übernommen. Die Wertübernahme von z1 in z2 und die 2 3
Das Zwischenergebnis darf nicht in einem Signal gespeichert werden. Denn eine Signalzuweisung in einem Abtastprozess wird in ein Register übersetzt. Die in den Beispielen unterstellte Nachbildung der Zielfunktionen mit Registern, Multiplexern, Gattern, Rechenwerken und Vergleichsschaltungen ist nicht zwingend. Das Entwurfssystem ISE [5] bildet z.B. im Gegensatz zu den hier getroffenen Annahmen If-Anweisungen mit mehreren Gattern statt mit einem Multiplexer nach und fasst Zähloperationen und Zählregister zu Zählerbausteinen zusammen. In Abb. 2.17 und 2.18 findet die Synthese z.B. beide Male einen VorwärtsRückwärts-Zähler. Wir bleiben dennoch bei unserem einfachen Bausteinsystem.
110
2 Synthese und Logikoptimierung
von z2 in y erfolgt gleichfalls mit einer Verzögerung um eine Taktperiode. Jede Zuweisung beschreibt ein Register und alle Register zusammen bilden ein 4-Bit-Schieberegister. Bei einer Umkehrung der Reihenfolge der Variablenzuweisungen würde der Wert des Eingabesignals x mit nur einem Takt Verzögerung an das Ausgabesignal y zugewiesen werden, ein Verhalten, das mit nur einem Register nachzubilden wäre. signal T, x, y: std_logic; ... process(T) variable z0, z1, z2: std_logic; begin if rising_edge(T) then y <= z2; z2 := z1; z1 := z0; z0 := x; end if; end process;
z0
x
z1
z2
y
T
⇒Web-Projekt: P2.1/VarSR.vhdl
Abb. 2.19. Beschreibung eines 4-Bit-Schieberegisters mit Variablen
Wenn der Wert einer Variablen in einem Abtastprozess nur für bestimmte Eingabe- oder Zustandswerte überschrieben wird, sehen die Anweisungen vor der bedingten Variablenzuweisung den neuen Wert erst zum nächsten Weckzeitpunkt. Für sie ist die Variable durch ein Register und die Übernahmebedingung durch einen Multiplexer, der zwischen dem Ist-Wert des Registers und dem Eingabewert umschaltet, nachzubilden. Die Anweisungen nach der bedingten Variablenzuweisung sehen den neu zugewiesenen Wert sofort, d.h. zum selben Weckzeitpunkt. In der synthetisierten Schaltung ist das der Wert am Dateneingang des Registers. Abbildung 2.20 zeigt einen Abtastprozess mit zwei Signalzuweisungen, die beide den Wert derselben Variablen v mit demselben Signal a EXORverknüpfen, einmal vor und einmal nach einer bedingten Wertzuweisung an v. Die beiden Signalzuweisungen werden durch Register mit der vorgelagerten EXOR-Verknüpfung nachgebildet. Die bedingte Variablenzuweisung beschreibt ein Speicherverhalten, das auch durch ein Register nachgebildet wird. Die EXOR-Verknüpfung vor der Variablenzuweisung wertet immer den Variablenwert vor der letzten Taktflanke, d.h. den Registerzustand, aus. Der Wert, den die EXOR-Verknüpfung nach der bedingten Variablenzuweisung auswertet, hängt von der Bedingung ab. Wird ein neuer Wert übernommen, ist der neue Wert weiterzuverarbeiten, sonst der alte, im Register gespeicherte Wert. Bei einer Nachbildung der bedingten Wertübernahme durch ein Register mit einem Eingabemultiplexer, der zwischen dem neuen und dem aktuellen Wert umschaltet, ist der Wert der Variablen für die erste Signalzuweisung am Re-
2.1 Register-Transfer-Synthese signal T, a, b, c, y0, y1: std_logic; ... process(T) variable v: std_logic; begin if rising_edge(T) then A1: y0 <= v xor a; if b=’1’ then A2: v := c; end if; A3: y1 <= v xor a; end if; end process;
Variable v f¨ ur Anweisung A3
Variable v f¨ ur Anweisung A1
0 1
c
=1
b a
111
=1
y0 y1
T ⇒Web-Projekt: P2.1/BedVarZuw.vhdl
Abb. 2.20. Bedingte Variablenzuweisung in einem Abtastprozess
gisterausgang und der für die zweite Signalzuweisung am Registereingang abzugreifen. Die Variable repräsentiert zwei unterschiedliche Schaltungspunkte im Datenfluss. Die Möglichkeit, unterschiedliche Schaltungspunkte zu repräsentieren, macht eine Variable, die ein Speicherverhalten beschreibt, zu einem fehleranfälligen Beschreibungsmittel, das nur wohlüberlegt eingesetzt werden sollte. In einem Abtastprozess lassen sich kombinatorische Funktionen genau wie in einem kombinatorischen Prozess durch logische Operatoren, arithmetische Operatoren, Vergleichsoperatoren und Fallunterscheidungen beschreiben, die von der Register-Transfer-Synthese durch Logikgatter, Rechenwerke, Vergleicher und Multiplexer nachgebildet werden. Signalzuweisungen werden, da sie an die aktive Taktflanke gebunden sind, in Register übersetzt, die auch als Zwischenspeicher verwendet werden können. Aus Variablen, die ihre zugewiesenen Werte zum selben Weckzeitpunkt weitergeben, werden Signale, die kombinatorische Teilschaltungen miteinander verbinden. In einem Abtastprozess ist es aber auch möglich, mit Variablen Register zu beschreiben. 2.1.4 Latches Latches sind schaltungstechnisch einfacher aufgebaute Datenspeicher als Register. Sie besitzen einen binären Freigabeeingang, einen Dateneingang und einen Datenausgang. Die Datenübernahme erfolgt zustandsgesteuert. Wenn das Freigabesignal am Freigabeeingang aktiv ist, übernimmt das Latch seine Eingabedaten, sonst speichert es seinen Wert. Bei einem ungültigen Freigabesignal ist nicht vorhersagbar, ob der Eingabewert übernommen wird oder ob der gespeicherte Wert erhalten bleibt. Falls sich der gespeicherte Wert vom Eingabewert unterscheidet, wird er ungültig. Voraussetzungen für die Übernahme gültiger Daten sind, dass das Eingabesignal vor der Deaktivierung des
112
2 Synthese und Logikoptimierung
Freigabesignals lange genug stabil anliegt und dass der Freigabeimpuls ausreichend lang ist. Das Simulationsmodell in Abb. 2.21 nähert das skizzierte Verhalten wie folgt an. Wenn das Freigabesignal nicht inaktiv ist und der gespeicherte Wert vom Eingabewert abweicht, wird der gespeicherte Wert nach einer Haltezeit th auf ungültig gesetzt. Wenn das Freigabesignal aktiv ist, wird dem Zustandssignal zusätzlich mit dem Transportverzögerungsmodell nach einer Verzögerungszeit td der Eingabewert zugewiesen. Zusätzlich werden die Zeiten zwischen der steigenden und der fallenden Flanke des Freigabesignals und die Zeiten seit der letzten Eingabedatenänderung vor der fallenden Flanke des Freigabesignals überprüft. Wenn sie kleiner als der Zeitparameter ts sind, wird der gespeicherte Zustand nachträglich invalidiert. signal E: std_logic; signal x, y: tTyp ; ... process(x, E) variable tE:delay_length; begin if E /= ’0’ and y /= x then y <= ungültig after th; end if; if E=’1’ then y <= transport x after td; end if; –- Kontrolle der Vorhaltebedingungen if rising_edge(E) then tE := now; elsif falling_edge(E) and (now-tE
tTyp x x E E
L
E
1 0
> ts > ts
x w1 th y w0 td th ts E tTyp
tTyp y
w2 td th w1
td w2
Verz¨ogerungszeit Haltezeit Vorhaltezeit Freigabeeingang Bit- oder Bitvektortyp Wert ung¨ ultig
⇒Web-Projekt P2.1/TestLatch.vhdl
Abb. 2.21. Simulationsmodell, Zeitverhalten und Symbol eines Latches
In der Synthesebeschreibung für ein Latch entfallen wieder die Beschreibung des Zeitverhaltens und die Modellierung der Gültigkeit. Übrig bleibt eine bedingte Signalzuweisung in einem Prozess mit allen Signalen in der Weckliste: process(x, E) begin if E=’1’ then y <= x; end if; end process;
2.1 Register-Transfer-Synthese
113
Der Verzicht auf die Berechnung der Signalgültigkeit ist bei einem Latch riskant. In der Beschreibung in Abb. 2.22 a werden zwei Bitvektoren verglichen. Bei Gleichheit soll der übereinstimmende Wert in das Latch übernommen werden. In allen anderen Fällen soll das Latch den zuletzt übernommenen Wert speichern. Bei einer Simulation mit der Synthesebeschreibung funktioniert das auch so. Die Synthese würde die Beschreibung in die Schaltung in Abb. 2.22 b übersetzen, ein Latch mit einem Vergleicher für die Bildung des Freigabesignals. Spätestens bei der Simulation der Schaltung mit Verzögerungen und Gültigkeitsinformationen stellt sich jedoch heraus, dass die Schaltung überhaupt nicht funktionieren kann (Abb 2.22 c). Der Vergleicherausgang, der das Übernahmesignal für das Latch liefert, wird nach jeder Eingabeänderung kurzzeitig ungültig und invalidiert den gespeicherten Wert (Fehlersituation F1). Wenn beide Eingabesignale übereinstimmen und sich das erste Eingabesignal ändert, wird das Freigabesignal des Latches erst nach der Eingabeänderung deaktiviert (Fehlersituation F2). Es ist nicht nur unsicher, sondern sogar unwahrscheinlich, dass das Latch die richtigen Datenwerte speichert. signal x1, x2, y: std_logic_vector(n-1 downto 0); ... process(x1, x2) begin if x1=x2 then y <= x1; end if; end process; a)
⇒Web-Projekt P2.1/VglLatch.vhdl
x1 b)
x2 x1 x2 E
c)
1 0
x == w1
E w3
w2
y
L
w4 w3 td th
F1
F2
Abb. 2.22. a) Synthesebeschreibung für ein Latch mit einem Vergleicher zur Bereitstellung des Freigabesignals b) extrahierte Schaltung c) Zeitverlauf der Eingabesignale und des Freigabesignals bei einer Simulation mit Gültigkeitsfenstern
Es gibt auch Schaltungen mit Latches, die zuverlässig arbeiten. Abbildung 2.23 a zeigt einen Blockspeicher mit vier adressierbaren Latches. Der Adressdecoder »Dec« erzeugt aus dem 2-Bit-Adresssignal vier high-aktive Auswahlsignale si . Diese werden mit dem Schreibsignal W UND-verknüpft. Wenn das Schreibsignal aktiviert wird, übernimmt das Latch mit dem aktiven Auswahlsignal si den Eingabewert. Die übrigen Latches behalten ihre Werte. Die Voraussetzungen dafür sind, dass das Schreibsignal W außerhalb der Übernahmezeitpunkte inaktiv und Glitch-frei ist, dass die Übernahmeimpulse ausreichend lang sind und dass sich die Auswahlsignale si und das Dateneingabesignal x, während W aktiv ist, nicht ändern. Abbildung 2.23 b zeigt die zugehörigen Zeitverläufe. Ein wesentlicher Aspekt dieser Beispielschaltung ist, dass sie nur funktioniert, wenn die Struktur aus Abb. 2.23 a beibehalten wird. Denn, wenn die
114
2 Synthese und Logikoptimierung signal W: std_logic; signal a: std_logic_vector(1 downto 0); signal x, s, q, y0, y1, y2, y3, s: std_logic_vector(3 downto 0); ... a Dec:process(a) begin case a is when "00" => s <= "0001"; a) when "01" => s <= "0010"; when "10" => s <= "0100"; when others => s <= "1000"; end case; end process; Und:process(s, W) begin if W=’1’ then q <= s; else q <= "0000"; end if; end process;
Latch0:process(x, q(0)) begin if q(0)=’1’ then y0 <= x; end if; end process; c)
b)
x s0 s1 Dec s2 s3
W a s0
11
& & & & 00
q0
x E
y0
q1
x E
y1
q2
x E
y2
q3
x E
y3
10
01
11
1 0
s1 10 W 10 q0 10 q1 10
... Latch3:process(x, q(3)) begin if q(3)=’1’ then y3 <= x; end if; end process; ⇒Web-Projekt/P2.1/LatchBS.vhdl
Abb. 2.23. Adressierbarer Blockspeicher a) Schaltung mit Latches b) Zeitverläufe für die Bildung der Freigabesignale c) VHDL-Beschreibung
UND-Verknüpfungen mit der Decoderfunktion zu einem Funktionsblock zusammengefasst werden, ist nicht mehr garantiert, dass bei Adresssignalwechseln bei inaktivem Schreibsignal alle Latch-Freigabesignale inaktiv bleiben. Die Synthesebeschreibung muss nicht nur die Funktion, sondern auch die Zielstruktur beinhalten. In Abb. 2.23 c beschreibt der erste Prozess den Decoder, der zweite die UND-Gatter und die restlichen vier Prozesse beschreiben die Latches. Zusätzlich ist dem Syntheseprogramm mitzuteilen, dass es die vorgegebene Struktur nicht zu Optimierungszwecken ändern darf. Letzteres erfolgt mit Hilfe von Constraints (siehe nächster Abschnitt). Durch die zusätzlich erforderlichen Strukturvorgaben ist der Entwurf von Schaltungen mit Latches deutlich komplizierter als der mit Registern. Auch in Abtastprozessen lassen sich Funktionen beschreiben, die die Synthese möglicherweise mit Latches nachbildet. Das sind z.B. bedingte Variablenzuweisungen, bei denen der zugewiesene Variablenwert nur nach der Zuweisung ausgewertet wird. In der VHDL-Beschreibung in Abb. 2.24 a wird einer Variablen v, wenn eine Bedingung b erfüllt ist, der Wert eines Signals c zugewiesen. Die nachfolgende Anweisung beschreibt eine EXOR-Verknüpfung des Variablenwertes und eine Ergebniszuweisung an ein Signal. Bei der Nach-
2.1 Register-Transfer-Synthese
115
bildung der bedingten Variablenzuweisung durch ein Register mit b als Freigabesignal wird die Übernahme bis zur nächsten aktiven Taktflanke verzögert. Wenn das Freigabesignal aktiv ist, muss die EXOR-Verknüpfung den Wert am Registereingang und sonst den Wert am Registerausgang weiterverarbeiten. Das kostet einen zusätzlichen Multiplexer (Abb. 2.24 b). Ein Latch übernimmt den Eingabewert, ohne auf die nächste aktive Taktflanke zu warten. Bei erfüllter Übernahmebedingung ist der Eingabewert auch am Latch-Ausgang verfügbar. Es genügt nicht nur eine einfachere und kleinere Schaltung für das Speicherelement, sondern es entfällt auch der Multiplexer (Abb. 2.24 c). signal T, a, b, c, y: std_logic; ... process(T) variable v: std_logic; begin if rising_edge(T) then if b=’1’ then v := c; end if; y <= v xor a; end if; a) end process;
a c b b)
x E
y
T c b
c)
=1
0 1
x E
a
=1
y T
⇒Web-Projekt/P2.1/BedVarZuwLatch.vhdl
Abb. 2.24. a) Abtastprozess mit einer bedingten Variablenzuweisung b) Nachbildung der Variablen durch ein Register c) Nachbildung der Variablen durch ein Latch
Der Ersatz des Registers und des Multiplexers durch ein Latch ist nicht unbedenklich. Die vereinfachte Schaltung funktioniert nur dann zuverlässig, wenn das Freigabesignal für das Latch garantiert Glitch-frei ist. Die einzigen garantiert Glitch-freien Signale in einer Synthesebeschreibung sind abgetastete Signale. Für die Ausgabesignale von kombinatorischen Schaltungen lässt sich Glitch-Freiheit nur in Verbindung mit Strukturvorgaben sicherstellen. Zusammenfassend gibt es außer Registern, die ihre Eingabedaten mit der aktiven Taktflanke übernehmen, auch zustandsgesteuerte Speicherelemente. Das sind die Latches. Die Datenspeicherung in Latches mindert den Schaltungsaufwand, verlangt aber zeitgenaue, Glitch-freie Freigabesignale für die Steuerung der Datenübernahme, was den Entwurf erheblich verkompliziert. In Synthesebeschreibungen ist deshalb von der Verwendung von Latches abzuraten.
116
2 Synthese und Logikoptimierung
2.1.5 Constraints Constraints sind Festlegungen oder Richtlinien zur Steuerung der automatisierten Entwurfsschritte von der Register-Transfer-Synthese bis hin zur Platzierung und Verdrahtung. Für eine laufzeitrobuste Funktionsbeschreibung, die nur aus Abtastprozessen und kombinatorischen Prozessen besteht, genügen in der Regel Constraints für das Anschlussverhalten: • • •
geometrische Constraints für die Anordnung der Anschlüsse, Constraints für das elektrische Verhalten der Anschlüsse4 und Constraints für das Zeitverhalten.
Schaltungen mit hoher Packungsdichte, laufzeitintoleranten Signalen oder Geschwindigkeitsanforderungen im Grenzbereich verlangen weitere Constraints. Ihr Entwurf erfordert manuelle Strukturvorgaben und zum Teil auch manuelle Vorgaben für die Platzierung und Verdrahtung. Dazu werden die kritischen Schaltungsteile in einer Strukturbeschreibung aus vorentworfenen Bausteinen zusammengesetzt. Zusätzlich wird der Synthese über Constraints mitgeteilt, dass sie die kritischen Strukturelemente nicht wegoptimieren, mit anderen Schaltungsteilen zusammenfassen oder duplizieren darf. Diese Constraints schalten praktisch die automatische Optimierung aus oder schränken sie ein. Der Entwickler sollte genau wissen, was er tut, wenn er solche Constraints einsetzt. Die automatisch generierten Lösungen sind recht gut und nur mit einem detaillierten Expertenwissen über die Zusammenhänge zwischen der Beschreibungsstruktur, der Schaltungsstruktur und den Gütekenngrößen Aufwand, Geschwindigkeit und Stromverbrauch zu überbieten. Kapitel 4 behandelt später diese Zusammenhänge, aber nicht so detailliert, dass es für einen professionellen Handentwurf ausreicht. Die Fixierung von internen Strukturelementen ist wiederum die Voraussetzung, Constraints für die Anordnung der Strukturelemente vorzugeben. Mit Anordnungs-Constraints ist für jedes Strukturelement beschreibbar, wo oder in Nachbarschaft welcher anderen Strukturelemente es angeordnet werden soll. Auf diese Weise lassen sich Verzögerungszeiten genau einstellen oder minimieren. Der nachfolgende Exkurs beschränkt sich auf das Entwurfssystem ISE, das auch in den Web-Projekten verwendet wird. Die einfachste Art der ConstraintVorgabe in diesem System ist, sie in die ucf-Datei (ucf – user constraints f ile) zu schreiben [1]. Constraints für das Anschlussverhalten beziehen sich auf Signale. In der ucf-Syntax ist das Schlüsselwort für Signal »NET«. Jedes Anschlusssignal hat in der Schaltungsbeschreibung einen Namen, der in den ucf-Einträgen in Hochkommas zu setzen ist. Für Bitvektoren wird jedes Bit einzeln betrachtet. Der Index steht in spitzen Klammern. Jedem Anschlusssignal ist in der Regel ein Gehäuseanschluss zuzuordnen. Das erfolgt mit dem 4
Zu den elektrischen Eigenschaften der Anschlüsse digitaler Schaltungen gehören die Spannungswerte zur Darstellung der Signalwerte »0« und »1«, die Umschaltschwelle zwischen beiden Signalwerten, der Innenwiderstand der Ausgangstreiber und der Eingangswiderstand der Empfänger.
2.1 Register-Transfer-Synthese
117
Schlüsselwort »LOC«. Anderenfalls ordnet das System die Gehäuseanschlüsse nach eigenem Ermessen und bei einer Wiederholung der Synthese möglicherweise anders zu. Für das S3Board, das für die Web-Projekte als Versuchsbaugruppe vorgeschlagen wurde [2], werden z.B. die Gehäuseanschlüsse, an denen die Tasten mit den Signalnamen »btn(0)« und »btn(1)« angeschlossen sind, wie folgt zugeordnet: NET "btn<0>" NET "btn<1>"
LOC = "M13"; LOC = "M14";
Mit anderen Schlüsselworten, z.B. »PULLUP«, lassen sich die elektrischen Eigenschaften der Anschlüsse anpassen. Die Zeit-Constraints für das Anschlussverhalten einer laufzeitrobusten Schaltung bestehen im Wesentlichen aus drei Informationsbausteinen: • • •
Beschreibungen für die Signalverläufe der Takte, Beschreibungen der Gültigkeitsfenster synchroner Eingabesignale und zuzusichernde Gültigkeitsfenster für synchrone Ausgabesignale.
Ein Takt ist ein zyklisches Signal mit zwei Merkmalen, der Taktperiode und dem Tastverhältnis. Das Tastverhältnis (engl. duty cycle) beschreibt den relativen Zeitanteil, den der Takt »1« ist. Beide Merkmale werden der Synthese unter Verwendung der Schlüsselworte »PERIOD« und »HIGH« mitgeteilt. Für einen 50-MHz-Takt T mit einem Tastverhältnis von 50% lautet der ucfEintrag z.B. NET "T" PERIOD 20 ns HIGH 50%;
Aus diesem Eintrag leitet die Synthese für alle Datenpfade zwischen allen Registern mit diesem Takt oder mit Takten, die aus diesem Takt abgeleitet sind5 , die maximal zur Verfügung stehende Verzögerungszeit ab. Gültigkeitsfenster von synchronen Eingabesignalen werden mit zwei Parametern beschrieben, der Vorhaltezeit vor der aktiven Flanke des Übernahmetaktes und der Gültigkeitsdauer. Die Beschreibung der Gültigkeitsfenster bezieht sich immer auf Signaländerungen an Schaltkreisanschlüssen. Der nachfolgende ucfEintrag definiert für ein Eingabesignal x ein zugesichertes Gültigkeitsfenster, das mindestens ts ≥ 1,2 ns vor der aktiven Taktflanke von T beginnt und mindestens ts + tn = 2 ns lang ist (ts – Vorhaltezeit; tn – Nachhaltezeit): NET "x" OFFSET = IN 1.2 ns VALID 2 ns BEFORE "T";
Der Entwickler sichert praktisch eine Vorhalte- und eine Nachhaltezeit in Bezug auf die aktive Flanke eines Eingabetaktes zu und das Entwurfssystem sucht eine Schaltung, die mit diesen Vorgaben sicher funktioniert. 5
Ein abgeleiteter Takt hat gegenüber dem Originaltakt eine bekannte Verzögerung und kann eine mit einem Taktteiler heruntergeteilte oder eine mit einem Phasenregelkreis vervielfachte Frequenz haben (siehe später Abschnitt 4.3.6).
118
2 Synthese und Logikoptimierung
Für Ausgabesignale lässt sich in ähnlicher Weise die maximal zulässige Verzögerung gegenüber der aktiven Taktflanke eines Eingabe- oder Ausgabetaktes beschreiben. Der nachfolgende ucf-Eintrag fordert, dass das Ausgabesignal y nicht später als 2 ns nach der aktiven Taktflanke von T den neuen gültigen Wert ausgeben soll: NET "y" OFFSET = OUT 2 ns AFTER "T";
Die Länge der Gültigkeitsfenster der Ausgabesignale oder die Vorhaltezeit vor der nächsten aktiven Taktflanke können leider nicht vorgegeben werden. Diese Information, die für die Zusicherung des Eingabegültigkeitsfensters für die nachfolgende Schaltung benötigt wird, lässt sich nur berechnen. Zur Unterstützung des Entwurfs von Schaltungen, die solche oder andere, nicht direkt vorgebbare Zeitbedingungen erfüllen müssen, gibt es so etwas Ähnliches wie Constraints. Das sind ucf-Anweisungen, mit denen sich auch Nachhaltezeiten, Zeitversätze zwischen Signalen und vieles mehr beschreiben lassen. Die beschriebenen Bedingungen werden aber nur kontrolliert, nicht zugesichert. Bei Verletzung werden Meldungen ausgegeben. Die Schaltungsnachbesserung überlässt das System in diesem Fall dem Entwickler, der dann Teile der Funktionsbeschreibung durch eine geeignete Strukturbeschreibung ersetzen und die Platzierung und Verdrahtung der zeitkritischen Schaltungsteile manuell vorgeben muss. Für die Strukturierung der Zeit-Constraints sollte Folgendes beachtet werden. Auf unnötig strenge Zeit-Constraints ist zu verzichten. Denn diese verlängern die Rechenzeit, die das Entwurfssystem für die Suche einer geeigneten Schaltung benötigt, erheblich und kosten unnötige zusätzliche Hardware. Bei hohen Taktfrequenzen muss unbedingt die Taktperiode in der ucf-Datei stehen. Sonst funktioniert die Schaltung am Ende garantiert nicht. Für alle anderen Anschlüsse sind nur dann Zeit-Constraints anzugeben, wenn diese in einer zeitkritischen Weise zu einem Anschlusstakt ausgerichtet sind. Asynchrone Ein- und Ausgabesignale sind z.B. zu keinem Anschlusstakt ausgerichtet, so dass ihnen keine sinnvollen Constraints zugeordnet werden können. Synchrone Signale mit übereinstimmenden Gültigkeitsfenstern sollten zu Gruppen zusammengefasst und die Constraints den Gruppen zugeordnet werden. Der Verzicht auf unnötig strenge Zeit-Constraints gilt auch für schaltungsinterne Signalpfade. Ein Constraint für die Taktperiode legt für alle TransferFunktionen zwischen Registern mit diesem Takt die maximale Verzögerungszeit fest. Es ist natürlich möglich, die Zielfunktion einer Schaltung so zu beschreiben, dass für zeitaufwändige Register-Transfer-Funktionen mehrere Takte zur Verfügung stehen. Wenn das beabsichtigt ist, sollten auch unbedingt die Zeit-Constraints für die betroffenen Signalpfade gelockert werden. Das ist möglich. Weiterführende Informationen sind in den Dokumentationen des Entwurfssystems zu finden [1].
2.1 Register-Transfer-Synthese
119
2.1.6 Entwurfsfehler und Fehlervermeidung Die Synthese wertet die Beschreibungsstruktur und weniger die beschriebene Funktion aus. Insbesondere bei weniger üblichen Beschreibungsstrukturen kann es Abweichungen zwischen dem beschriebenen Verhalten und der Funktion der synthetisierten Schaltung geben. Für den Nachweis und die Lokalisierung von Synthesefehlern ist es zu empfehlen, sowohl die Schaltungsbeschreibung vor der Synthese als auch die Beschreibung der synthetisierten Schaltung gründlich zu simulieren und die Ergebnisse zu vergleichen. Weitere Kontrollmöglichkeiten sind • • • •
eine Inspektion der VHDL-Beschreibung auf typische Beschreibungsfehler, eine Sichtung der Warnungen im Synthesereport (laufzeitkritische Schaltungsteile, verletzte Zeitbedingungen etc.), ein Überschlag, ob die Anzahl der von der Synthese laut Synthese-Report gefundenen Teilschaltungen stimmen könnte, und eine Sichtprüfung der von der Synthese generierten Schaltpläne.
Die Inspektion der VHDL-Beschreibung und die kritische Sichtung der Warnungen im Synthesereport sollte insbesondere die Anzahl der Fehler der nachfolgenden Typen mindern: • • • • • •
vergessene und unzulässige Signale in einer Weckliste, Verarbeitung asynchroner Eingabesignale (Abschnitt 1.5.1), asynchrone Initialisierung (Abschnitt 1.5.3), Quellen für Takt- und Latch-Freigabesignale, die nicht garantieren, dass ihre Ausgabesignale Glitch-frei sind (Abschnitt 1.3.1 und 2.1.4), ganz allgemein unübliche Beschreibungsstrukturen und fehlende oder überflüssige Constraints (Abschnitt 2.1.5).
Der Synthese-Report listet unter anderem die Anzahl der gefundenen Rechenwerke, Register, Latches, Blockspeicher etc. auf. Im Grunde gehört zu jedem Entwurf einer synthesefähigen Beschreibung eine Skizze der Register und Register-Transfer-Operationen, aus der die Anzahl der benötigten größeren Schaltungsbausteine ablesbar ist. Kleine Abweichungen können durch vernachlässigte Details und Optimierungen zustande kommen. Große Abweichungen – zusätzliche Latches, wenn keine vorgesehen sind, eine unplausible Anzahl von Rechenwerken, Blockspeichern etc. – deuten auf Entwurfsfehler hin, die es zu lokalisieren und zu beseitigen gilt. Die Sichtung der von der Synthese generierten Signalflusspläne dient hauptsächlich dazu zu verstehen, wie die Synthese arbeitet, und zur Lokalisierung von Synthesefehlern. Dabei ist es zu empfehlen, die Synthesebeschreibung in kleine Prozesse aufzuspalten und diese einzeln zu synthetisieren. Anderenfalls werden die manuell zu inspizierenden Signalflusspläne zu groß und zu unübersichtlich.
120
2 Synthese und Logikoptimierung
Mit den modernen programmierbaren Schaltkreisen ist auch ein PrototypTest kein Problem mehr. Die Schaltung wird in eine Konfigurationsdatei umgerechnet und in Sekundenschnelle in einen Schaltkreis programmiert (vgl. Abschnitt 4.6). Ein Prototyp-Test dient vor allem zur Aufdeckung von Fehlern, die erst in der Funktionsumgebung sichtbar werden. Für die Fehlerlokalisierung ist eine Simulation besser geeignet. Von den zahlreichen Fallstricken und Fehlersituationen, die beim Entwurf einer digitalen Schaltung auftreten können, sollen hier noch zwei behandelt werden. Kombinatorische Rückführungen Eine kombinatorische Rückführung ist eine Rückführung eines Ausgabesignals einer kombinatorischen Schaltung auf einen ihrer Eingänge. Schaltungen mit dieser Struktur sind so laufzeitkritisch, dass selbst in manuellen Entwürfen in der Regel davon abzuraten ist. Die einzigen Ausnahmen sind Bottom-UpEntwürfe von Speicherzellen und Oszillatoren. Abbildung 2.25 a zeigt eine kombinatorische Schaltung, bei der der Ausgang auf einen ihrer Eingänge rückgeführt ist. Für x1 = 0 bildet die Beispielschaltung ein RS-Flipflop nach (Abb. 2.25 b, siehe später Abschnitt 4.3.2). Ein RS-Flipflop hat ein Speicherverhalten. Der Ausgabewert kann mit einem kurzen Impuls auf dem Signal x2 auf »1« gesetzt und mit einem kurzen Impuls auf x3 auf »0« zurückgesetzt werden. Damit ist das Verhalten glitch-empfindlich. Wenn die Eingabewerte von x1 bis x3 zeitgleich von ungültig oder »1« nach »0« wechseln, lässt sich zwar vorhersagen, dass danach ein Wert gespeichert wird, aber nicht welcher. Das ist kein reproduzierbares Verhalten. Für x1 = 1 und x2 = x3 = 0 verhält sich die Beispielschaltung wie ein Ringinverter und erzeugt ein periodisches Rechtecksignal (Abb.2.25 c, siehe später Abschnitt 4.2.3). Die Schwingungsperiode hängt von den Signalverzögerungen ab. Wenn die Schaltung neu synthetisiert wird, kann sie sich ändern. Aber auch als gefertigte Schaltung ist die Frequenz eines Ringinverters für die meisten Anwendungen zu ungenau. a)
x1 x2 x3
b)
Gesamtschaltung G1 =1
G2 ≥1
G3 ≥1
y
x2 x3 x3 1 0 0
x1 = 0 G2 ≥1
G3 ≥1
c) y
y x2 ucksetzen 0 r¨ 1 setzen 0 speichern
x1 = 1, x2 = x3 = 0 G1 G2 G3
y
td y
1 0
2 · td
t
Abb. 2.25. a) Kombinatorische Rückführung als Ursache für ein nicht reproduzierbares Fehlverhalten b) Betriebsart RS-Flipflop c) Betriebsart Ringinverter
2.1 Register-Transfer-Synthese
121
Eine Schaltung, die in Abhängigkeit von ihren Eingabewerten wahlweise einen Zustand speichert, in einen unbestimmten Zustand übergeht oder mit einer nicht genau vorhersagbaren Frequenz schwingt, ist ein Albtraum für jeden Test und jede Inbetriebnahme. Unerwartete Latches durch fehlerhafte Auswahlanweisungen Kombinatorische Prozesse und Prozesse, die Latches beschreiben, ähneln einander so sehr, dass Latches oft wegen eines Beschreibungsfehlers in eine Schaltung eingebaut werden und dann möglicherweise auch ein nicht reproduzierbares Fehlverhalten zur Folge haben. Abbildung 2.26 a zeigt das am Beispiel einer Auswahlanweisung, die eigentlich durch eine kombinatorische Schaltung nachgebildet werden soll. Es sei bekannt, dass nur die Auswahlwerte »00«, »01« und »10« im fehlerfreien Fall auftreten können. Da es für die übrigen Auswahlwerte egal ist, welcher Wert zugewiesen wird, steht in der Entwurfsbeschreibung im Sonst-Fall die Null-Anweisung für »keine Zuweisung«. In einem imperativen Programm ist das die Lösung mit dem geringsten Rechenaufwand. Die Synthese übersetzt jedoch »ändere den Wert nicht« in ein Latch zur Speicherung des Wertes und eine Logikschaltung, die den Freigabeeingang des Latches nur für die Auswahlwerte »00«, »01« und »10«, d.h. für die NAND-Verknüpfung der beiden Bits des Auswahlwertes, aktiviert (Abb. 2.26 b). Im Beispiel sollte das zusätzliche Latch außer unnützem Schaltungsaufwand und einer Zusatzverzögerung keine Funktionsbeeinträchtigung zur Folge haben, aber das muss beim Einbau eines überflüssigen Latches nicht immer so sein. Um zu vermeiden, dass die Synthese das Latch einbaut, muss auch im Others-Zweig ein Wert zugewiesen werden. Abbildung 2.26 c zeigt eine vereinfachte Schaltung, die auch für den Auswahlwert »11« das Eingabesignal x3 an den Ausgang weiterreicht. signal x1, x2, x3, y: std_logic_vector(3 downto 0); signal s: std_logic_vector(1 downto 0); 4 x1 00 ... 4 01 x 2 process(s, x1, x2, x3) 4 x3 10 begin 2 case s is s b) when "00" => y <= x1; when "01" => y <= x2; c) when "10" => y <= x3; when others => null; –- Korrektur: when others => y <= x3 statt null end case; a) end process; ⇒Web-Projekt:
4
x & x1 x2 x3 s
L
y
E 4 4 4
00 01 sonst
4
y
2
P2.1/MuxCaseLatch.vhdl
Abb. 2.26. Unbeabsichtigtes Latch durch eine fehlende Zuweisung in einer Auswahlanweisung a) Beschreibung mit Korrekturhinweis b) Syntheseergebnis mit Latch c) Syntheseergebnis der korrigierten Beschreibung
122
2 Synthese und Logikoptimierung
Bei Entwurfsfehlern mit einem absonderlichen oder nicht reproduzierbaren Fehlverhalten ist das Schlimmste bereits gebannt, wenn der Entwickler die Ursachen und Wirkungen solcher Fehler kennt und weiß, wo er bei der Fehlersuche und der Fehlervermeidung ansetzen muss. Anderenfalls kann der Zeitaufwand für die Inbetriebnahme und die Fehlersuche gigantische Ausmaße annehmen. 2.1.7 Zusammenfassung und Übungsaufgaben Die Synthese sucht für ein imperativ beschriebenes Simulationsmodell eine Schaltung mit derselben logischen Funktion. Das ist ein schlecht gestelltes Problem, das nur bei einer Beschränkung auf bewährte Beschreibungsschablonen lösbar ist. Der Berechnungsfluss zur Beschreibung einer kombinatorischen Schaltung kann logische Operatoren, Vergleichsoperatoren, arithmetische Operatoren und Fallunterscheidungen enthalten und wird in einen Datenfluss aus Logikgattern, Rechenwerken, Vergleicherschaltungen und Multiplexern übersetzt. Der Beschreibungsrahmen ist entweder ein kombinatorischer Prozess mit allen Eingabesignalen in der Weckliste oder ein Abtastprozess, der nur den Takt und optional ein Initialisierungssignal in der Weckliste enthält. Ein kombinatorischer Prozess beschreibt eine reine kombinatorische Schaltung und ein Abtastprozess eine kombinatorische Schaltung mit Abtastregistern entweder an allen Eingängen oder an allen Ausgängen. Die Synthese kennt außer Registern, die ihre Eingabedaten mit der Taktflanke übernehmen, auch taktzustandsgesteuerte Speicherelemente. Das sind die Latches. Der Ersatz eines Registers durch ein Latch verringert den Schaltungsaufwand, verkompliziert aber den Entwurf. In einem synthesebasierten Entwurf ist davon abzuraten. Eine erfolgreiche Schaltungssynthese benötigt außer der Beschreibung der Zielfunktion Constraints für das Anschlussverhalten. Das sind Vorgaben oder Richtlinien zur Steuerung der automatisierten Entwurfsschritte. Die wichtigsten Constraints für die Programmierung von Logikschaltkreisen sind die für die Zuordnung der Schaltungsanschlüsse zu Schaltkreisanschlüssen und die für die Beschreibung der Signalform der Taktsignale. Weitere Constraints, z.B. für das elektrische Anschlussverhalten, für die Gültigkeitsfenster der Anschlusssignale etc. bis hin zu Platzierungs- und Verdrahtungsvorgaben, können erforderlich sein. Beim synthesegestützten Entwurf digitaler Schaltungen gibt es einige gemeine Fallstricke, die bekannt sein sollten, um die entsprechenden Fehler zu finden bzw. zu vermeiden.
2.1 Register-Transfer-Synthese
123
Aufgabe 2.1 Gegeben ist die folgende Synthesebeschreibung aus drei Abtastprozessen: signal K, L, M, N, P, Q: std_logic_vector (7 downto 0); signal c: std_logic_vector (1 downto 0); ... process(c(0)) begin if rising_edge(c(0)) then L <= K; end if; end process; process(c(1))
begin if rising_edge(c(1)) then N <= M; M <= L; end if; end process; process(c(1)) begin if falling_edge(c(1)) then P <= L; Q <= P; end if; end process; ⇒WEB-Projekt: P2.1/AfgSynthRegExtr.vhdl
a) Extrahieren Sie für alle mit diesen Prozessen beschriebenen Register die Bezeichner der Anschlusssignale, die Anzahl der Datenbits und die aktive Taktflanke. b) Zeichnen Sie den Signalflussplan. Aufgabe 2.2 Beschreiben Sie die kombinatorische Schaltung in Abb. 2.27 in einer synthesefähigen Form mit einem kombinatorischen Prozess. a b
4
& 4
s0
≥1
4 4 4 4
0 1 0 1
4 4
0 1
Signaltypen:
4
+ 4
y
s1
4
STD LOGIC
tUnsigned(3 downto 0)
Abb. 2.27. Schaltung zu Aufgabe 2.2
Aufgabe 2.3 Abbildung 2.28 zeigt die Schaltung eines Vorwärts-Rückwärts-Zählers. Beschreiben Sie die Schaltung mit einem synthesefähigen VHDL-Abtastprozess. Die bitorientierten Signale seien vom Typ std_logic und die 8-Bit-Signale vom Typ »tSigned(7 downto 0)«. Der bei I = 1 zuzuweisende Wert sei »alles null«.
124
2 Synthese und Logikoptimierung +1 −1
8
0 1
8
8
0 1
8
E
I
R
8
x I
y
T¯
Abb. 2.28. Schaltung zu Aufgabe 2.3
Aufgabe 2.4 Beschreiben Sie den Schaltungsausschnitt in Abb. 2.29 in einer synthesefähigen Form mit möglichst wenigen Prozessen. Alle Signale seien vom Typ std_logic. Der Wert, mit dem y initialisiert wird, sei »0«. Reg1 x’
x
G ==
T
Reg2 I
y
x I
Abb. 2.29. Schaltung zu Aufgabe 2.4
Aufgabe 2.5 Beschreiben Sie den Automaten in Abb. 2.30 mit der in Tabellenform gegebenen Übergangs- und Ausgabefunktion in einer synthesefähigen kompakten Form. Der mit I = 1 einstellbare Anfangswert sei »00«. Die Bit-Signale I und T seien vom Typ std_logic und alle 2-Bit-Vektoren vom Typ »std_logic_vector(1 downto 0)«.
y
fa (x, s) x
2
fs (x, s)
s+ 2 I
d) 2 x I
s T
s+ = fs (x, s) y = fa (x, s)
s x: 00 01 10 11
00 01 10 11 00
01 00 01 10 11
10 11 00 01 10
00 01 10 11 00
01 00 01 10 11
10 11 00 01 10
Abb. 2.30. Schaltung und Wertetabellen zu Aufgabe 2.5
Aufgabe 2.6 Skizzieren Sie den Signalflussplan, den der nachfolgende Abtastprozess beschreibt.
2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra
signal x, tmp, acc, y: tUnsigned (3 downto 0); signal op: std_logic_vector (1 downto 0); signal T: std_logic; ... process(T) begin if rising_edge(T) then case op is
125
when "00" => acc <= x; when "01" => acc<=acc + tmp; when "10" => acc<=acc - tmp; when others => null; end case; tmp <= x; end if; end process; y <= acc; ⇒WEB-Projekt: P2.1/AfgSynthALU2.vhdl
2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra Definition 2.1 (Logische Funktion) Eine n-stellige logische Funktion ist eine Abbildung eines n-Bit-Vektors aus freien binären Variablen auf eine abhängige binäre Variable: y = f (xn−1 , xn−2 , . . . , x0 ) mit y, xi ∈ {0, 1} (y – abhängige Variable; xi – freie Variablen). Jede logische Funktion kann durch unbegrenzt viele unterschiedliche logische Ausdrücke und Schaltungen nachgebildet werden. Die von der RegisterTransfer-Synthese extrahierten logischen Ausdrücke werden anschließend unter Beibehaltung der logischen Funktion umgeformt und vereinfacht. In Abschnitt 1.2.3 wurden bereits zwei grundlegende Vereinfachungsverfahren vorgestellt: • •
die Konstantenelimination und die Verschmelzung.
Die Konstantenelimination ersetzt alle Operationen mit konstanten Operanden durch die Operationsergebnisse. Das reduziert die Stelligkeit der Funktion und die Anzahl der Operationen in den Ausdrücken. Die Verschmelzung fasst gleiche Teilberechnungen, die mehrfach vorkommen, zusammen und reduziert so gleichfalls die Anzahl der Operationen. Die Schaltalgebra hält zahlreiche weitere Optimierungs- und Umformungsmöglichkeiten bereit. 2.2.1 Umformungs- und Vereinfachungsregeln Die Vereinfachung logischer Ausdrücke erfolgt regelbasiert. Tabelle 2.1 zeigt die wichtigsten Umformungsregeln. Zwei Negationen heben sich gegenseitig auf. Die Eliminationsgesetze beschreiben Ausdrücke, die durch Konstanten
126
2 Synthese und Logikoptimierung
ersetzt, die Absorptionsgesetze Ausdrücke, die durch einfachere Ausdrücke ersetzt werden können. Die übrigen Regeln werden genutzt, um Vereinfachungsmöglichkeiten zu schaffen oder um auf eine Struktur zuzuarbeiten, die sich gut mit vorentworfenen Gattern nachbilden lässt. Tabelle 2.1. Umformungsregeln für logische Ausdrücke Nr.
Umformungsregel
Bezeichnung
1
¯=x x
2 3 4 5
x∨1=1 x∨x ¯=1 x∧0=0 x∧x ¯=0
6 7
x1 ∨ (x1 ∧ x2 ) = x1 x1 ∧ (x1 ∨ x2 ) = x1
8 9
x1 ∧ x2 = x ¯1 ∨ x ¯2 x1 ∨ x2 = x ¯1 ∧ x ¯2
1. De Morgan’sche Regel 2. De Morgan’sche Regel
10 11
x1 ∧ x2 = x2 ∧ x1 x1 ∨ x2 = x2 ∨ x1
1. Kommutativgesetz 2. Kommutativgesetz
12 13
(x1 ∨ x2 ) ∨ x3 = x1 ∨ (x2 ∨ x3 ) (x1 ∧ x2 ) ∧ x3 = x1 ∧ (x2 ∧ x3 )
1. Assoziativgesetz 2. Assoziativgesetz
14 15
x1 ∧ (x2 ∨ x3 ) = (x1 ∧ x2 ) ∨ (x1 ∧ x3 ) x1 ∨ (x2 ∧ x3 ) = (x1 ∨ x2 ) ∧ (x1 ∨ x3 )
1. Distributivgesetz 2. Distributivgesetz
doppelte Negation 1. 2. 3. 4.
Eliminationsgesetz Eliminationsgesetz Eliminationsgesetz Eliminationsgesetz
1. Absorptionsgesetz 2. Absorptionsgesetz
Der Beweis der Allgemeingültigkeit der Umformungsregeln aus Tabelle 2.1 erfolgt durch Aufstellen und Vergleich der Wertetabellen. Tabelle 2.2 zeigt das am Beispiel der De Morgan’schen Regeln6 . Tabelle 2.2. Überprüfung der De Morgan’schen Regeln mit einer Wertetabelle x1
x2
x1 ∧ x2
x ¯1 ∨ x ¯2
x1 ∨ x2
x ¯1 ∧ x ¯2
0
0
1
1
1
1
0
1
1
1
0
0
1
0
1
1
0
0
1
1
0
0
0
0
In Abb. 2.31 ist ein NAND-Gatter mit einem nachgeschalteten Inverter dargestellt. Der logische Ausdruck für diese Schaltung ist eine doppelt negierte 6
benannt nach Augustus De Morgan (1806 - 1871)
2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra
127
UND-Verknüpfung. Mehrfache Negationen im Signalfluss heben sich paarweise auf. Übrig bleibt ein UND-Gatter. x1 x2 x3
&
z
x1 x2 x3
y
z = x1 ∧ x2 ∧ x3 y = z¯
&
y
y = x1 ∧ x2 ∧ x3 = x1 ∧ x2 ∧ x3
Abb. 2.31. Schaltungsumformung durch doppelte Negation
Auf den logischen Ausdruck der Schaltung in Abb. 2.32 a kann zuerst das vierte und anschließend das dritte Eliminationsgesetz angewendet werden. Im Endeffekt ergibt sich, dass die Ausgabe unabhängig von der Eingabe immer »1« ist. Das lässt sich auch über die Aufstellung der Wertetabelle zeigen. Abbildung 2.32 b zeigt ein Anwendungsbeispiel für das zweite Absorptionsgesetz. Dazu wird der Ausdruck zuerst mit dem zweiten Assoziativgesetz in die erforderliche Form gebracht.
x1 x2
1
y
y = (x1 ∧ x ¯ ) ∧ x2 = 0 ∧ x2 = 0 = 1 | {z }1 | {z }
a)
0
x1
x3
0
&
x2
b)
y
&
&
y
≥1
x1 x2
&
y
gegebene Funktion: y = (x1 ∧ x2 ) ∧ (x2 ∨ x3 ) Assoziativgesetz: y = x1 ∧ (x2 ∧ (x2 ∨ x3 )) Absorptionsgesetz: y = x1 ∧ x2
Abb. 2.32. Schaltungsvereinfachung a) mit Hilfe der Eliminitionsgesetze b) unter Anwendung des zweiten Absorptionsgesetzes
Die De Morgan’schen Regeln haben eine besondere Bedeutung in der Digitaltechnik. Logische Ausdrücke bilden eine logische Funktion mit UND- und ODER-Verknüpfungen nach. Bei der Technologieabbildung erfolgt die Nachbildung oft durch Gatter mit invertierter Ausgabe, im einfachsten Fall durch NAND- und NOR-Gatter. Die UND-ODER-Form wird wie folgt in die NANDNAND-Form umgeformt (Abb. 2.33 a): x1 x2 ∨ x3 x4 = x1 x2 ∨ x3 x4 = x1 x2 x3 x4
128
2 Synthese und Logikoptimierung
Die Umformungsregel für die ODER-UND-Form in die NOR-NOR-Form lautet (Abb. 2.33 b) (x1 ∨ x2 ) (x3 ∨ x4 ) = (x1 ∨ x2 ) (x3 ∨ x4 ) = (x1 ∨ x2 ) ∨ (x3 ∨ x4 )
a)
UND-ODER-Struktur x1 & x2 ≥1 y x3 & x4
NAND-NAND-Struktur x1 & x2 & y x3 & x4
b)
ODER-UND-Struktur x1 ≥1 x2 y & x3 ≥1 x4
NOR-NOR-Struktur x1 ≥1 x2 ≥1 y x3 ≥1 x4
Abb. 2.33. Anwendung der De Morgan’schen Regeln
2.2.2 Optimierungsziele Die wichtigsten Optimierungsziele für Logikumformungen sind geringer Schaltungsaufwand und hohe Geschwindigkeit. Auch für die Geschwindigkeitsoptimierung gibt es Regeln. Eine davon ist die Nachbildung von Ausdrücken assoziativer Operatoren durch Bäume statt durch Ketten. Assoziativ bedeutet, dass die Ausführungsreihenfolge beliebig gewählt werden darf. Beispiel sei der Ausdruck y = x6 ◦ x5 ◦ x4 ◦ x3 ◦ x2 ◦ x1 ◦ x0 (◦ – beliebige assoziative Operation, z.B. UND, ODER, EXOR, Addition oder Multiplikation). Bei einer Ausführungsreihenfolge von rechts nach links y = x6 ◦ (x5 ◦ (x4 ◦ (x3 ◦ (x2 ◦) (x1 ◦ x0 )))) wird der Ausdruck in die Kettenstruktur in Abb. 2.34 a übersetzt. Wenn immer paarweise benachbarte Eingabewerte oder Teilergebnisse zu neuen Teilergebnissen zusammengefasst werden y = (x6 ◦ (x5 ◦ x4 )) ◦ ((x3 ◦ x2 ) ◦ (x1 ◦ x0 )) extrahiert die Synthese eine Schaltung mit einer Baumstruktur (Abb. 2.34 b). In einer Kette nimmt die Verzögerung linear mit der Anzahl der Operationen zu und in einem Baum nur logarithmisch. In der O-Notation spricht man von einer Laufzeit der Ordnung O (n) bzw. O (log (n)) [51].
2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra tdOp x0 x1 x2 x3 x4 x5 x6
tdOp tdOp tdOp tdOp tdOp tdOp x0 x1
y x2
x3
x4
x5
x6
tdKette = n · tdOp
a)
Pfad der l¨ angsten Verz¨ogerung
b)
tdOp
129
tdOp y
tdBaum ≥ log2 (n) · tdOp
Abb. 2.34. Nachbildung eines Ausdrucks aus assoziativen Operationen a) Kette b) Baum
Die Schaltung mit dem geringsten Aufwand ist nicht unbedingt die schnellste und umgekehrt (Abb. 2.35 a). Ziel ist in der Regel eine möglichst kleine Schaltung, die alle Zeitvorgaben erfüllt. Dazu sucht die Synthese zuerst nach einer möglichst kleinen Schaltung, kontrolliert die Einhaltung der Zeitvorgaben und versucht dann iterativ mit zusätzlichem Schaltungsaufwand die Laufzeiten entlang der Pfade zu verringern, die die Zeitbedingungen verletzen. Abbildung 2.35 b zeigt eine aufwandsminimierte Schaltung zur Nachbildung der Ausdrücke x0 ⊕x1 , x0 ⊕x1 ⊕x2 und x0 ⊕x1 ⊕x2 ⊕x3 . Der längste Pfad verläuft von x0 nach y2 durch drei EXOR-Gatter. Falls die Verzögerung entlang dieses Pfades zu groß ist, würde die Synthese die Kette zur Berechnung von y2 in einen Baum umformen und dafür ein Gatter mehr verbrauchen (Abb. 2.35 c). max
minimale Verz¨ogerung andere optimierte L¨osungen minimaler Aufwand
Aufwand
min a)
min
Signalverz¨ogerung
l¨ angster Pfad
x0 x1
y0
=1 =1
x2 b)
c)
=1
x3 x0 x1 x2
max
x3
y1
=1 =1
y2
=1
y0 y1
=1
y2
Abb. 2.35. a) Beziehung zwischen der Signalverzögerung und dem Schaltungsaufwand b) aufwandsoptimierte Schaltung c) Verringerung der Signalverzögerung mit einem zusätzlichen Gatter
2.2.3 Schaltungsvereinfachung mit Konjunktionsmengen Die beispielhaften Vereinfachungen in Abschnitt 2.2.1 basierten alle auf der Methode des scharfen Blicks. In diesem Abschnitt wird ein systematisches Ver-
130
2 Synthese und Logikoptimierung
fahren eingeführt, das logische Ausdrücke als Konjunktionsmengen modelliert und diese systematisch minimiert. Eine Konjunktion ist ein logischer Ausdruck, der direkte und negierte Eingabevariablen UND-verknüpft, z.B. x3 ∧ x ¯ 2 ∧ x1 ∧ x ¯ 0 = x3 x ¯ 2 x1 x ¯0 (K1010 ) {z } | verkürzte Schreibweisen
Ein Minterm ist eine vollständige Konjunktion, die alle Eingabevariablen entweder in direkter oder in negierter Form enthält. Satz 2.1 (Überführung in Konjunktionsmengen) Jede logische Funktion lässt sich durch eine ODER-Verknüpfung einer Menge von Mintermen oder die negierte ODER-Verknüpfung der Komplementmenge zu dieser Menge darstellen. Die Beweisidee hierzu lautet, dass sich jede logische Funktion durch eine Wertetabelle darstellen lässt. Jeder Zeile einer Wertetabelle ist ein Minterm zugeordnet, der genau für den Eingabewert dieser Zeile »1« und sonst »0« ist (vgl. Abb. 2.36). x2 x1 x0
Minterm
y
x2 x1 x0
Minterm
y
0
0
0
x ¯2 x ¯1 x ¯0 (K000 )
0
1
0
0
x2 x ¯1 x ¯0 (K100 )
1
0
0
1
x ¯2 x ¯1 x0 (K001 )
1
1
0
1
x2 x ¯1 x0 (K101 )
1
0
1
0
x ¯ 2 x1 x ¯0 (K010 )
0
1
1
0
x2 x1 x ¯0 (K110 )
1
0
1
1
x ¯2 x1 x0 (K011 )
0
1
1
1
x2 x1 x0 (K111 )
0
Abb. 2.36. Wertetabelle mit den den Zeilen zugeordneten Mintermen
Es gibt zwei Möglichkeiten, einen Ausdruck aus einer Menge von Mintermen zu konstruieren: • •
Entwicklung nach den Einsen: ODER-Verknüpfung aller Minterme, denen der Funktionswert »1« zugeordnet ist. Entwicklung nach den Nullen: ODER-Verknüpfung der Minterme, denen der Funktionswert »0« zugeordnet ist und abschließende Negation.
Aus Abb. 2.36 lassen sich z.B. die folgenden Konjunktionsmengen und Ausdrücke ablesen: •
Entwicklung nach den Einsen: {K001 , K100 , K101 , K110 } ⇒ y = x ¯2 x ¯ 1 x0 ∨ x2 x ¯1 x ¯ 0 ∨ x2 x ¯ 1 x0 ∨ x2 x1 x ¯0
•
Entwicklung nach den Nullen:
2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra
131
{K000 , K010 , K011 , K111 } ⇒ y = x ¯2 x ¯1 x ¯0 ∨ x ¯ 2 x1 x ¯0 ∨ x ¯2 x1 x0 ∨ x2 x1 x0 Ein auf diese Weise nach den Einsen entwickelter logischer Ausdruck wird als disjunktive Normalform7 (DNF) bezeichnet. Der nach den Nullen entwickelte logische Ausdruck kann durch zweifache Anwendung der De Morgan’schen Regeln in eine funktionsgleiche UND-Verknüpfung von ODER-Termen (Disjunktionen) umgeformt werden: y = (x2 ∨ x1 ∨ x0 ) ∧ (x2 ∨ x ¯1 ∨ x0 ) ∧ (x2 ∨ x ¯1 ∨ x ¯0 ) ∧ (¯ x2 ∨ x ¯1 ∨ x ¯0 ) Diese Darstellungsform wird als konjunktive Normalform (KNF) bezeichnet. Vereinfachung Satz 2.2 (Konjunktionszusammenfassung) Zwei Konjunktionen, die sich nur in der Invertierung einer Variablen unterscheiden, können zu einer Konjunktion mit einer Variablen weniger zusammengefasst werden. Zur Vereinfachung werden zuerst das erste Distributivgesetz und anschließend das zweite Eliminationsgesetz aus Tabelle 2.1 angewendet. Die erste Zeile der nachfolgenden Tabelle zeigt zwei ODER-verknüpfte Konjunktionen, die sich nur in der Invertierung von x0 unterscheiden. In der zweiten Zeile sind die übereinstimmenden Teile der beiden Konjunktionen ausgeklammert. Der Ausdruck in der Klammer ist »1« und die UND-Verknüpfung eines Ausdrucks mit »1« ist der Ausdruck selbst. Die dritte Zeile zeigt die resultierende ODER-Verknüpfung, in der die beiden Konjunktionen zu einer Konjunktion mit einer Variablen weniger zusammengefasst sind. Die fehlende Variable erhält in der Darstellung der Konjunktionsmenge das Indexkennzeichen »-« (don’t care, Wert beliebig). Zeile
ODER-Verknüpfung
Konjunktionsmenge
1 2 3
. . . ∨ x2 x ¯1 x ¯ 0 ∨ x2 x ¯ 1 x0 ∨ . . . . . . ∨ x2 x ¯1 (¯ x0 ∨ x0 ) ∨ . . . . . . ∨ x2 x ¯1 ∨ . . .
{. . . , K100 , K101 , . . .} {. . . , K10- , . . .}
2.2.4 KV-Diagramm Das KV-Diagramm, benannt nach Karnaugh und Veitch, ist eine Wertetabelle, in der sich die Konjunktionen der benachbarten Felder genau in einer Negation unterscheiden. Es wird zur Schaltungsminimierung per Hand eingesetzt. Abbildung 2.37 zeigt den Tabellenaufbau mit den Konjunktionen, die den Feldern zugeordnet sind, und den Nummern der Eingabevariablen an den Kanten, die im Nachbarfeld invertiert ist. 7
Eine Normalform ist eine eindeutige Beschreibung einer logischen Funktion. Die Normalformen – dazu gehören auch die Wertetabelle und die Menge der Minterme, denen »1« bzw. »0« zugeordnet ist – dienen dazu, logische Funktionen auf Gleichheit zu testen.
132
2 Synthese und Logikoptimierung x2
x0
3
K
3
K
3
K
3
K
1
0000 2 0 0001 2 1 0011 2 0 0010 2 1
K K K K
1
0100 3 0 0101 3 1 0111 3 0 0110 3 1
K K K K
1
1100 2 0 1101 2 1 1111 2 0 1110 2 1
K K K K
x0 = 0
1
1000 3 0 1001 3 1 1011 3 0 1010 3 1
x3 = 0 x1
x0 = 1 x3 = 1 x2 = 1 x1 = 0
x2 = 0
x1 = 1
x3
Abb. 2.37. Aufbau eines KV-Diagramms
Benachbarte Konjunktionen können zu Zweierblöcken mit einer Don’tCare-Stelle, benachbarte Zweierblöcke weiter zu einem Viererblock mit zwei Don’t-Care-Stellen und benachbarte Viererblöcke weiter zu einem Achterblock mit drei Don’t-Care-Stellen zusammengefasst werden (Abb. 2.38). x2 K000x0
3 3 3 3
K001-
1
1
1
0
0
0
K-101
K0000 2 K0100 3 K1100 2 K1000
3
K1001
3
K1011
3
K1010
3
K K K
0001 2 1 0011 2 0 0010 2 1
K K K
K-11-
0101 3 1 0111 3 0 0110 3 1
K K K
0
1101 2 1 1111 2 0 1110 2 1
x3
K10--
1
0 1
x1
- don’t care Felder mit y = 0 Felder mit y = 1
Abb. 2.38. Blockbildung mit KV-Diagrammen
Zum Ablesen eines minimierten Ausdrucks werden zuerst entweder die Einsen oder die Nullen mit Rechtecken der Kantenlänge 1, 2 oder 4 abgedeckt. Als Zweites werden die Konjunktionen der Rechtecke abgelesen und daraus der gesuchte Ausdruck gebildet. In Abb. 2.38 sind alle Felder mit y = 0 mit dunklen und alle Felder mit y = 1 mit hellen Rechtecken abgedeckt. Die Abdeckung der Einsen erfordert einen Zweier- und zwei Viererblöcke. Die zugehörigen Konjunktionen haben eine bzw. zwei Don’t-Care-Stellen. Der minimierte logische Ausdruck besteht aus drei Konjunktionen, einer mit drei und zwei mit zwei freien Variablen: {K000- , K-11- , K10-- } ⇒ y = x ¯3 x ¯2 x ¯ 1 ∨ x2 x1 ∨ x3 x ¯2 Für die Abdeckung der Nullen genügen im Beispiel zwei Rechtecke, so dass sich ein kleinerer Ausdruck ergibt, der jedoch zusätzlich negiert werden muss: ¯3 x ¯ 2 x1 ∨ x2 x ¯1 {K001- , K-10- } ⇒ y = x
2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra
133
Praktische Arbeit mit KV-Diagrammen Für die praktische Arbeit ist das KV-Diagramm einer vierstelligen logischen Funktion eine Tabelle mit vier Zeilen und vier Spalten. Am Rand ist gekennzeichnet, in welchen Spalten und Zeilen die Werte der Eingabevariablen »1« sind. Diese Tabelle ist zuerst mit den Funktionswerten zu füllen. Das ist der Teil der Arbeit, bei dem die meisten Fehler entstehen. Dann ist zu entscheiden, ob nach den Einsen oder Nullen entwickelt wird. Nur die Felder eines der beiden Logikwerte werden mit Rechtecken abgedeckt. Zur Bestimmung der den Rechtecken zugeordneten Konjunktion wird für jede Eingabevariable nachgeschaut, ob sie für die Auswahl eines Feldes des Rechtecks »0« sein muss, »1« sein muss oder ihr Wert ohne Bedeutung ist. Die Rechtecke, mit denen die Felder mit dem ausgewählten Logikwert abgedeckt werden, dürfen sich überlagern. Der Beweisgedanke hierfür ist, dass die Konjunktionen ODER-verknüpft werden. Für die Eingaben, in deren Feldern sich zwei Rechtecke überlagern, sind die Konjunktionen beider Rechtecke gleichzeitig »1«. Eine ODERVerknüpfung ist »1«, wenn eine oder mehrere Konjunktionen »1« sind. Mit einer Überlagerung der Rechtecke lassen sich die Ausdrücke vieler Funktionen weiter vereinfachen als ohne. Die Blockbildung kann zirkular über den Rand erfolgen. In Abb. 2.39 a sind z.B. die Nullen an den vier Ecken zu einem Viererblock zusammengefasst. x2
a) c a
x0
b a b
a
0 1 0 0 c a
b)
c
0
1
1
0 d
1
1
0
e
1
0 c
x2
a
a
x3
0
a
0 0
b
0
a b
x1
a: b: c: d: e:
x ¯2 x ¯0 x ¯2 x1 x ¯3 x ¯0 x3 x ¯2 x3 x1 x0
x0
a
1 0 0
0 b c
1
1 a
d
0
1b 0
1
0
0
1d 0
0
x3
x1
a: b: c: d:
x ¯3 x ¯1 x0 x2 x ¯1 x0 x ¯3 x2 x0 x3 x2 x ¯0
y=x ¯2 x ¯0 ∨ x ¯2 x1 ∨ x ¯3 x ¯0 ∨ x3 x ¯2 ∨ x3 x1 x0 y = x ¯3 x ¯1 x0 ∨ x2 x ¯1 x0 ∨ x ¯3 x2 x0 ∨ x3 x2 x ¯0 Abb. 2.39. Beispielminimierungen a) Entwicklung nach den Nullen b) Entwicklung nach den Einsen
KV-Diagramme sind auch dafür geeignet, Ausdrücke für unvollständig spezifizierte Funktionen zu optimieren. Unvollständig spezifiziert bedeutet, dass die Ausgabewerte nur für einen Teil der Eingaben von Bedeutung sind. Die übrigen Eingabemöglichkeiten können
134
2 Synthese und Logikoptimierung
entweder nicht auftreten, oder im Fall ihres Auftretens wird der Funktionswert nicht ausgewertet. Im KV-Diagramm werden die Felder für nicht spezifizierte Ausgabewerte mit dem Pseudo-Signalwert »-« (don’t care) belegt. Bei der Abdeckung mit Rechtecken ist es egal, ob sie mit abgedeckt werden oder nicht. Auf diese Weise lassen sich oft größere Rechtecke über das Diagramm legen, die zu einfacheren Ausdrücken führen (Abb. 2.40). x2 d
1 1
x0
1
d
0
e
0 1
a
b
1 0
c
-
1
-
0
-
1c
b
x1
0
-
a: x ¯1 x ¯0 b: x2 x1 x0 c: x3 x1 x0 d: x ¯3 x ¯2 x ¯0 e: x ¯3 x ¯2 x ¯1 y=x ¯1 x ¯0 ∨ x2 x1 x0 ∨ x3 x1 x0 ∨¯ x3 x ¯2 x ¯0 ∨ x ¯3 x ¯2 x ¯1
x3
Abb. 2.40. Ausdrucksminimierung für eine unvollständig spezifizierte Funktion
Die Schaltungsoptimierung mit KV-Diagrammen lässt sich auch auf dreistellige Funktionen übertragen. Dazu wird das Diagramm einer vierstelligen Funktion halbiert (Abb. 2.41). x2
x1
x0
1
2
K000
1
K010
2
K110
1
K100
2
2
K001
1
K011
2
K111
1
K101
2
0
0
0
0
x2
1
K000
2
K100
K001
2
K101
K011
2
K111
K010
2
K110
0
x0
0
1
1
0
1
0
x1
1
Abb. 2.41. KV-Diagramme für dreistellige Funktionen
Die Erweiterung auf mehr als vierstellige Funktionen ist nur bedingt möglich. Denn in einer ebenen Tabelle hat jedes Feld nur vier Nachbarn. Dadurch können neben jeder Konjunktion nur vier Konjunktionen angeordnet werden, die sich genau in einer Negation unterscheiden. Bei einer sechsstelligen Funktion könnte man den fünften und sechsten Nachbarn darüber und darunter anordnen. Das führt zu einer würfelförmigen Tabelle, in der die Einsen oder Nullen mit Quadern der Kantenlänge 1, 2 oder 4 abzudecken sind. Praktisch arbeitet man dann mit vier ebenen KV-Diagrammen, was recht mühsam und fehleranfällig ist (Abb. 2.42).
2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra x4
x4 = 0
x5
a
0 0
x0 x1
b
a
c
1
1
0
1
1
0
1
0
0
0
1
0
0
1 d
x2
0 0
x0 x1 d
b
x4 = 1
1
1 a
0
0 a
1
1
0
1
1
0 0
1
1
0
0
1
0
0
0
0
0
0
1
0
0
1
0
0
0
0
0
1
0
0
0 0 x3 x0 a: x ¯5 x2 x ¯1 1 0 x1 b b: x ¯4 x ¯3 x ¯2 x1 1 0 c: x5 x3 x ¯1 x2 d: x3 x ¯2 x1 x ¯0 y=x ¯5 x2 x ¯1 ∨ x ¯4 x ¯3 x ¯2 x1 ∨ x5 x3 x ¯1 ∨ x3 x ¯2 x1 x ¯0
d
1 c
c
d
1
1
1
0
0
1
1
0
0
0
0
0
0
1
0
0
0
d
x3
x2
d
x5 = 0
1
1 0
135
x5 = 1
1
x3
Abb. 2.42. KV-Diagramme für sechsstellige Funktionen
Schaltungen mit mehreren Ausgängen Für eine Schaltung mit mehreren Ausgängen muss für jeden Ausgang ein eigenes KV-Diagramm aufgestellt werden. Auf gleiche Produktterme kann die Regel für die Verschmelzung angewendet werden, d.h., sie brauchen nur einmal gebildet und können mehrfach verwendet werden. Abbildung 2.43 a zeigt die Funktionsbeschreibung eines 7-Segment-Decoders zur Darstellung der Ziffern »0« bis »9. Abbildung 2.43 b zeigt die KV-Diagramme der einzelnen Steuersignale. Die durch Rechtecke abgedeckten Produktterme sind mit den kleinen Buchstaben a bis f durchnummeriert und decken die Nullen ab. Ein Teil der Produktterme ist mehrfach vorhanden, z.B. der Produktterm a in den KV-Diagrammen für die Ansteuersignale y0 , y3 und y5 (Abb. 2.43 c). Abbildung 2.43 c zeigt eine VHDL-Beschreibung mit den minimierten Ausdrücken. In einem Prozess mit dem Eingabevektor in der Weckliste werden zuerst die Produktterme a bis f berechnet und in gleichnamigen Variablen gespeichert. Die daraus gebildeten ODER-Verknüpfungen werden, da in den KV-Diagrammen die Nullen abgedeckt wurden, invertiert und den einzelnen Bits des Ausgabesignals zugewiesen. Die fehlenden Zuweisungen an die Variablen f bis j und an die Signale y3 bis y6 sollen in Aufgabe 2.10 selbst ergänzt werden. Das Beispiel vermittelt einen Eindruck, um wie viel einfacher es ist, die Soll-Funktion mit einer Wertetabelle zu beschreiben und die Optimierung dem Rechner zu überlassen, als den gesamten Entwurf manuell durchzuführen. Beispiel für einen Automatenentwurf Die Schaltung für einen Automaten besteht aus kombinatorischen Teilschaltungen für die Übergangs- und die Ausgabefunktion, dem Zustandsregister
136
2 Synthese und Logikoptimierung y0 y5
y6
y4 a)
x 0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 .. 1111 y1 y2
y3
beliebig
y yi = 0
yi = 1
y0 1
x2 0
0a 1
signal x: std_logic_vector(3 downto 0); x0 1 1 signal y: std_logic_vector(6 downto 0); 1 1 ... process(x) variable a,b,c,d,e,f,g,h,i,j: std_logic; begin a := not x(3) and not x(2) and not x(1) and x(0); b := x(2) and not x(1) and not x(0); y3 c := x(2) and not x(1) and x(0); 1 0 d := x(2) and x(1) and not x(0); 0a 1 e := not x(2) and x(1) and not x(0); x0 1 0 ... 1 1 y(0) <= not(a or b); y5 y(1) <= not(c or d); 1 1 y(2) <= not e; 0a 1 ... x0 0 0 end process; 0h 1 c)
⇒Web-Projekt: P2.2/Seg7Dec.vhdl
Abb. 2.43. 7-Segment-Decoder a) Funktion Beschreibung mit logischen Ausdrücken
b)
-b 1 - 1
x2
y1 1
1
1
0
- 1 -c 1 -
-
-
1
1
-
-
-
1
0
-d -
y2 1
1
1
1
-
1
1
1
-
-
0e 1
-
-
x3 x0
-b 1 - 1
1
y4 1
0
0
0
-b 1 - 0
-f -
0
0
-
-g
-
-
1
1
-
-
-
1
y6 0
1
1
0j 1
-
1
-
-i
1
0
-f -
-
-
1
1
-
x3
b) KV-Diagramme
x1
x1
x1
1
-
x1
x3
c) VHDL-
und optionalen Registern für die Eingabe- und Ausgabeabtastung (vgl. Abschnitt 1.6.2). Der Entwurf startet in der Regel mit einer Aufstellung des Zustandsgraphen, aus dem dann über den Schritt der Zustandscodierung die Übergangs- und die Ausgabefunktion abgeleitet werden. Die Register sind vorentworfene Schaltungen. Die kombinatorischen Funktionen wurden früher, als die Logikoptimierung noch Handarbeit war, mit KV-Diagrammen minimiert. Als Beispiel soll der Entwurf des Vorwärts-Rückwärts-Zählers aus Abschnitt 1.6.1 zu Ende geführt werden. Abbildung 2.44 a zeigt den Zustandsgraphen. Bei der symbolischen Eingabe »V« werden die Zustände »A«, »B«, »C« und »D« zyklisch in dieser Reihenfolge durchlaufen. Bei der Eingabe »H« bleibt der Zähler stehen und bei der Eingabe »R« durchläuft er die Zustände rückwärts. Sowohl die Unterscheidung der drei Eingabewerte als auch die der vier Zustände und der vier Ausgabewerte erfordert zwei Bit. Für die Codierung in Bitvektorwerte ist jeweils die aufsteigende Zählreihenfolge gewählt (Abb. 2.44 b). Abbildung 2.44 c zeigt die Übergangs- und die Ausgabefunktion in tabellarischer Form sowohl mit Symbolen als auch mit den zugeordneten Bitvektorwerten.
2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra a)
H/K A R/N
V/K D H/N
R/K
b) Symbol V, A, K H, B, L R, C, M Code 00 01 10
H/L
V/L
B R/L
R/M
s x 00(V) 00(A) 01(B) 01(B) 10(C) 10(C) 11(D) 11(D) 00(A)
V/M C
V/N
s+ = fs (x, s)
c)
H/M
V, H, R symbolische Eingabewerte A, B, C, D symbolische Zust¨ ande K, L, M, N symbolische Ausgabewerte
01(H) 00(A) 01(B) 10(C) 11(D)
137
D, N 11
y = fa (x, s)
10(R) 11(D) 00(A) 01(B) 10(C)
00(V) 01(H) 10(R) 01(L) 00(K) 11(N) 10(M) 01(L) 00(K) 11(N) 10(M) 01(L) 00(K) 11(N) 10(M)
x Eingabesignal (2 Bit) s, s+ Zustand, Folgezustand (je 2 Bit) y Ausgabesignal
Abb. 2.44. Vorwärts-Rückwärts-Zähler aus Abb. 1.65 a) Zustandsgraph b) Zustandscodierung c) tabellarische Übergangs- und Ausgabefunktion
In einem synthesebasierten Entwurf würde man die Zielfunktion als Nächstes mit Auswahlanweisungen beschreiben, die Schaltung synthetisieren und die Optimierung dem Rechner überlassen. In einem Handentwurf wird für jedes Zustands- und jedes Ausgabebit ein KV-Diagramm aufgestellt und aus jedem dieser Diagramme ein minimierter logischer Ausdruck abgelesen. Das funktioniert offensichtlich nur, wenn der Zustands- und der Eingabevektor zusammen aus nicht mehr als vier Bits bestehen. Für Eingaben und Zustände, die nicht auftreten können, ist die Ausgabe und der Folgezustand beliebig (std_logic-Wert »-«). Im Beispiel ist die Zustandscodierung so gewählt, dass die Übergangs- und die Ausgabefunktion identisch sind, so dass insgesamt zwei KV-Diagramme genügen (Abb. 2.45). In beiden KV-Diagrammen sind die Einsen abgedeckt. Die minimierten Ausdrücke für die Bildung der Folgezustands- und der Ausgabebits lauten y0 = s+ ¯0 x ¯0 ∨ s0 x0 0 = s
y1 = s+ ¯1 s0 x ¯1 x ¯0 ∨ s¯1 s¯0 x1 ∨ s1 s¯0 x ¯1 ∨ s1 x0 ∨ s1 s0 x1 1 = s s+ 1 = y1 V H R
x0 x1
a
0
1
0
0
1
b
0
s+ 0 = y0 c
1
a
1
0
1
1
0
1
-
-
-
-
-
0
1
1
0
s0
d
e
0
s1
A B D C
a
s0
b
0
1
1
0
-
-
0
1
a
a
s1
A B D C
Abb. 2.45. KV-Diagramme für den Automaten in Abb. 2.44
138
2 Synthese und Logikoptimierung
Vom KV-Diagramm zur Schaltung Im nächsten Entwurfsschritt werden die Übergangs- und die Ausgabefunktion des Automaten mit logischen Gattern nachgebildet und das Zustandsregister aus Speicherzellen zusammengesetzt. Dieser Schritt wird bei der Synthese als Technologieabbildung bezeichnet. Die technologieunabhängigen UND- und ODER-Verknüpfungen und Invertierungen in den Ausdrücken sind mit in der angestrebten Fertigungstechnologie verfügbaren Logikbausteinen nachzubilden. Früher, als digitale Schaltungen noch aus niedrig integrierten Schaltkreisen bestanden, waren die Bausteine vorzugsweise NAND-Gatter mit unterschiedlicher Eingangsanzahl. Die Umwandlung logischer Ausdrücke aus UNDund ODER-Verknüpfungen in die NAND-NAND-Form erfolgt mit Hilfe der De Morgan’schen Regeln (vgl. Abschnitt 2.2.1): y0 = s+ ¯0 x ¯0 ∨ s0 x0 = (¯ s0 x ¯0 ) (s0 x0 ) 0 = s y1 = s+ ¯1 s0 x ¯1 x ¯0 ∨ s¯1 s¯0 x1 ∨ s1 s¯0 x ¯1 ∨ s1 x0 ∨ s1 s0 x1 1 = s = (¯ s1 s0 x ¯1 x ¯0 ) (¯ s1 s¯0 x1 ) (s1 s¯0 x ¯1 ) (s1 x0 ) (s1 s0 x1 ) Das Zustandsregister soll im Beispiel aus 1-Bit-Registern mit Rücksetzeingang nachgebildet werden. Abbildung 2.46 zeigt die komplette Schaltung des Automaten. Wie das Beispiel gezeigt hat, ist der Entwurf ohne Synthese selbst für eine so kleine Schaltung recht mühsam. s1 s¯1 s0 s¯0 x1 x ¯1 x0 x ¯0 &
x0
&
y0 &
x R
&
x R
s0
&
x1
& & & I T
y1 s1
& ⇒Web-Projekt: P2.2/Test VRZ.vhdl
Abb. 2.46. Schaltung zur Automatenbeschreibung aus Abb. 2.44
2.2.5 Verfahren von Quine und McCluskey Das Verfahren von Quine und McCluskey ist ein tabellenbasiertes Minimierungsverfahren, das gleichfalls auf der Zusammenfassung von Konjunktionen,
2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra
139
die sich nur in einer Invertierung unterscheiden, basiert: n o n o . . . K10 0 , K10 1 , . . . ⇒ . . . , K10 - , . . . Im Vergleich zur Minimierung mit KV-Diagrammen ist dieses Verfahren auch für Ausdrücke mit weit mehr als vier binären Eingabevariablen geeignet. Ausgangspunkt ist eine Liste der Minterme, für die der Funktionswert »1«, bzw. bei einer Entwicklung nach den Nullen, für die er »0« ist. Die Berechnung teilt sich in zwei Teile. Zuerst werden mit Hilfe der sog. Quine’schen Tabellen die Primterme bestimmt. Das sind minimierte Konjunktionen, die von keiner Konjunktion mit einer größeren Anzahl von Don’t-Care-Stellen vollständig abgedeckt werden. Im zweiten Teil wird aus allen gefundenen Primtermen eine minimierte Teilmenge ausgewählt, die alle Minterme abdeckt. Zur Bestimmung der Primterme wird zuerst die Quine’sche Tabelle nullter Ordnung aufgestellt. Das ist eine Tabelle mit allen Mintermen, in der diese aufsteigend nach der Anzahl der Einsen im Konjunktionsindex einsortiert sind (Abb. 2.47). Die Konjunktionsnummern vor den Tabellenzeilen in der Abbildung sind die Dezimalwerte der Konjunktionsindizes in der Tabelle. In dieser Tabelle wird für alle Konjunktionspaare, deren Anzahl von Einsen sich um eins unterscheidet, getestet, ob sie zu einer Konjunktion mit einer Don’tCare-Stelle zusammengefasst werden können. Falls das möglich ist, werden die resultierenden Konjunktionen mit der Don’t-Care-Stelle nach der Anzahl der Einsen in die Quine’sche Tabelle erster Ordnung einsortiert und die abgedeckten Konjunktionen in der Quine’sche Tabelle nullter Ordnung abgehakt. Mit der vollständigen Quine’schen Tabelle erster und später allgemein n-ter Ordnung wird genauso verfahren, um jeweils die Quine’sche Tabelle n + 1-ter Visualisierung der Blockbildungsm¨ oglichkeiten mit einem KV-Diagramm x2
x0
4 12 8
1
5 6 13 9
3 2
3
1
0
2
7
5
4
7 15 11 6 14 10 3
x1
1
x3 m¨ogliche Zweierbl¨ocke i m¨oglicher Vierererblock i Primtermnummer P √i Primterm Konjunktion abgedeckt i
Quine’sche Tabellen Quine’sche Tabelle nullter Ordnung erster und zweiter Ordnung x3 x2 x1 x0 2 8 3 5 10 12 7 13 14
0 1 0 0 1 1 0 1 1
0 0 0 1 0 1 1 1 1
1 0 1 0 1 0 1 0 1
0 0 1 1 0 0 1 1 0
√ √ √ √ √ √ √ √ √
x3 x2 x1 x0 2, 3 2, 10 8, 10 8, 12 3, 7 5, 7 5, 13 10, 14 12, 13 12, 14
Mintermnummer 8, 10, 12, 14
0 1 1 0 0 1 1 1
0 0 0 1 1 1 1
1 1 0 1 0 1 0 -
0 0 0 1 1 1 0 0
P2 P √3 √
P4 P5 P √6 P √7
x3 x2 x1 x0 1 - - 0 P1
Abb. 2.47. Berechnung der Quine’schen Tabellen
140
2 Synthese und Logikoptimierung
Ordnung zu erzeugen (n – Anzahl der Don’t-Care-Stellen im Konjunktionsindex). In Abb. 2.47 lassen sich in der Quine’schen Tabelle nullter Ordnung die Mintermpaare (2,3), (2,10) etc. bis (12,14) zusammenfassen. Dabei werden alle Minterme abgehakt. In der Quine’schen Tabelle erster Ordnung gibt es nur die Zusammenfassungsmöglichkeiten (8,10)+(12,14) und (8,12)+(10,14), die beide auf dieselbe Vierergruppe (8,10,12,14) führen. Die Quine’sche Tabelle zweiter Ordnung enthält nur diese eine Konjunktion, so dass keine weiteren Zusammenfassungen möglich sind. Alle nicht abgehakten Konjunktionen – in der Quine’schen Tabelle zweiter Ordnung »P1«, in der Quine’schen Tabelle erster Ordnung »P2« bis »P7« und in der Quine’schen Tabelle nullter Ordnung keine – bilden die Menge der Primterme für den zweiten Teil des Algorithmus. Im KV-Diagramm in Abb. 2.47 links ist der Primterm »P1« aus der Quine’schen Tabelle zweiter Ordnung ein Viererblock. Die Primterme »P2« bis »P7« aus der Quine’schen Tabelle erster Ordnung sind Zweierblöcke. Ganz allgemein deckt ein Primterm aus einer Quine’schen Tabelle n-ter Ordnung 2n Minterme ab. Im zweiten Teil des Algorithmus werden die Primterme beginnend mit denen mit der größten Anzahl von Don’t-Care-Stellen gemeinsam mit den abgedeckten Mintermen in die Tabelle der Primterme übernommen (Abb. 2.48). Mit Hilfe dieser Tabelle wird eine minimale Abdeckungsmenge für alle Minterme gesucht. Die Lösungsmenge ist im Beispiel {P1 , P2 , P5 , P7 } Das entspricht dem minimierten logischen Ausdruck x3 x ¯ ∨x ¯ x ¯ x ∨x x x ∨x x x ¯ | {z }0 | 3 {z2 }1 | 3 {z2 }0 | 3 {z2 }1 P1
P2
P5
P7
(xi – Eingabesignale oder -variablen). Für eine n-stellige logische Funktion wachsen die Quine’schen Tabellen im ungünstigsten Fall exponentiell mit der Stelligkeit n. Praktische Programme arbeiten bei einer großen Anzahl von Eingabebits mit Heuristiken und mit unvollständigen Quine’schen unvollständigen Tabellen. x2
x0
4 12 8
1
5 6 13 9
3 2
3
2
2
1
0
7
5
4
7 15 11 6 14 10 3
x1
1
x3 genutzte Primterme
3
5
7
8 10 12 13 14
P1 P2 P3 P4 P5 P6 P7 ungenutzte Primterme
Abb. 2.48. Tabelle zur Auswahl der Primterme
2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra
141
2.2.6 Zusammenfassung und Übungsaufgaben Die Schaltungsvereinfachung auf der Basis logischer Ausdrücke erfolgt mit Hilfe von Umformungsregeln. Die Optimierungsziele – geringer Schaltungsaufwand, geringe Verzögerung etc. – widersprechen sich zum Teil. Die beiden klassischen systematischen Optimierungsverfahren – die Optimierung mit KVDiagrammen und das Verfahren von Quine und McCluskey – basieren auf der Vereinfachung von Konjunktionsmengen. Weiterführende und ergänzende Literatur siehe [13, 24, 38, 40, 42, 44, 48, 50]. Aufgabe 2.7 Wandeln Sie die Schaltung in Abb. 2.49 in eine Schaltung aus NAND-Gattern und Invertern um. x0 x1
& ≥1
y
&
x2
Abb. 2.49. Schaltung zu Aufgabe 2.7
Aufgabe 2.8 Lesen Sie aus den KV-Diagrammen in Abb. 2.50 je einen minimierten logischen Ausdruck ab. x2
x2 1 x0
1
0
1
1
1
1
1
0
1
0
1
0
0
0
-
-
-
-
0
0
1
1
1
1
1
0
1
0
0
1
0 x3
x0 x1 a)
x3
Abb. 2.50. KV-Diagramme zu Aufgabe 2.8
x1 b)
142
2 Synthese und Logikoptimierung
Aufgabe 2.9 a) Warum dürfen sich die Rechtecke, mit denen in einem KV-Diagramm die Einsen abgedeckt werden, gegenseitig überlagern? b) In welchen Mintermen überlagern sich die Rechtecke im KV-Diagramm für den Ausdruck x ¯0 ∨ x2 x1 ∨ x3 x2 Aufgabe 2.10 Ergänzen Sie die fehlenden Zuweisungen an die Variablen f bis j und die Signale y3 bis y6 in der VHDL-Beschreibung des 7-Segment-Decoders in Abb. 2.43. Aufgabe 2.11 Entwickeln Sie für den Automatengraphen in Abb. 2.51 eine funktionsgleiche Schaltung. Die Initialisierung soll in der üblichen Weise über die Initialisierungseingänge der Speicherzellen erfolgen und der Zustandswechsel mit der steigenden Taktflanke. a) Füllen Sie die KV-Diagramme für die Ausdrücke zur Bildung der drei Zustandsbits und des Ausgabebits aus. Decken Sie entweder die Nullen oder die Einsen mit Blöcken ab und lesen Sie aus den KV-Diagrammen die minimierten logischen Ausdrücke ab. b) Welche redundanten Zustände hat der Automat? Bestimmen Sie aus den abgelesenen Funktionen, welche Folgezustände die redundanten Zustände haben, und zeichnen Sie die redundanten Zustände und die von ihnen abgehenden Kanten mit in den Zustandsgraphen ein. c) Zeichnen Sie die resultierende Schaltung.
1/0 011 0/0 1/0
s+ 2
1/0
000
0/1 001 0/1
010
1/0
x/y
s2
s0 s1 y
s+ 0
0/0
1/0
s+ 1
1/0
100 s2 s1 s0
s2
s0 0/1
111
s1 0/0
x
x
Abb. 2.51. Zustandsgraph und KV-Diagramme zu Aufgabe 2.11
2.3 Binäre Entscheidungsdiagramme
143
Aufgabe 2.12 Gegeben ist die Menge der Minterme einer sechsstelligen logischen Funktion: K ∈ {100000, 100100, 101010, 101110, 111110, 110000, 011000, 101011, 101111, 101000, 101001} für die der Funktionswert »1« sein soll. Bestimmen Sie mit Hilfe des Verfahrens von Quine und McCluskey einen minimierten logischen Ausdruck.
2.3 Binäre Entscheidungsdiagramme Ein binäres Entscheidungsdiagramm (BDD – binary decision diagram) setzt eine logische Funktion aus binären Entscheidungen zusammen. Die Definition ist rekursiv. Das Ergebnis einer Entscheidung ist entweder konstant (»0« oder »1«) oder eine durch eine binäre Variable getroffene Entscheidung zwischen zwei Alternativen. Beide Alternativen sind die Ergebnisse zuvor getroffener Entscheidungen. Alle Entscheidungen zusammen bilden ein binäres Entscheidungsdiagramm mit konstanten Werten als Blätter und dem Entscheidungsergebnis als Wurzel (Abb. 2.52). bin¨ are Entscheidung Konstante 0
Entscheidung anhand einer bin¨ aren Variablen
1
xi
0
1
Abb. 2.52. Definition eines binären Entscheidungsdiagramms
Zur Bestimmung der Wertetabelle aus einem Entscheidungsdiagramm werden nacheinander alle Kanten des Graphen durchlaufen. Die Kantenbeschriftungen bilden die abhängigen Größen. Der Funktionswert ist die Blattkonstante, die über die Kantenfolge erreicht wird (Abb. 2.53). x1 x0
y
0 0 1 1
0 1 1 0
0 1 0 1
0 0 0
x1
x0
1
1
0
1
1
x1
1 0
Abb. 2.53. Wertetabelle und binäres Entscheidungsdiagramm
144
2 Synthese und Logikoptimierung
2.3.1 Vereinfachung binärer Entscheidungsdiagramme Ein unreduziertes binäres Entscheidungsdiagramm bildet einen Baum mit 2n Blättern und 2n−1 Entscheidungen (n – Stelligkeit der Funktion). Es ist direkt aus der Wertetabelle ablesbar (Abb. 2.54). x0
0 x1
0 0
x2
1
1
1
0
0
0
x2
0
x2
x2 0 0 0 0 1 1 1 1
1
1
0
1
0
x1
1
1
0
1
1
x1
1 0
x1 0 0 1 1 0 0 1 1
x0 0 1 0 1 0 1 0 1
y 1 0 0 1 0 1 1 0
Abb. 2.54. Unreduziertes ungeordnetes binäres Entscheidungsdiagramm mit der zugehörigen Wertetabelle
Zur Schaltungsminimierung ist die Anzahl der Entscheidungen zu minimieren. Dafür gibt es zwei Vereinfachungsregeln: • •
Verschmelzung gleicher Teilgraphen und Löschen von Knoten mit zwei gleichen Nachfolgern (Abb. 2.55).
0
xi
1
xj
1
0
xi
1 0
xj
1
≡
a)
0 b)
0
xi
1
Entscheidungsknoten Teilbaum
Abb. 2.55. Vereinfachungsregeln a) Verschmelzung gleicher Teilgraphen b) Löschen von Knoten mit gleichen Nachfolgern
Die offensichtliche Optimierungsvoraussetzung ist, dass die Eingabevariablen auf allen Entscheidungswegen in derselben Reihenfolge abgefragt werden. Das führt auf ein geordnetes binäres Entscheidungsdiagramm (OBDD – ordered binary decision diagram). Abbildung 2.56 zeigt ein OBDD für die Funktion aus Abb. 2.54 mit der Abfragereihenfolge x0 -x1 -x2 . Die Suche nach Verschmelzungsmöglichkeiten beginnt an den Blättern. Es gibt nur die zwei Blatttypen »0« und »1«, die durch zwei Symbole ersetzt werden (Abb. 2.57). Dann werden auf der untersten Entscheidungsebene die
2.3 Binäre Entscheidungsdiagramme x0
0 x1
0 0
x2
1
1
1 0
0
0
x1
0
x2
1
145
x2
1
0
1
0
1
1
0
1
1
x2
1 0
Abb. 2.56. Unreduziertes geordnetes binäres Entscheidungsdiagramm
Teilgraphen in Typen eingeteilt. Theoretisch gibt es hier vier Möglichkeiten, von denen im Beispiel aber nur zwei vorkommen. Auf der nächsten Ebene gibt es im Beispiel keine Verschmelzungsmöglichkeit mehr. Nach der Detektion aller Verschmelzungsmöglichkeiten wird das Entscheidungsdiagramm so umgezeichnet, dass jeder Teilgraphentyp nur noch einmal enthalten ist. Alle Knoten mit gleichen Nachfolgern werden gelöscht. Ein vollständig reduziertes, geordnetes binäres Entscheidungsdiagramm wird als ROBDD (reduced ordered binary decision diagram) bezeichnet. unterste Ebene x1
0 0 Υ1
x2
x0
0
1 Υ2
1 0
n¨achste Ebene
1 x1
0 x2
Υ2
x2
1 0 Υ1 Υ2
1 Υ1
x2
Υ1
Υ2 :
1
0
Υ2
Υ1
x2
Υ4 : 1
Υ2
Ergebnis 0
Υ3 : 0
Υ3
1
unterschiedliche Teilgraphen: Υ1 :
0 Υ2
x0
1
x1 1 1 x1 0 0
1 0
0
x2
1 Υ1
Υ4 x0
1
x1 1 1 x1 0 0 x2 1 1 x2 0 0 1 0
Abb. 2.57. Vereinfachung eines geordneten binären Entscheidungsdiagramms
Reduzierte geordnete binäre Entscheidungsdiagramme mit einer bestimmten Abfragereihenfolge sind genau wie Wertetabellen und die Auflistung der Minterme für alle Einsen bzw. Nullen aus der Wertetabelle Normalformen8 . Wertetabellen haben als Normalform den Nachteil, dass die Anzahl der zu vergleichenden Werte für den Test zweier Funktionen auf Gleichheit exponentiell mit der Stelligkeit zunimmt. Ab etwa 30 Eingabebits sind Wertetabellen auch 8
Normalformen sind eindeutige Beschreibungen logischer Funktionen, mit denen getestet werden kann, ob zwei Funktionen identisch sind (vgl. Abschnitt 2.2.3).
146
2 Synthese und Logikoptimierung
auf dem Rechner kaum noch darstellbar und verarbeitbar. Die Auflistung der Minterme für alle Einsen bzw. Nullen ist meist auch nicht signifikant kleiner, ein ROBBD in der Regel schon. 2.3.2 Zweistellige Operationen mit ROBDDs Ein ROBDD kann nicht nur aus der Wertetabelle, sondern auch aus einem logischen Ausdruck berechnet werden. Die Grundlage hierfür bildet der folgende Entwicklungssatz: • •
Für zwei beliebige Teilbäume, bei denen im obersten Knoten dieselbe Variable ausgewertet wird, kann die Operation eine Entscheidungsebene tiefer verschoben werden (Abb. 2.58 a). Fehlende Entscheidungsknoten sind wie Entscheidungsknoten mit gleichen Nachfolgern zu behandeln (Abb. 2.58 b).
0 a)
Υ1
1
⋄
xi
1 Υ2
0
xi
Υ3
Υ2
Υ1
0 b)
xi
⋄
0
1 Υ4
xi
1
Υ3
0
xi
Υ1 ⋄ Υ3 0 Υ1 ⋄ Υ3
1 Υ2 ⋄ Υ4
xi
1 Υ2 ⋄ Υ3
are Operation ⋄ beliebige zweistellige bin¨
Abb. 2.58. Entwicklungssatz für die Verknüpfung zweier ROBDDs mit einer zweistelligen binären Operation a) Verschiebung der Operation eine Entscheidungsebene tiefer b) Einfügen eines gelöschten Entscheidungsknotens, um den Entwicklungssatz anwenden zu können
In Abb. 2.59 wird das ROBDD für den Ausdruck x1 ∨ x0 entwickelt. Die Abfragereihenfolge sei x0 -x1 . Zur Anwendung des Entwicklungssatzes muss in der ROBDD-Darstellung der Variablen x1 der eliminierbare Entscheidungsknoten für x0 ergänzt werden. Auf der Ebene tiefer wird jeweils eine Konstante mit einem Teilbaum ODER-verknüpft. Eine ODER-Verknüpfung mit »0« hat keinen Einfluss auf das Ergebnis und damit auf den Teilbaum und eine ODER-Verknüpfung mit »1« ist »1«. In Abb. 2.60 wird das ROBDD für den Ausdruck x1 ∨ x0 weiter mit dem ROBDD für den Ausdruck x ¯2 UND-verknüpft. Die Abfragereihenfolge des gesamten ROBDDs sei x0 -x1 -x2 . In dem ROBDD für x ¯2 müssen gedanklich die gelöschten Knoten für die Abfrage von x0 und x1 ergänzt werden bzw. die Operation kann gleich zwei Entscheidungsebenen tiefer verlagert werden. In
2.3 Binäre Entscheidungsdiagramme ODER-Verkn¨ upfung der ROBDDs der Einzelvariablen
x0
0 0
1
0
∨
1
0
Anwendung des Entwicklungssatzes 0
x1
0
x1
1
0 ∨ 0
1
0
1
x1
1
x0
0
1 0
x0
0
Ergebnis
x0
0
1
x1
x1
1 ∨ 0
1
0
1 1
1
1
0
1
147
1
1
Abb. 2.59. Entwicklung des ROBDDs für den Ausdruck x1 ∨ x0
dieser Ebene werden einmal eine »0« und einmal eine »1« mit dem verbleibenden Baum des zweiten Ausdrucks UND-verknüpft. Man könnte hier wieder abbrechen, denn die UND-Verknüpfung mit »0« ist immer »0« und die UND-Verknüpfung mit »1« verändert den restlichen Ausdruck nicht. Alternativ kann die Operation auch bis auf die Blattebene verlagert und ausgeführt werden. In dem resultierenden Entscheidungsdiagramm hat der linke untere Verkn¨ upfung (x1 ∨ x0 ) ∧ x ¯2 0
x1
1
0 x2
0 1
0 0 0 ∧ 1
x2
1 0 ∧ 0
1
1 ∧ 0
0
1
0 0
x2
1 0
1 ∧ 1
x2
0 1
x2
1 0
Vereinfachung x0
0
1 0
1
1 0
x0
1
1
x1
0
x2
x0
0
1
1
0 x1
1 0
x0
0 x1
x2
1
1
Operation zwei Entscheidungsebenen tiefer
Operation drei Entscheidungsebenen tiefer
0
∧
1
0
0 ∧
Ausf¨ uhrung der Operation auf der untersten Ebene
x0
0
1
x1 1 1 ∧ 0
0
1 1
0
x2
0 1
Abb. 2.60. Entwicklung des ROBDDs für den Ausdruck (x1 ∨ x0 ) ∧ x ¯2
148
2 Synthese und Logikoptimierung
Knoten gleiche Nachfolger und kann eliminiert werden. Abschließend sind die beiden Blätter mit dem Wert »0« zusammenzufassen. 2.3.3 Umsetzung von ROBDDs in minimierte Schaltungen Zur Umsetzung eines ROBDDs in eine Schaltung werden die Entscheidungsknoten durch Multiplexer ersetzt. Ein Multiplexer bildet eine dreistellige logische Funktion nach, die minimiert mit einem KV-Diagramm durch folgenden logischen Ausdruck dargestellt werden kann (Abb. 2.61): y = x1 s¯ ∨ x2 s
(2.1)
x1 y 0 x1
s
x1 x2
1 x2
s
0 1
y
0 s
0
1a 1 0
0
1b 1
a: x1 s¯ b: x2 s y = x1 s¯ ∨ x2 s
x2
Abb. 2.61. Minimierung des logischen Ausdrucks für einen Multiplexer
Wenn die Auswahl zwischen einer Konstanten und einer Variablen erfolgt, realisiert ein Multiplexer eine zweistellige Funktion. Je nach dem konstanten Wert und dem Eingang, an dem er anliegt, ist das eine der nachfolgenden UND- oder ODER-Verknüpfungen: x1 x1 x2 x2
=0:y =1:y =0:y =1:y
= (0 ∧ s¯) ∨ x2 s = x2 s = (1 ∧ s¯) ∨ x2 s = s¯ ∨ x2 s = s¯ ∨ x2 = x1 s¯ ∨ (0 ∧ s) = x1 s¯ = x1 s¯ ∨ (1 ∧ s) = x1 s¯ ∨ s = x1 ∨ s
Bei der Auswahl zwischen zwei unterschiedlichen konstanten Werten bildet der Multiplexer eine einstellige Funktion nach, eine Identität oder eine Negation: x1 = 0; x2 = 1 : y = (0 ∧ s¯) ∨ (1 ∧ s) = s x1 = 1; x2 = 0 : y = (1 ∧ s¯) ∨ (0 ∧ s) = s¯ In dem in Abb. 2.60 entwickelten ROBDD für die Funktion (x1 ∨ x0 ) ∧ x ¯2 kann der Entscheidungsknoten für x0 durch einen Multiplexer, der Entscheidungsknoten für x1 durch ein UND-Gatter und der Entscheidungsknoten für x2 durch einen Inverter nachgebildet werden. Der Multiplexer kann nach Gleichung 2.1 weiter durch eine Schaltung aus einem ODER-Gatter, zwei UNDGattern und einem Inverter ersetzt werden. Die resultierende Schaltung ist wesentlich aufwändiger, als wenn der Ausdruck direkt in eine Schaltung aus einem ODER- und einem UND-Gatter übersetzt wird (Abb. 2.62).
2.3 Binäre Entscheidungsdiagramme Abfragereihenfolge: x0 -x1 -x2 y x0
0
1
x1 0
x2
1
0
≥1 &
1
x2
x1
x2 1
&
&
x1
Abfragereihenfolge: x2 -x1 -x0 y
y
x0
&
1 1
0
0
x0 x2
149
0 0
&
0 0 x0
x1 1 1 1
x2
≥1 x0 x1
Abb. 2.62. Schaltungsnachbildung der ROBDDs für den Ausdruck x ¯2 (x1 ∨ x0 )
Mit der Abfragereihenfolge x2 -x1 -x0 entsteht ein anderes ROBDD, in dem der erste Abfrageknoten durch ein UND-Gatter mit einem invertierten Eingang und der zweite durch ein ODER-Gatter nachzubilden sind. Das Ergebnis ist dieselbe Schaltung, die sich auch aus dem Ausdruck ablesen lässt. Zur Schaltungsoptimierung mit ROBDDs sind offensichtlich mehrere Abfragereihenfolgen durchzuprobieren. 2.3.4 Zusammenfassung und Übungsaufgaben Ein binäres Entscheidungsdiagramm beschreibt eine logische Funktion durch binäre Entscheidungen. Zur Anwendung der beiden Vereinfachungsregeln – Verschmelzung und Knotenelimination – muss der Entscheidungsfluss geordnet sein. Das bedeutet, die Eingabevariablen müssen auf allen Entscheidungswegen in derselben Reihenfolge abgefragt werden. Ein reduziertes geordnetes binäres Entscheidungsdiagramm (ROBDD) mit einer vorgegebenen Abfragereihenfolge ist eine Normalform, d.h. eine eindeutige Darstellung einer logischen Funktion und geeignet für den Test zweier Funktionen auf Gleichheit. Die Verknüpfung logischer Funktionen, die durch ROBDDs dargestellt sind, erfolgt mit Hilfe des Entwicklungssatzes. Dieser besagt, dass eine zweistellige Operation zweier ROBBDs rekursiv eine Entscheidungsebene tiefer verschoben werden darf. Schaltungstechnisch werden Entscheidungsknoten durch Multiplexer nachgebildet. Multiplexer mit konstanten Eingabewerten vereinfachen sich zu UND- oder ODER-Gattern mit zwei Eingabevariablen, einem Inverter oder einer Identität mit einer Eingabevariablen oder einer Konstanten. Weiterführende und ergänzende Literatur siehe [10, 15, 19, 24, 34].
150
2 Synthese und Logikoptimierung
Aufgabe 2.13 Gegeben ist die VHDL-Beschreibung signal a, b, c, y: std_logic; ... y <= a xor b xor c;
a) Stellen Sie die Wertetabelle für die Schaltung auf. b) Entwickeln Sie aus der Wertetabelle das unreduzierte geordnete binäre Entscheidungsdiagramm für die Abfragereihenfolge a-b-c. c) Vereinfachen Sie das geordnete binäre Entscheidungsdiagramm durch Anwendung der Verschmelzungsregel und durch Löschen der Knoten mit gleichen Nachfolgern. d) Entwickeln Sie aus dem reduzierten Entscheidungsdiagramm eine minimierte Schaltung. Aufgabe 2.14 Gegeben ist das binäre Entscheidungsdiagramm in Abb. 2.63. a) Stellen Sie die Wertetabelle auf. b) Bilden Sie das Entscheidungsdiagramm durch eine Schaltung nach.
0 x1 0
x0 1 0
0
1 x2 1 1
Abb. 2.63. Binäres Entscheidungsdiagramm zu Aufgabe 2.14
2.4 Zahlendarstellung Zahlen sind geordnete Mengen darstellbarer Werte, für die die arithmetischen Grundoperationen – Addition, Subtraktion, Multiplikation und Division – und die Ordnungsrelationen – Test auf größer, kleiner etc. – definiert sind. Ihre Codierung in Bitvektoren ist so gewählt, dass die arithmetischen Grundoperationen minimalen Schaltungsaufwand erfordern.
2.4 Zahlendarstellung
151
2.4.1 Stellenwertsystem In einem Stellenwertsystem wird eine Zahl durch eine Folge von n Ziffern dargestellt: bn−1 bn−2 . . . b1 b0 Jedes Stellenwertsystem hat eine Basis B. Das ist die Anzahl der zu unterscheidenden Ziffern. Die Ziffern haben die Werte null bis B −1. Jede Ziffer hat eine Stelle i mit dem Stellenwert B i . Der Wert einer Zahl Z ist die Summe der Produkte aus den Ziffernwerten und den Stellenwerten: Z=
n−1 X
bi · B i
(2.2)
i=0
Die kleinste darstellbare Zahl hat den Wert null und die größte mit n Ziffern darstellbare Zahl hat den Wert B n −1. Wenn die Basis einer Zahlendarstellung nicht eindeutig aus dem Kontext hervorgeht, ist sie bei der Zahlendarstellung explizit anzugeben, z.B. wie in Abb. 2.64 als Index. dezimal 0 1 ··· 7 8
bin¨ ar 02 12 ··· 1112 10002
oktal 08 18 ··· 78 108
hex. 016 116 ··· 716 816
dezimal bin¨ ar ··· ··· 11112 15 16 100002 17 100012 ··· ···
oktal ··· 178 208 218 ···
hex. ··· F16 1016 1116 ···
Abb. 2.64. Zahlendarstellung im Dezimal-, im Binär-, im Oktal- und und im Hexadezimalsystem
Das im Alltagsleben verwendete Dezimalsystem hat die Basis B = 10 und die zehn Ziffern mit den Werten von null bis neun. Die Zahl 543 hat beispielsweise den Wert 543 = 5 · 102 + 4 · 101 + 3 · 100 Mit drei Ziffern lassen sich die Werte 0 bis 999 darstellen. Das in der Digitaltechnik verwendete Binärsystem hat die Basis B = 2. Zur Darstellung von drei Dezimalziffern werden etwa zehn Binärziffern benötigt. Das Oktalsystem hat die Basis B = 8 und die Ziffernmenge bi ∈ {0, 1, . . . , 7}. Das Hexadezimalsystem hat die Basis B = 16. Die Ziffernmenge umfassen die zehn Dezimalziffern und die ersten sechs Buchstaben des Alphabets: bi ∈ {0, 1, . . . , 9, A, B, C, D, E, F} Jede Oktalziffer fasst genau drei Binärziffern und jede Hexadezimalziffer vier Binärziffern zusammen. Oktal- und Hexadezimalzahlen dienen hauptsächlich als verkürzte, besser lesbare Schreibweisen für Binärzahlen.
152
2 Synthese und Logikoptimierung
Zur Umwandlung eines ganzzahligen Wertes in eine Ziffernfolge wird der Wert solange durch die Basis des Zahlensystems geteilt, bis der Quotient null ist. Der Ziffernwert der niederwertigsten Stelle ist der Divisionsrest der ersten Division. Der Ziffernwert der nächsthöheren Stelle ist der nächste Divisionsrest etc. Zuletzt wird die höchstwertigste Ziffer berechnet, die in der Zahlendarstellung ganz links steht. Abbildung 2.65 zeigt als Beispiel die Berechnung der Ziffernfolgen, mit denen der Wert 75 im Binär-, im Hexadezimal- und im Oktalsystem dargestellt wird. Umwandlung in eine Bin¨ arzahl
Umwandlung in eine Hexadezimalzahl
75 37 18 9 4 2 1
75 : 16 = 4 Rest: 11 = (B)16 4 : 16 = 0 Rest: 4 = (4)16
: : : : : : :
2 2 2 2 2 2 2
= 37 = 18 = 9 = 4 = 2 = 1 = 0
Rest: Rest: Rest: Rest: Rest: Rest: Rest:
b0 b1 b2 b3 b4 b5 b6
=1 =1 =0 =1 =0 =0 =1
Umwandlung in eine Oktalzahl 75 : 8 = 9 Rest: 3 9 : 8 = 1 Rest: 1 1 : 8 = 0 Rest: 1
Ergebnis: (1001011)2 = (4B)16 = (113)8
Abb. 2.65. Berechnung der Ziffernfolgen zur Darstellung des Wertes 75 im Binär-, im Oktal- und im Hexadezimalsystem
Die schrittweise Division ist umkehrbar und liefert wie auch Gleichung 2.2 den Wert der Ziffernfolge. Zu Beginn wird eine Akkumulatorvariable mit dem Wert null initialisiert. Dann wird für jede Ziffer beginnend mit der höchstwertigsten der akkumulierte Wert mit der Basis multipliziert und dazu der Ziffernwert addiert. Im nachfolgenden Code-Beispiel stehen die n Ziffern einer Binärzahl in absteigender Reihenfolge im Bitvektor b. Die Akkumulation erfolgt in der mit null initialisierten natural-Variablen Z. Im Schleifenkörper wird der Wert in jedem Iterationsschritt verdoppelt und, wenn das zugehörige Bit »1« ist, zusätzlich um »1« erhöht. Eine direkte Addition des Bitwertes zum Zahlenwert wäre nur möglich, wenn der Bitwert zuvor in einen zu einem natural-Wert addierbaren Zahlenwert konvertiert wird 9 : variable Z: natural; variable b: bit_vector(n-1 downto 0); ... Z := 0; for idx in b’high downto b’low loop Z := 2 * Z; if b(idx)=’1’ then Z := Z + 1; end if; end loop; 9
Die VHDL-Details – Schleifen, Datentypen etc. – werden später in Kapitel 3 ausführlich behandelt. Den skizzierten Algorithmus gibt es als vordefinierte Funktion im Package ieee.numeric_bit für die Umwandlung von Bitvektoren in Zahlenwerte.
2.4 Zahlendarstellung
153
2.4.2 Vorzeichenbehaftete Zahlen im Zweierkomplement Eine vorzeichenbehaftete Zahl wird auf dem Papier durch Vorzeichen und Betrag dargestellt. Für die rechnerinterne Verarbeitung gibt es eine besser zu verarbeitende Darstellung, die Darstellung mit Hilfe des Stellenkomplements. Das Stellenkomplement zu einer Ziffer bi ist die Differenz zur größten darstellbaren Ziffer, die immer den Wert B − 1 hat: ¯bi = B − 1 − bi (B – Basis des Zahlensystems). Das Stellenkomplement der Zahl 437 ist z.B. 562. Die Summe einer Zahl und ihres Stellenkomplements ist stets die größte mit der betrachteten Ziffernanzahl darstellbare Zahl. Für die dreistellige Dezimalzahl 437 und ihr Stellenkomplement gilt z.B. 437 + 562 = 999 Addiert man eins hinzu, ergibt sich die kleinste nicht darstellbare Zahl: Z + Z¯ + 1 = B n
(2.3)
Gleichung 2.3 lässt sich nach dem negierten Wert von Z umstellen: −Z = Z¯ + 1 − B n Der Summand −B n ist nicht darstellbar, weil der Stellenindex nur bis n − 1 reicht. Im darstellbaren Wertebereich ist eine negative Zahl gleich dem Stellenkomplement des Betrags plus eins: −Z = Z¯ + 1
(2.4)
Aber Achtung, eine Ziffernfolge kann nur entweder den positiven Wert Z=
n−1 X
bi · B i
i=0
oder den negativen Wert Z=
n−1 X
! bi · B
i
− Bn
i=0
aber nicht gleichzeitig beide Werte darstellen. Das verlangt eine Zusatzdefinition, bis zu welcher Ziffernfolge der Wert positiv und ab welcher Ziffernfolge er negativ ist. In Abb. 2.66 beginnt der Bereich der negativen Zahlen mit der Ziffernfolge 500.
154
2 Synthese und Logikoptimierung
500 −1000 −500
998 −1000 −2
999 −1000 −1
0
1
2
3
498 499
nicht darstellbar Addition ohne Wertebereichsbegrenzung Addition modulo-B n
Abb. 2.66. Zahlenkreis für vorzeichenbehaftete dreistellige Dezimalzahlen
Eine Subtraktion lässt sich stets durch die Addition des Stellenkomplements und einer Eins nachbilden. Dazu ein Beispiel: 231 − 008 = 223 231 + 991 + 1 = (1)223 Der Betrag eines negativen Ergebnisses wird wieder durch die Komplementbildung plus eins berechnet: 008 − 231 = −223 |−223| = 223 008 + 768 + 1 = 776 + 1 = 777 |777| = 222 + 1 = 223 Gegenüber der Darstellung vorzeichenfreier Zahlen ändert sich nur der Überlaufpunkt. Die Zählreihenfolge bleibt gleich. Dadurch sind auch die Berechnungsvorschriften für die Addition und Subtraktion identisch mit denen für vorzeichenfreie Zahlen. Das Binärsystem hat die Basis B = 2. Das Stellenkomplement einer »1« ist »0« und umgekehrt. Das ist eine bitweise Invertierung. Die Überlaufgrenze ist standardmäßig so festgelegt, dass das höchstwertige Bit das Vorzeichen beschreibt. Alle Ziffernfolgen mit bn−1 = 1 stellen negative und alle Ziffernfolgen mit bn−1 = 0 positive Werte dar. Diese Darstellung wird als Zweierkomplement bezeichnet. Gleichung 2.2 zur Berechnung des Wertes erhält für die Darstellung im Zweierkomplement die Form (P n−1 bi · 2i für bn−1 = 0 Z = Pi=0 n−1 i n b · 2 − 2 für bn−1 = 1 i=0 i und vereinfacht sich unter Nutzung der Beziehung 2n−1 − 2n = −2n−1 zu Z=
n−2 X i=0
bi · 2i − bn−1 · 2n−1
(2.5)
2.4 Zahlendarstellung
155
0102 + 0102 7→ 1002 100 Wert: −4 011 +1 0100∗
101 −3
110 −2
111 −1
000 0
001 1
010 +1 011
001 +1 010
000 invertierter Bitvektor +1 001 Bitvektor des Betrags
010 2
011 3 ∗
nicht mit drei Bits darstellbar
Abb. 2.67. Zahlenkreis für vorzeichenbehaftete dreistellige Binärzahlen
Die kleinsten und die größten mit einem n-Bit-Vektor darstellbaren Werte sind (vgl. Abb. 2.67) Z (1000...02 ) = −2n−1 Z (0111...12 ) = +2n−1 − 1
2.4.3 Festkommazahlen Zur Darstellung gebrochener Zahlen wird die Ziffernfolge um ein Komma und m Nachkommastellen erweitert. Die Nachkommastellen haben einen negativen Stellenindex. Der Wert einer vorzeichenfreien gebrochenen Zahl beträgt Z=
n−1 X
bi · B i
(2.6)
i=−m
Die vorzeichenfreie gebrochene Dezimalzahl 23,89 hat z.B. den Wert 2 · 101 + 3 · 100 + 8 · 10−1 + 9 · 10−2 und die vorzeichenfreie gebrochene Binärzahl 1001,112 hat den Wert 1 · 23 + 0 · 22 + 0 · 21 + 1 · 20 + 1 · 2−1 + 1 · 2−2 = 9,75 Vorzeichenbehaftete gebrochene Zahlen werden genau wie vorzeichenbehaftete ganze Zahlen mit Hilfe des Stellenkomplements dargestellt. Das führende Bit ist auch hier das Vorzeichenbit. Der Wert der positiven Zahlen ergibt sich nach Gleichung 2.6. Der Wert der negativen Zahlen ist um B n kleiner, bzw. das führende Bit wird negativ bewertet: Z=
n−2 X
bi · 2i − bn−1 · 2n−1
(2.7)
i=−m
Die größte darstellbare Zahl 01. . .1,11. . . 12 hat dem Wert 2n − 2−m und die kleinste darstellbare Zahl 10...0,00. . . 02 hat den Wert −2n .
156
2 Synthese und Logikoptimierung
2.4.4 Gleitkommazahlen Bei einer Festkommadarstellung bestimmt die Anzahl der Vorkommastellen den Wertebereich und die Anzahl der Nachkommastellen den Rundungsfehler. Für eine gegebene Bitanzahl ist die Kommaposition ein Kompromiss zwischen der Größe des Wertebereichs und der Genauigkeit. Zur Darstellung einer vorzeichenfreien Festkommazahl mit acht Bit gibt es z.B. die Möglichkeiten Vorkommastellen
Nachkommastellen
Wertebereich
maximaler Rundungsfehler
2
6
0 bis 22 − 2−6
±2−7
4
−4
±2−5 ±2−3
4
4
0 bis 2 − 2
6
2
0 bis 26 − 2−2
In einer Software-Entwicklung gilt es oft als zu aufwändig, vorab für alle variablen Größen den genutzten Wertebereich und die zulässigen Rundungsfehler zu untersuchen, um die Kommaposition optimal festzulegen. Die Alternative ist eine Gleitkommadarstellung. Die heute gebräuchlichen Gleitkommadarstellungen folgen dem Standard IEEE-754 [7]. Sie stellen den Zahlenwert durch ein Vorzeichenbit s, eine Mantisse M und eine Charakteristik c dar. Das Vorzeichenbit ist für negative Werte eins und sonst null. Die Mantisse beschreibt die Ziffernfolge und die Charakteristik die Kommaposition. In der normierten Darstellung wird die Kommaposition so gewählt, dass die Mantisse einen Festkommawert zwischen eins und zwei beschreibt. Das führende Bit der Mantisse M0 , das dann immer »1« ist, wird nicht mit dargestellt. Die Charakteristik ist eine vorzeichenfreie ganze Zahl, aus der sich die Kommaposition durch Subtraktion einer Verschiebekonstanten c0 , die etwa in der Mitte des Wertebereichs liegt, errechnet. Insgesamt berechnet sich der Wert wie folgt: Z = (−1)s · (1, M−1 . . . M−m ) · 2c−c0 (s – Wert des Vorzeichenbits; Mi mit 0 < i ≤ m – Bitwerte der Mantisse; c – Charakteristik; c0 – Verschiebekonstante). Die normierte Darstellung gilt nur für 0 < c < cmax Für c = 0 wird die Mantisse denormiert, d.h. mit einer führenden Null oder Eins dargestellt: Z = (−1)s · M0 , M−1 . . . M−(m−1) · 2−c0 Der größte Zahlenwert der Charakteristik ist für die Pseudo-Werte »unendlich«, »minus unendlich« und »ungültig« (nan – not a number) reserviert:
2.4 Zahlendarstellung
157
∞ für s = 0 und M = 0 Z = −∞ für s = 1 und M = 0 nan für M 6= 0 Ein Beispiel sei das standardisierte 32-Bit-Format »IEEE-754 single«. Es unterteilt einen 32-Bit-Vektor in ein Vorzeichenbit, 23 Bits für die Darstellung der Mantisse und acht Bits für die Charakteristik. Die Verschiebekonstante beträgt c0 = 7f16 . Im ersten Beispiel in Abb. 2.68 ist das Vorzeichen positiv und die Charakteristik um den Wert »4« größer als die Verschiebekonstante c0 . Der Mantisse ist die führende Vorkomma-Eins, die in der Bitvektordarstellung weggelassen wird, voranzustellen. Dann ist das Komma um vier Stellen nach rechts zu verschieben. Der Wert ist entsprechend positiv und größer als sechzehn. Im zweiten Beispiel ist das Vorzeichen negativ und die Charakteristik viel kleiner als c0 . Der Wert ist negativ und betragsmäßig viel kleiner als eins. Im dritten Beispiel ist die Charakteristik null. In der denormierten Darstellung ist das erste Bit der Charakteristik die Vorkommastelle und das Komma ist um 7F16 = 127 Stellen nach links zu verschieben. Bitvektor 31
24 23
16 15
8
Bitnummer 7 0
s c M 0 1 00000 1 1 001001 0 00000000 000000000 8
3
2
7
9
C
4
0
0
0
3
A
1
0
+ c = 8316 M = 1, 24000016 1 0111100 11100101 10011101 000011100 B
C
Wert
+1.24000016 · 28316 −7f 16 = 18,25 −1.CB3A1C16 · 27916 −7f 16 ≈ −2,8029 · 10−2
− c = 7916 M = 1,CB3A1C16 0 0000000 00001100 10111101 0001100100 +0,32F46416 · 20−7f 16 , 3 2 F 4 6 4 ≈ 1,170 · 10−39 0/denorm. M = 0,32F46416
Abb. 2.68. Darstellung von Beispielwerten im IEEE-754-32-Bit-Gleitkommaformat
Eine Gleitkommaoperation besteht aus mehreren Festkommaoperationen. Für eine Addition sind z.B. zuerst die Charakteristiken zu vergleichen. Die kleinere Charakteristik ist an den Wert der größeren anzupassen, indem der Mantisse eine entsprechende Anzahl von Nullen vorangestellt wird. Die eigentliche Addition erfordert eine Fallunterscheidung nach den Vorzeichenbits beider Summanden. Die Charakteristik des Ergebnisses ist wieder so anzupassen, dass die Mantisse eine führende Eins hat. Hinzukommen die Sonderbehandlungen für die denormierte Darstellung und die Pseudo-Werte für ungültig etc. Die Ausführung einer Gleitkommaoperation erfordert entweder mehrere Zeitschritte oder mehrere Festkommarechenwerke.
158
2 Synthese und Logikoptimierung
2.4.5 Zahlendarstellung und -verarbeitung in VHDL In VHDL können Zahlenwerte durch Zahlentypen oder Bitvektortypen dargestellt werden. Zahlentypen stellen die Werte abstrakt und Bitvektortypen stellen sie in der Bitcodierung der Schaltung dar. Der vordefinierte Zahlentyp type integer is range -2**31 to (2**31)-1;
stellt seine Werte in der Hardware als 32-Bit-Vektor im Zweierkomplement dar. Von integer sind zwei wertebereichsbeschränkte Untertypen abgeleitet: subtype natural is integer range 0 to integer’high; subtype positive is integer range 1 to integer’high;
Deren Werte werden in der Hardware durch vorzeichenfreie 31-Bit-Vektoren dargestellt. In Abschnitt 3.2.1 wird später gezeigt, dass in VHDL beliebig viele weitere ganzzahlige Datentypen und Untertypen mit beliebigen, an die jeweilige Aufgabenstellung anpassbaren Wertebereichen vereinbart werden können, die in der Hardware mit der entsprechenden Bitanzahl und Bitcodierung dargestellt werden. Für alle Zahlentypen sind die arithmetischen Operationen in Tabelle 2.3 definiert. Tabelle 2.3. Arithmetische Operatoren für Zahlentypen a ∗ ∗b
Potenzieren ab
a∗b
Multiplikation
a/b
Division
a rem b
Divisionsrest von
a b
a mod b Modulo-Operation
+a
Identität
−a
Negation
abs a
Betrag |a|
a+b
Addition
a−b
Subtraktion
Für Bitvektortypen sind die arithmetischen Operatoren selbst zu programmieren. VHDL bietet dazu die Möglichkeit, jeden vordefinierten Operator für beliebige Datentypen zu überladen. Da die Operationsausführung von der Zahlendarstellung abhängt, ist für jede Art der Zahlendarstellung ein eigener Bitvektortyp zu definieren10 . Das Buch verwendet für die Darstellung vorzeichenfreier ganzer Zahlen und für die Zweierkomplementdarstellung vorzeichenbehafteter ganzer Zahlen die beiden mit std_logic_vector eng verwandten Bitvektortypen type tUnsigned is array (natural range <>) of std_logic; type tSigned is array (natural range <>) of std_logic; 10
Das ist die einzige Möglichkeit, die Operatorfunktionen für unterschiedliche Zahlendarstellungen unterschiedlich zu überladen.
2.4 Zahlendarstellung
159
Diese sind gemeinsam mit allen Operatoren und Unterprogrammen für sie in den Packages »Tuc.Numeric_Sim« und »Tuc.Numeric_Synth« definiert (siehe später Abschnitt 3.2.7 und Anhang A.2.4). Festkommazahlen werden im Weiteren auf der Bitvektorebene durch ganze Zahlen mit gedachter Kommaposition dargestellt, und für Gleitkommazahlen werden in den nachfolgenden Simulationsmodellen ausschließlich reellwertige Zahlentypen verwendet. Die Werte von Zahlentypen werden durch Ziffernfolgen dargestellt. Zur Verbesserung der Lesbarkeit darf zwischen zwei Ziffern ein einzelner Unterstrich eingefügt sein, z.B. »1_000_000« für eine Million. Zur Darstellung in einem anderen Zahlensystem als dem Dezimalsystem wird die Ziffernfolge in Nummernzeichen »#« eingerahmt und der Wert der Basis des Zahlensystems vorangestellt (Tabelle 2.4). Die Basis des Zahlensystems darf dabei auch einen von »2«, »8« oder »16« abweichenden Wert haben. Zahlenkonstanten für Bitvektortypen bestehen aus dem vorangestellten Zeichen »b«, »o« oder »x« für die Basis des Zahlensystems und der Ziffernfolge als Zeichenkette. Auch hier ist ein einzelner Unterstrich zur Trennung von Zifferngruppen erlaubt (siehe Anhang A.1.5). Alternativ können die Zahlenwerte auch durch ganz normale Bitvektorkonstanten aus den Zeichen »U«, »X«, »0«, »1« etc. dargestellt werden. Eine Dezimaldarstellung gibt es für Bitvektorkonstanten nicht, dafür aber Konvertierungsfunktionen, die auch dezimal dargestellte Zahlenwerte in die entsprechende Bitvektordarstellung umwandeln (siehe Anhang A.2.4). Tabelle 2.4. Darstellung ganzzahliger Werte Basis
Beispielwert
10
1.000
VHDL-Zahlenkonstante VHDL-Bitvektorkonstante 1_000
to_t[Un]signed(1_000)
2
1110112
2#11_1011#
b"11_1011"
8
731468
8#73146#
o"73146"
16
1FC2AA16
16#1FC2AA#
x"1FC2AA"
Bis zur Standardrevision VHDL-2008 entspricht eine Hexadezimalziffer in der Bitvektordarstellung exakt vier und eine Oktalziffer exakt drei Binärziffern. Ab VHDL-2008 dürfen überflüssige Nullen vorangestellt sein. Dann kann z.B. x"5" auch zur Darstellung der 3-Bit-Zahl "101" verwendet werden. 2.4.6 Zusammenfassung und Übungsaufgaben Vorzeichenfreie Zahlen werden in Hardware im Stellenwertsystem mit der Basis »2« dargestellt und negative Zahlen im Zweierkomplement. Für beide Darstellungsarten ist die Zählreihenfolge identisch. Nur die Werte, bei denen der Zahlenbereich überläuft, sind zirkular verschoben. Die Darstellung von Festkommazahlen erfolgt mit ganzen Zahlen und einer gedachten Kommaposition.
160
2 Synthese und Logikoptimierung
Gleitkommazahlen werden durch ein Vorzeichenbit, einen Bitvektor für die Kommaposition und einen Bitvektor für die Ziffernfolge dargestellt. In VHDL können Zahlenwerte abstrakt durch Zahlentypen oder codiert als Bitvektoren dargestellt werden. Für alle Zahlentypen sind die arithmetischen Operationen – Addition, Subtraktion etc. – definiert. Um diese Operationen auch für Bitvektortypen definieren zu können, benötigt jede Art der Zahlendarstellung ihren eigenen Bitvektortyp. Weiterführende und ergänzende Literatur siehe [11, 36, 39]. Aufgabe 2.15 Konvertieren Sie die folgenden Dezimalzahlen durch fortlaufende Division in Binärzahlen und konvertieren Sie anschließend die Binärzahlen durch Zusammenfassung von Zifferngruppen in Oktalzahlen und Hexadezimalzahlen: a) 345 b) 1023 Aufgabe 2.16 Stellen Sie die folgenden negativen Zahlen im »Stellenkomplement plus eins« dar: a) −(2A2F)16 (vier darstellbare Hexadezimalziffern) b) − (0110 1001)2 (acht darstellbare Binärziffern) Kontrollieren Sie Ihr Ergebnis über die Probe, dass die Summe einer negativen Zahl und ihres Betrags stets null sein muss. Aufgabe 2.17 Welche der nachfolgenden Zeichenfolgen beschreiben in VHDL Zahlenkonstanten? Welche Werte und Typen haben die Konstanten in diesem Fall? a) b) c) d)
16#3f1# 2#101_312# 1fe5 2e2
Aufgabe 2.18 Welche Werte stellen die folgenden Bitvektoren im 32-Bit-IEEE-754-Gleitkommaformat dar? a) 57A3281016 b) FFFFFFFF16 c) A7A631FE16
2.5 Addierer
161
2.5 Addierer Alle arithmetischen Operationen basieren auf der Addition. Die Negation wird durch eine bitweise Invertierung und die Addition einer »1« nachgebildet. Die Subtraktion ist gleich der Addition des negierten Wertes. Die Multiplikation und Division beinhaltet mehrere Additionen bzw. Subtraktionen. 2.5.1 Ripple-Addierer Die Addition in einem Stellenwertsystem erfolgt ziffernweise. Bei einer Ziffernsumme, die größer als der Wert der größten darstellbaren Ziffer ist, wird der Übertrag zur nächsthöheren Stelle addiert (Abb. 2.69). 0 +1 (0)
1
1 0 (0)
1
1 0 (0)
1
0 0 (0)
0
0 0 (0)
1
1 0 (1)
0
0 1 (1)
0
1 1 (1)
erster Summand zweiter Summand ¨ Ubertrag Summe
0
Abb. 2.69. Ein Beispiel für eine binäre Addition
Die binäre Addition fasst für jede Stelle zwei Ziffernbits und ein Übertragsbit zu einem Summenbit und einem Übertragsbit zusammen. Eine Schaltung mit dieser Funktion wird als Volladdierer bezeichnet. Abbildung 2.70 zeigt die Wertetabelle und das im Weiteren verwendete Symbol. Das Summenbit si ist genau dann »1«, wenn ein oder alle Eingabebits »1« sind. Das entspricht der EXOR-Verknüpfung si = ai ⊕ bi ⊕ ci (2.8) (ai , bi – Operandenbits; ci – einlaufender Übertrag; si – Summenbit). Der weiterzureichende Übertrag ist »1«, wenn mindestens zwei der drei zu addierenden Bits »1« sind: ci+1 = ai bi ∨ ai ci ∨ bi ci (2.9) ci bi ai
ci+1 si
ai bi ci
Halbaddierer
0 0 0 0
0 0 1 1
0 1 0 1
0 0 0 1
0 1 1 0
ai
1 1 1 1
0 0 1 1
0 1 0 1
0 1 1 1
1 0 0 1
ai bi ci
bi
HA
s
si
c
ci+1
ai
Volladdierer
a)
VA
s
si
c
ci+1
bi
b)
ci
=1
=1
si
≥1
ci+1
& & &
c)
Abb. 2.70. Voll- und Halbaddierer a) Wertetabelle b) Symbole c) Schaltungsbeispiel für den Volladdierer
162
2 Synthese und Logikoptimierung
Es gibt einen weiteren Addierer-Grundbaustein, der nur zwei Eingabebits addiert. Das ist der Halbaddierer. Seine Summen- und seine Übertragsfunktion ergeben sich aus denen des Volladdierers durch Einsetzen von ci = 0 und Elimination der Konstanten in den Ausdrücken: si = ai ⊕ bi
(2.10)
ci+1 = ai bi
(2.11)
Das Funktionsmodell eines n-Bit-Addierers ist eine Schleife, die für alle Operandenbits beginnend mit dem niedrigsten das Summen- und das Übertragsbit berechnet (Abb. 2.71). Eine Wiederholschleife beginnt mit dem Schlüsselwort »for«. Dann folgt die Schleifenvariable, die nacheinander alle Werte des danach angegebenen Bereichs – im Beispiel die Werte von »0« bis n − 1 – annimmt (siehe später Abschnitt 3.1.3). Für jeden Indexwert wird im Schleifenkörper ein Volladdierer nachgebildet. Aufgerollt ergibt sich eine Kette aus n Volladdierern. Die aufgerollte Schaltung wird als Ripple-Addierer bezeichnet. In einem Ripple-Addierer verläuft der Pfad mit der längsten Verzögerung, der die Gesamtverzögerung der Schaltung bestimmt, vom Übertragseingang cin zum Übertragsausgang cout durch alle Volladdierer. Die Gesamtverzögerungszeit nimmt linear mit der Bitbreite n zu: tdAdd = n · tdVA
(2.12)
(tdVA – Verzögerung eines einzelnen Volladdierers; tdAdd – Verzögerung des gesamten Addierers). signal a,b,s:std_logic_vector(n-1 downto 0); signal cin, cout: std_logic; ... cin process(a, b, cin) a0 b0 variable c: std_logic; begin c := cin; a1 for i in 0 to n-1 loop b1 s(i) <= a(i) xor b(i) xor c; c := (a(i) and b(i)) or (a(i) and c) or (b(i) and c); an−1 end loop; bn−1 cout <= c; end process; ⇒Web-Projekt:
tdVA VA
VA
VA
s
s0
c s
s1
c s
sn−1
c
cout
P2.5/RippleAdd.vhdl
Abb. 2.71. Iterative Funktionsbeschreibung und Schaltung eines n-Bit-RippleAddierers
Die Addition von Zahlen im Zweierkomplement erfolgt nach demselben Algorithmus. Denn beide Zahlendarstellungen verwenden denselben Zahlenkreis. Nur der Überlaufspunkt, an dem auf den größten der kleinste darstellbare
2.5 Addierer
163
Wert folgt, liegt an einer anderen Stelle. Zur Kontrolle, dass das Additionsergebnis im zulässigen Bereich liegt, ist zusätzlich der Übertrag der höchstwertigen Bitstelle auszuwerten. Für eine vorzeichenfreie Zahl ist er null, wenn das Ergebnis im darstellbaren Bereich liegt. Eine Summe im Zweierkomplement liegt genau dann im darstellbaren Bereich, wenn das Übertragsbit mit dem Vorzeichenbit übereinstimmt11 (Abb. 2.72).
a)
±1∗∗
−1∗
+1∗
Darstellung: 1 111 0 001 0 000 0 010 · · · 0 110 0 111 1 000 Wert: -1 0 2 ··· 6 7 8 b)
±1∗∗
−1∗
+1∗
Darstellung: 1 011 1 100 1 101 1 110 · · · 0 010 0 011 0 100 -5 -4 -3 -2 · · · 2 3 4 Wert: darstellbarer Bereich
∗ ∗∗
¨ Ubertragsbit nichtdarstellbar Operation auf dem Zahlenstrahl Operation auf dem Zahlenkreis
Abb. 2.72. Zahlenkreis a) für vorzeichenfreie 3-Bit-Zahlen b) für vorzeichenbehaftete 3-Bit-Zahlen
2.5.2 Serieller Addierer Die Verzögerungszeit eines Ripple-Addierers nimmt nach Gleichung 2.12 proportional mit der Bitbreite zu. So stellt sich die berechtigte Frage, ob es nicht zweckmäßiger ist, die Bits nacheinander, in aufeinanderfolgenden Takten zu addieren. In einer Software-orientierten Ablaufbeschreibung ist dafür im Schleifenkörper nach jeder Bitaddition eine Warteanweisung auf die nächste aktive Taktflanke einzufügen (Abb. 2.73 a). Die Kernoperation erfordert jetzt nur noch einen Volladdierer. Es kommen aber noch weitere Teilschaltungen hinzu, z.B. ein 1-Bit-Register zur Speicherung des Übertrags mit einem Eingangsmultiplexer, der zwischen dem Eingangsübertrag cin und dem Übertragsausgang des Volladdierers auswählt (Abb. 2.73 b). In der VHDLBeschreibung in Abb. 2.73 a wartet der Prozess zusätzlich vor jeder Addition auf die Aktivierung eines Startsignals und einen weiteren Takt auf die Übergabe der Summanden und des Eingabeübertrags cin an die Signale a, b und c. Während der Addition wird ein Busy-Signal aktiviert, das der umgebenden Schaltung mitteilt, dass das Rechenwerk beschäftigt ist. Abbildung 2.73 c zeigt einen Beispielsignalverlauf. Nach Aktivierung des Startsignals übernehmen die Summandenregister a und b die Bitwerte »1010« 11
Merkhilfe: Wenn eine Zweierkomplementzahl um ein führendes Bits verlängert wird, darf sich das Vorzeichen nicht ändern.
164
2 Synthese und Logikoptimierung
tdVA signal a,b,s:std_logic_vector(3 downto 0); tdmux signal T,c,cin,cout,Start,busy:std_logic; ai s s ts tdr i ... bi VA c c 0 process cout 1 c+ cin begin busy <= ’0’; Start T wait until Start=’1’ and rising_edge(T); b) Pfad mit der max. Verz¨ogerung busy <= ’1’; c <= cin; a <= ...; b <= ...; T wait until rising_edge(T); Start for i in 0 to 3 loop busy s(i) <= a(i) xor b(i) xor c; a U 1010 1000 c <= (a(i) and b(i)) or (a(i) and c) b U 0011 1001 or (b(i) and c); c U wait until rising_edge(T); U s0 end loop; s1 U U s2 end process; U ⇒Web-Projekt: P2.5/SerAdd.vhdl a) cout <= c; c) s3
Abb. 2.73. Funktionsmodell eines seriellen Addierers a) VHDL-Beschreibung b) Schaltungsskizze der Kernoperation c) Beispielsignalverlauf
(zehn) und »0011« (drei). Der Eingangsübertrag ist im Beispiel cin = 0. Mit der Wertübernahme wird das Busy-Signal aktiviert. Dann folgen die vier bitweisen Additionen. Der erste Additionsschritt berechnet s0 = 1 und c = 0, der zweite s1 = 0 und c = 1 etc. Nach allen Additionsschritten steht im Summenregister s der Bitvektor »1101« mit dem Wert dreizehn. Dem Ausgabeübertrag wird der Wert cout = 0 zugewiesen. Ein Prozess wird für die Simulation in eine Endlosschleife übersetzt. Nach Abarbeitung der Schleife springt der Berechnungsfluss wieder zur ersten Anweisung, die das Busy-Signal deaktiviert. Mit der nächsten Aktivierung des Startsignals wiederholt sich der gesamte Ablauf mit anderen Operanden. Zur Abschätzung der Dauer einer Addition gilt Folgendes. Die Dauer der Taktperiode muss größer als die maximale Register-Register-Verzögerungszeit plus der Vorhaltezeit sein. Jeder Berechnungsschritt dauert mindestens TP ≥ tdr + tdVA + tdmux + ts (TP – Taktperiode; tdr – Verzögerung des Registers; tdVA – Verzögerung Volladdierers; tdmux – Verzögerung des Multiplexers; ts – Vorhaltezeit Registers). Die Ausführungszeit ist mindestens so groß wie die Summe Zeiten für die n 1-Bit-Additionen und den Zeitschritt für die Übernahme Eingabedaten: tAdd ≥ (n + 1) · TP
des des der der
Durch die zusätzlichen Verzögerungszeiten, die zusätzliche Vorhaltezeit und den zusätzlichen Berechnungsschritt für die Eingabedatenübernahme verdop-
2.5 Addierer
165
pelt sich etwa die Zeit für die gesamte Addition gegenüber der des RippleAddierers in Abb. 2.71 b. Dafür wird nur noch ein Volladdierer statt der n für den Ripple-Addierer benötigt. Das sieht wie ein gutes Preis-LeistungsVerhältnis aus. Für einen detaillierten Vergleich ist der Schaltungsentwurf des seriellen Addierers zu Ende zu führen, was hier auch exemplarisch erfolgen soll. Für die Überführung der Software-orientierten Ablaufbeschreibung aus Abb. 2.73 in eine synthesefähige Beschreibung ist nach Abschnitt 1.6 entweder ein Zustandsgraph oder ein Operationsablaufgraph aufzustellen. Der Unterschied zwischen beiden Ansätzen ist, dass bei einem Zustandsgraph alle Informationen im Zustand gespeichert werden, während bei einer Beschreibung mit einem Operationsablaufgraphen der größte Teil der Information in anderen Datenobjekten gespeichert wird, die mit einem beschränkten Satz von Operationen – in den bisherigen Beispielen nur mit Zähloperationen – bearbeitet werden. Für die serielle Addition bietet sich die Berechnung der Summen- und Übertragsbits mit einem Volladdierer geradezu an. Weiterhin ist es nahe liegend, die Bitnummer mit einer Zähloperation weiterzustellen. Im Operationsablauf ist dann nur noch zwischen den zwei Zuständen »bereit« und »beschäftigt« zu unterscheiden. Der Zustand ist praktisch der Wert des Busy-Signals (Abb. 2.74 a). Bei der Definition der Datenobjekte, der auf sie anzuwendenden Verarbeitungsoperationen und des gesamten Operationsablaufs für den seriellen Addierer taucht ein Problem auf. Die Beschreibung in Abb. 2.73 b enthält indizierte Registerzugriffe zum Lesen der Summanden- und zum Schreiben der Summenbits. Dafür wurde noch keine Schaltung eingeführt. Wenn, wie bei der seriellen Addition, der Zugriff auf die einzelnen Bits immer in derselben Reihenfolge erfolgt, können die wahlfrei adressierbaren Register durch Schieberegister ersetzt werden. Die Summandenregister werden parallel beschrieben und stellen die Bits in aufsteigender Reihenfolge bereit. Das Summenregister übernimmt die Summenbits in aufsteigender Reihenfolge und gibt sie parallel als Bitvektor weiter. Der Datenflussteil des seriellen Addierers besteht insgesamt aus den Schieberegistern für die Summanden und die Summe, aus dem Register für den Übertrag, dem Volladdierer, dem Bitzähler und Schaltelementen für die Datenflussumschaltung (Abb. 2.74 b). Durch die Umstellung auf Schieberegister wandern die Bits in den Summanden- und im Summenregister in der Simulation in Abb. 2.74 c und dann auch in der zu entwerfenden Schaltung nach jedem Additionsschritt eine Bitposition nach links. Das ist nicht mehr genau dasselbe Verhalten wie in der Software-orientierten Ablaufbeschreibung in Abb. 2.73 c. Die Nachbildung des Operationsablaufs in Abb. 2.74 a und der RegisterTransfer-Operationen in Abb. 2.74 b durch eine VHDL-Beschreibung ist relativ einfach. Der Beschreibungsrahmen ist ein Abtastprozess mit dem Takt in der Weckliste. Das Busy-Signal übernimmt die Rolle des Zustandssignals (Abb. 2.74 d). Wenn es eins ist, sind alle in Abb. 2.74 b dargestellten Operationen auszuführen. Wenn der Zähler seinen Endwert erreicht hat, ist zusätzlich
166
2 Synthese und Logikoptimierung
signal a,b,s:std_logic_vector(3 downto 0); busy = 0 sonst signal T,c,cin,cout,Start,busy: std_logic; Start = 1 signal ct: tUnsigned(1 downto 0); ct = 11 ... ct <= 00; c <= cin; a <= ...; b <= ...; process(T) begin busy = 1 sonst if rising_edge(T) then a) Bitaddition ct = ct + 1 if busy=’1’ then s <= (a(0) xor b(0) xor c) & s(3 downto 1); c <= (a(0) and b(0)) or (a(0) and c) or (b(0) and c); a <= ’0’ & a(3 downto 1); a3 a2 a1 a0 s3 s2 s1 s0 b <= ’0’ & b(3 downto 1); s ct <= ct + "1"; VA c ct b3 b2 b1 b0 if ct="11" then c +1 busy <= ’0’; b) T end if; T elsif Start=’1’ then Start ct <= "00"; busy <= ’1’; busy c <= cin; a <= ...; b <= ...; ct 00 01 10 11 00 end if; c U end if; a UUUU 1010 0101 0010 0001 0000 end process; b UUUU 0011 0001 0000 ⇒Web-Projekt: P2.5/SerAdd.vhdl c) s d) cout <= c; UUUU 1UUU 01UU 101U 1101 Abb. 2.74. Synthesefähige Beschreibung eines seriellen 4-Bit-Addierers a) Operationsablaufgraph b) Datenfluss mit Schieberegistern c) Beispielsignalverlauf d) VHDL-Beschreibung (»-« – Wert ohne Bedeutung, don’t care)
das Busy-Signal zu deaktivieren. Wenn das Busy-Signal nicht aktiv ist, passiert nur etwas, wenn das Startsignal aktiv ist. In diesem Fall sind alle an der abgehenden Graphenkante in Abb. 2.74 a aufgelisteten Signalzuweisungen auszuführen: Bitzähler rücksetzen, Summanden in die Summandenregister und Eingabeübertrag in das Übertragsregister übernehmen. Der Ausgabeübertrag ist gleich dem Wert des Zustandsregisters nach Abschluss der Addition und wird nebenläufig, d.h. außerhalb des Prozesses, zugewiesen. Zusammenfassend hat ein serieller Addierer doch kein überragend gutes Aufwand-Nutzen-Verhältnis. Der Pfad mit der längsten Verzögerung ist offensichtlich der durch den Bitzähler, nicht der durch den Volladdierer. Die erforderliche Taktperiode nimmt mit der Zählerbreite und damit mit der Bitbreite n der Addition zu. Die gesamte Additionsdauer hat die Ordnung log (n)·n. Der Schaltungsaufwand wird hauptsächlich durch den Aufwand für die Register bestimmt und wächst genau wie bei einem Ripple-Addierer näherungsweise proportional zur Bitbreite. Das Beispiel hat einige praktische Aspekte der Nachbildung von Algorithmen durch sequenzielle Schaltungen gezeigt. Die Verteilung der Berech-
2.5 Addierer
167
nungsschritte zur Nachbildung einer kombinatorischen Funktion auf mehrere Zeitschritte erfordert nur das Einfügen von Warteanweisungen auf die nächste aktive Taktflanke. Nach dieser kleinen Erweiterung kann der sequenzielle Ablauf bereits komplett simuliert und verifiziert werden. Zur Überführung in eine synthesefähige Beschreibung ist der Ablauf zuerst durch Datenflussoperationen und einen Operationsablaufgraphen nachzubilden. In dieser Teilaufgabe steckt einiges an Kreativität und Arbeitsaufwand, bis eine gute Lösung gefunden ist. Die weitere Umsetzung in eine synthesefähige VHDL-Beschreibung hat Rezeptcharakter und lässt sich mit etwas Erfahrung schnell hinschreiben. Auch wenn beim Übergang zu einer synthesefähigen Beschreibung die Modellentwicklung noch einmal von vorn beginnt, ist die Entwicklung der Softwareorientierten Ablaufbeschreibung davor keine Zeitverschwendung. Denn mit diesem Modell wurde der Algorithmus entwickelt und kontrolliert, so dass der Entwurf der zweiten Modellversion mit einer formal beschriebenen und verifizierten Funktionsbeschreibung als Vorlage starten konnte. Die Entwicklung mit mehreren Beschreibungsversionen ist typisch für den Hardware-Entwurf und auch im Sprachkonzept von VHDL verankert12 . 2.5.3 Schneller Übertragsdurchlauf Die Gesamtverzögerung des Ripple-Addierers in Abb. 2.71 ist nach Gleichung 2.12 die Summe der Übertragsverzögerungen alle Volladdierer. Zur Verkürzung der Additionsdauer genügt es folglich, die Verzögerungen zwischen den Übertragseingängen und den Übertragsausgängen der einzelnen Volladdierer zu minimieren. Der im Weiteren vorgestellte Ansatz benutzt dazu zwei Hilfssignale: • •
pi – Übertrag weiterleiten (propagate) und gi – Übertrag generieren (generate).
Das Propagate-Signal pi soll genau dann eins sein, wenn der ausgangsseitige Übertrag gleich dem eingangsseitigen Übertrag ist. Das ist genau dann der Fall, wenn eines der beiden Summandenbits ai oder bi , aber nicht beide gleichzeitig eins sind: pi = ai ⊕ bi (2.13) Das Generate-Signal soll eins sein, wenn der ausgangsseitige Übertrag des Volladdierers unabhängig vom eingangsseitigen Übertrag eins ist. Dafür müssen ai und bi gleichzeitig eins sein: gi = ai bi
(2.14)
Mit diesen beiden Hilfssignalen liefert ein Volladdierer genau dann an seinem Ausgang einen Übertrag, wenn entweder ein Eingangsübertrag weitergeleitet oder wenn ein Übertrag generiert wird: 12
Eine Entwurfseinheit kann mehrere Beschreibungsversionen haben, die anhand des Beschreibungsnamens unterschieden werden.
168
2 Synthese und Logikoptimierung
ci+1 = gi ∨ (pi ∧ ci )
(2.15)
Das Summenbit am Ausgang des Volladdierers ist die EXOR-Verknüpfung des Eingangsübertrags mit dem Propagate-Signal: si = ai ⊕ bi ⊕ ci = pi ⊕ ci
(2.16)
Abbildung 2.75 zeigt einen Ripple-Addierer mit den so modifizierten Volladdierern. Jeder Volladdierer besteht aus den drei Gattern zur Bildung des Propagate-, des Generate- und des Summensignals sowie der UND-ODERVerknüpfung für die Übertragsweiterleitung und -generierung. Der längste Pfad verläuft hauptsächlich über die UND-ODER-Verknüpfungen der Übertragskette. Durch die Wahl optimaler Gatter und kurzer Verbindungen entlang dieses Pfades lässt sich die Additionszeit mit dieser Schaltungsstruktur gegenüber der eines Ripple-Addierers ohne schnellen Übertragsdurchlauf etwa halbieren, ohne dass dafür ein nennenswerter zusätzlicher Schaltungsaufwand erforderlich ist [39]. s0 tdex
s1
=1
tdcy &
c0
p0 tdex
=1 a0
tdex ≥1
=1
&
b0
≥1
p1 tdex
=1
&
a1
p2 tdex
b1
l¨ angster Pfad f¨ ur tdex > tdcy tdex Verz¨ogerung eines EXOR-Gatters
=1 a2
=1
tdcy tdex
c2
g1 &
s3
=1
tdex
tdcy
c1
g0 &
s2
≥1
&
c3
g2 & b2
tdcy
p3 tdex
=1 a3
≥1
c4
g3 & b3
tdcy Verz¨ogerung der UND¨ ODER-Ubertragslogik
Abb. 2.75. Addierer mit schnellem Übertragsdurchlauf
2.5.4 Hierarchische Addierer Ein anderes Prinzip zur Verringerung der Verzögerungszeit ist die Umformung von Ketten in Bäume. Für die Addition ist es dazu erforderlich, die Übertragsberechnung in einen Ausdruck aus assoziativen Operatoren umzuformen (vgl. Abschnitt 2.2.2). Der hierfür vorgestellte Ansatz basiert auf der Definition der Generate- und der Propagate-Signale als Blocksignale. Das BlockPropagate-Signal p0.i sei genau dann »1«, wenn der Eingangsübertrag c0 über i + 1 Bitstellen bis zum Übertrag ci+1 weitergeleitet wird. Es ist praktisch die UND-Verknüpfung der Propagate-Signale p0 bis pi : p0,i = pi ∧ pi−1 ∧ . . . ∧ p0
2.5 Addierer
169
Rekursiv beschrieben ist es die UND-Verknüpfung des Propagate-Signals der Bitstelle i mit dem Block-Propagate-Signal der Bitstelle i − 1: p0.i = pi ∧ p0.i−1 Das Block-Generate-Signal ist eins, wenn Stelle j einen Übertrag generiert oder ein für die Stelle j − 1 generierter Übertrag weitergeleitet wird: g0.i = gi ∨ (pi ∧ g0.i−1 ) Die rekursive Berechnung beider Blocksignale lässt sich zu einer OperatorFunktion zusammenfassen, die jeweils aus dem Propagate- und dem GenerateSignal der aktuellen Bitstelle und dem Block-Propagate- und dem BlockGenerate-Signal der vorherigen Stelle das Block-Propagate- und das BlockGenerate-Signal der aktuellen Stelle berechnet: (p0.i , g0.i ) = (pi , gi ) ◦ (p0.i−1 , g0.i−1 ) Mit dem Rekursionsanfang (p0.0 , g0.0 ) = (p0 , g0 ) lautet die Berechnung für den Kettenanfang (p2 , g2 ) ◦ [(p1 , g1 ) ◦ (p0 , g0 )] = (p2 p1 p0 , g2 ∨ g1 p2 ∨ g0 p2 p1 ) Jetzt kommt der springende Punkt. Der so definierte Operator ist assoziativ. Eine Änderung der Berechnungsreihenfolge hat keinen Einfluss auf das Ergebnis:
c0 p0 g0 p1 g1 p2 g2 p3 g3
a) Kettenstruktur p0,0 g0,0 p0,1
& &
≥1
≥1
c1
& ≥1
g0,1
c2
& ≥1
g0,2
≥1
c3
≥1
c4
p0,3
& &
&
p0,2
& &
b) Baumstruktur
& ≥1
g0,3
p0 g0 p1 g1 p2 g2 p3 g3
p0,1
& &
≥1
p2,3
& &
g0,1
≥1
g2,3
& &
p0,3 = p3 p2 p1 p0 ≥1
g0,3 = g3 ∨ g2 p3 ∨ g1 p3 p2 ∨ g0 p3 p2 p1
td◦ td◦ verk¨ urzte Darstellung (p0 , g0 ) (p0,3 , g0,3 ) (p1 , g1 ) (p2 , g2 ) td◦ Verz¨ogerung Operator (p3 , g3 ) pg-Tupel (2-Bit-Signal)
Abb. 2.76. Umformung der Übertragsberechnung mit Block-Propagate- und BlockGenerate-Signalen von einer Kettenstruktur in eine Baumstruktur
170
2 Synthese und Logikoptimierung
[(p2 , g2 ) ◦ (p1 , g1 )] ◦ (p0 , g0 ) = (p2 p1 p0 , g2 ∨ g1 p2 ∨ g0 p2 p1 ) Damit ist die Bedingung für die Umwandlung in eine Baumstruktur erfüllt (Abb. 2.76 b). Abbildung 2.77 zeigt einen 8-Bit-Addierer, der die Block-Propagate- und die Block-Generate-Signale mit einer Baumstruktur berechnet. Die Berechnung der einfachen Propagate- und Generate-Signale pi und gi erfolgt wie in Abb. 2.75 jeweils mit einem EXOR- und einem UND-Gatter (nicht dargestellt). Dann folgt die Baumstruktur für die Bildung der Blocksignale p0,i und g0,i . In der nächsten Verarbeitungsschicht werden aus den Blocksignalen und dem Eingangsübertrag c0 die Überträge der einzelnen Bitstellen berechnet. Die Summenbits sind die EXOR-Verknüpfungen der (einfachen) PropagateSignale mit dem Übertrag der vorherigen Bitstelle. ≈ td◦
≤ 4 · td◦
(p7 , g7 )
(p0,7 , g0,7 )
(p6 , g6 )
(p0,6 , g0,6 )
(p5 , g5 )
(p0,5 , g0,5 )
(p4 , g4 )
(p0,4 , g0,4 )
(p3 , g3 )
(p0,3 , g0,3 )
(p2 , g2 )
(p0,2 , g0,2 )
(p1 , g1 ) (p0 , g0 )
(p0,1 , g0,1 ) l¨ angster Pfad pg-Tupel (2-Bit-Vektor)
⇒ Web-Projekt: P2.5/HiAdd.vhdl
c0
≈ 2 · td◦ (c0 ∧ p0.7 ∨ g0.7 )
c8
(c0 ∧ p0.6 ∨ g0.6 )
c7
(c0 ∧ p0.5 ∨ g0.5 )
c6
(c0 ∧ p0.4 ∨ g0.4 )
c5
(c0 ∧ p0.3 ∨ g0.3 )
c4
(c0 ∧ p0.2 ∨ g0.2 )
c3
(c0 ∧ p0.1 ∨ g0.1 )
c2
(c0 ∧ p0 ∨ g0 )
c1
p7 p6 p5 p4 p3 p2 p1 p0
cout =1
s7
=1
s6
=1
s5
=1
s4
=1
s3
=1
s2
=1
s1
=1
s0
Abb. 2.77. Hierarchischer 8-Bit-Addierer mit logarithmischer Laufzeit
Die größte Anzahl der Operatorfunktionen liegt auf dem Pfad von (a0 , b0 ) über (p0 , g0 ) nach s7 und beträgt vier. Zur Verzögerung durch die Operatoren kommen noch weitere Verzögerungen von insgesamt etwa 3 · td◦ für die Bildung der Propagate- und Generate-Signale am Eingang und die Bildung der Übertrags- und der Summensignale am Ausgang hinzu. Eine Verdopplung der Eingangsanzahl vergrößert die Verzögerung bei dieser Struktur jeweils nur um 1 · td◦ . Bei einem Ripple-Addierer verdoppelt sie sich im Vergleich dazu bei einer Verdopplung der Bitanzahl. Die geringere asymptotische Ordnung verkürzt aber nur für größere Bitbreiten der Operanden die Additionsdauer wirksam:
2.5 Addierer
n
8
16
32
171
64
tdAddR ≈ n · td◦ ≈ 8 · td◦ ≈ 16 · td◦ ≈ 32 · td◦ ≈ 64 · td◦ tdAddH ≈(4 + log2 n) · td◦ ≈ 7 · td◦ ≈ 8 · td◦ ≈ 9 · td◦ ≈ 10 · td◦ (n – Bitanzahl der Operanden; tdAddR – Verzögerung eines Ripple-Addierers; tdAddH – Verzögerung des hierarchischen Addierers; td◦ – Verzögerungszeit einer Operatorfunktion, entspricht hier etwa zwei Gatterverzögerungen). Der Preis für die geringere Verzögerung ist, wie aus Abb. 2.77 ablesbar, ein erheblich höherer Schaltungsaufwand. 2.5.5 Carry-Save-Addierer Eine Addition mehrerer Summanden lässt sich prinzipiell mit einer Kette oder einem Baum von Einzeladditionen nachbilden. Bei einer Kette nimmt die Laufzeit proportional und bei einem Baum logarithmisch mit der Anzahl der Additionen zu. Es gibt aber noch eine viel bessere Lösung. Das ist ein Baum aus Carry-Save-Addierern. Ein Carry-Save-Addierer besteht aus einem Volladdierer je Summandenbit und fasst drei n-Bit-Summanden zu einem n-Bit Summenvektor und einem n-Bit Übertragsvektor zusammen (Abb. 2.78 a). Der Summen- und der Übertragsvektor werden anschließend mit einem normalen Addierer zusammengefasst. Der springende Punkt ist, dass der Carry-Save-Addierer nur die Laufzeit eines Volladdierers hat, so dass die Addition von drei Summanden nur eine Volladdierer-Laufzeit mehr als eine Addition von zwei Summanden benötigt. Bei einer Addition von mehr als drei Summanden erfolgt die Zusammenfassung in einer Baumstruktur jeweils von drei zu zwei Summanden. Abbildung 2.78 b zeigt ein Beispiel mit sechs Summanden. Die erste Ebene fasst die sechs Summanden mit zwei Carry-Save-Addierern zu einer Summe aus
a0 b0 c0 a1 b1 c1 a2 b2 c2
CSA VA VA VA
RA s0
s c s c s c
HA VA
VA tdVA
a)
s c
s1
c s
s2
c
···
··· an−1 bn−1 cn−1
s
VA HA tdRA
x1 x2 x3
n -1 : 0
x4 x5 x6
n -1 : 0
n -1 : 0 n -1 : 0 n -1 : 0 n -1 : 0
tdVA CSA
CSA
s c
n -1 : 0 n :1
s n -1 : 0 n :1 c
tdVA CSA
s c
n:0 n +1 :1
tdVA CSA
tdRA s
n +1 :0
c n +2 :1
RA
n +2 :0∗
y
b) s
sn−1
c s
sn
c
cn+1
o:u
obere und untere Grenze des Indexbereichs des Bitvektors Maximalwert des Ergbnisses: 6 · (2n − 1) < 2n+3 CSA Carry-Save-Addierer RA Ripple-Addierer tdVA Verz¨ogerung eines Volladdierers tdRA Verz¨ogerung eines Ripple-Addierers Pfad mit der l¨ angsten Verz¨ogerung ∗
Abb. 2.78. a) Carry-Save-Addierer mit nachgeschaltetem Ripple-Addierer Carry-Save-Addierer-Baum für sechs Summanden
b)
172
2 Synthese und Logikoptimierung
vier, die zweite Ebene weiter zu einer Summe aus drei und die letzte Ebene zu einer Summe aus zwei Summanden zusammen. Alle drei Ebenen haben zusammen nur eine Verzögerung von drei Volladdierer-Laufzeiten. Die letzten beiden Summanden müssen mit einem normalen Addierer zusammengefasst werden, einem Ripple-Addierer oder einem hierarchischen Addierer. 2.5.6 Zusammenfassung und Übungsaufgaben Addierer – die Grundschaltungen aller arithmetischen Verarbeitungseinheiten – bestehen in ihrer einfachsten Form, dem Ripple-Addierer, aus einer Kette von Volladdierern und haben eine zur Bitbreite proportionale Additionsdauer. Prinzipiell kann die Addition auch seriell ausgeführt werden. Ein serieller Addierer ist ein Automat, der die Bitadditionen in aufeinanderfolgenden Berechnungsschritten ausführt, nur einen Volladdierer, dafür aber zusätzliche Register und eine Ablaufsteuerung benötigt. Er ist ein gutes Beispiel für den Entwurf einer sequenziellen Schaltung. Nennenswerte Hardware-Einsparungen sind mit ihm kaum möglich. Eine nützliche Schaltungserweiterung für den Ripple-Addierer ist ein schneller Übertragsdurchlauf. Ohne nennenswerten zusätzlichen Hardware-Aufwand lässt sich so die Additionsdauer gegenüber der des Ripple-Addierers etwa halbieren. Eine schnelle Addition sehr großer Summanden erfordert einen hierarchischen Addierer mit einer nur logarithmisch mit der Bitbreite zunehmenden Additionsdauer. An einem Beispiel wurde gezeigt, dass sich ein solcher Addierer konstruieren lässt, der Hardware-Aufwand dann aber überproportional mit der Bitbreite zunimmt. Für die Addition mehrerer Summanden ist die cleverste Lösung ein CarrySave-Addierer. Statt die Summanden paarweise zu Teilsummen zusammenzufassen, bildet ein Carry-Save-Addierer aus drei Summanden einen Summenund einen Übertragsvektor. Nach diesem Prinzip lassen sich viele Summanden mit einer mit der Summandenanzahl nur logarithmisch zunehmenden und von der Bitbreite unabhängigen Verzögerung zu einer Summe aus zwei Summanden zusammenfassen. Die abschließende Addition erfordert einen normalen Addierer. Die gesamte Additionsdauer für mehr als zwei Summanden ist auf diese Weise nicht erheblich größer als die für zwei Summanden. Aufgabe 2.19 Entwickeln Sie die Schaltung für einen Volladdierer mit der Wertetabelle in Abb. 2.70 a) für den Übertrag mit Hilfe eines KV-Diagramms und b) für die Summenfunktion mit Hilfe eines ROBDDs (vgl. Abschnitt 2.3).
2.6 Weitere Rechenwerke
173
2.6 Weitere Rechenwerke Die Rechenwerke für die weiteren arithmetischen Operationen – Subtraktion, Betragsbildung, Multiplikation etc. – enthalten Addierer oder mit Addierern verwandte Schaltungen. 2.6.1 Subtrahierer Bei der Subtraktion werden für jedes Bit beginnend mit dem niederwertigsten vom Minuendenbit das Subtrahendenbit und das Übertragsbit der Stelle davor abgezogen (Abb. 2.79 a). Die Differenz hat den Wertebereich {−2, −1, 0, 1}. Um alle Werte darstellen zu können, muss der Ausgabeübertrag, der doppelt zählt, negativ bewertet werden: ai − bi − ci 7→ −2 · ci+1 + si Mit einer Invertierung der drei negativ bewerteten Anschlüsse ergibt sich genau die Wertetabelle des Volladdierers (Abb. 2.79 b). Die Basisoperation kann entsprechend durch einen Volladdierer und Inverter für alle negativ bewerteten Bits – das sind das Subtrahendenbit, der Eingangsübertrag und der Ausgangsübertrag – nachgebildet werden (Abb. 2.79 c). Bei der Verkettung zu einem n-Bit-Subtrahierer heben sich jeweils die doppelten Invertierungen der Überträge zwischen den Volladdierern auf. Das Ergebnis ist ein Ripple-Addierer, bei dem der Subtrahend, der Eingangsübertrag und der Ausgangsübertrag bitweise negiert sind (Abb. 2.79 d). Der Ripple-Addierer kann dabei wie in Abschnitt 2.5.3 einen schnellen Übertragsdurchlauf haben oder durch einen hierarchischen Addierer aus Abschnitt 2.5.4 ersetzt sein. 1 1 − 1 0
(1)
(-1)
0 0 a)
c)
ai bi ci
d)
c0 a0 b0
0 0
0 1 1 0
0 1 1 1
ai 0 0 (-1) (-1) (-1) 0 1 1 1 0 1 0 0 (-1) Eins borgen 1 1 si s 1 VA 1 ci+1 c b) 1 1
(1)
(1)
VA
s
s0
c
c1
(1)
a1 b1
bi 1 1 0 0 1 1 0 0
ci 1 0 1 0 1 0 1 0
VA
ai − bi − ci ci+1 −2 = −2 + 0 1 −1 = −2 + 1 1 −1 = −2 + 1 1 0 = −0 + 0 0 −1 = −2 + 1 1 0 = −0 + 0 0 0 = −0 + 0 0 1 = −0 + 1 0
s c
s1 c2
si 0 1 1 0 1 0 0 1
ai 0 0 0 0 1 1 1 1
¯bi 0 0 1 1 0 0 1 1
cn−1 an−1 bn−1
VA
c¯i 0 1 0 1 0 1 0 1
s c
c¯i+1 0 0 0 1 0 1 1 1
si 0 1 1 0 1 0 0 1
sn−1 cn
Abb. 2.79. Subtraktion a) Beispiel b) Wertetabelle der bitweisen Subtraktion c) Schaltung für die bitweise Subtraktion d) n-Bit-Subtrahierer
174
2 Synthese und Logikoptimierung
2.6.2 Zähler Zähler sind Automaten mit der Inkrement-Operation (+1) oder der Dekrement-Operation (−1) als Übergangsfunktion. Die Inkrement-Operation ist eine Addition mit der Konstanten Null als zweitem Summanden und einem eingangsseitigen Übertrag »1«. Der Übertragsausgang bleibt ungenutzt (Abb. 2.80 a). Durch Konstanteneliminierung vereinfacht sich der erste Volladdierer zu einem Inverter. Die übrigen Volladdierer vereinfachen sich zu Halbaddierern, bestehend aus je einen UND- und einem EXOR-Gatter (Abb. 2.80 b). Im letzten Halbaddierer entfällt das UND-Gatter für die Übertragsbildung (Abb. 2.80 c). a0 0 1
0 a1 VA
s
0 an−1 VA
c
a0
a1
s1 a2
&
s c
sn−1
=1 s1
=1 s2
sn−1
c1 s0 0 1 1 0
ci bi ai
ci+1 si
0 0 1 1
an−1 &
=1 s0
VA
c
s0
a)
c)
s
c0 b0 a0 1 0 0 1 0 1
b)
0 0 0 0
0 1 0 1
0 0 0 1
0 1 1 0
a0
c1 s0
Halbaddierer ai & ci+1 ci
=1
si
konstant nicht weitergef¨ uhrt
Abb. 2.80. Inkrement-Rechenwerk a) Addierer mit konstanten und variablen Anschlussbelegungen b) Wertetabellen zur Konstantenelimination und minimierte Bitoperationen c) optimierte Schaltung
Die Dekrement-Operation zum Rückwärtszählen ist eine Subtraktion mit dem Subtrahenden null und einem eingangsseitigen Übertrag. Eine Subtraktion ist wiederum durch eine Addition des invertierten Subtrahenden und des invertierten Übertrags nachbildbar (Abb. 2.81 a). Aus den Wertetabellen der bitweisen Operationen mit den eingesetzten Konstanten ist ablesbar, dass das niederwertigste Summenbit genau wie bei der Inkrement-Operation mit einem Inverter und die Überträge und die übrigen Ausgabebits mit sehr ähnlichen Schaltungen wie für die Inkrement-Operation in Abb. 2.80 gebildet werden (2.81 b). 2.6.3 Negation und Betragsbildung Die Negation ist in der Zweikomplementdarstellung eine bitweise Invertierung und eine Addition einer »1«. In Abb. 2.82 ist die Schaltung für die InkrementOperation aus Abb. 2.80 übernommen. Die Negation ist nur für Operanden mit einem Betrag kleiner 2n−1 ausführbar. Denn der positive Wert 2n−1 ist
2.6 Weitere Rechenwerke 1 a1
a0 1 0
VA
1 an−1
s
VA
c
a)
s0 a0
VA
c
s c
s1
sn−1
≥1
≥1
=1
=1
=1 s2
s1
s0
c1 s0 0 1 1 0
ci bi ai
ci+1 si
0 0 1 1
an−1
a2
a1
c)
s
c0 b0 a0 1 0 0 1 0 1
sn−1
1 1 1 1
0 1 1 1
0 1 0 1
1 0 0 1
175
c1 s0
a0
ai ci
≥1
ci+1
=1
si
konstant nicht weitergef¨ uhrt
b)
Abb. 2.81. Dekrement-Rechenwerk a) Addierer mit konstanten und variablen Anschlussbelegungen b) Wertetabellen zur Konstantenelimination und minimierte Bitoperationen c) optimierte Schaltung
mit einem n-Bit-Vektor nicht im Zweierkomplement darstellbar. Zur Betragsbildung wird der negierte Wert gebildet. Die nachfolgenden Multiplexer leiten bei xn−1 = 0 den direkten und sonst den negierten Wert weiter. Das niederwertigste Bit ändert sich bei der Betragsbildung nicht und wird unverändert durchgereicht. Das höchstwertige Bit des Betrags einer ZweierkomplementZahl ist immer null (Abb. 2.82 b). Der längste Datenpfad ist in beiden Schaltungen die Übertragskette. Die Operationsdauer hat folglich dieselbe Ordnung wie die der Addition. a) Negation: y <= −x x x0
y0
-x
y ⇒ x
x1
+1 xn−1
x2 &
&
=1
=1 y1
y
b) Betragsbildung: y <= abs(x) −x x +1 1 x abs(x) y ⇒ 0 xn−1 x0 x1 x2 xn−2 xn−1
=1 y2
yn−1
y0
&
&
=1
=1
=1
0 1
0 1
0 1
y1
y2
yn−2
Abb. 2.82. Rechenwerke zur Negation und Betragsbildung
0 yn−1
y
176
2 Synthese und Logikoptimierung
2.6.4 Multiplizierer Eine Multiplikation von zwei durch Bitvektoren dargestellten vorzeichenfreien Zahlen besteht aus 1-Bit-Multiplikationen und Additionen (Abb. 2.83 a). Das Produkt zweier Bitwerte ist »1«, wenn beide Faktoren »1« sind. Das ist die UND-Verknüpfung. Die Teilprodukte müssen anschließend stellenrichtig addiert werden. Abbildung 2.83 b zeigt einen 4 × 4-Bit-Matrixmultiplizierer, der 4 × 4 Teilprodukte bildet und mit Halb- und Volladdierern aufsummiert. (a3 · 23 + a2 · 22 + a1 · 21 + a0 · 20 ) · (b3 · 23 + b2 · 22 + b1 · 21 + b0 · 20 ) = a3 b0 · 23 + a2 b0 · 22 + a1 b0 · 21 + a0 b0 · 20 a3 b1 · 24 + a2 b1 · 23 + a1 b1 · 22 + a0 b1 · 21 a3 b2 · 25 + a2 b2 · 24 + a1 b2 · 23 + a0 b2 · 22 a3 b3 · 26 + a2 b3 · 25 + a1 b3 · 24 + a0 b3 · 23 a)
p7
p6
p5 b0
p4
p3
b1
a0
&
a1
&
a2
&
a3
&
& & & &
HA
s c
VA
s c
VA
s c
HA
s c
einer der l¨ angsten Pfade
p1
p0
b3 p0 p1
& & & &
b)
p2
b2
p2
HA
s c
VA
s c
&
VA
s c
&
VA
s c
& &
HA
s c
p3
VA
s c
p4
VA
s c
p5
VA
s c
p6 p7
Abb. 2.83. a) Multiplikation vorzeichenfreier Zahlen b) Matrixmultiplizierer
Der Hardware-Aufwand nimmt quadratisch mit der Bitanzahl n der Faktoren zu. Die laufzeitkritischen Pfade, die die Verzögerung bestimmen, verlaufen nicht durch alle, sondern nur durch weniger als 2 · n Voll- oder Halbaddierer. Die Verzögerung nimmt entsprechend nur linear mit der Bitanzahl zu und ist etwa doppelt bis dreimal so groß wie bei einem Ripple-Addierer gleicher Bitbreite [39]. Bei einer vorzeichenbehafteten Zahl geht der Summand des führenden Bits bn−1 · 2n−1 negativ in die Werteberechnung ein (vgl. Gleichung 2.5). Wenn zwei vorzeichenbehaftete Zahlen multipliziert werden, entstehen entsprechend auch negative Teilprodukte, die vorzeichenrichtig aufsummiert werden müssen (Abb. 2.84 a). Abbildung 2.84 b zeigt die Wertetabellen für die Aufsummierung von null bis drei negativ bewerteten Bits. Bei einem negativ bewerteten
2.6 Weitere Rechenwerke
177
und zwei positiv bewerteten Summandenbits ist der Wertebereich der Summe [−1, 2], darstellbar durch ein negativ bewertetes Summenbit und ein positiv bewertetes Übertragsbit. Für zwei negativ bewertete Summandenbits ist der Wertebereich der Summe [−2, 1], darstellbar durch ein positiv bewertetes Summenbit und ein negativ bewertetes Übertragsbit. Wenn alle Summandenbits negativ bewertet sind, repräsentieren auch das Summen- und das Übertragsbit negative Werte. Die Schaltung für nur positiv oder nur negativ bewertete Summandenbits ist der normale Volladdierer. Die beiden anderen Wertetabellen verlangen Schaltungen, die einem Volladdierer sehr ähneln. Der gesamte Matrixmultiplizierer für vorzeichenbehaftete Zahlen hat dieselbe Grundstruktur wie der für vorzeichenfreie Zahlen. Die Konstruktion beginnt am einfachsten damit, den Matrixmultiplizierer für vorzeichenfreie Zahlen zu zeichnen. Dann sind alle negativen Teilprodukte und Voll- oder Halbaddierereingänge, auf die diese führen, als negativ bewertet zu kennzeichnen. Aus der Anzahl der negativ bewerteten Eingänge folgt für jeden Voll- und jeden Halbaddierer, welche Ausgänge und daraus wieder welche weiteren Voll- und Halbaddiereranschlüsse negativ zu bewerten sind. Zur Probe muss gelten, dass das höchste Bit des Produktvektors negativ und alle anderen Produktbits positiv bewertet sind. Das ist insgesamt im Grunde ein Puzzle, das in Aufgabe 2.20 für den 4 × 4-Bit-Matrixmultiplizierer selbst gelöst werden soll. (−a3 · 23 + a2 · 22 + a1 · 21 + a0 · 20 ) · (−b3 · 23 + b2 · 22 + b1 · 21 + b0 · 20 ) = −a0 b3 · 23 + a0 b2 · 22 + a0 b1 · 21 + a0 b0 · 20 −a1 b3 · 24 + a1 b2 · 23 + a1 b1 · 22 + a1 b0 · 21 −a2 b3 · 25 + a2 b2 · 24 + a2 b1 · 23 + a2 b0 · 22 +a3 b3 · 26 − a3 b2 · 25 − a3 b1 · 24 − a3 b0 · 23 a)
−p7 · 27
+p6 · 26
+p5 · 25
ai + bi + ci Summanden ci bi ai Wert ci+1 si
b)
+p4 · 24
+p3 · 23
+p2 · 22
+p1 · 21
+p0 · 20
ai + bi − ci ai − bi − ci −ai − bi − ci Wert ci+1 −si Wert −ci+1 si Wert −ci+1 −si
0 0 0 0
0 0 1 1
0 1 0 1
0 1 1 2
0 0 0 1
0 1 1 0
0 1 1 2
0 1 1 1
0 1 1 0
0 1 -1 0
0 0 1 0
0 1 1 0
0 -1 -1 -2
0 0 0 1
0 1 1 0
1 1 1 1
0 0 1 1
0 1 0 1
1 2 2 3
0 1 1 1
1 0 0 1
-1 0 0 1
0 0 0 1
1 0 0 1
-1 0 -2 -1
1 0 1 1
1 0 0 1
-1 -2 -2 -3
0 1 1 1
1 0 0 1
Abb. 2.84. a) Beispiel einer Multiplikation vorzeichenbehafteter Zahlen b) Wertetabellen der Addition von positiv und negativ bewerteten Summandenbits
2.6.5 Dividierer Die ganzzahlige Division von zwei vorzeichenfreien Bitvektoren a und b berechnet einen Bitvektor q für den Quotienten und einen Bitvektor r für den
178
2 Synthese und Logikoptimierung
Divisionsrest:
a r =q+ b b Abbildung 2.85 zeigt eine Beispieldivision. Zur Berechnung wird der Rest gleich dem Dividenden und der Subtrahend gleich dem um n − 1 Stellen nach links verschobenen Divisor gesetzt (n – Bitanzahl des Dividenden). Danach folgen in einer Schleife für jedes Quotientenbit i beginnend mit dem größten die Schritte: • •
•
Subtraktion des Subtrahenden vom Rest. Wenn die Differenz größer null ist, erfolgt eine Übernahme der Differenz als neuem Rest und das Quotientenbit wird gesetzt qi = 1. Sonst wird mit dem bisherigen Rest weitergerechnet und das Quotientenbit zurückgesetzt qi = 0. Vor der nächsten Subtraktion wird der Subtrahend halbiert. Bitnummer 3 Rest 1011 Subtrahend − 0 0 1 1 Differenz negativ Quotient q3 = 0
2 1011 − 0011 negativ q2 = 0
1 1011 − 0011 0 10 1 q1 = 1
0 0 10 1 − 0011 0 0 10 q0 = 1
Ergebnis r = 0010
q = 0011
Abb. 2.85. Division vorzeichenfreier Binärzahlen
Ein nicht trivialer Zielalgorithmus sollte vor der Hardware-Nachbildung als Programm aufgeschrieben und getestet werden. Zu Beginn der Division wird der Variablen für den Rest der Wert des Dividenden und der Variablen für den Subtrahenden der mit 2n−1 multiplizierte Wert des Divisors zugewiesen. Anschließend wird in einer Schleife für alle Quotientenbits beginnend mit dem größten die Differenz gebildet, der Übertrag ausgewertet, mit einer Fallunterscheidung der neue Rest ausgewählt und das Quotientenbit zugewiesen. Der Beschreibungsrahmen für einen Algorithmus ist in VHDL ein Prozess. Die Testeingaben und Testausgaben können auch in VHDL im Dialog bereitgestellt und ausgegeben werden. Der nachfolgende Testprozess fordert solange zur Eingabe eines Dividenden und eines Divisors auf und gibt den berechneten Quotienten und den berechneten Divisionsrest aus, bis die Simulation durch Eingabe der Zeichenkette »Exit« beendet wird: Testprozess: process variable Rest, q: tUnsigned(3 downto 0); variable Subtr, d: tUnsigned(6 downto 0); begin read("Eingabe Dividend (4 Bit): ", Rest); read("Eingabe Divisor (4 Bit): ", Subtr(6 downto 3)); Subtr(2 downto 0) := "000";
2.6 Weitere Rechenwerke
179
for i in 3 downto 0 loop d := Rest - Subtr; Subtr := Subtr srl 1; q(i) := not d(6); if d(6)=’0’ then Rest := d(3 downto 0); end if; end loop; write("Quotient= " & str(q) & " Rest=" & str(Rest)); end process; ⇒WEB-Projekt: P2.6/DivAlg.vhdl
Für die Nachbildung durch eine kombinatorische Schaltung wird der Berechnungsfluss aufgerollt. Die Operationen werden durch Rechenwerke und die Fallunterscheidungen durch Multiplexer nachgebildet. Jeder Divisionsschritt benötigt ein Subtraktionswerk und einen Multiplexer für die Restauswahl. Bei einer Differenz größer null ist das führende Bit »0« sonst »1«. Das Quotientenbit ist folglich gleich dem negierten führenden Bit der Differenz. Die Halbierung durch eine bitweise Rechtsverschiebung ändert nur die Bitzuordnung des Subtrahenden und kostet keine Hardware. Abbildung 2.86 zeigt die resultierende Schaltung, einen n-Bit-Matrixdividierer. Resti a Subtri
Resti+1
1 b (a − b) slr
y c
0
a Subtri+1
b (a − b)
y c
0 Subtri+2
slr
qn−i l¨ angster Pfad
Resti+2
1
qn−i−1
slr Rechtsverschiebung (Halbierung)
Abb. 2.86. Signalfluss eines Matrixdividierers
Der längste Signalpfad in einem Matrixdividierer verläuft durch alle Subtrahierer vom niedrigsten Bit bis zum höchsten Bit und über den Multiplexer zurück zum niedrigsten Bit. Die Verzögerung wächst entsprechend quadratisch mit der Bitanzahl. Im Vergleich zu einem Matrixmultiplizierer, bei dem die Verzögerung nur linear mit der Bitbreite zunimmt, besitzt ein Matrixdividierer kaum einen Geschwindigkeitsvorteil gegenüber einem Divisionswerk, das die Quotientenbits in aufeinanderfolgenden Zeitschritten berechnet. Deshalb wird eine Division in der Regel sequenziell ausgeführt. Der Übergang von einem Matrixdividierer, der alle Operationen in einem Zeitschritt ausführt, zu einem seriellen Dividierer, der die Quotientenbits in aufeinanderfolgenden Zeitschritten berechnet, beginnt mit der Aufteilung der Berechnungsschritte in Zeitschritte. Dazu sind die Variablen durch Signale zu ersetzen und zwischen den Berechnungsschritten Warteanweisungen einzufügen. In den Berechnungspausen wartet ein serielles Rechenwerk im Allgemeinen auf die Aktivierung eines Startsignals. Anschließend übernimmt es die
180
2 Synthese und Logikoptimierung
Eingabedaten und aktiviert ein Busy-Signal. Die Eingabedatenbereitstellung erfolgt im Simulationsmodell wieder im Dialog. Die simulierte Dauer für die Eingabeübernahme wird von der darauf folgenden Warteanweisung festgelegt und ist im Beispielmodell die Zeit bis zur nächsten aktiven Taktflanke. Innerhalb der Schleife werden dieselben Operationen wie im Vorgängermodell ausgeführt, nur dass die Werte Signalen zugewiesen werden und dass nach jedem Divisionsschritt die Simulationszeit bis zur nächsten aktiven Taktflanke weitergestellt wird. Nach Abschluss aller Divisionsschritte werden das BusySignal deaktiviert, die Ergebnisse auf dem Bildschirm ausgegeben und auf die nächste Aktivierung des Startsignals gewartet. Mit dem so modifizierten Simulationsmodell lassen sich alle Zeitabläufe, Signalverläufe und Zwischenergebnisse des zu entwerfenden seriellen Divisionswerks visualisieren und kontrollieren: signal Rest, q: tUnsigned(3 downto 0); signal Subtr: tUnsigned(6 downto 0); signal Start, busy, T: std_logic; ... process variable d: tUnsigned(6 downto 0); begin wait until rising_edge(T) and Start=’1’; Rest <= ...; Subtr <= ... & "000"; busy <= ’1’; wait until rising_edge(T); for i in 0 to 3 loop d := Rest - Subtr; Subtr <= ’0’ & Subtr(6 downto 1); q <= q(2 downto 0) & (not d(6)); if d(6) = ’0’ then Rest <= d(3 downto 0); end if; wait until rising_edge(T); end loop; write("Quotient= " & str(q) & " Rest=" & str(Rest)); busy <= ’0’; –- Rechenwerk bereit ⇒WEB-Projekt: P2.6/SerDivAlg.vhdl end process;
Der nächste Entwurfsschritt ist die Überführung in eine synthesefähige Beschreibung. In der bis hierher entwickelten Funktionsbeschreibung des seriellen Dividierers sind alle Signalzuweisungen an die aktive Taktflanke gebunden. Es gibt weder eine asynchrone Initialisierung noch irgendeine andere Signalzuweisung, die nicht an der aktiven Taktflanke ausgerichtet ist. Die Schablone für eine synthesefähige Beschreibung für ein solches Verhalten ist ein Abtastprozess mit dem Takt in der Weckliste, in dem die Zustände anhand
2.6 Weitere Rechenwerke
181
eines Zustandssignals oder einer Zustandsvariablen statt durch unterschiedliche Warteanweisungen im Berechnungsfluss unterschieden werden. An dieser Stelle beginnt der Entwurf noch einmal von vorn mit der Spezifikation der zu steuernden Operationen und des Operationsablaufgraphen. Die bisher entwickelten Simulationsmodelle dienen dabei als Vorlage für die Soll-Funktion, zur Entwicklung von Testbeispielen und als Referenzmodelle für vergleichende Simulationen. Der Operationsablauf in Abb. 2.87 stimmt mit dem des seriellen Addierers in Abb. 2.74 weitgehend überein. Er hat auch hier nur die zwei Zustände »bereit« und »beschäftigt«, die anhand des Ausgabesignals »busy« unterschieden werden. Zur Unterscheidung der Divisionsschritte dient der Bitzähler ct, der zu Beginn jeder Division zurückgesetzt und in jedem Divisionsschritt um eins erhöht wird. Zur Vermeidung eines bitadressierbaren Quotientenspeichers wird wieder ein Schieberegister verwendet. Im Gegensatz zur Addition wird der Quotient einer Division absteigend beginnend mit dem höchstwertigen Quotientenbit berechnet. Das aktuelle Quotientenbit muss entsprechend in das niederwertigste Schieberegisterbit geschrieben und in jedem nachfolgenden Berechnungsschritt eine Bitstelle nach links verschoben werden. Die Zusammenstellung der Register-Transfer-Operationen und die Konstruktion des Operationsablaufgraphen ist der kreative Teil des Entwurfs, für den einige Tage Arbeitszeit einzuplanen sind. Der nächste Schritt – die Nachbildung in VHDL – hat rezeptartigen Charakter. Dieser Aufgabenteil ist inkl. der Entwicklung eines Testrahmens und der Simulation einiger Testbeispiele in vergleichbar kurzer Zeit zu bewältigen und soll in Übungsaufgabe 2.22 selbstständig gelöst werden. busy = 0 ct = 11
a)
sonst
Kernoperation
Start = 1 ct <= 00 Subtr <= Divisor & 000 Rest <= Divident
busy = 1 Kernoperation ct <= ct + 1
1 Subtri slr
sonst b)
a b
(a − b)
y c
Resti
0 q0
q1
T Schieberegister
c) Rechenaufgabe 11:3=3 Rest 2 10 1 1 10 1 1 Rest 10 1 1 0 1 01 Subtrahend − 0 0 1 1 000 − 0 0 1 1 00 − 0 0 1 1 0 − 0 0 1 1 <0 0 0 10 Differenz <0 0 1 01 0011 Quotient (q) - - -0 - - 00 - 001
slr logische Rechtsverschiebung ct Bitz¨ahler l¨ angster Pfad
Abb. 2.87. Serieller Dividierer a) Operationsablaufgraph b) Register-TransferDatenfluss der Kernoperation c) Beispieldivision
182
2 Synthese und Logikoptimierung
2.6.6 Vergleicher Vergleicher testen zwei Bitvektoren auf Übereinstimmung oder Nicht-Übereinstimmung. Wenn die Bitvektoren Zahlen darstellen, ist auch ein Größenvergleich mit den Operatoren für »größer«, »größer/gleich« etc. möglich. Der Ergebnistyp ist boolean. Die Synthese übersetzt den boolean-Wert false in der Regel in eine »0« und true in eine »1« (vgl. Abschnitt 2.1.2). Der Test auf Gleichheit zweier n-Bit-Vektoren erfolgt bitweise mit XNORGattern, deren Ausgänge UND-verknüpft sind (Abb. 2.88 a). Die Schaltung für den Test auf Ungleichheit hat eine zusätzliche Ausgabeinvertierung. Mit einer Konstanten als zweitem Operanden vereinfachen sich die XNOR-Gatter bei der Konstantenelimination zu Verbindungen ohne logische Funktion oder zu Invertern (Abb. 2.88 b). Ein Vergleicher mit einer Konstanten ist praktisch eine UND-Verknüpfung direkter und invertierter Operandenbits (Abb. 2.88 c).
signal a, b: std_logic_vector(n-1 downto 0); signal v: std_logic; a0 b0 a1 b1 ···
a)
an−1 bn−1
== ==
==
v <= (a=b); v0 v1 .. & v . vn−1
0 ai ai
b)
1 ai ai
==
vi
v <= (a=”1001”) a0 a1 a2 a3
vi ==
vi vi
&
v
c)
Abb. 2.88. Test auf Gleichheit a) Schaltung b) Regeln für die Konstantenelimination c) optimierter Vergleicher mit einem konstanten Bitvektor
Der Test auf »größer«, »größer gleich« etc. wird mit einer Subtraktion nachgebildet. Für vorzeichenfreie Zahlen wird der Übertrag und für vorzeichenbehaftete Zahlen das Vorzeichenbit der Differenz ausgewertet (Abb. 2.89). Die Schaltung ist in jedem Fall ein Subtrahierer bzw. ein Addierer mit dem bitweise negierten Subtrahenden wahlweise mit oder ohne Eingangsübertrag und ohne die Schaltungsteile für die Bildung der nicht benötigten Ausgabebits. Ein Test auf »größer« etc. kostet einen Subtrahierer, während ein Test auf Gleichheit mit einer einfacheren Schaltung auskommt. Eine Optimierungsregel für digitale Schaltungen lautet folglich, wenn möglich, einen Test auf (Un-) Gleichheit statt eines Größenvergleichs zu verwenden. Das ist eine Optimierung, die nur der Entwickler, nicht aber das Synthesesystem vornehmen kann. 2.6.7 Verschiebung und Rotation Die Verschiebeoperatoren verschieben die Bits in einem Bitvektor um eine vorgegebene Anzahl von Stellen. Dabei ist zwischen einer logischen Verschie-
2.6 Weitere Rechenwerke vorzeichenfreie Operanden Subtraktion kein Subtrak- Subtraktionstions¨ ubertrag u ¨bertrag
183
vorzeichenbehaftete Operanden Differenz nicht negativ
Differenz negativ
a−b
a≥b
a
a≥b
a
a−b−1
a>b
a≤b
a>b
a≤b
Gr¨ oßenvergleich
Abb. 2.89. Größenvergleich durch Subtraktion
bung, einer arithmetischen Verschiebung und einer Rotation zu unterscheiden (Abb. 2.90). Bei einer logischen Verschiebung mit den Operatoren »srl« (logische Rechtsverschiebung) und »sll« (logische Linksverschiebung) werden die freiwerdenden Bitstellen mit Nullen gefüllt. Bei einer arithmetischen Verschiebung mit den Operatoren »sra« (arithmetische Rechtsverschiebung) und »sla« (arithmetische Linksverschiebung) werden die freiwerdenden Bits mit dem Wert des Vorzeichenbits gefüllt. Das ist bei einer Zweierkomplementdarstellung und absteigender Vereinbarung des Indexbereichs das linke und bei aufsteigender Vereinbarung des Indexbereichs das rechte Bit des Bitvektors. Die nachgebildete Funktion ist eine n-fache Halbierung, bei der das Vorzeichen erhalten bleibt. Eine Rotation mit den Operatoren »rol« (Rotation nach links) und »ror« (Rotation nach rechts) bewegt die Bits im Kreis. Bei der n-fachen Halbierung gehen die Nachkommastellen verloren. Das Ergebnis hat immer den nächstkleineren ganzzahligen Wert. Die logische Rechtsverschiebung in Abb. 2.90 teilt den Wert 26 + 23 + 22 + 20 = 77 durch vier. Von dem Ergebnis 24 + 21 + 20 + 2−2 = 19,25 bleiben nur die Vorkommastellen mit dem Wert 24 + 21 + 20 = 19 erhalten. Bei der arithmetiarithmetische Verschiebung
logische Verschiebung nach rechts
y := x srl 2;
y := x ror 2;
x: 0
0 1 0 0 1 1 0 1
x:
1 0 1 1 0 0 1 0
x:
0 1 0 0 1 1 0 1
y:
0 0 0 1 0 0 1 1
y:
1 1 1 0 1 1 0 0
y:
0 1 0 1 0 0 1 1
y := x sll 2; nach links
Rotation
y := x sra 2;
x: 0 1 0 0 1 1 0 1 y: 0 0 1 1 0 1 0 0
y := x sla 2; 0
y := x rol 2;
x: 1 0 1 1 0 0 1 0
x:
0 1 0 0 1 1 0 1
y: 1 1 0 0 1 0 0 0
y:
0 0 1 1 0 1 0 1
Abb. 2.90. Verschiebe- und Rotationsoperationen
184
2 Synthese und Logikoptimierung
schen Rechtsverschiebung rechts daneben hat der Operand, der zweimal halbiert wird, in der Zweierkomplementdarstellung nach Gleichung 2.5 den Wert 25 +24 +21 −27 = −78. Das Ergebnis hat den Wert 26 +25 +23 +22 −27 = −20. Das ist die nächstkleinere ganze Zahl von −78/4 = 26 +25 +23 +22 +2−1 −27 = −19,5. Eine Verschiebung oder Rotation um einen konstanten Wert beschreibt eine Bitumordnung ohne logische Verknüpfungen. Variable Verschiebungen werden mit Block-Shiftern nachgebildet. Ein Block-Shifter besteht je Bit des Verschiebeoperanden aus einer Multiplexerebene. Die erste Ebene schaltet zwischen Verschiebung null und eins, die zweite zwischen null und zwei und die i-te Ebene zwischen null und 2i um. Die Verschiebungen addieren sich. Die Gesamtverschiebung ist gleich dem Binärwert des Verschiebeoperanden. Abbildung 2.91 zeigt als Beispiel die Datenflussstruktur eines Block-Shifters für die logische Linksverschiebung. Verschiebung 0 oder 1 0 x0
x1
x2
x3 x4
Verschiebung 0 oder 2
Verschiebung 0 oder 4
1 0
0
1 0
0
1 0
y0
1 0
0
1 0
0
1 0
y1
1 0
1 0
0
1 0
y2
1 0
1 0
0
1 0
y3
1 0
1 0
1 0
y4
s0
s1
s2
Abb. 2.91. Block-Shifter für die logische Linksverschiebung
2.6.8 Synthese-Suchmuster und Optimierung Jedes der behandelten Rechenwerke hat spezielle Suchmuster für die Synthese. Ein normaler Addierer mit einlaufendem und auslaufendem Übertrag wird durch eine Addition von drei Bitvektoren beschrieben, wobei der Bitvektor für den Eingangsübertrag nur ein Bit lang sein darf. Um den Ergebnisübertrag weiterverarbeiten zu können, wird der größte Summand durch Konkatenation
2.6 Weitere Rechenwerke
185
um eine führende Null verlängert13 . In der Beschreibung eines Subtrahierers werden der zweite Operand und der eingangsseitige Übertrag abgezogen: signal a, b: tUnsigned(n-1 downto 0); signal y: tUnsigned(n downto 0); signal c: tUnsigned(0 downto 0); ... –- Suchmuster Addierer y <= (’0’ & a) + b + c; –- Suchmuster Subtrahierer y <= (’0’ & a) - b - c;
Die Inkrement- und die Dekrement-Operation für vorzeichenfreie Zahlen haben die Beschreibungsschablone »±”1”«14 . Die Negation hat die Beschreibungsschablone »-a«, der Betrag »abs(a)« und die Multiplikation »a*b«. Algorithmen mit arithmetischen Operationen bieten zahlreiche Optimierungsmöglichkeiten, die die Synthese nur zum Teil erkennt und ausnutzt. Eine bedingte Inkrement-Operation der Form signal a, y: tUnsigned(n-1 downto 0); signal c: tUnsigned(0 downto 0);
... if c="1" then y <= a + "1"; else y <= a; end if;
a c
n
n
+1
0 1
n
y
wird nach den bisher skizzierten Regeln in die Schaltung rechts daneben überführt. Die ideale Lösung ist ein Addierer mit dem Übertrag als zweitem Summanden: y <= a + c;
a c
n
+
n
y
Wenn die Synthese diese Lösung nicht selbstständig findet, muss die Funktion in der Form links daneben beschrieben werden. Ein häufig verwendetes Beschreibungselement ist eine Fallunterscheidung, in der in Abhängigkeit von einem Auswahlwert eine von mehreren zur Auswahl stehenden Operationen mit denselben Operanden und demselben Zuweisungsziel ausgeführt wird. Bei einer Umschaltung zwischen Addition und Subtraktion findet die Register-Transfer-Synthese einen Addierer und einen Subtrahierer, jeweils mit einem eingangs- und einem ausgangsseitigen Übertrag, und einen Multiplexer, der entweder die Summe oder die Differenz weiterleitet: 13 14
Achtung, der Operand, nicht die Summe! Denn anderenfalls ist das zusätzliche Bit »0« und nicht, wie beabsichtigt, der Übertrag. Im Zweierkomplement wird der Wert »1« durch die Bitfolge »01« dargestellt. Der 1-Bit-Vektor »1« hat den negativen Wert »-1«.
186
2 Synthese und Logikoptimierung signal a, b: tUnsigned(n-1 downto 0); signal y: tUnsigned(n downto 0); signal c, s: std_logic; ... if s=’0’ then y <= (’0’ & a) + b + c; else y <= (’0’ & a) - b - c; end if;
n
a b c
n
n
+ +
n+1
0 1 n+1
y
s
Wie bei einem ROBDD (reduziertes geordnetes binäres Entscheidungsdiagramm, Abschnitt 2.3.2) können bei einem Multiplexer identische Operationen an den Eingängen zu einer gemeinsamen Operation am Ausgang zusammengefasst werden. Die Addition und die Subtraktion werden beide durch Addierer nachgebildet, einmal mit dem direkten und einmal mit dem bitweise invertierten zweiten Operanden. Die resultierende Schaltung hat nur noch einen Addierer und einen Multiplexer am zweiten Addierereingang: a b
signal a, b: tUnsigned(n-1 downto 0); signal y: tUnsigned(n downto 0); signal c, s: std_logic;
n n
0 1
c
... variable vb: tUnsigned(n-1 downto 0); variable vc: tUnsigned(0 downto 0) ... if s=’0’ then vb := b; vc(0) := c; else vb := not b; vc(0) := not c; end if; y <= (’0’ & a) + vb + vc;
+
n+1
y
s
Wenn die Synthese diese Optimierungsmöglichkeit nicht erkennt, muss die Beschreibung auch hier an die gewünschte Zielstruktur angepasst werden. In der Beschreibung links daneben werden zuerst in einer Fallunterscheidung der Übertrag und der zweite Summand optional invertiert und in Variablen gespeichert. Dann folgt die Addition. Die Umschaltung zwischen dem direkten und dem invertierten zweiten Operanden kann weiter durch eine bitweise EXOR-Verknüpfung ersetzt werden (Abb. 2.92). Auch diese Zielstruktur lässt sich in VHDL beschreiben. Das lohnt aber weniger, weil die Synthese bei der bitorientierten Optimierung im Allgemeinen recht gut ist und solche Optimierungsmöglichkeiten selbst findet.
a b c s
=1
+
y
=1
Abb. 2.92. Optimierte Schaltung zur wahlweisen Addition oder Subtraktion
2.6 Weitere Rechenwerke
187
2.6.9 Zusammenfassung und Übungsaufgaben Die arithmetischen Hardware-Operationen basieren alle auf der Addition. Ein Subtraktionswerk ist ein Addierer mit bitweise invertiertem Subtrahenden und Übertrag. Die Rechenwerke für die Inkrement-Operation (+1) und die Dekrement-Operation (−1), die z.B. als Übergangsfunktionen für Zähler benötigt werden, sind stark vereinfachte Addierer. Die Negation setzt sich aus einer bitweisen Invertierung und einer Inkrement-Operation zusammen. Die Betragsbildung ist eine bedingte Negation. Ein Hardware-Multiplizierer ist eine Matrix aus Bitmultiplizierern (UNDGattern) und Addierern. Der Schaltungsaufwand wächst quadratisch mit der Operandenbreite, die Verzögerung aber nur linear. Ein Hardware-Dividierer hat sowohl einen quadratisch zunehmenden Aufwand als auch eine quadratisch zunehmende Verzögerungszeit. Wegen dieser Eigenschaften werden zwar Multiplizier als kombinatorische Schaltungen realisiert, die Division jedoch vorzugsweise sequenziell nachgebildet. Während die Addition und die Subtraktion für vorzeichenfreie Zahlen und für Zweierkomplementzahlen identisch sind, unterscheiden sich die Funktionen von Multiplizierern und Dividierern für die beiden Zahlendarstellungen. Der Test auf Gleichheit verlangt einfache kombinatorische Schaltungen, während Größenvergleiche auf der Subtraktion basieren und dadurch langsamer und aufwändiger sind. Die Operatoren »>«, »≥« etc. sollten deshalb in einer Synthesebeschreibung nur verwendet werden, wenn ein Algorithmus nicht mit einem Test auf Gleichheit oder Ungleichheit beschrieben werden kann. Verschiebung und Rotation um einen konstanten Wert erfordern keine logischen Verknüpfungen. Rotationen und Verschiebungen um einen variablen Wert werden mit Block-Shiftern – das sind matrixartige Multiplexerschaltungen – nachgebildet. Arithmetische Schaltungen sind relativ aufwändig und Schaltungsbeschreibungen mit arithmetischen Operationen bieten zahlreiche Optimierungsmöglichkeiten, von denen die Synthese manche, aber nicht alle findet. Hier ist der Entwickler gefordert. Er sollte für seine Beschreibung die erforderliche Anzahl der Rechenwerke abschätzen und seine Schätzwerte mit den Werten im Synthese-Report vergleichen. Wenn die Synthese zu viele oder zu aufwändige Rechenwerke einbaut, sollte das Syntheseergebnis genauer untersucht, die Ursachen, warum das System überflüssige Rechenwerke einfügt, lokalisiert und die Synthesebeschreibung entsprechend nachgebessert werden. Weiterführende und ergänzende Literatur siehe [6, 16, 28, 39]. Aufgabe 2.20 a) Ändern Sie die Matrixmultipliziererschaltung für vorzeichenfreie 4-BitFaktoren aus Abb. 2.83 in einen Matrixmultiplizierer für 4-Bit-Zweierkomplementfaktoren. Kennzeichnen Sie dazu alle negativ bewerteten Produktbits, Teilsummenbits und Übertragsbits sowie alle negativ bewerteten Halb- und Volladdiereranschlüsse.
188
2 Synthese und Logikoptimierung
b) Testen Sie die Schaltung mit dem Zahlenbeispiel 00112 · 11012 = 11101112 (3 · (−3) = −9), indem Sie an alle Signale in der Schaltung die Bitwerte für diese Eingabewerte schreiben. Aufgabe 2.21 Entwerfen Sie eine Funktionsbeschreibung und den zugehörigen Signalflussplan für eine vorzeichenbehaftete 4 × 4-Bit-Multiplikation, bei der von den Faktoren der Betrag gebildet wird, die Multiplikation vorzeichenfrei mit 3Bit-Faktoren ausgeführt und das Produkt, falls es negativ ist, abschließend negiert wird. a) Warum muss für diese Lösung der Wertebereich der Faktoren auf ±23 − 1 beschränkt werden? b) Zeichnen Sie den Signalflussplan. c) Beschreiben Sie die Zielfunktion mit einem kombinatorischen Prozess. Aufgabe 2.22 Entwickeln Sie eine synthesefähige Beschreibung für die serielle Division mit dem Operationsablaufgraphen aus Abb. 2.87. Aufgabe 2.23 Welche Werte werden den Variablen y1 bis y3 zugewiesen? variable x: tSigned(7 downto 0) := b"1011_0111"; variable y1, y2, y3: tSigned(7 downto 0); ... y1 := x ror 3; y2 := x sra 2; y3 := x srl 5;
3 VHDL im Detail
Nach der überblickshaften Einführung in die Grundkonzepte der Modellierung, Simulation und Synthese digitaler Schaltungen wird in diesem Kapitel ein Satz von VHDL-Beschreibungsmitteln herausgearbeitet, der es im Weiteren erlaubt, Schaltungen kompakt, übersichtlich und in einer gut testbaren Weise zu beschreiben.
3.1 Imperative Beschreibungsmittel Funktionsmodelle für Schaltungen und Testabläufe bestehen aus nebenläufigen, über Signale miteinander kommunizierenden Prozessen. Die Prozesse arbeiten intern imperativ (befehlsorientiert) und bestehen aus nacheinander abzuarbeitenden Anweisungen (Zuweisungen, Fallunterscheidungen, Schleifen etc.). Testrahmen für Experimente Für Experimente eignet sich weiterhin der Testrahmen aus Abschnitt 1.2.3, Abb. 1.18 unter Nutzung folgender Packages: ieee.std_logic_1164: Standardisiertes Package mit den Typvereinbarungen für std_logic und std_logic_vector und mit den auf diese Typen anwendbaren Unterprogrammen. Tuc.Ausgabe: Package zum Buch mit Typen- und Unterprogrammen für die Erzeugung von Textausgaben. Tuc.Numeric_Sim: Package zum Buch mit den Vereinbarungen der Bitvektortypen »tSigned« und »tUnsigned« für die Zahlendarstellung und den auf sie anwendbaren Unterprogrammen. Tuc.Eingabe: Package zum Buch mit Typen- und Unterprogrammvereinbarungen für das Einlesen von Daten aus Textdateien und für die dialogorientierte Eingabe. G. Kemnitz, Technische Informatik, eXamen.press, DOI 10.1007/978-3-642-17447-6_3, © Springer-Verlag Berlin Heidelberg 2011
190
3 VHDL im Detail
Die Packages zum Buch sind in Anhang A.2 beschrieben und stehen als Download auf der Projekt-Web-Seite [27] zur Verfügung. Die Anleitung für das Compilieren und die Nutzung der Bibliothek »Tuc« mit dem Simulator GHDL ist im zugehörigen Hilfetext nachzulesen. Das Package »Tuc.Eingabe« stellt unter anderem Prozeduren read(prompt, w );
(prompt – Ausgabetext, der zur Eingabe auffordert; w – Variable mit dem Typ des einzulesenden Wertes) für die interaktive Eingabe bereit, mit deren Hilfe einfache Dialoge für Testeingaben programmiert werden können. Abbildung 3.1 zeigt als Beispiel einen dialogorientierten Test für die Addition von zwei vorzeichenfreien 4-Bit-Zahlen. Zu Beginn werden alle benötigten Vereinbarungen importiert. Das Package ieee.std_logic_1164 wird in diesem Beispiel ausnahmsweise nicht benötigt. Der Testrahmen hat nur einen Prozess mit den Eingabeanweisungen, der zu testenden Anweisung und einer Ausgabeanweisung. Der Prozess wird in eine Endlosschleife übersetzt, so dass nach jeder Ausgabe eine Eingabeaufforderung folgt. Die Simulation kann mit »Exit« beendet werden. –- Vorspann –- library ieee; use ieee.std_logic_1164.all; library Tuc; use Tuc.Eingabe.all; use Tuc.Ausgabe.all; use Tuc.Numeric_Sim.all; entity DialogAdd is end entity; architecture Sim of DialogAdd is –- Vereinbarungen im Testrahmen, im Beispiel keine begin Testprozess: process –- Vereinbarungen im Testprozess variable a, b, c: tUnsigned(3 downto 0); begin –- Anweisungsfolge im Testprozess read("Bitte 4-Bit-Vektor für a eingeben (Abbruch mit Exit)", a); read("Bitte 4-Bit-Vektor für b eingeben (Abbruch mit Exit)", b); c := a + b; write("Summe: " & str(c)); end process; end architecture; ⇒WEB-Projekt: P3.1/DialogAdd.vhdl Abb. 3.1. Dialogtestrahmen zum Ausprobieren und Debuggen imperativer Anweisungen
3.1 Imperative Beschreibungsmittel
191
Auch wenn VHDL eine Hardware-Beschreibungssprache ist, lassen sich die imperativen Beschreibungselemente genau wie in einer normalen imperativen Sprache mit Hilfe von Ein- und Ausgaben testen. In den nachfolgenden Beispielen werden wie auch bisher nur die Vereinbarungen und die zu testenden Anweisungen beschrieben. Die kompletten Testrahmen befinden sich jeweils auf der Projekt-Web-Seite [27]. 3.1.1 Operatoren, Ausdrücke und Zuweisungen Wie in jeder imperativen Programmiersprache gibt es in VHDL Operatoren, Ausdrücke und Zuweisungen. Eine Variablenzuweisung weist der Variablen auf der linken Seite des Zuweisungsoperators den Wert des Ausdrucks auf der rechten Seite zu: Variablenname := Ausdruck ;
Signalzuweisungen sind eine Hardware-spezifische Besonderheit. Bei ihnen steht auf der rechten Zuweisungsseite ein Signalverlauf, beschrieben durch ein oder mehrere Wert-Verzögerungszeit-Tupel: Signalname <= [Verzögerungsmodell ] W [after td ]{, W after td };
Die Werte W und die Verzögerungszeiten td werden dabei durch Ausdrücke beschrieben (vgl. Abschnitt 1.2.3). Ein Ausdruck ist eine Berechnungsvorschrift für einen Wert. Er kann eine Konstante, der Wert einer Variablen, der aktuelle Wert eines Signals oder eine Operatorfunktion mit einem oder zwei Ausdrücken als Operanden sein (vgl. Abb. 1.19 a). Durch die rekursive Definition, dass die Operanden selbst Ausdrücke sein dürfen, kann ein Ausdruck beliebig viele Operationen zusammenfassen. Die Ausführungsreihenfolge der Operationen wird durch Klammern oder die Abarbeitungspriorität der Operatoren festgelegt. Tabelle 3.1 zeigt die im Buch benutzten Operatoren in der Reihenfolge ihrer Priorität. Klammern haben immer Vorrang. Ohne Klammern hat das Potenzieren Vorrang vor allen anderen Operationen. Die zweistelligen logischen Operationen haben die niedrigste Priorität und werden in einem ungeklammerten Ausdruck immer zuletzt ausgeführt. Ein Ausdruck ist so zu beschreiben, dass er die Abarbeitungsreihenfolge entweder eindeutig festlegt oder dass die Abarbeitungsreihenfolge keinen Einfluss auf das Ergebnis hat. Mehrdeutige Ausdrücke gelten als Beschreibungsfehler. Der Ausdruck »a nand b nand c« ist z.B. unzulässig, weil nicht festgelegt ist, welcher der beiden Operatoren zuerst auszuführen ist, und weil sich die Ausdrücke für die beiden möglichen Ausführungsreihenfolgen unterscheiden: (a nand b) nand c
6=
a nand (b nand c)
192
3 VHDL im Detail
Tabelle 3.1. VHDL-Operatoren in der Reihenfolge von der höchsten zur niedrigsten Abarbeitungspriorität, zulässige Operandentypen und Ergebnistyp Operator
Operation
Typ von a
Typ von b
Ergebnistyp
a ** b
Potenzieren
ZT
GZT
Typ von a
abs a
Betrag
ZT
Typ von a
not a
Invertierung
BT , BV
Typ von a
a ∗ b, a/b
Multiplikation, Division
ZT
Typ von a
Typ von a
a mod b a rem b
modulo, Divisionsrest
GZT
Typ von a
Typ von a
+a, −a
Vorzeichen
ZT , time
a + b, a − b
Addition, Subtraktion
ZT , time
Typ von a
Typ von a
Konkatenation
Elemente- oder Vektortyp
Typ von a
größerer Vektor
sll, srl, rol sla, sra, ror
Verschiebung und Rotation
BV
GZT
Typ von a
a = b, a /= b
gleich, ungleich
alle Typen außer Dateien
Typ von a
boolean
a < b, a<=b, a > b, a>=b
Größenvergleich
ZT
Typ von a
boolean
a and b, a or b, a nand b, a nor b, a xor b, a nxor b
zweistellige logische Operatoren
BT , BV
Typ von a
Typ von a
a&b
Typ von a
ZT – Datentyp zur Zahlendarstellung, bis zu diesem Abschnitt sind das integer, real, »tUnsigned« und »tSigned«; GZT – ganzzahliger Typ, alle bisherigen Typen für die Zahlendarstellung außer real; BT – Bittyp, das werden auch im Weiteren nur bit, boolean und std_logic sein; BV – Bitvektortyp, das werden auch im Weiteren nur bit_vector, std_logic_vector, »tUnsigned« und »tSigned« sein; Untertypen wie natural, positive und delay_length werden bei der Typprüfung wie ihr Basistyp behandelt.
VHDL ist eine Sprache mit strenger Typbindung. Die einzelnen Operatoren sind alle nur für bestimmte Operandentypen vordefiniert. Der Ergebnistyp hängt von den Operandentypen ab. Regelverletzungen verursachen Übersetzungsfehler. In den nachfolgenden Abschnitten werden wir kennenlernen, dass VHDL ein außergewöhnlich flexibles Typkonzept hat. Die arithmetischen Operatoren für die Addition, Subtraktion etc. sind für alle Zahlentypen vordefiniert. Bei der Bildung von Potenzen muss der Exponent auch bei einer reellwertigen Zahl als Basis ganzzahlig sein. Alle anderen zweistelligen arithme-
3.1 Imperative Beschreibungsmittel
193
tischen Operatoren verlangen typgleiche Operanden. Eine Verknüpfung von real und integer, z.B. die Multiplikation variable n: integer := 2; variable r: real := 2.5; ... := n * r;
ist unzulässig. Die logischen Operatoren not, and, nand, etc. sind für die Grundtypen bit, boolean und bit_vector vordefiniert. Für die zweistelligen Operatoren müssen die Operanden typgleich sein. Die Verschiebe- und Rotationsoperatoren sind für Bitvektortypen vorgesehen. Der zweite Operand muss eine ganze Zahl sein. Die Konkatenation »&« fasst typgleiche Vektoren und Elemente zu größeren Vektoren zusammen und ist auf alle Vektortypen anwendbar. Die Vergleichsoperatoren verlangen typgleiche Operanden und liefern ein Ergebnis vom Typ boolean. Der Test auf Gleichheit oder Ungleichheit ist für alle Datentypen (außer Datentypen für Dateien) möglich. Größenvergleiche sind hauptsächlich für Zahlentypen, aber auch für Zeitangaben vorgesehen. Darüber hinaus lässt sich jede Operatorfunktion für beliebige Operandentypen neu- oder umdefinieren. Das Package ieee.std_logic_1164 definiert beispielsweise erst die logischen Operatoren für die Typen std_logic und std_logic_vector, das Package »Tuc.Numeric_Sim« die logischen Operatoren sowie die arithmetischen Operatoren für die Typen »tUnsigned« und »tSigned«. Kontrollfrage 3.1: Warum verursacht der folgende Ausdruck einen Übersetzungsfehler? variable a,b,c,d: std_logic; ... ... a and b = c and d ...; Der Vergleich in der Mitte wird wegen seiner höheren Priorität zuerst ausgeführt und liefert ein Ergebnis vom Typ boolean, das nicht mit den übrigen Operanden vom Typ std_logic verknüpft werden kann.
Die ungewöhnlich strengen Plausibilitätstests für Ausdrücke verursachen häufig Übersetzungsfehler, darunter auch solche mit schwer lokalisierbaren Ursachen. In einer solchen Situation kann man einen Testrahmen für einzelne Ausdrücke nach dem Schema in Abb. 3.1 schreiben. Bei der Fehlermeldung, eine Operatorfunktion sei nicht definiert, untersucht man die potenziell fehlerhaften Teilausdrücke einzeln. Zur Bestimmung des Typs eines Teilausdrucks sind die Str-Funktionen im Package »Tuc.Ausgabe« so programmiert, dass sie mit dem Formatbuchstaben »t« eine Textdarstellung für den Typ, statt für den Wert erzeugen (siehe Anhang A.2.1, Tabelle A.1).
194
3 VHDL im Detail
3.1.2 Fallunterscheidungen Wie bereits eingeführt, gibt es in VHDL Fallunterscheidungen und Auswahlanweisungen. Eine binäre Fallunterscheidung führt, wenn die Bedingung erfüllt ist, die eine Anweisungsfolge und sonst optional eine andere Anweisungsfolge aus (Abb. 3.2). Bedingungen sind Ausdrücke vom Typ boolean und werden in der Regel durch Vergleiche mit den Operatoren »=«, »/=«, »>« etc. gebildet (vgl. Tabelle 3.1).
if b then Anweisung {Anweisung} [[else Anweisung {Anweisung}]] end if ;
¯b
b A0
b
A1
A0
A1
b Bedingung (BOOLEAN) Ai Anweisungsfolge
Abb. 3.2. Binäre Fallunterscheidung
Ein Anwendungsbeispiel für eine binäre Fallunterscheidung ist die Betragsbildung: –- Vereinbarungen im Testprozess variable x: integer; variable y: natural; –- Anweisungsfolge im Testprozess read("Bitte INTEGER eingeben (Abbruch mit Exit):", x); if x<0 then y := -x; else y := x; end if; ⇒WEB-Projekt: P3.1/Betrag.vhdl write("Betrag(" & str(x)&")=" & str(y));
Ein gutes Simulationsmodell nutzt alle Möglichkeiten zur Fehlererkennung. Der Typ eines Datenobjekts sollte so gewählt werden, dass die Zuweisung unzulässiger Werte Fehlermeldungen verursachen. Deshalb wird im Beispiel für den Wert des Betrags der Typ natural statt integer verwendet, der keine negativen Werte darstellen kann. In einer erweiterten Form werden mehrere Bedingungen verkettet. Wenn eine Bedingung b0 erfüllt ist, wird eine Anweisungsfolge A0 abgearbeitet. Wenn sie nicht erfüllt ist, können weitere Bedingungen bi i ∈ {1, 2, . . . , n − 1} folgen, unter denen eine Anweisungsfolge Ai abgearbeitet wird. Ist keine der Bedingungen erfüllt, wird optional eine Anweisungsfolge An abgearbeitet (Abb. 3.3). Ein Anwendungsbeispiel war die Beschreibungsschablone für ein initialisierbares Register in Abb. 2.2, in der dem Signal für den Registerzustand bei einem aktiven Initialisierungssignal der Anfangswert und sonst bei einer aktiven Taktflanke der Eingabewert zugewiesen wird.
3.1 Imperative Beschreibungsmittel if b0 then Anweisung {Anweisung} {{elsif bi then Anweisung {Anweisung}} [[else Anweisung {Anweisung}]] end if ;
b0 A0
¯b0
¯b1
b0
b1 A1 .. . An
195
b1 A1
A0
An
bi Bedingung (BOOLEAN) Ai Anweisungsfolge
Abb. 3.3. Verkettete binäre Fallunterscheidung
Eine Auswahlanweisung wertet einen Ausdruck aus. In Abhängigkeit von seinem Wert werden unterschiedliche Anweisungsfolgen abgearbeitet. Auswahlwerte, für die dieselben Anweisungen abzuarbeiten sind, können mit dem Symbol »|« zusammengefasst werden. Der letzte Auswahlwert »others« steht für alle anderen Werte, die der für die Auswahl ausgewertete Ausdruck annehmen kann (Abb. 3.4).
case Ausdruck is when Wert { |W ert } => Anweisung {Anweisung} {when Wert { |W ert } => Anweisung {Anweisung}} [[when others => Anweisung {Anweisung}]] end case ;
s w0 A0 w1 A1 .. . An
s w0 A0
w1 A1
sonst ... ...
An
s Auswahlausdruck wi Auswahlwert(e) i Ai Anweisungsfolge i
Abb. 3.4. Auswahlanweisung
Auswahlanweisungen werden z.B. für die Nachbildung von Wertetabellen genutzt. Abbildung 3.5 zeigt eine Wertetabelle und die zugehörige VHDLBeschreibung. Eingabewerte mit übereinstimmenden Ausgaben bilden jeweils einen Auswahlfall. Der Sonst-Fall erfasst im Beispiel alle ungültigen Eingaben und weist der Ausgabe den Wert »ungültig« zu. Damit der Typ std_logic_vector genutzt werden kann, ist im Vorspann des Testrahmens das Package ieee.std_logic_1164 zu importieren. 3.1.3 Schleifen Eine Schleife arbeitet eine eingeschlossene Anweisungsfolge mehrfach ab. VHDL unterstützt alle üblichen Schleifentypen — Wiederholschleifen, Abbruchschleifen, Abweisschleifen, Endlosschleifen etc. Die Endlosschleife in Abb. 3.6 a arbeitet die eingeschlossenen Anweisungen solange immer wieder ab, bis sich der Prozess dauerhaft schlafen legt oder die Simulation auf andere Weise beendet wird. Alternativ kann der Schleifenkörper Abbruchanweisungen
196
3 VHDL im Detail –- Vereinbarungen im Testprozess variable x, y: std_logic_vector(2 downto 0); –- Anweisungsfolge im Testprozess read("Bitte STD_LOGIC_VECTOR der Länge 3 eingeben:", x); case x is –- Tabellenfunktion when "000" => y := "100"; when "001"|"011"|"110" => y := "110"; when "010"|"101" => y := "101"; when "100" => y := "001"; when "111" => y := "011"; when others => y := "XXX"; end case; write("x=" & str(x) & " y=" & str(y));
x 000 001 010 011 100 101 110 111 sonst
y 100 110 101 110 001 101 110 011 XXX
⇒ Web-Projekt: P3.1/WTab.vhdl
Abb. 3.5. Beschreibung einer Tabellenfunktion mit einer Auswahlanweisung
enthalten (Abb. 3.6 b). Ist die Abbruchbedingung wahr, wird die Abarbeitung mit der ersten Anweisung nach der Schleife fortgesetzt. Im Gegensatz dazu wird in der Abweisschleife in Abb. 3.6 c die Anweisungsfolge im Schleifenkörper solange wiederholt, bis die im Schleifenkopf abgefragte Bedingung nicht mehr erfüllt ist. Das kann auch vor dem ersten Schleifeneintritt sein. a) Endlosschleife b) Abbruchschleife c) Abweisschleife d) Wiederholschleife loop
loop
while w loop
for ... loop
end loop;
end loop;
exit [[ when a ]]; end loop;
end loop;
a Abbruchbedingung ( BOOLEAN) w Eintritts- und Wiederholbedingung (BOOLEAN)
Abb. 3.6. Schleifenkonstrukte in VHDL
Auch Schleifen lassen sich mit dem Testrahmen in Abb. 3.1 ausprobieren. In der nachfolgenden Abweisschleife wird bis zur Simulationszeit tsim = 10 ns in jedem Schleifendurchlauf die aktuelle Simulationszeit ausgegeben und der Prozess für 1 ns schlafen gelegt: –- Anweisungsfolge im Testprozess while now < 10 ns loop write("aktuelle Simulationszeit: " & str(now)); wait for 1 ns; end loop; ⇒WEB-Projekt: P3.1/TestWhile.vhdl wait;
3.1 Imperative Beschreibungsmittel
197
Die aktuelle Simulationszeit wird mit der vordefinierten, aufrufparameterfreien Funktion »now« abgefragt. Nach der Schleife folgt die übliche Warteanweisung ohne Weckbedingung, die den Prozess dauerhaft schlafen legt und so die Simulation beendet. Die vollständige Beschreibung der Wiederholschleife in Abb. 3.6 d lautet for Schleifenzähler in diskreter_Bereich loop Anweisung {Anweisung} end loop;
Im Schleifenkopf wird ein Zähler vereinbart, der nacheinander alle Werte des angegebenen Bereichs annimmt. Der Wert des Schleifenzählers kann weder innerhalb noch außerhalb der Schleife verändert werden. Das ist wichtig für synthesefähige Beschreibungen iterativer Schaltungen1 . Ein diskreter Bereich kann in VHDL aufsteigend oder absteigend geordnet sein: ug to og og downto ug
–- aufsteigender diskreter Bereich –- absteigender diskreter Bereich
(ug – Untergrenze; og – Obergrenze). Später in Abschnitt 3.2.5 kommen als weitere Beschreibungsmöglichkeiten für diskrete Bereiche diskrete Zahlentypen und Indexbereiche von Vektoren und Feldern hinzu. Vorwegnehmend werden der Indexbereich eines Vektors, z.B. eines Bitvektors, mit dem Attribut ’range oder ’reverse_range, die Untergrenze mit dem Attribut ’low und die Obergrenze mit dem Attribut ’high abgefragt. Beispiel 3.1: Das nachfolgende Programm gibt für die Bitvektorkonstante »bv« alle Bitwerte in absteigender Reihenfolge aus: –- Vereinbarungen im Testprozess constant bv: std_logic_vector(3 downto 0) := "10UX"; –- Anweisungsfolge im Testprozess for idx in 3 downto 0 loop write("bv(" & str(idx) & ")=" & str(bv(idx))); end loop; wait; ⇒WEB-Projekt: P3.1/TestFor.vhdl Wie ändert sich die Ausgabe, wenn der Indexbereich durch a) bv’range, b) bv’high-1 downto bv’low, c) bv’low+1 to bv’high und d) bv’reverse_range ersetzt wird? 1
Iterative Hardware-Beschreibungen werden bei der Synthese aufgerollt. Die Beschreibung dafür ist eine Schleife mit einer zur Übersetzungszeit bekannten Iterationsanzahl.
198
3 VHDL im Detail
In Fall a wird der Indexbereich des Bitvektors übernommen. Die Ausgabe bleibt gleich. In Fall b nimmt »idx« nacheinander die Werte »2«, »1« und »0« an. Die Ausgabezeile »bv(3)=1« fehlt. In Fall c wird »bv(1)=U« bis »bv(3)=1« ausgegeben. In Fall d werden wie beim Originalprogramm alle vier Zeilen nur in umgekehrter Reihenfolge ausgegeben.
In Schleifenbeschreibungen treten öfter Indexfehler auf. Bei der Suche nach solchen Fehlern rentiert sich ein Testrahmen für den separaten Test einzelner Anweisungsfolgen mit an beliebigen Stellen einfügbaren Kontrollausgaben. Die Anweisung für einen vorzeitigen Schleifenabbruch lautet exit [Schleifenbezeichner ] [when Bedingung];
Die optionale Bedingung ist ein Ausdruck vom Typ boolean. Wenn sie erfüllt ist, wird die Schleife verlassen. Ist keine Abbruchbedingung angegeben, wird die Schleife bei der Abarbeitung einer Abbruchanweisung immer verlassen. Der optionale Schleifenbezeichner ist der Schleifenanweisung vorangestellt. Er erlaubt es, bei mehreren verschachtelten Schleifen anzugeben, welche Schleife bei erfüllter Abbruchbedingung verlassen wird. In Abb. 3.7 wird, wenn die Bedingung b1 erfüllt ist, die äußerste Schleife verlassen. Wenn die Bedingung b2 erfüllt ist, wird nur die innerste Schleife verlassen. S1: for . . . S2: for . . . S3: for . . . exit S1 when b1 exit S3 when b2
Abb. 3.7. Abbruchanweisungen in verschachtelten Wiederholschleifen
3.1.4 Funktionen Eine VHDL-Funktion ist ein Unterprogramm, das aus Eingabewerten einen Rückgabewert berechnet. Sie ersetzt in der aufrufenden Einheit einen Ausdruck. Hier im Buch werden Unterprogramme immer in einem Package vereinbart und beschrieben. Die Funktionsvereinbarung steht im Package-Kopf: package Packagename is function Funktionsname[(Parameterliste)] return Rückgabetyp; ... end package;
3.1 Imperative Beschreibungsmittel
199
Sie definiert den Funktionsnamen, die Eingabeparameter und den Typ des Rückgabewertes. Die Parameterliste vereinbart für alle Eingabeparameter den Bezeichner, den Datentyp und optional den Objekttyp und den Anfangswert: [Objekttyp] Bezeichner {, Bezeichner }: Typ [:= aw ]{, ...} mit Objekttyp => signal | file
(aw – Anfangswert). Ohne Aufrufparameter entfällt die gesamte Parameterliste inkl. der Klammern. Die Beschreibungen der in einem Package vereinbarten Funktionen folgen im Package-Körper. Eine Funktionsbeschreibung beginnt mit der Funktionsvereinbarung, gefolgt vom Schlüsselwort »is«, einem lokalen Vereinbarungsteil und einem Anweisungsteil. Im lokalen Vereinbarungsteil werden hauptsächlich Konstanten und Variablen vereinbart. Es sind aber z.B. auch Typvereinbarungen zulässig2 . Der Anweisungsteil enthält die Zuweisungen, Fallunterscheidungen, Schleifen etc. Warteanweisungen sind in Funktionen nicht erlaubt. Die letzte Anweisung im Kontrollfluss muss immer eine Rückkehranweisung mit dem Rückgabewert sein: package body Packagename is function Funktionsname[(Parameterliste)] return Rückgabetyp is –- Vereinbarungen innerhalb der Funktion {lokale_Variable/Konstante} ... begin –- Anweisungsfolge der Funktion Anweisung {Anweisung} return Rückgabewert; –- Rückkehranweisung end function; ... end package body;
Ein Kontrollfluss mit mehreren Endpunkten verlangt mehrere Rückkehranweisungen. Der Rückgabewert muss den vereinbarten Typ haben. Eine interne Speicherung von Daten von einem Aufruf zum nächsten ist nicht möglich. Beschreibung einer kombinatorischen Funktion Eine VHDL-Funktion bildet genau wie eine kombinatorische Funktion aus Eingabewerten Ausgabewerte, ohne Zwischenergebnisse speichern zu können. Sie ist dadurch das perfekte Beschreibungsmittel, um das Eingabe-AusgabeVerhalten einer kombinatorischen Schaltung getrennt vom Zeitverhalten und von der umgebenden Schaltung beschreiben und testen zu können (siehe später Abschnitt 3.4.1). 2
Signale als nebenläufige Beschreibungselemente können nicht in imperativen Beschreibungsumgebungen, zu denen außer den Prozessen auch die Unterprogramme gehören, vereinbart werden.
200
3 VHDL im Detail
Ein Beispiel sei die Funktion zur Nachbildung der Wertetabelle in Abb. 3.8. Der Eingabeparameter und der Funktionswert sind 3-Bit-Vektoren vom Typ std_logic_vector. Ein Vektor als Rückgabetyp einer Funktion braucht keine Indexbeschränkung. Für den Rückgabewert wird eine lokale Variable vereinbart, die im Gegensatz zum Rückgabetyp einen definierten Indexbereich haben muss. Die Auswahlanweisung im Anweisungsteil ist aus Abb. 3.5 übernommen. Für gültige Eingabewerte wird der zugehörige Funktionswert und sonst der Pseudo-Wert »ungültig« zurückgegeben. function fWTab(x: std_logic_vector(2 downto 0)) return std_logic_vector is x y variable y: std_logic_vector(2 downto 0); 000 100 begin 001 110 010 101 case x is 011 110 when "000" => y := "100"; 100 001 when "001"|"011"|"110" => y := "110"; 101 101 ... 110 110 when others => y := "XXX"; 111 011 sonst X X X end case; return y; end function; ⇒ Web-Projekt: P3.1/UP pack.vhdl Abb. 3.8. Tabellenfunktion als Unterprogramm
Das komplette simulierbare Web-Projekt besteht aus zwei VHDL-Dateien, dem Package und dem Testrahmen. Im Package wird die Funktion vereinbart und beschrieben. Der Testrahmen importiert außer den üblichen Package-Vereinbarungen auch die Vereinbarung der zu testenden Funktion. Die Eingabe- und die Ausgabeanweisung für den Test der Funktion sind genau wie die Wertetabelle aus Abb. 3.5 übernommen: –- Vereinbarungen im Testprozess variable x, y: std_logic_vector(2 downto 0); –- Anweisungsfolge im Testprozess read("Bitte STD_LOGIC_VECTOR der Länge 3 eingeben:", x); y := fWTab(x); ⇒WEB-Projekt: P3.1/Test_fWTab.vhdl write("x=" & str(x) & " y=" & str(y));
In der Schaltungsbeschreibung wird die Package-Funktion genauso wie im Testrahmen importiert und aufgerufen. Das Modell für eine Schaltung mit eben dieser Funktion, einer Haltezeit und einer Verzögerungszeit ist eine Signalzuweisung, die nach der Haltezeit den Wert für ungültig und nach der Verzögerungszeit den neuen Funktionswert zuweist:
3.1 Imperative Beschreibungsmittel
201
signal x, y: std_logic_vector(2 downto 0); constant th: delay_length := ...; constant td: delay_length := ...; ... y <= "XXX" after th, fWTab(x) after td ;
In der Synthesebeschreibung entfallen die grau unterlegten Beschreibungsbestandteile3 . Eine Package-Funktion zur Beschreibung des Eingabe-AusgabeVerhaltens einer kombinatorischen Schaltung wird in einem Entwurfsprojekt mehrfach verwendet: einmal für den separaten Test, mindestens einmal im Simulationsmodell und mindestens einmal in der Funktionsvorgabe für die Synthese. Deshalb ist die Auslagerung in ein Unterprogramm auch zu empfehlen. Felder mit variablem Indexbereich Die lokalen Variablen und Konstanten eines Unterprogramms werden erst beim Aufruf angelegt und initialisiert. Dadurch ist es möglich, die Anfangswerte der Datenobjekte und die Größe der Felder aus den Werten der Aufrufparameter berechnen zu lassen. Das gilt insbesondere auch für Konstanten. Ein Beispiel für eine VHDL-Funktion mit einer Bitvektorvariablen, deren Größe und Anfangswert von einem Eingabeparameter festgelegt wird, sei die Übergangsfunktion eines Spezialzählers zur Erzeugung von Testeingaben vom Typ std_logic_vector. Der Zähler soll zyklisch alle gültigen und auch alle Eingabevariationen mit einem einzelnen ungültigen Bit erzeugen. In Abb. 3.9 a ist die Zählreihenfolge für einen 3-Bit-Zustandsvektor dargestellt. Ein function countx(x: std_logic_vector) return std_logic_vector is variable y: std_logic_vector(x’range) := x; begin for idx in x’low to x’high loop if y(idx)=’0’ then y(idx) := ’1’; exit; elsif y(idx)=’1’ and not is_X(y(y’high x2 x1 x0 x2 x1 x0 x2 x1 x0 downto idx+1)) then y(idx) := ’X’; exit; 0 0 0 0 1 X 1 0 X 1 X 1 else y(idx) := ’0’; 0 0 1 0 X 0 1 1 0 X 0 0 0 0 X 0 X 1 1 1 1 X 0 1 end if; 0 1 0 1 0 0 1 1 X X 1 0 end loop; 0 1 1 1 0 1 1 X 0 X 1 1 return y; a) b) end function; ⇒ Web-Projekt: P3.1/UP pack.vhdl Abb. 3.9. Übergangsfunktion eines Spezialzählers zur Erzeugung von Testeingaben a) Zählzyklus b) VHDL-Funktion 3
Außerdem darf in einer Synthesebeschreibung der Pseudo-Werte für ungültig auch in Unterprogrammen weder ausgewertet noch zugewiesen werden.
202
3 VHDL im Detail
Bit i ändert sich nur, wenn alle niederwertigeren Bits nach »0« wechseln. Ist diese Bedingung erfüllt, gilt Folgendes: • • •
Wenn der Bitwert »0« ist, dann setze ihn gleich »1«. Wenn der Bitwert »1« und kein höherwertigeres Bit ungültig ist, dann setze ihn gleich »X«, sonst setze ihn gleich »0«.
Der Eingabeparameter x ist innerhalb des Unterprogramms nur lesbar. Deshalb wird eine typgleiche lokale Variable y für den Ausgabewert angelegt, die den Indexbereich und den Wert des Eingabeparameters übernimmt (Abb. 3.9 b). Die Wiederholschleife im Anweisungsteil läuft von der unteren zur oberen Grenze des Indexbereichs, führt die entsprechenden Bitzuweisungen aus und bricht ab, wenn das aktuelle Bit nicht nach »0« wechselt. Die so definierte Hilfsfunktion dient zur Simulation erschöpfender Tests für kombinatorische Schaltungen. Ein erschöpfender Test ist ein Test, der eine Funktionseinheit nach einem Vollständigkeitskriterium – hier mit einem Testsatz, der alle Eingabevariationen von »0«, »1« und »X« mit maximal einem »X« enthält, – testet [29]. Im nachfolgenden Testrahmen wird die kombinatorische Funktion »fWTab(...)« aus dem Vorabschnitt mit einem mit der Funktion »countx(...)« generierten Testsatz erschöpfend getestet. Die Variable x wird mit dem ersten Eingabewert für die zu testende Funktion »000« initialisiert. Nach jeder Bildschirmausgabe des Testeingabewertes und des zugehörigen Funktionswertes bildet die Funktion »countx(...)« den Folgewert für die Testeingabevariable x. Ist dieser gleich dem Anfangswert, wird eine Warteanweisung ohne Weckbedingung ausgeführt, die den Prozess und mit ihm die gesamte Simulation beendet. Ansonsten – ein Prozess wird in eine Endlosschleife übersetzt – wird die Abarbeitung mit der nächsten Bildschirmausgabe fortgesetzt. –- Vereinbarungen im Testprozess variable x: std_logic_vector(2 downto 0) := "000"; –- Anweisungsfolge im Testprozess write("x=" & str(x) & " fWTab(x)=" & str(fWTab(x))); x := countx(x); ⇒WEB-Projekt: P3.1/Test_countx.vhdl if x="000" then wait; end if;
Der Objekttyp Standardmäßig werden die Eingabeparameter an ein Unterprogramm als initialisierte Konstanten übergeben. Bei dieser Art der Übergabe ist innerhalb des Unterprogramms nur der übergebene Wert lesbar. Um von einem Signal innerhalb eines Unterprogramms auch die Signalattribute auswerten zu können, muss der Parameter den Objekttyp »signal« haben. Ein Beispiel sei eine Funktion zur Detektierung einer steigenden Signalflanke. Der Eingabeparameter ist als Signal vereinbart, so dass funktionsintern
3.1 Imperative Beschreibungsmittel
203
außer dem Wert auch das ’event-Attribut gelesen werden kann, das angibt, ob sich der Signalwert zum aktuellen Simulationszeitpunkt geändert hat. function my_rising_edge(signal x: std_logic) return boolean is begin return x’event and x=’1’; end function;
Diese Funktion ersetzt praktisch den Ausdruck »x’event and x=’1’« und muss mit einem Signal als Parameter aufgerufen werden. Optionale Aufrufparameter In einer Funktionsdefinition können einem Teil der Schnittstellenparameter – üblicherweise den letzten in der Liste – Standardwerte zugeordnet werden. Beim Aufruf der Funktion können die Standardwerte überschrieben oder beibehalten werden. Zum Überschreiben wird dem Schnittstellenparameter ein Aufrufparameter und zum Beibehalten das Schlüsselwort »open« zugeordnet. Die Zuordnung von »open« kann entfallen, wenn die Zuordnung namensbasiert erfolgt oder wenn die Parameter, deren Standardwerte beibehalten werden, am Ende der Parameterliste stehen. Beispiel sei die Funktion function str(x: bit_vector, fmt:character := ’b’) return string;
aus »Tuc.Ausgabe«. Sie wandelt einen Bitvektor in eine Textdarstellung um. Der Buchstabe »fmt« legt die Darstellung fest (siehe Anhang A.2.1, Tabelle A.1). Der Standardwert ist ’b’ für Binärdarstellung. Mit write(str(x), open);
oder vereinfacht write(str(x));
wird der Formatbuchstabe für die Binärdarstellung beibehalten. Um den Standardwert z.B. mit dem Formatbuchstaben für die Darstellung des Bitvektors als vorzeichenfreie Dezimalzahl zu überschreiben, ist im Funktionsaufruf dem zweiten Parameter der Wert »d« zuzuordnen: write(str(x, ’d’));
Überladen von Funktionen Ein Funktionsaufruf verlangt, dass den Schnittstellenparametern typgleiche Datenobjekte mit den passenden Objekttypen zugeordnet werden. Wie lässt sich dann eine Funktion, z.B. »str(...)« schreiben, die mit unterschiedlichen Datentypen als Argumente aufgerufen werden kann und eine typspezifische Zeichenkettendarstellung zurückgibt?
204
3 VHDL im Detail
Bei gleichnamigen Funktionen mit unterschiedlichen Eingabeparametertypen ist für jede zu unterstützende Typenkombination eine eigene Funktion zu vereinbaren und zu programmieren. Das Package »Tuc.Eingabe« definiert z.B. unter anderem folgende Str-Funktionen: function str(x: bit, fmt: character := ’b’) return string; function str(x: boolean, fmt: character := ’b’) return string; function str(x: std_logic, fmt: character := ’b’) return string; ...
Man spricht vom Überladen eines Funktionsbezeichners oder kurz einer überladenen Funktion. Beim Überladen müssen sich die Operandentypen der Pflichtparameter aller gleichnamigen Funktionen unterscheiden. Die Typen der optionalen Parameter und der Typ des Rückgabewertes bleiben unberücksichtigt. Es ist z.B. nicht möglich, eine Funktion »read« für unterschiedliche Rückgabetypen zu überladen4 . 3.1.5 Unreine Funktionen und globale Variablen Eine »reine« Funktion bildet aus einer variablen Anzahl von nur lesbaren Eingabeparametern einen Rückgabewert. Eine unreine Funktion (engl. impure function) kann zusätzlich Variablen und andere Datenobjekte aus der Umgebung, in der sie definiert ist, lesen und verändern. In Packages können unreine Funktionen ihre Zwischenergebnisse in globalen Variablen aufbewahren. Globale Variablen werden in einem Package oder einer Entwurfseinheit mit shared variable Bezeichner : Typ [:=Anfangswert];
definiert. Unreine Funktionen werden wie normale Funktionen, nur mit dem zusätzlich vorangestellten Schlüsselwort für »unrein« vereinbart: impure function Funktionsname[(Parameterliste)] return Rückgabetyp;
Es gibt eine einzige vordefinierte unreine Funktion. Das ist impure function now return delay_length;
aus dem Package std.standard, die die aktuelle Simulationszeit zurückgibt. Diese Funktion hat insofern eine Sonderstellung, als dass die Simulationszeit auf keine andere Weise gelesen werden kann. Eine denkbare Anwendung für unreine Funktionen ist die Bereitstellung von pseudo-zufälligen Testeingaben. Pseudo-zufällig bedeutet, dass die erzeugten Wertefolgen Zufallscharakter besitzen, aber bei Wiederholung der Simulation immer gleich sind. Ein Pseudo-Zufallsgenerator ist praktisch ein autonomer Automat mit einem Zustand, einer Übergangs- und einer Ausgabefunktion. Das Package ieee.math_real definiert eine geeignete Prozedur, die als 4
Mit Prozeduren ist das möglich, siehe später Abschnitt 3.1.8.
3.1 Imperative Beschreibungsmittel
205
Übergangs- und Ausgabefunktion für einen Pseudo-Zufallsgenerator gedacht ist: procedure uniform(seed1, seed2: inout positive; w: out real);
Der Zustand wird durch zwei positive ganze Zahlen beschrieben. Bei jedem Aufruf bildet die Prozedur aus dem durch »seed1« und »seed2« beschriebenen Ist-Zustand den Folgezustand und einen Ausgabewert im Bereich 0 ≤ w < 1. In einer unreinen Funktion kann der Zustand in globalen Variablen versteckt werden, die die Funktion liest, verändert und für die Berechnung des Ausgabewertes benutzt: library ieee; use ieee.math_real; ... shared variable seed1, seed2: positive; ... impure function rand return real is variable y: real; begin ieee.math_real.uniform(seed1, seed2, y); return y; ⇒WEB-Projekt: P3.1/Rand_pack.vhdl end function;
Die so definierte Funktion hat keine Aufrufparameter und gibt bei jedem Aufruf einen Zufallswert zurück. Die nachfolgende Anweisungsfolge im Prozess eines Testrahmens ruft hintereinander zehnmal diese Funktion auf und gibt jeweils den Rückgabewert aus: –- Anweisungsfolge im Testprozess for idx in 1 to 10 loop write("Zufallswert " & str(idx) & ": " & str(rand)); end loop; ⇒WEB-Projekt: P3.1/TestRand.vhdl wait;
3.1.6 Operatorfunktionen Die Operatorausdrücke »ab« und » b« (a – linker Operand; b – rechter Operand; – Operatorsymbol) sind Spezialschreibweisen für Funktionsaufrufe. Die in VHDL vordefinierten Operatorsymbole können für beliebige Operandentypen neu- oder umdefiniert werden. Die Vereinbarung einer Operatorfunktion gleicht fast der Vereinbarung einer normalen Funktion: function ""([a: Typ;] b: Typ) return Rückgabetyp;
Der Funktionsname ist das in doppelte Hochkommas eingeschlossene Operatorsymbol. Die Anzahl der Operanden darf nur eins oder zwei betragen und
206
3 VHDL im Detail
muss zum Operatorsymbol passen. Erlaubt sind alle in VHDL vordefinierten Operatorsymbole +, -, *, /, abs, mod, not, and, or, xor, xnor etc. (siehe Tabelle 3.1). Neue Operatorsymbole können nicht definiert werden. Als Beispiel soll der Additionsoperator »+« für den Datentyp bit mit der ODERVerknüpfung überladen werden: function "+"(a, b: bit) return bit is begin return a or b; end function;
Wenn jetzt in einem Ausdruck zwei Bit-Werte addiert werden, wird der Operator »+« durch die neu definiert Funktion, d.h. durch eine ODER-Verknüpfung, ersetzt: signal x1, x2, y: bit; ... y <= x1 + x2 after 2 ns; –- identisch mit y <= x1 or x2 after 2 ns
3.1.7 Prozeduren Eine Prozedur ersetzt in der aufrufenden Einheit eine Anweisung oder eine Anweisungsfolge. Sie hat wie eine Funktion einen Namen und eine Parameterliste, aber keinen Rückgabewert: procedure Prozedurname [(Parameterliste)] is –- Vereinbarungen der Prozedur {lokale_Variable/Konstante} ... begin –- Anweisungsfolge der Prozedur Anweisung{Anweisung} end procedure;
Dafür können die Parameter auch als Ausgabe oder Ein- und Ausgabe definiert sein. Für die interne Beschreibung stehen alle imperativen Beschreibungsmittel inkl. der Warteanweisung, die im Anweisungsteil von VHDL-Funktionen nicht erlaubt ist, zur Verfügung. Der Kontrollfluss einer Prozedur kann, aber muss nicht mit einer Rückkehranweisung enden. Eine optionale Rückkehranweisung hat keinen Rückgabewert. Die Parameterliste einer Prozedur legt für alle Übergabeparameter den Bezeichner, den Datentyp und optional den Objekttyp, die Übergaberichtung sowie einen Standardwert fest: [Objekttyp] Bezeichner {, Bezeichner }: [Richtung] Typ [:= aw ]{, ...} mit Objekttyp => signal | file und Richtung => in | out | inout
(aw – Anfangswert). Ohne Richtungsangabe sind die Parameter wie bei einer Funktion Eingabeparameter. Der Objekttyp ist für Eingabeparameter,
3.1 Imperative Beschreibungsmittel
207
wenn er nicht explizit angegeben ist, »constant« und für Ausgabe- sowie für Ein- und Ausgabeparameter »variable«. Prozeduren können im Gegensatz zu Funktionen mehrere Ergebnisse zurückgeben. Die Prozedur procedure IncDec(a, b: inout integer; c: out boolean) is begin a := a + 1; b := b - 1; c := (a=b); end procedure;
zählt z.B. eine Variable hoch, verringert den Wert einer anderen Variablen und gibt in einer dritten Variablen bei Übereinstimmung true und sonst false zurück. Eine Prozedur ist immer »unrein«, d.h., sie kann lesend und schreibend auf Datenobjekte der Beschreibungsumgebung, in der sie definiert ist, zugreifen. Das kann auch versehentlich passieren und führt zu schwer lokalisierbaren Fehlern. Auch aus diesem Grund ist es zu empfehlen, Prozeduren in Packages zu vereinbaren. Denn in einem Package gibt es in der Regel keine sichtbaren Datenobjekte, die die Prozedur versehentlich auswerten oder verändern könnte5 . Prozeduren mit Ein- und Ausgabesignalen Bei der Vereinbarung eines Schnittstellenparameters mit dem Objekttyp »signal« wird nicht der Wert, sondern ein Zeiger auf das Signal übergeben, über den bei der Übergaberichtung »in« Signalwerte und Attribute gelesen, bei der Übergaberichtung »out« Änderungen zugewiesen und bei der Übergaberichtung »inout« sowohl Werte und Attribute gelesen als auch Änderungen zugewiesen werden können. Der gelesene Signalwert ist immer der aktuelle Signalwert, der sich bei Abarbeitung einer Warteanweisung auch ändern kann. Die Prozedur im nachfolgenden Beispiel hat einen Takt T als Eingabesignal und ein bitorientiertes Ausgabesignal y. Nach jedem Aufruf wartet sie auf die nächste aktive Taktflanke, setzt das Ausgabesignal auf »1«, wartet für weitere fünf aktive Taktflanken, setzt das Ausgabesignal auf »0« und gibt den Kontrollfluss ab: procedure W5T(signal T: std_logic; signal y: out std_logic) is begin wait until rising_edge(T); y <= ’1’; for idx in 1 to 5 loop wait until rising_edge(T); end loop; y <= ’0’; ⇒WEB-Projekt: P3.1/W5T_pack.vhdl end procedure;
5
Die Ausnahme sind die globalen (shared) Variablen (vgl. Abschnitt 3.1.5).
208
3 VHDL im Detail
Eine Prozedur ist ein imperatives Beschreibungsmittel. Zum Testen muss sie in einem Prozess aufgerufen werden. Der Testrahmen in Abb. 3.10 a enthält außer dem Prozess mit der zu testenden Prozedur einen Prozess für die Erzeugung des Taktsignals und einen Prozess zur Ausgabe aller Signaländerungen auf dem Bildschirm. Zum Simulationsbeginn ruft der erste Prozess die zu testende Prozedur auf. Diese wartet auf die erste steigende Taktflanke und ändert danach den Ausgabewert von »U« (nicht initialisiert) nach »1«. Nach weiteren fünf aktiven Taktflanken setzt sie den Ausgabewert auf »0« und beendet sich. Da die Anweisungsfolge in einem Prozess in eine Endlosschleife übersetzt wird, ruft der Prozess die Prozedur sofort wieder neu auf und arbeitet sie bis zur ersten Warteanweisung ab. Diese legt den Prozess bis zur nächsten aktiven Taktflanke schlafen, setzt die Ausgabe auf »1« etc. Insgesamt wird zyklisch ein Ausgabesignal erzeugt, das immer für fünf Taktperioden »1« und für eine Taktperiode »0« ist. Abbildung 3.10 b listet alle mit dem Ausgabeprozess erfassten Signaländerungen auf und Abb. 3.10 c zeigt die berechneten Signalverläufe für den Takt und das Ausgabesignal. 13.00 ns: T=0 y=0 0 fs: T=U y=U –- Vereinbarungen im Testrahmen 14.00 ns: T=1 y=0 1.00 ns: T=0 y=U signal T, y: std_logic; 14.00 ns: T=1 y=1 2.00 ns: T=1 y=U –- Prozess mit Testobjekt 15.00 ns: T=0 y=1 2.00 ns: T=1 y=1 process 16.00 ns: T=1 y=1 3.00 ns: T=0 y=1 begin 17.00 ns: T=0 y=1 4.00 ns: T=1 y=1 W5T(T, y); 18.00 ns: T=1 y=1 5.00 ns: T=0 y=1 end process; 19.00 ns: T=0 y=1 6.00 ns: T=1 y=1 –- Prozess zur Takterzeugung 20.00 ns: T=1 y=1 7.00 ns: T=0 y=1 process 21.00 ns: T=0 y=1 8.00 ns: T=1 y=1 wait for 1 ns; 22.00 ns: T=1 y=1 9.00 ns: T=0 y=1 begin 23.00 ns: T=0 y=1 10.00 ns: T=1 y=1 T <= ’0’; 24.00 ns: T=1 y=1 11.00 ns: T=0 y=1 while now < 25 ns loop 24.00 ns: T=1 y=0 12.00 ns: T=1 y=1 25.00 ns: T=0 y=0 12.00 ns: T=1 y=0 wait for 1 ns; b) T <= not T; end loop; T 10 end process; y 10 –- Testausgabeprozess 0 10 ns 20 ns tsim c) process(T, y) begin write(rechts(str(now), 9) & ": T=" & str(T) & " y=" & str(y)); end process; a) ⇒ WEB-Projekt: P3.1/TestW5T.vhdl
Abb. 3.10. Testrahmen für die Prozedur W5T a) VHDL-Beschreibung b) Bildschirmausgabe während der Simulation c) berechnete Signalverläufe
3.1 Imperative Beschreibungsmittel
209
3.1.8 Nebenläufige Prozeduren Eine nebenläufige Prozedur ist ein Prozeduraufruf außerhalb eines Prozesses. Er wird in einen Prozess mit allen Eingabesignalen der Prozedur in der Weckliste und dem Prozeduraufruf als einzige Anweisung übersetzt. Der Prozess ruft die Prozedur zum Simulationsbeginn auf, wartet nach ihrer Beendigung auf eine Änderung eines der Eingabesignale der Prozedur und ruft sie dann erneut auf. Mit diesem Verhaltensmodell lässt sich jedes Verhalten nachbilden, das ein einzelner Prozess beschreiben kann. Die Vorteile von nebenläufigen Prozeduren gegenüber Prozessen sind, dass sich Prozeduren in Packages auslagern, parametrisiert beschreiben, separat testen und mehrfach verwenden lassen. Als Beispiele sollen typische Bausteine eines Testrahmens – ein Testobjekt, ein Testeingabegenerator und ein Ausgabeprozess – als nebenläufige Prozeduren beschrieben werden. Das Testobjekt sei ein NAND-Gatter mit einer Halte- und einer Verzögerungszeit. Die Ein- und Ausgabesignale müssen den Objekttyp »signal« haben, die Eingabesignale, damit sie in die Weckliste übernommen werden, und das Ausgabesignal, damit ihm Signaländerungen zugewiesen werden können. Die Haltezeit und die Verzögerungszeit sind normale Eingabeparameter: t h , td procedure nand_proc(signal a, b: std_logic; signal y: out std_logic; th, td: delay_length) is a & y begin b y <= ’X’ after th; if not is_x(a) and not is_x(b) then y <= transport a nand b after td; end if; ⇒WEB-Projekt: P3.1/UP_pack.vhdl end procedure;
Eingerahmt in ihren Prozess wird die Prozedur zum Simulationsbeginn und dann nach jeder Änderung eines der beiden Eingabesignale aufgerufen. Bei jedem Aufruf wird dem Ausgabesignal nach der Haltezeit der Pseudo-Wert »ungültig« und, wenn der Eingabewert gültig ist, zusätzlich nach der Verzögerungszeit der neue Funktionswert zugewiesen. Die Zuweisung des neuen gültigen Wertes erfolgt nach dem Transportverzögerungsmodell, damit die schwebende Änderung nach »ungültig« nicht gelöscht wird (vgl. Abschnitt 1.3.3). Die zweite Beispielprozedur soll einen erschöpfenden Testsatz für das NAND-Gatter unter Verwendung der Übergangsfunktion »countx(...)« aus Abb. 3.9 erzeugen. Das zu erzeugende Signal ist für die Prozedur ein Ausgabesignal. Der zweite Aufrufparameter legt die Dauer der Testschritte fest. Da die Prozedur kein Eingabesignal hat, wird sie nur zum Simulationsbeginn aufgerufen. Sie legt dann zwei Datenobjekte an, eine Konstante für den Startwert und eine Variable für den Zustand. Beide übernehmen die Bitbreite des Testeingabesignals und werden mit dem Startwert »alles null« initialisiert.
210
3 VHDL im Detail
Der Anweisungsteil besteht aus einer Schleife, in der der interne Zustand an das Ausgabesignal zugewiesen, mit der Übergangsfunktion aus Abb. 3.9 b der Folgezustand berechnet und für die Dauer eines Testschritts gewartet wird. Wenn wieder der Startwert erreicht wird, beendet sich die Prozedur und mit ihr auch der einrahmende Prozess. Die Bitbreite des Testeingabesignals wird erst beim Aufruf festgelegt, so dass die Prozedur für den Test von Schaltungen mit einer beliebigen Eingabebitbreite genutzt werden kann: procedure Testgen(signal y: out std_logic_vector; tP: delay_length) is constant Startwert: std_logic_vector(y’range) := (others=>’0’); variable z: std_logic_vector(y’range) := Startwert; begin loop y <=vz; z := countx(z); Testeingabey wait for tP; generator exit when z=Startwert; end loop; ⇒WEB-Projekt: P3.1/UP pack.vhdl end procedure;
Das dritte Beispiel ist die Auslagerung eines Prozesses für die Protokollierung der Anschlusssignaländerungen des NAND-Gatters in eine nebenläufige Prozedur. Für diese Prozedur sind alle Gatteranschlusssignale Eingabesignale: procedure Ausgabe(signal x: std_logic_vector; Bildschirmausgabe signal y: std_logic) is begin x write(rechts(str(now), 10) & ": x=" & str(x) y & " y=" & str(y)); ⇒WEB-Projekt: P3.1/UP pack.vhdl end procedure;
Der einrahmende Prozess ruft die Prozedur zum Simulationsbeginn und bei jeder Änderung von einem der beiden Schnittstellensignale auf. Bei jedem Aufruf werden die aktuelle Simulationszeit und die Signalwerte auf dem Bildschirm ausgegeben. Danach gibt die Prozedur den Kontrollfluss bis zum nächsten Aufruf ab. Die verwendete Funktion »rechts(Zeichenkette, Zeichenanzahl )« dient zur Formatierung der Textausgabe. Sie ist im Package »Tuc.Ausgabe« definiert und verlängert die übergebene Zeichenkette um führende Leerzeichen auf die Länge »Zeichenanzahl « (siehe Anhang A.2.1). Im Testrahmen in Abb. 3.11 a werden alle drei Prozeduren nebenläufig aufgerufen. Zum Testbeginn startet jeder der einrahmenden Prozesse seine Prozedur. Die Testgeneratorprozedur weist den ersten Ausgabewert zu und legt sich intern für 2 ns schlafen. Die Gatterprozedur weist den ersten Ausgabewert zu und wartet dann auf Eingabeänderungen. Die Protokollprozedur schreibt die erste Zeile und wartet auf eine Änderung eines ihrer überwachten Eingabesignale. An den Bildschirmausgaben während der Simulation in
3.1 Imperative Beschreibungsmittel
211
Abb. 3.11 b ist zu erkennen, dass der Protokollprozess zum Simulationszeitpunkt null ein zweites Mal geweckt wird, und zwar von der Eingabeänderung der ersten Signalzuweisung des Eingabeprozesses. Abbildung 3.11 c zeigt die berechneten Signalverläufe. th = 0,25 ns Bildschirmausgabe td = 0,5 ns Testeingabegenerator
a)
x
&
y
0 0 250.00 500.00 2.00 2.25 2.50 4.00 4.25
–- Vereinbarungen im Testrahmen signal x:std_logic_vector(1 downto 0); b) signal y:std_logic; –- nebenläufige Anweisungen x Testgen(x, 2 ns); y 10 nand_proc(x(0), x(1), y, 0 0.25 ns, 0,5 ns); c) Ausgabe(x, y); ⇒ WEB-Projekt: P3.1/TestNLProc.vhdl
00
2
fs: fs: ps: ps: ns: ns: ns: ns: ns:
x=UU x=00 x=00 x=00 x=01 x=01 x=01 x=0X x=0X
01
0X
4
y=U y=U y=X y=1 y=1 y=X y=1 y=1 y=X
6.00 6.50 8.00 8.25 8.50 10.00 10.25 12.00 14.00
10
6
11
8
nicht initialisiert (U)
ns: ns: ns: ns: ns: ns: ns: ns: ns:
1X
10
x=10 x=10 x=11 x=11 x=11 x=1X x=1X x=X0 x=X1
X0
y=X y=1 y=1 y=X y=0 y=0 y=X y=X y=X
X1
t in ns ung¨ ultig (X)
Abb. 3.11. a) Testrahmen mit nebenläufigen Prozeduren b) Bildschirmausgabe c) graphische Darstellung der berechneten Signalverläufe
Schnittstellenvektoren, Standardwerte und Überladen Wie bei Funktionen ist es auch bei Prozeduren möglich, die Bitbreite von Schnittstellenvektoren, die Größe lokaler Felder, die Werte lokaler Konstanten und die Anfangswerte lokaler Variablen aus den Werten der Aufrufparameter berechnen zu lassen. Im Gegensatz zu Funktionen ist es dabei auch möglich, Ergebnisvektoren an die Größe der zugeordneten Ausgabevektoren anzupassen. Die nachfolgende Prozedur invalidiert alle Bits eines Signalvektors. Dazu legt sie eine lokale Konstante mit dem Indexbereich des Ausgabesignals an, initialisiert jedes Bit der Konstanten mit »X«. Dann weist sie die so gebildete Konstante dem Ausgabesignal zu: procedure xx(signal y: out std_logic_vector) is constant z: std_logic_vector(y’range) := (others=>’X’); begin y <= z; end procedure;
In einer Prozedurvereinbarung können für die Eingabeparameter genau wie für die Schnittstellenparameter von Funktionen Standardwerte vorgegeben werden. Der Prozeduraufruf kann den Standardwert überschreiben oder beibehalten. Zum Überschreiben wird dem Schnittstellenparameter ein Aufrufparameter und zum Beibehalten das Schlüsselwort »open« zugeordnet. Die Zuordnung von »open« kann entfallen, wenn die Zuordnung namensbasiert
212
3 VHDL im Detail
erfolgt oder wenn die Parameter, deren Standardwerte beibehalten werden, am Ende der Parameterliste stehen. Auch Prozeduren können überladen werden. Dabei gilt die Regel, dass sich alle gleichnamigen Prozeduren in den Typen ihrer Schnittstellenparameter unterscheiden müssen. Die Typen der Ausgabeparameter und der Ein- und Ausgabeparameter zählen dabei mit, Eingabeparameter mit Standardwerten nicht. Es ist z.B. möglich, die oben definierte Prozedur zur Invalidierung von Signalen procedure xx(signal y: out tTyp);
auch für jeden anderen Datentyp »tTyp«, der den Pseudo-Wert »ungültig« darstellen kann, zu überladen. 3.1.9 Zusammenfassung und Übungsaufgaben VHDL unterstützt alle typischen Beschreibungsmittel imperativer Programmiersprachen. Die Anweisungen und Kontrollstrukturen lassen sich ganz normal mit Eingabe- und Ausgabeanweisungen testen. Hardware-spezifische Besonderheiten gibt es bei der For-Schleife und den Unterprogrammen. Die ForSchleife hat einen nicht manipulierbaren Schleifenzähler, um Schaltungsstrukturen mit festen Iterationsgrenzen beschreiben zu können, die bei der Synthese aufgerollt werden. Reine Funktionen haben, um Fehler bei der Beschreibung von kombinatorischen Funktionen zu vermeiden, keine Möglichkeit, Informationen von einem bis zum nächsten Aufruf zu speichern. Prozeduren mit Schnittstellensignalen eignen sich als Beschreibungsrahmen für sequenzielle Schaltungen und Testhilfen. Nebenläufige Prozeduren ersetzen einen Prozess im übergeordneten Modell. Weiterführende und ergänzende Literatur siehe [11, 45]. Aufgabe 3.1 Welche der nachfolgenden Anweisungen sind, ohne das dafür Operatorfunktionen überladen oder Packages genutzt werden, zulässig? Welcher Wert wird zugewiesen bzw. warum ist die Anweisung unzulässig? variable r, s: real := 2.0; variable i: integer := 6; variable b: boolean; ... A1: r := s + 1; A2: b := r/=s; A3: b := (r=0) and (i=5); A4: s := 0.5 * r**i;
3.1 Imperative Beschreibungsmittel
213
Aufgabe 3.2 Entwickeln Sie eine Funktionsbeschreibung function seg7(x: std_logic_vector(2 downto 0)) return std_logic_vector;
die aus dem 3-Bit-Eingabevektor x entsprechend Abb. 3.12 den Ansteuervektor für eine 7-Segmentanzeige zur Darstellung der Zeichen »O«, »P«, »U« und »F« erzeugt. Im Sonst-Fall soll die Anzeige dunkel bleiben (alle Ausgabebits null). y0 y5
y6
y4
y1 y2
x
000
001
010
011 sonst
y
y3
yi = 0 yi = 1
Abb. 3.12. Zeichendarstellung zu Aufgabe 3.2
Aufgabe 3.3 Schreiben Sie eine Funktion function count1(x: std_logic_vector) return natural;
die die Anzahl der Einsen im Eingabevektor zählt und zurückgibt. Aufgabe 3.4 a) Schreiben Sie eine nebenläufige Prozedur procedure TMG(signal T, I: out std_logic; signal x: out tUnsigned(1 downto 0));
die die Signalverläufe in Abb. 3.13 a erzeugt. Die beiden Bits der Testeingabevektoren zi sollen mit Hilfe der Funktion »rand« aus Abschnitt 3.1.5 mit 50%-iger Wahrscheinlichkeit mit »0« und sonst mit »1« belegt werden. b) Schreiben Sie eine nebenläufige Prozedur procedure Akku(signal T, I: std_logic; signal x: tUnsigned(1 downto 0); signal y: inout tUnsigned(3 downto 0));
die das Verhalten der Schaltung in Abb. 3.13 b nachbildet. Abbildung 3.13 c zeigt als Hilfestellung zusätzlich eine Beschreibung der Funktion der Schaltung aus Abb. 3.13 b durch einen Abtastprozess.
214
3 VHDL im Detail
a) zu erzeugende Signalverl¨ aufe I
1 0
T
1 0
x
00 z0 z1 z2 z3 z4
0 2 4 6 t sim in ns zi zuf¨allige Bitvektorwerte
b) nachzubildende Schaltung
x I T
(1)
+
(2)
x I
y
(2) (2)
tUnsigned(1 downto 0) tUnsigned(3 downto 0) Initialisierungswert: ”0000”
(1) (2)
y
c) Prozessbeschreibung der nachzubildenden Schaltung process(T, I) begin if I=’1’ then y <= ”0000”; elsif RISING EDGE(T) then; y <= x + y; end if; end process;
Abb. 3.13. Zu erzeugende Signalverläufe und Schaltung zu Aufgabe 3.4
3.2 Anwendungsspezifische Datentypen Der Grundwortschatz von VHDL definiert keine Datentypen, sondern Beschreibungsmittel, um Datentypen zu definieren. Die bisher verwendeten Typen sind alle mit Ausnahme von »tUnsigned« und »tSigned« in standardisierten Packages vordefiniert. Letztere sind selbst definiert. Auch für viele andere Sachverhalte sind eigene Typen erforderlich oder zweckmäßig. Dieser Abschnitt beschreibt, wie eigene Typen definiert und verwendet werden. 3.2.1 Zahlentypen Ein Zahlentyp beschreibt einen zusammenhängenden Wertebereich. Er wird vereinbart mit type Zahlentyp is range Wertebereich;
Der Wertebereich kann aufsteigend oder absteigend geordnet sein: type tWochentag is range 1 to 7; –- Wertebereich: 1, 2, . . ., 7 type tBitnummer is range 3 downto 0; –- Wertebereich: 3, 2, 1, 0
Mit ganzzahligen Konstanten als Indexgrenzen wird ein diskreter (abzählbarer) Wertebereich vereinbart, mit reellwertigen Indexgrenzen ein stetiger Wertebereich. Beispiel für einen stetigen Wertebereich sei ein Datentyp für Wahrscheinlichkeitswerte: type tWahrscheinlichkeit is range 0.0 to 1.0;
Die Syntaxregeln für Zahlenkonstanten stehen in Anhang A.1.5. Der vordefinierte Typ für ganze Zahlen ist integer und der vordefinierte Typ für reellwertige Zahlen ist real. Reellwertige Zahlentypen werden als Gleitkommazahlen simuliert (vgl. Abschnitt 2.4.4).
3.2 Anwendungsspezifische Datentypen
215
Strenge Typen- und Wertebereichskontrolle Ein durchschnittlicher Hardware-Fehler, der beim Entwurf entsteht und erst im Einsatz bemerkt wird, ist wesentlich teurer als ein vergleichbarer SoftwareFehler. Deshalb ist in einer Hardware-Beschreibung jede Hilfe für das Aufspüren von Fehlern willkommen, selbst wenn sie den Entwurfsaufwand vergrößert. Ein wichtiges Mittel, um Entwurfsfehler zu erkennen, sind strenge Typen- und Wertebereichskontrollen. In VHDL gelten folgende Regeln. Bei einer Wertzuweisung an eine Variable Variablenname := Ausdruck ;
muss der Ausdruck auf der rechten Seite der Zuweisung denselben Typ wie die Variable auf der linken Seite haben. Ein Ausdruck mit dem am Anfang des Abschnitts definierten Typ »tWochentag« kann z.B. nicht an eine Variable vom Typ »tBitnummer« zugewiesen werden. Bei einer Signalzuweisung Signalname <= [Verzögerungsmodell ] W [after td ]{, W after td };
müssen die Ausdrücke der zugewiesenen Werte W den Typ des Signals und die Ausdrücke für die Verzögerung td den Typ delay_length haben. Bei einer Zuordnung, namensbasiert BS => BZ {, BS => BZ }
oder positionsbasiert BZ {, BZ }
(BS – Bezeichner, dem ein Objekt zugeordnet wird; BZ – Bezeichner des zugeordneten Objekts), müssen die einander zugeordneten Objekte denselben Typ haben (vgl. Abschnitt 1.2.5 und später Abschnitt 3.2.6). Eine Operation ist nur mit bestimmten Operandentypen ausführbar und ein Unterprogramm ist nur mit bestimmten Parametertypen aufrufbar. Für alle Zahlentypen – auch für selbst definierte – sind implizit alle arithmetischen Operationen und alle Vergleichsoperationen aus Tabelle 3.1 definiert. Für die Operandentypen gelten dabei folgende Regeln. Der Exponent bei der Potenzbildung muss ganzzahlig sein. Für alle anderen Operationen mit zwei Operanden müssen die Operandentypen übereinstimmen. Der Rückgabetyp der arithmetischen Operationen ist der des ersten Operanden. Vergleichsoperationen haben den Rückgabetyp boolean. Ein Operand mit dem zu Beginn des Abschnitts definierten Typ »tWahrscheinlichkeit« kann z.B. nicht mit einem Operanden vom Typ real multipliziert oder verglichen werden. Es ist jedoch möglich, jede Operatorfunktion für beliebige Operanden, d.h. auch für unterschiedliche Operandentypen, zu überladen (vgl. Abschnitt 3.1.6). Bei Zahlenangaben wird nur unterschieden, ob sie Nachkommastellen haben oder nicht. Der Wert »0.5« kann sowohl mit einem Operanden vom Typ real als auch mit einem Operanden vom Typ »tWahrscheinlichkeit« verknüpft oder
216
3 VHDL im Detail
verglichen werden, aber z.B. nicht mit den zuvor definierten ganzzahligen Typen »tWochentag« oder »tBitnummer«. Für den ganzzahligen Wert »5« gilt das Gegenteil. Auch der Ausdruck »10*0.5« ist unzulässig, weil in VHDL die ganzzahlige Konstante »10« nicht mit der reellwertigen Konstanten »0.5« multipliziert werden darf. Außer der Typverträglichkeit werden bei der Analyse eines VHDL-Programms und bei der Simulation auch die Wertebereiche kontrolliert. Konstante Werte außerhalb ihres zulässigen Bereichs verursachen Analysefehler und unzulässige Werte während der Simulation einen Simulationsabbruch mit einer entsprechenden Fehlermeldung. In der synthetisierten Schaltung existieren diese Fehlererkennungsmöglichkeiten nicht mehr. Deshalb ist es wichtig, eine Schaltung vor der Synthese gründlich zu simulieren. Beispiel 3.2: Es seien folgende Datentypen und Variablen vereinbart: type tAepfel is range 1 to 10; type tBirnen is range 1 to 10; variable apfel_1, apfel_2: tAepfel := 3; variable birne: tBirnen := 2; Welche der nachfolgenden Anweisungen verursacht einen Fehler bei der Analyse oder bei der Simulation? A1: apfel_1 := apfel_2 + 11; A2: birne := apfel_1 * apfel_2; A3: birne := birne + apfel_1;
⇒WEB-Projekt: P3.2/TestApfel.vhdl
A1: tAepfel ist ganzzahlig und darf mit der ganzzahligen »11« addiert werden. Die Summe hat wie das Zuweisungsziel den Typ »tAepfel«. Die Analyse findet keinen Fehler. Der zugewiesene Wert »3+11« liegt jedoch außerhalb des Wertebereichs von »tAepfel«, so dass sich die Simulation bei Ausführung der Zuweisung mit einer Fehlermeldung beendet. A2: Die Faktoren sind typgleich. Das Produkt vom Typ »tAepfel« darf aber nicht an eine Variable vom Typ »tBirnen« zugewiesen werden, so dass die Analyse mit einer Fehlermeldung abbricht. A3: Die Analyse meldet, dass Datenobjekte vom Typ »tAepfel« und »tBirnen« nicht miteinander addiert werden dürfen, und beendet sich.
Untertypen Ein Untertyp wird von seinem Basistyp durch eine Wertebereichsbeschränkung abgeleitet6 . So kann z.B. vom Typ »tWochentag« mit dem Wertebereich {1, 2, . . . , 7} ein Untertyp für das Wochenende abgeleitet werden, der nur die Werte {6, 7} darstellen kann: 6
Eine weitere Form der Untertypvereinbarung – die Zuordnung einer Auflösungsfunktion für Signale mit mehreren Quellen – folgt in Abschnitt 4.1.3.
3.2 Anwendungsspezifische Datentypen
217
subtype tWochenende is tWochentag range 6 to 7;
Ein Untertyp wird bei der Typprüfung wie sein Basistyp behandelt und erbt alle zulässigen Operationen, Funktionen etc. Aber es dürfen ihm keine Werte außerhalb seines Wertebereichs zugewiesen werden. Beispiel 3.3: Bei welcher der nachfolgenden Anweisungen findet die Analyse oder die Simulation einen Fehler? variable wt: tWochentag; variable we: tWochenende; ... A1: we := wt; A2: wt := ((we + 4) mod 7) + 1; A1: Die Zuweisung eines Objekts mit dem Basistyp an ein Objekt mit einem Untertypen ist erlaubt (kein Analysefehler). Einen Simulationsfehler gibt es, wenn der Wert der Variablen für den Wochentag nicht im Wertebereich für das Wochenende liegt. A2: Der Ausdruck auf der rechten Seite hat wie das Zuweisungsziel den Basistyp »tWochentag« und liegt für jeden darstellbaren Wert von »we« im zulässigen Bereich.
Statt mit einer Untertypvereinbarung kann die Wertebereichsbeschränkung auch bei der Vereinbarung der Datenobjekte erfolgen, z.B. variable Wochenende: tWochentag range 6 to 7; variable zaehler: natural range 0 to 99; variable Wahrscheinlichkeit: real range 0.0 to 1.0;
Typkonvertierung Eine typfremde Zuweisung, Zuordnung etc. verlangt eine Typkonvertierung. Zahlentypen sind untereinander eng verwandt. Für eng verwandte Datentypen wird mit der Typvereinbarung implizit eine gleichnamige Konvertierungsfunktion bereitgestellt, die Ausdrücke mit einem anderen Zahlentyp in eben diesen umwandelt: apfel_2 := tAepfel(birne); –-
Konvertierung in tAepfel
Bei der Konvertierung von Werten mit Nachkommastellen in ganzzahlige Werte wird gerundet. Bei der Konvertierung von einem ganzzahligen Typ in einen Typ mit Nachkommastellen ändert sich nur die rechnerinterne Zahlendarstellung: variable i, j: integer := 35; variable r: real := 12.67;
... i := integer(r); –r := real(j); –-
Zuweisung von 13 Zuweisung von 35.0
218
3 VHDL im Detail
Zahlentypen in Synthesebeschreibungen Die Synthese bildet alle Datenobjekte durch Bitvektoren nach. Nicht negative ganzzahlige Bereiche verwenden eine vorzeichenfreie Darstellung. Die Bitanzahl ergibt sich nach Gleichung 1.2 aus der Obergrenze des Wertebereichs. Zahlenbereiche, die auch negative Werte einschließen, werden im Zweierkomplement mit der dafür erforderlichen Bitanzahl dargestellt. Ohne Wertebereichsbeschränkung werden natural, dessen Wertebereich von null bis 231 −1 reicht, als 31 Bit vorzeichenfreie Zahl und integer, dessen Wertebereich von −231 bis 231 − 1 reicht, als 32 Bit Zahl im Zweierkomplement dargestellt. Wenn die Synthese kürzere Bitvektoren verwenden soll, ist der Wertebereich des Zahlentyps oder des Datenobjekts entsprechend zu beschränken. Zahlentypen mit stetigen Wertebereichen kann die Synthese nicht unterstützen, weil die erforderliche Darstellungsgenauigkeit nicht in der Typdefinition enthalten ist. Die Typdefinition erlaubt keine Rückschlüsse darüber, ob ein reellwertiger Zahlentyp in Hardware als eine Festkomma- oder eine Gleitkommazahl darzustellen ist. Für eine Festkommadarstellung ist die erforderliche Anzahl der Nachkommastellen und für eine Gleitkommadarstellung die erforderliche Größe der Mantisse nicht ablesbar (siehe Abschnitt 2.4.3 und 2.4.4). Die Hardware-Darstellung reellwertiger Zahlen verlangt deshalb immer geeignete Bitvektortypen. Die einzige Operation mit reellwertigen Zahlen, die die Synthese unterstützt, ist die Berechnung von Bitvektorkonstanten aus Zahlenkonstanten zum Übersetzungszeitpunkt. 3.2.2 Aufzählungstypen Ein Aufzählungstyp wird in VHDL durch eine Aufzählung seiner zulässigen Werte beschrieben. Die Zuordnung der Wertemenge erfolgt mit dem Schlüsselwort »is« gefolgt von einer kommaseparierten Liste. Die Listenelemente dürfen wie bei dem vordefinierten Aufzählungstyp type bit is (’0’, ’1’);
druckbare Zeichen oder wie bei dem vordefinierten Typ type boolean is (false, true);
Bezeichner sein. Druckbare Zeichen haben den Vorteil, dass sich die Werte der zugehörigen Vektortypen als Zeichenketten darstellen lassen. Unterschiedliche Aufzählungstypen sind keine eng verwandten Datentypen. Für sie gibt es nicht die implizit vereinbarten namensgleichen Konvertierungsfunktionen. Dafür gibt es für die Konvertierung zwei auf alle Aufzählungstypen anwendbare Attribute. Das Attribut »tAZ ’pos(x)« gibt die Listenposition des Wertes von x in der Definition des Aufzählungstyps »tAZ « und das Attribut »tAZ ’val(p)« gibt den Wert zur Position p in der Aufzählung zurück. In der folgenden Konvertierung von boolean nach bit wird vom zu konvertierenden Wert die Position und vom Zieltyp der Wert zur Position bestimmt:
3.2 Anwendungsspezifische Datentypen
219
variable xbit: bit; variable xbool: boolean; ... xbit := bit’val(boolean’pos(xbool));
Bei der Bildung von Untertypen wird der Positionsbereich eingeschränkt: type tAWochentag is (Mo, Di, Mi, Do, Fr, Sa, So); subtype tAWerktag is tAWochentag range Mo to Fr;
Dadurch können nur zusammenhängende Elementebereiche einen Untertyp bilden. Von dem soeben vereinbarten Basistyp »tAWochentag« lässt sich z.B. kein Untertyp mit den Werten »Mo« und »Mi« bilden. Die Operationen für Aufzählungstypen beschränken sich im Wesentlichen auf Zuweisungen und Vergleiche. Bei einem Größenvergleich wird die Listenposition ausgewertet. Ein Wert ist größer, wenn er in der Elementeauflistung weiter rechts steht. Aufzählungstypen in Synthesebeschreibungen In der Hardware werden die Werte eines Aufzählungstyps durch Bitvektorkonstanten dargestellt. Die Zuordnung der Bitvektorkonstanten zu den darzustellenden Werten wird als Codierung bezeichnet. Für schaltungsinterne Zustände hat die Codierung keinen Einfluss auf das Anschlussverhalten, sondern nur auf den Schaltungsaufwand, die Geschwindigkeit etc. Deshalb wird sie in der Regel dem Entwurfssystem überlassen. Es gibt aber auch wie für alle anderen Entwurfsentscheidungen die Möglichkeit, die Codierungen für Aufzählungstypen explizit vorzugeben. 3.2.3 Physikalische Typen Physikalische Typen sind eine VHDL- bzw. Ada-spezifische Besonderheit. Sie definieren einen ganzzahligen Bereich und (mindestens) eine Maßeinheit. Die darstellbaren Werte sind ganzzahlige Vielfache der kleinsten Maßeinheit. Bei der Vereinbarung mehrerer Maßeinheiten sind die Umrechnungsfaktoren mit anzugeben. Der vordefinierte Datentyp time ist im Package std_standard wie folgt definiert: type time is range -2147483647 to +2147483647 units fs; ps = 1000 fs; ns = 1000 ps; ms = 1000 us; sec = 1000 ms; min = 60 sec; end units;
us = 1000 ns; hr = 60 min;
Nach demselben Schema können auch Datentypen für die Spannung, den Strom und andere physikalische Größen definiert werden.
220
3 VHDL im Detail
Für die physikalischen Typen – auch für selbst definierte – sind die arithmetischen Operationen außer der Potenzbildung und die Vergleichsoperationen definiert. Bei der Multiplikation darf der zweite Operand nur ein Zahlentyp sein. Zur Umwandlung einer physikalischen Größe in einen dimensionslosen Wert wird sie durch einen typgleichen Wert dividiert. Das Ergebnis ist ganzzahlig. Der Ausdruck »1,23846 us/ps« hat z.B. den Wert 1238. Die Nachkommastellen gehen verloren. Ein grundlegendes Problem der physikalischen Typen ist der ganzzahlige Wertebereich. Die Werte physikalischer Typen können in unterschiedlichen Größenordnungen liegen. Um z.B. mit einem einzigen Zeit-Typen sowohl Werte im Femtosekunden- als auch im Stundenbereich darstellen zu können, muss der Zahlenbereich knapp 20 Zehnerpotenzen überdecken. Das erfordert eine 64-Bit-Darstellung für Zeitangaben im Simulator7 . Für eigene Typen zur Darstellung physikalischer Größen eignen sich reellwertige Zahlentypen besser, weil diese als Gleitkommazahlen simuliert werden. Gleitkommazahlen passen die Kommaposition in der Darstellung automatisch an die Größe des Wertes so an, dass möglichst genau und ohne Wertebereichsüberläufe gerechnet wird (siehe später Abschnitt 4.2.1). 3.2.4 Attribute von elementaren Datentypen Attribute sind in VHDL an Objekte gebundene Zusatzinformationen. Für alle elementaren Datentypen – dazu zählen die Zahlentypen, die Aufzählungstypen und die physikalischen Typen – stehen die Attribute in Tabelle 3.2 zur Verfügung. In der Spalte »Typ von T « bedeutet »beliebig«, dass das Attribut für alle elementaren Typen definiert ist, und »diskret«, dass es nur für Typen mit abzählbaren Werten, d.h. für ganzzahlige Typen, Aufzählungstypen und physikalische Typen, definiert ist. Beispiel 3.4: Was geben die einzelnen Write-Anweisungen aus? –- type std_logic is (’U’, ’X’, ’0’, ’1’, ...); subtype tSInt is integer range -128 to 127; type tFarbe is (rot, gruen, blau); variable b: std_logic := ’1’; variable i: tSInt := 25; variable f: tFarbe; ... A1: write("std_logic’low=" & str(std_logic’low) & " std_logic’pred(b)=" & str(std_logic’pred(b))); A2: write("tSInt’succ(tSInt’low+i)=" & str(integer(tSInt’succ(tSInt’low+i)))); 7
Im Widerspruch zur Typvereinbarung von time, die den Wertebereich auf −231 bis 231−1 Femtosekunden beschränkt (32-Bit-Darstellung), verwenden GHDL und andere Simulatoren intern für Zeitangaben eine 64-Bit-Darstellung.
3.2 Anwendungsspezifische Datentypen
221
Tabelle 3.2. Vordefinierte Attribute für elementare Datentypen Attribut
Typ von T
Ergebnis- Ergebnis typ
T ’left, T ’right, T ’low, T ’high
beliebig
Basistyp von T
T ’ascending
beliebig
boolean true für aufsteigende, false für absteigende Wertebereiche
T ’image(x)
beliebig
string
Textdarstellung des Wertes von x
T ’value(s)
beliebig
Basistyp von T
Wert der Zeichenkette in der Darstellung von T
T ’pos(x)
diskret universal Position von Wert x integer(1)
T ’val(i)
diskret
T ’succ(x), T ’pred(x), diskret T ’leftof(x), T ’rightof(x)
linke, rechte, untere bzw. obere Grenze des Wertebereichs
Wert von Position i Basistyp von T
Vorgänger, Nachfolger, bzw. linker Wert von x
rechter
(x – Datenobjekt vom Typ T ; s – Datenobjekt vom Typ string; i – ganzzahliger Typ; (1) Spezialtyp, der ohne Konvertierung mit jedem ganzzahligen Typ verknüpft und jedem ganzzahligen Typ zugewiesen werden kann; diskret – ganzzahliger Typ, Aufzählungstyp oder physikalischer Typ)
f := tFarbe’succ(gruen); A3: write("tFarbe’image(tFarbe’succ(gruen))="&tFarbe’image(f));
A1: Der kleinste Wert von std_logic ist »U«. Der Vorgänger von »1« ist »0«: Ausgabetext: std_logic’low=U std_logic’pred(b)=0 A2: Der kleinste Wert vom Typ »tSInt« ist −128. Dazu 25 addiert ergibt −103. Davon der Nachfolger ist −102: Ausgabetext: tSInt’succ(tSInt’low+i)=-102 A3: Der Nachfolger von »gruen« ist »blau«. Das ist aber nur ein Symbol, das zur Ausgabe mit dem Image-Attribut in einen Text umgewandelt werden muss: Ausgabetext: tFarbe’image(tFarbe’succ(gruen))=blau ⇒WEB-Projekt: P3.2/ SkalAttr.vhdl
3.2.5 Felder Ein Feld ist eine Zusammenfassung typgleicher Elemente, in der die einzelnen Elemente über einen Index oder über mehrere Indizes ausgewählt werden können. Ein Feldtyp wird wie folgt vereinbart:
222
3 VHDL im Detail type Feldtyp is array (IXB {, IXB }) of Elementtyp;
(IXB – Indexbereich). Die Indexbereiche sind diskrete Bereiche. Sie können aufsteigende oder absteigende diskrete Zahlenbereiche sein: –- Vereinbarung von zwei Indexbereichen subtype tIdx1 is integer range 1 to 5; subtype tIdx2 is integer range 7 downto 4; –- Typ für ein 2D-Bitfeld mit beiden Indexbereichen type tFeld2D is array (tIdx1, tIdx2) of std_logic;
Der Indexbereich darf auch ein Aufzählungstyp sein: type tZustand is (S1, S2, S3, S4); type tByte is array (7 downto 0) of std_logic; type tZustandTab is array (tZustand) of tByte;
Ein Boxoperator »<>« hinter dem Indextyp wie bei type bit_vector is array (natural range <>) of bit;
beschränkt den Indexbereich auf den Wertebereich eines diskreten Typs, ohne die Indexgrenzen festzulegen. Der Indexbereich eines so definierten Typs wird später bei der Vereinbarung von Untertypen, bei der Vereinbarung von Datenobjekten oder beim Unterprogrammaufruf festgelegt. Der Zugriff auf Feldelemente erfolgt über den Index. Teilfelder werden über Indexbereiche ausgewählt: –- Vereinbarungen im Testprozess variable Feld2D: tFeld2D; variable idx1: tIdx1; variable idx2: tIdx2; variable x, y: std_logic; –- Anweisungsfolge im Testprozess y := Feld2D(idx1, idx2); –- Feldelement lesen Feld2D(idx1, idx2) := x; –- Feldelement beschreiben
Indexbereiche, Elementtypen und Feldtypen werden vorzugsweise in einem Package gemeinsam mit den Unterprogrammen zu ihrer Verarbeitung vereinbart. Feldattribute Alle Feldtypen besitzen die in Tabelle 3.3 aufgelisteten Attribute zur Abfrage der Indexbereiche. Diese Attribute können auch auf die Feldobjekte selbst angewendet werden. Der optionale Wert n gibt die Nummer des Indexbereichs an. Ohne Angabe beziehen sich die Attribute auf den ersten Indexbereich. Das folgende Beispiel definiert einen Typ für ein zweidimensionales Feld und eine Variable von diesem Typ. Die Attribute ’left, ’right, ..., ’length
3.2 Anwendungsspezifische Datentypen
223
Tabelle 3.3. Feldattribute Attribut
Ergebnis
F ’left[(n)], F ’right[(n)], F ’low[(n)], F ’high[(n)]
linker, rechter, kleinster bzw. größter Indexwert
F ’range[(n)], F ’reverse_range[(n)]
Indexbereich, umgekehrt geordneter Indexbereich
F ’length[(n)]
Anzahl der Elemente
F ’ascending[(n)]
true für einen aufsteigenden und false für einen absteigenden Indexbereich
(F – Feldobjekt oder Feldtyp; n – Nummer des Indexbereichs)
und ’ascending liefern integer bzw. boolean-Werte, die sich auch als Texte ausgeben lassen. Die mit ’range und ’reverse_range abgefragten Bereiche können als Iterationsbereiche für Schleifen und in Vereinbarungen von Feldtypen und Feldobjekten verwendet werden. Beispiel 3.5: Was geben die nachfolgenden Write-Anweisungen aus? type tTab is array (1 to 5, 6 downto 4) of natural; variable Tab: tTab; ... A1: write("Tab’left(1)=" & str(Tab’left(1))); A2: write("Tab’ascending(2)=" & str(Tab’ascending(2))); A3: for idx in Tab’reverse_range(2) loop write("idx=" & str(idx)); ⇒WEB-Projekt: P3.2/Test_Feld.vhdl end loop;
A1: Die linke Indexgrenze des ersten Indexbereichs ist Tab’left(1)=1 A2: Der zweite Indexbereich ist nicht aufsteigend definiert: Tab’ascending(2)=false A3: Die For-Schleife gibt die Indexwerte des zweiten Indexbereichs in umgekehrter Reihenfolge, d.h. aufsteigend, aus: idx=4 idx=5 idx=6
Operationen und eng verwandte Feldtypen Für Felder gibt es nur wenige vordefinierte Operationen. Typgleiche Felder können einander zugewiesen, zugeordnet oder miteinander verglichen wer-
224
3 VHDL im Detail
den. Für Bitvektortypen sind zusätzlich die bitweisen Logikoperationen, die Verschiebe- und die Rotationsoperationen vorgesehen (vgl. Tabelle 3.1). Felder mit gleichen Indexbereichen und gleichem Elementtyp sind wie Zahlentypen miteinander eng verwandt. Auch für sie wird bei der Typvereinbarung automatisch eine gleichnamige Konvertierungsfunktion bereitgestellt. Die im standardisierten Package ieee.numeric_bit definierten Bitvektortypen type unsigned is array (natural range <>) of bit; type signed is array (natural range <>) of bit;
für die Darstellung vorzeichenfreier und vorzeichenbehafteter Zahlen sind z.B. mit dem Typ bit_vector eng verwandt. Zusätzlich zu den für den Typ bit_vector definierten Operationen sind für unsigned und signed in ihrem Package die arithmetischen Operatorfunktionen und die Größenvergleiche für vorzeichenfreie bzw. für Zweierkomplementzahlen definiert. Ein gutes Beispiel für die Nutzung der engen Verwandtschaft ist die Beschreibungsschablone der Addition vorzeichenfreier Zahlen für Datenobjekte vom Typ bit_vector. Die Datenobjekte müssen, damit der richtige Additionsoperator aufgerufen wird, vor der Addition in den Typ unsigned und die Summe zurück in den Typ bit_vector konvertiert werden. Die Konvertierung erfolgt in beiden Fällen mit dem Bezeichner des Zieltyps: variable a, b, c: bit_vector(7 downto 0); ... a := bit_vector(unsigned(b) + unsigned(c));
Eine Konvertierung zwischen eng verwandten Typen kostet nur Beschreibungsaber keinen Simulations- und keinen Hardware-Aufwand. 3.2.6 Zuordnungslisten für Feldelemente Eine Zuordnungsliste ordnet Elementen einer Menge Elemente einer anderen Menge zu. VHDL unterstützt eine positionsbasierte und eine namensbasierte Zuordnung. Die Objekte, denen etwas zugeordnet wird – Schnittstellensignale von Entwurfseinheiten, Aufrufparameter von Unterprogrammen und Elemente zusammengesetzter Datenobjekte – sind in einer bestimmten Reihenfolge vereinbart. Bei einer positionsbasierten Zuordnung bilden die zugeordneten Objekte eine kommaseparierte Liste derselben Elementeanzahl und Reihenfolge BZ {, BZ }
Eine namensbasierte Zuordnungsliste besteht aus Zuordnungstupeln der Form BS => BZ {, BS => BZ }
3.2 Anwendungsspezifische Datentypen
225
(BS – Bezeichner der Objekte, denen etwas zugeordnet wird; BZ – Bezeichner der zugeordneten Objekte, vgl. Abschnitt 1.2.5). Bei Feldern sind die Bezeichner der Objekte, denen etwas zugeordnet wird, Indexwerte oder Indexbereiche. Im folgenden Beispiel werden den Elementen eines Zeichenkettenfeldes mit dem Indexbereich »1 bis 3« verschiedene Zeichenkettenwerte zugeordnet. Positionsbasiert wird dem ersten Feldelement der erste Wert der Zuordnungsliste, dem zweiten Feldelement der zweite Wert etc. zugeordnet: type tTextFeld is array (1 to 3) of string(1 to 5); constant TextFeld: tTextFeld := ("Text1","Text2","Text3");
Namensbasiert werden der Indexwert und der Zuordnungsoperator dem zugeordneten Datenobjekt vorangestellt: constant TextFeld:tTextFeld := (1=>"Text1",2=>"Text2",3=>"Text3");
Bei der namensbasierten Zuordnung können Elemente, denen dasselbe Objekt zugeordnet wird, zusammengefasst werden. In der folgenden Anweisung wird dem ersten Feldelement »Text1« und allen anderen Feldelementen »Text2« zugeordnet: TextFeld := (1=>"Text1", others=>"Text2");
Die Initialisierung eines Bitvektors vom Typ std_logic_vector mit dem Pseudo-Wert »ungültig« wird üblicherweise als Zuordnung von »X« an »alle anderen«, d.h an alle Elemente, beschrieben: variable x: std_logic_vector(...) := (others=>’X’);
Nach diesem Schema werden Vektoren in Unterprogrammen, deren Länge erst beim Aufruf festgelegt wird, initialisiert. Zuordnungslisten – positions- oder namensbasiert – können auch in Variablen- und Signalzuweisung zur Zusammenfassung mehrerer Anweisungen genutzt werden. Dazu werden mehrere Ausdrücke oder Signale auf der rechten Zuweisungsseite zu einem Feld oder Verbund zusammengefasst und/oder die Ergebniselemente auf der linken Seite unterschiedlichen Variablen oder Signalen zugeordnet: variable x: std_logic_vector(1 downto 0); variable a, b, c, d, e, f: std_logic; ... x := (0=>a, 1=>b); –- Vektortyp := Zuordnungsliste (c, d) := x; –- Zuordnungsliste := Vektortyp –- Zuordnungsliste := Zuordnungsliste (e, f) := std_logic_vector’(1 => a and b, 0 => a or b); ⇒WEB-Projekt: P3.2/TestZL.vhdl
226
3 VHDL im Detail
Das flexible Typkonzept von VHDL verursacht gelegentlich das Problem, dass der Typ eines Ausdrucks oder einer Zuordnungsliste aus der Beschreibung nicht eindeutig hervorgeht. Für die beiden Zuordnungslisten rechts und links des Zuweisungsoperators in der letzten Programmzeile ist nur ablesbar, dass die Elemente vom Typ std_logic sind, aber nicht welchen Typ die daraus zusammengesetzten Vektoren haben. Die Analyse bricht in diesem Fall mit einer Meldung ab und verlangt eine explizite Typzuordnung mit einer Typqualifikation. Eine Typqualifikation hat folgende Syntax: Typ’(Ausdruck )
Man beachte das Hochkomma, in dem sich eine Typqualifikation von einem Funktionsaufruf unterscheidet [11]. 3.2.7 Bitvektoren Bitvektoren sind eindimensionale Felder mit Bittypen als Elemente, für die außer den allgemeinen Feldoperationen – Zuweisung, Zuordnung sowie Test auf Gleich- und Ungleichheit – auch Hardware-spezifische Operationen, z.B. die bitweisen Logikoperationen, vorgesehen sind (vgl. Abschnitt 2.1.2). Die vordefinierten Bitvektortypen lassen sich nach dem Elementtyp und der dargestellten Information in Gruppen einteilen (Tabelle 3.4). Es gibt zum einen vordefinierte Bitvektortypen mit dem zweiwertigen Elementtyp bit und zum anderen mit dem neunwertigen Elementtyp std_logic, der außer den Logikwerten »0« und »1« auch Pseudo-Werte wie »nicht initialisiert« und »ungültig« darstellen kann. Aus Sicht der Informationsdarstellung gibt es die universellen Bitvektortypen bit_vector und std_logic_vector, auf die nur zusätzlich zu den allgemeinen Vektoroperationen die logischen, die Verschiebe- und die Rotationsoperationen anwendbar sind, und die Bitvektortypen unsigned für vorzeichenfreie und signed für vorzeichenbehaftete ganze Zahlen mit den entsprechenden arithmetischen Operationen. Zusätzlich zu den vordefinierten Bitvektortypen lassen sich beliebige weitere Bitvektortypen definieren. Die Synthese unterstützt nicht standardisierte Bitvektortypen jedoch nur dann, wenn sie in Packages definiert sind, die zum Synthesesystem gehören oder wenn sie mit einem unterstützten Bitvektortyp eng verwandt sind. Tabelle 3.4. Bitvektortypen darzustellende Information
Elementetyp BIT
STD LOGIC
nicht informationsspezifisch BIT VECTOR STD LOGIC VECTOR vorzeichenfreie Zahlen UNSIGNED tUnsigned vorzeichenbehaftete Zahlen SIGNED tSigned ··· ··· ··· standardisierte Bitvektortypen mit direkter Syntheseunterst¨ utzung
3.2 Anwendungsspezifische Datentypen
227
Die beiden standardisierten Bitvektortypen für die Zahlendarstellung unsigned und signed haben den Elementtyp bit und können damit keine Gültigkeitsinformationen darstellen. Damit die Gültigkeitsinformation bei der Simulation nicht verloren geht, verwenden die Beispiele im Buch die zwei selbst definierten, mit std_logic_vector eng verwandten Typen type tUnsigned is array (natural range <>) of std_logic; type tSigned is array (natural range <>) of std_logic;
Damit die Synthese die Unterprogramm- und Operatorfunktionsaufrufe zur Beschreibung von Hardware-Bausteinen für diese Typen als solche erkennt, müssen diese über Konvertierungsfunktionen mit den Unterprogramm- und Operatoraufrufen der standardisierten Typen nachgebildet werden. Die logischen Operatorfunktionen sind so definiert, dass die Operanden nach std_logic_vector konvertiert werden, mit diesem Typ die jeweilige Operatorfunktion aufrufen und das Ergebnis zurückkonvertiert wird. Da beide Typen mit std_logic_vector eng verwandt sind, erfolgt die Konvertierung mit dem Typbezeichner und verursacht bei der Simulation keinen Rechenaufwand. Die Funktionsdefinition des UND-Operators für den Typ »tUnsigned« lautet z.B. wie folgt: function "and"(a, b: tUnsigned) return tUnsigned is begin return tUnsigned(std_logic_vector(a) and std_logic_vector(b)); end function;
Die arithmetischen Operatorfunktionen und die Konvertierungsfunktionen zwischen Zahlen- und Bitvektortypen müssen in einer synthesefähigen Beschreibung auf die entsprechenden Funktionsaufrufe der Typen unsigned und signed zurückgeführt werden. Die Konvertierung von std_logic_vector nach bit_vector und umgekehrt erfolgt mit den beiden Funktionen function to_bitvector(x: std_logic_vector) return bit_vector; function to_stdlogicvector(x: bit_vector) return std_logic_vector;
aus dem Package ieee.std_logic_1164, die auch in synthesefähigen Beschreibungen zugelassen sind. Bei der Konvertierung nach bit_vector werden »L« und »0« durch »0«, »H« und »1« durch »1« und alle übrigen PseudoWerte durch den Wert der package-internen Konstante »xmap«, die »0« oder »1« sein kann, ersetzt. Die Zurückführung von Funktionsaufrufen für die Typen »tUnsigned« und »tSigned« auf die von unsigned und signed erfordert zwei weitere Konvertierschritte, zum einen zwischen den miteinander eng verwandten Typen »t[Un]signed« und std_logic_vector und zum anderen zwischen den miteinander eng verwandten Typen bit_vector und »[un]signed« (Abb. 3.14). In den Packages »Tuc.Numeric_...« sind diese dreistufigen Konvertierungen jeweils zu einer Funktion zusammengefasst. Für die Konvertierung zwischen »tUnsigned« und unsigned lauten diese:
228
3 VHDL im Detail int(...)(4) to unsigned(...), to signed(...)(4)
(1) (2) STD LOGIC VECTOR(...) TO BITVECTOR(...)
tUnsigned tSigned
STD LOGIC VECTOR
tUnsigned(...)(1) tSigned(...)(1)
(1) UNSIGNED(...) (1) SIGNED(...)
BIT VECTOR
(3) TO INTEGER(...)
UNSIGNED SIGNED
INTEGER
(3) 2 (1) TO UNSIGNED(...) TO STDLOGICVECTOR(...) BIT VECTOR(...) (3) TO SIGNED(...)
to tUnsigned(...), to tSigned(...)(4) to tUnsigned(...), to Signed(...)(4) (1) (2) (3) (4)
eng verwandte Typen, Konvertierung mit dem Bezeichner des Zieltyps definiert im Package IEEE.STD LOGIC 1164 definiert im Package IEEE.NUMERIC BIT definiert in den Packages Tuc.Numeric Sim und Tuc.Numeric Synth
Abb. 3.14. Konvertierungsschema zur Nachbildung von Operationen für die selbst definierten Bitvektortypen »tUnsigned« und »tSigned« mit Operationen für die standardisierten Typen std_logic_vector, unsigned, signed und integer
function to_unsigned(x: tUnsigned) return unsigned is begin return unsigned(to_bitvector(std_logic_vector(x))); end function; function to_tUnsigned(x: unsigned) return tUnsigned is begin return tUnsigned(to_stdlogicvector(bit_vector(x))); end function;
Die komplette Operatorfunktion für die Addition von Operanden vom Typ »tUnsigned« lautet: function "+" (a, b: tUnsigned) return tUnsigned is begin return to_tUnsigned(to_Unsigned(a) + to_unsigned(b)); end function; ⇒WEB-Projekt: Tuc/Numeric_Synth.vhdl
Das ist aber nur die Beschreibung für die Synthese, bei der die Pseudo-Werte ignoriert werden. Die Simulation benötigt zusätzlich eine Sonderbehandlung für ungültige Summanden. Da die Gültigkeitsinformation bei der Konvertierung nach [un]signed verloren geht, muss diese Sonderbehandlung vor der Konvertierung erfolgen. In der nachfolgenden Operatorfunktion für die Simulation der Addition wird zuerst geprüft, ob die Summanden gültig sind. Nur wenn das der Fall ist, folgen die Konvertierung und die Ergebnisberechnung. Im anderen Fall wird der Pseudo-Wert für ungültig zurückgegeben:
3.2 Anwendungsspezifische Datentypen
229
function "+" (a, b: tUnsigned) return tUnsigned is constant cXX: tUnsigned(max(a’length, b’length)-1 downto 0) := (others=>’X’); begin if is_x(a) or is_x(b) then return cXX; else return to_tUnsigned(to_Unsigned(a) + to_Unsigned(b)); end if; ⇒WEB-Projekt: Tuc/Numeric_Sim.vhdl end function;
Wie in Abb. 3.14 gezeigt, gibt es des Weiteren auch ein vierstufiges Konvertierungsschema von t[Un]signed nach integer und umgekehrt. Die entsprechenden Konvertierungsfunktionen werden für die Initialisierung von Bitvektoren mit Zahlenwerten und für die Umwandlung von Bitvektorwerten in Indexwerte für die Adressierung von Feldern benötigt. Beim Übergang von der Simulation zur Synthese ist das Package »Tuc.Numeric_Sim« durch das Package »Tuc.Numeric_Synth« zu ersetzen und umgekehrt. So werden numerische Operationen mit Gültigkeitsinformation simuliert und die Synthese wird nicht mit Informationen belästigt, die sie nicht verarbeiten kann. Die beiden Numeric-Packages sind relativ umfangreich, aber nach einem einfachen Schema programmiert. Nach demselben Schema lassen sich weitere von der Synthese zu unterstützende Bitvektortypen mit den zugehörigen Operatorfunktionen definieren, z.B. Bitvektortypen für Gleitkommaformate. Das Entwurfssystem ISE von Xilinx, das in Abschnitt 2.1 zum Testen der Synthese-Beispiele genutzt wurde, schreibt beim Erzeugen einer neuen VHDLDatei in den Vorspann die Import-Anweisungen für die nicht standardisierten Packages ieee.std_logic_arith, ieee.std_logic_unsigned und ieee.std_logic_signed. Diese Packages der Firma Synopsis stellen auch überladene arithmetische Operatorfunktionen für mit std_logic_vector eng verwandte Bitvektortypen bereit. Sie sind jedoch nicht ganz mit dem VHDL-Standard kompatibel und sollten nur in Systemen genutzt werden, die sie ausdrücklich unterstützen. In diesen Systemen haben sie den Vorteil, dass die Sonderbehandlungen für ungültige Werte bei der Synthese genau wie bei der Verwendung des Packages ieee_std_logic_1164 automatisch deaktiviert werden, so dass nicht wie bei »Tuc.Numeric_Sim« und »Tuc.Numeric_Synth« das Package ausgetauscht werden muss. Das Package std_logic_arith definiert die Typen unsigned und signed als Vektortypen mit Elementen vom Typ std_logic und exportiert dafür dieselben Operatorfunktionen und Unterprogramme, wie das standardisierte Package ieee.numeric_bit für die gleichnamigen mit bit_vector eng verwandten Typen. Beide Packages dürfen deshalb nicht zusammen genutzt werden. Das Package ieee.std_logic_unsigned überlädt die arithmetischen Operatoren für den Typ std_logic_vector mit den arithmetischen Operatorfunktionen für vorzeichenfreie ganze Zahlen und das Package ieee.std_logic_signed mit den arithmetischen Operatorfunktionen für
230
3 VHDL im Detail
vorzeichenbehaftete ganze Zahlen. Auch diese beiden Packages können nicht gleichzeitig genutzt werden. 3.2.8 Verbund Ein Verbund ist eine Zusammenfassung von Datenobjekten mit beliebigen Typen, z.B. type tPaket is record Nummer: tUnsigned(3 downto 0); Name: string(1 to 11); gesendet: bit; end record;
Der Zugriff auf die einzelnen Elemente erfolgt über den Objektnamen und den Elementnamen, z.B. variable Paket: tPaket; ... Paket.Nummer := "0110";
Die Zuordnung von Objekten und Werten zu den Elementen eines Verbunds wird mit einer Zuordnungsliste beschrieben, z.B. Paket := (Nummer=>"0110", gesendet=>’1’, Name=>"Nachricht 3");
Ein Verbund für Gleitkommazahlen Eine Gleitkommazahl besteht aus den drei Teildatenobjekten: Vorzeichen, Charakteristik und Mantisse. Das in Abschnitt 2.4.4 behandelte IEEE-754Single-Format definiert einen 32-Bit-Vektor, der sich aus einem Vorzeichenbit, einer 8-Bit-Charakteristik und einem 23-Bit-Vektor für die Mantisse zusammensetzt. Das ist ein Verbund: type tFloat32 is record s: std_logic; c: tUnsigned (7 downto 0); m: tUnsigned (22 downto 0); end record;
–- Vorzeichen –- Charakteristik –- Mantisse
Die Charakteristik ist eine vorzeichenfreie ganze Zahl und die Mantisse, die eigentlich eine vorzeichenfreie Festkommazahl repräsentiert, lässt sich auch als ganze Zahl mit einer gedachten Kommaposition modellieren. In einem Entwurfsprojekt werden eigene Datentypen in der Regel in einem Package gemeinsam mit den Unterprogrammen zu ihrer Verarbeitung definiert. Zu einem neuen Zahlentyp gehören Konvertierungsfunktionen, überladene arithmetische Operatoren etc.:
3.2 Anwendungsspezifische Datentypen
231
package tFloat32_pack is type tFloat32 ...; –- Konvertierung in einen Ausgabetext function str(x: tFloat32; fmt: character := ’2’) return string; –- Initialisierung mit einer Zahlenkonstante function to_tFloat32(x: real) return tFloat32; –- Test ob ungültig function is_x(x: tFloat32) return boolean; –- arithmetische Operatorfunktionen function "+"(a, b: tFloat32) return tFloat32; function "-"(a, b: tFloat32) return tFloat32; ... end package;
Mit einem solchen Package sieht eine Funktionsbeschreibung mit Gleitkommazahlen nicht viel anders aus als eine Beschreibung mit ganzen Zahlen: variable a, b, c, d: tFloat32; ... a := to_tFloat32(18.25); b := to_tFloat32(-2.151E-2); c := to_tFloat32(1.170E-39) d := (a + b) * c;
Zur Demonstration, um wie viel übersichtlicher eine strukturierte Beschreibung mit Packages ist, sollen den Variablen a bis c ihre Werte auch einmal ohne die Konvertierungsfunktion »to_tFloat32(...)« zugewiesen werden. Dazu sind als erstes die Gleitkommakonstanten manuell in Bitvektorkonstanten umzurechnen. Das ist für die drei speziellen Zahlenwerte bereits in Abschnitt 2.4.4 in Abb. 2.68 erfolgt. Das Ergebnis steht in der nachfolgenden Tabelle rechts. Links daneben sind dieselben Zuordnungen in VHDL in unterschiedlicher Weise beschrieben: variable a, b, c: tFloat32; ... a := (s=>’0’, m=>(20|17=>’1’, others=>’0’), c=>x"83"); b := (s=>’1’, m=>x"CB3A1" & "110", c=>x"79"); c := (s=>’0’, m=>"000" & x"CBD19", c=>(others=>’0’));
Bitnummer
a b
31 24 23 16 15 8 7 0 s c m 0 1 00000 1 1 001001 0 00000000 00000000 8
3
20
7
9
C
17
1 0111100 11100101 10011101 00001110 B
3
A
1
c 0 0000000 00001100 10111101 00011001 C
B
D
1
9
Bei der Mantisse der Variablen a sind die Bitstellen, denen »1« zugeordnet wird, zusammengefasst und allen anderen Bitstellen ist der Wert »0« zugeordnet. Den 8-Bit-Charakteristika von a und b sind zwei Hexadezimalziffern zugeordnet. Die Werte der 23-Bit-Mantissen für die Variablen b und c sind aus fünf Hexadezimalziffern und drei Einzelbits zusammengesetzt. Alle drei Zuordnungen haben eines gemeinsam. Die beabsichtigte Zielfunktion – die
232
3 VHDL im Detail
Zuweisung eines bestimmten Zahlenwertes – ist fast nicht mehr zu erkennen. Eine unübersichtliche Beschreibung enthält erfahrungsgemäß Fehler, die schwer zu finden sind. Spätestens beim Debuggen wird offensichtlich, dass die Programmierung eines Packages zur übersichtlichen Entwurfsstrukturierung, auch wenn das einige Arbeitstage in Anspruch nimmt, eine gute Investition ist. Texte variabler Länge Der Datentyp string kann nur Zeichenketten konstanter Länge darstellen, aber keine Ausgabetexte, deren Länge variabel ist. Ein Typ für Texte variabler Länge ist im einfachsten Fall ein Verbund aus einer ausreichend langen Zeichenkette und einem Zeiger auf die erste freie Position: constant Zeilenlaenge: positive := 120; type tString is record str: string(1 to Zeilenlaenge); –- Zeichenkette ptr: positive; –- erste freie Position end record;
Die wichtigsten Textverarbeitungsfunktionen sind die Zuweisung einer Zeichenkette, das Anhängen einer Zeichenkette und die Rückumwandlung in eine Zeichenkette. Beim Anhängen wird die übergebene Zeichenkette, solange noch Platz ist, Zeichen für Zeichen an das Textende kopiert und der Zeiger erhöht. Wenn das Textobjekt voll ist, geht der Rest der Zeichen verloren: procedure append(x: inout tString; s: string) is begin for idx in s’range loop if x.prt>x.zk’high then exit; else x.zk(x.ptr) := s(idx); x.ptr := x.ptr + 1; end if; end loop; ⇒WEB-Projekt: Tuc/Ausgabe.vhdl end procedure;
Zum Beschreiben mit einem neuen Wert wird das Textobjekt zuerst durch Rücksetzen des Zeigers geleert. Dann wird die übergebene Zeichenkette angehängt: procedure assign(x: inout tString; s: string) is begin x.ptr := 1; append(x, s); ⇒WEB-Projekt: Tuc/Ausgabe.vhdl end procedure;
Zur Rückumwandlung in eine Zeichenkette wird der Zeichenketteninhalt bis zur letzten mit einem Zeichen gefüllten Position (Zeigerwert minus eins) zurückgegeben:
3.2 Anwendungsspezifische Datentypen function str(x: tString) return string is begin return x.str(1 to x.ptr - 1); end function;
233
⇒WEB-Projekt: Tuc/Ausgabe.vhdl
Die zugewiesenen oder angehängten Zeichenketten können auch Konkatenationen mehrere Einzelzeichenketten sein. 3.2.9 Zusammenfassung und Übungsaufgaben VHDL unterscheidet zwischen elementaren und zusammengesetzten Datentypen. Elementare Typen sind die Zahlentypen, die Aufzählungstypen und die physikalischen Typen. Zahlentypen – zu ihnen gehören die vordefinierten Typen integer und real – beschreiben zusammenhängende Wertebereiche, für die die arithmetischen Operationen (Addition, Subtraktion etc.) und Größenvergleiche definiert sind. Aufzählungstypen wie bit, boolean und character beschreiben diskrete Mengen von Werten für symbolische Beschreibungen. Physikalische Typen, zu denen time gehört, sind aus Zahlenwerten und Maßeinheiten zusammengesetzt und erlauben eine zusätzliche Maßeinheitenkontrolle und -konvertierung. Zur gemeinsamen Bearbeitung und Speicherung können zusammengehörige Teilobjekte vom selben Typ zu Feldern und Teilobjekte mit beliebigen Typen zu einem Verbund zusammengefasst werden. Die Definition ist rekursiv. Die Elemente zusammengesetzter Typen können elementare Typen oder zusammengesetzte Typen sein. Das flexible Typkonzept und die strenge Typprüfung dienen dazu, möglichst viele Entwurfsfehler bei der Simulation zu erkennen. Eigene Typen werden in der Regel in einem Package definiert, das zusätzlich Operatorfunktionen, Konvertierungsfunktionen und andere Unterprogramme für die Bearbeitung von Objekten des Typs bereitstellt. Weiterführende und ergänzende Literatur siehe [11, 36, 45]. Aufgabe 3.5 Welche der nachfolgenden Anweisungen sind zulässig? subtype tUint8 is integer range 0 to 255; type tInt8 is range -128 to 127; subtype tWahrscheinlichkeit is real range 0.0 to 1.0; ... variable t1, t2: time; variable b1, b2: tInt8; variable c1: tWahrscheinlichkeit; variable w1: boolean; ... A1: t1 := (1 ns * c1) + t2; A2: b2 := (5 * b1) + 9.3; A3: w1 := (tUint8’high>tInt8’high);
234
3 VHDL im Detail
Aufgabe 3.6 Definieren Sie einen diskreten Zahlentyp »tZeichenwert« mit dem Wertebereich von eins bis neun, einen Aufzählungstyp »tZeichen« für die Ziffern ’1’, ’2’, ..., ’9’ und programmieren Sie die Konvertierungsfunktionen function to_Zeichenwert(x: tZeichen) return tZeichenwert; function to_Zeichen(x: tZeichenwert) return tZeichen;
zur Umwandlung beider Typen ineinander. Aufgabe 3.7 Definieren Sie einen physikalischen Typ »tSpannung« mit den Maßeinheiten mV (Millivolt) und V (Volt) und dem darstellbaren Wertebereich von ±10 V. Aufgabe 3.8 Vereinbaren Sie einen Feldtyp »tMem« mit einem variablen, durch natural beschränkten Adressbereich und Bitvektoren der Länge 32 vom Typ »tUnsigned« als Elemente. Vereinbaren Sie eine Variable »Daten« mit dem Typ der Feldelemente und dem Anfangswert »alles Nullen«, eine 8-Bit-Variable »Adresse« vom Typ »tUnsigned« und eine Feldvariable »Mem« vom Typ »tMem« mit einem durch einen 8-Bit-Vektor adressierbaren Indexbereich. Aufgabe 3.9 a) Schreiben Sie einen Verbund »tZustand3SG« für die Darstellung des Zustands des Dreiecksignalgenerators aus Abb. 1.79, bestehend aus einem Zustandsbit s vom Typ std_logic und dem 4-Bit-Zähler y vom Typ »tUnsigned«. b) Schreiben Sie für diesen Typ eine Initialisierungsprozedur procedure Init(signal x: out tZustand3SG);
die s mit »0« und y mit »0000« initialisiert. c) Schreiben Sie eine Zählprozedur procedure Count(signal x: inout tZustand3SG);
die bei s = 0 den Wert von y um »1« erhöht und bei s = 1 den Wert von y um »1« verringert. d) Schreiben Sie weiterhin eine Konvertierungsfunktion function str(x: tZustand3SG) return string;
die den Wert des Verbunds in eine Textdarstellung der Form (s=ws , y=wy ) (ws – Textdarstellung des Bitwertes von s; wy – Textdarstellung des Bitvektors y) umwandelt.
3.3 Ein- und Ausgabe
235
3.3 Ein- und Ausgabe Komfortable Ein- und Ausgaben sind eine wesentliche Voraussetzung für eine schnelle und gründliche Fehlersuche. In den vergangenen Abschnitten wurden die Unterprogramme für die Ein- und Ausgabe von den Packages »Tuc.Eingabe« und »Tuc.Ausgabe« bereitgestellt. Dieser Abschnitt behandelt, wie diese Packages und Unterprogramme aufgebaut sind, funktionieren und erweitert werden können. 3.3.1 Das Dateikonzept von VHDL Die Ein- und Ausgabe erfolgt in VHDL wie in praktisch jeder anderen Hochsprache über Dateiobjekte. Ein Dateiobjekt wird in VHDL in der Regel bei der Vereinbarung gleich mit einer geöffneten Datei verbunden: file Dateiobjekt: Typ [open fok ] is Dateiname;
Der Typ von Dateiobjekten ist hier im Buch stets der im Package std.textio vereinbarte Typ type text is file of string;
Er definiert die einzulesenden und auszugebenden Dateizeilen als Zeichenketten und ist der einzige Dateityp, der problemlos auch von anderen Programmen gelesen und mit anderen Programmen bearbeitet werden kann. Der optionale Parameter »fok « beschreibt, wie die Datei zu öffnen ist. Er hat den Aufzählungstyp type file_open_kind is ( read_mode, –- Lesen ab Dateianfang write_mode, –- Schreiben ab Dateianfang append_mode); –- Schreiben ab Dateiende
Ohne Angabe wird die Datei zum Lesen geöffnet. Der Dateiname ist gemeinsam mit dem Pfad als Zeichenkette in der vom Betriebssystem geforderten Form zu übergeben, z.B. unter Linux "~/myProjekt/Protokolldatei.txt"
Im Package std.textio sind zwei geöffnete Dateiobjekte für die interaktive Ein- und Ausgabe vereinbart: •
eines für die Standardeingabe
•
und eines für die Standardausgabe
file input: text open read_mode is "std_input"; file output: text open write_mode is "std_output";
236
3 VHDL im Detail
Mit der Vereinbarung eines Typs für Dateiobjekte werden implizit je eine Prozedur zum Lesen, eine Prozedur zum Schreiben und eine Funktion zum Testen, ob beim Lesen das Dateiende erreicht ist, bereitgestellt. Für Dateiobjekte vom Typ text haben diese Unterprogramme die Aufrufschnittstellen procedure read(file f: text; s: out string; len: out natural); procedure write(file f: text; s: string); function endfile(file f: text) return boolean;
Wie der Typbezeichner text müssen diese Bezeichner aus dem Package std.textio importiert werden. Der Objekttyp eines Dateiobjekts ist in der Parameterliste eines Unterprogramms immer »file«. Die Leseprozedur kopiert den Inhalt der aktuellen Textzeile in den Ausgabeparameter »s«. Wenn die Eingabezeile kürzer als die Zeichenkette »s« ist, bleibt der Rest von »s« unverändert. Sonst, wenn sie länger als »s« ist, gehen die restlichen Zeichen verloren. Der zweite Rückgabeparameter, die Zeichenanzahl »len«, gibt auch, wenn der Zeichenkettenparameter »s« zu kurz ist, die tatsächliche Länge der gelesenen Dateizeile zurück. Die Schreibfunktion hängt den übergebenen Text als neue Textzeile an die Datei an. Die Testfunktion endfile liefert, wenn beim Lesen das Dateiende erreicht ist, den Wert true und sonst den Wert false. Dateiobjekte können in Packages, Entwurfseinheiten, Prozessen und Unterprogrammen vereinbart werden. Der Vereinbarungsort beeinflusst die Lebensdauer und die Sichtbarkeit des Dateiobjekts. In Packages und Entwurfseinheiten vereinbarte Dateiobjekte bleiben während der gesamten Simulation geöffnet8 . In Prozessen vereinbarte Dateiobjekte werden zum Simulationsbeginn geöffnet und bei Beendigung des Prozesses geschlossen. Dateiobjekte in Unterprogrammen werden bei jedem Unterprogrammaufruf geöffnet und beim Verlassen wieder geschlossen. Die Sichtbarkeit unterscheidet sich nicht von der anderer Datenobjekte. Vereinbarungen in Packages sind in den dort vereinbarten Unterprogrammen sichtbar und können in andere Beschreibungen importiert werden. Vereinbarungen in Entwurfseinheiten, in Prozessen und in Unterprogrammen sind nur in diesen und deren Bestandteilen sichtbar. 3.3.2 Textausgabe Die bisher verwendete Write-Funktion aus »Tuc.Ausgabe« ruft die Schreibfunktion für den Dateityp text mit der Standardausgabe als Dateiobjekt auf: procedure write(s: string) is begin write(output, s); end procedure; 8
wenn sie nicht explizit geschlossen werden
3.3 Ein- und Ausgabe
237
Für die Ausgabe in eine Datei wird die Schreibfunktion aus std.textio direkt aufgerufen. Der auszugebende Wert ist zuvor in eine Zeichenkette umzuwandeln. Textdarstellungen für die Werte von Datenobjekten werden auch weiterhin mit den überladenen Str-Funktionen erzeugt und gemeinsam mit konstanten Zeichen und Zeichenketten mit dem Konkatenationsoperator »&« zu Ausgabetexten zusammengefügt. Der Zeilenumbruch ist das nicht druckbare Zeichen »LF«. Für Debug-Ausgaben in größeren Projekten mit zahlreichen Packages und Entwurfseinheiten sind weiterhin die Namensattribute in Tabelle 3.5 hilfreich. Das Attribut »bez ’path_name« stellt dem Bezeichner den Pfad durch die Entwurfshierarchie von der obersten Entwurfseinheit bis zum bezeichneten Objekt voran. Das Attribut »bez ’instance_name« gibt zusätzlich auch noch die Beschreibungsnamen der Entwurfseinheiten mit aus. Mit diesen Zusatzangaben lassen sich die Objekte, auf die sich die Simulationsausgaben beziehen, einfacher lokalisieren. Tabelle 3.5. Attribute für Bezeichner bez ’simple_name
Umwandlung des Bezeichners in eine Textdarstellung
bez ’path_name
wie simple_name plus zusätzliche Pfadangabe durch die Entwurfshierarchie von der obersten Entwurfseinheit bis zum bezeichneten Objekt
bez ’instance_name
wie path_name, jedoch mit der zusätzlichen Angabe der Beschreibungsnamen der Teilschaltungen bez – Bezeichner für Datenobjekte, Typen, Unterprogramme etc.
Aufbau und Funktionsweise der Str-Funktionen Üblicherweise hat eine moderne Programmiersprache für ihre vordefinierten Datentypen Funktionen zur Typkonvertierung in eine Textdarstellung. In VHDL gibt es diese erst ab Revision VHDL-2008. Für ältere Versionen und für eigene Datentypen sind diese Konvertierungsfunktionen selbst zu schreiben. Die nachfolgenden Funktionsbeschreibungen sind gegenüber denen im Package »Tuc.Ausgabe« vereinfacht. Für elementare Datentypen wie boolean und integer gibt die StrFunktion den Wert des image-Attributes zurück: function str(x: boolean) return string is begin return boolean’image(x); end function;
⇒WEB-Projekt: Tuc/Ausgabe.vhdl
Für Aufzählungen von druckbaren Zeichen – z.B. für die Typen std_ulogic9 und bit – kann die zeichenweise Konvertierung auch mit einer Tabelle erfol9
Basistyp von std_logic
238
3 VHDL im Detail
gen. Die Tabelle ist eine Zeichenkettenkonstante, die mit den zu druckenden Zeichen initialisiert ist. Aufzählungstypen haben zwei Attribute. Das PosAttribut »tAZ ’pos(x)« gibt die Listenposition des Wertes »x« in der Typvereinbarung und das Val-Attribut »tAZ ’val(p)« den Wert mit der Position »p« zurück (tAZ – Aufzählungstyp, vgl. Abschnitt 3.2.2). Die Str-Funktion berechnet zuerst aus dem Ausgabewert die Position in der Typvereinbarung und gibt den zugehörigen Tabellenwert zurück. Achtung, die Positionszählung der Elemente eines Aufzählungstyps beginnt mit »0« und der Indexbereich einer Zeichenkette mit »1«, so dass der Tabellenindex immer um »1« größer als die Positionsnummer ist: constant STL_Tab: string := "UX01ZWLH-"; function str(x: std_ulogic) return string is begin return STL_Tab(std_ulogic’pos(x) + 1); end function;
⇒WEB-Projekt: Tuc/Ausgabe.vhdl
Druckbare Zeichen werden hauptsächlich deshalb als Aufzählungselemente verwendet, damit die Werte der zugehörigen eindimensionalen Felder als Zeichenketten dargestellt werden können. Im nachfolgenden Beispiel werden eine Konstante und eine Variable mit demselben von »1« aufsteigenden Indexbereich und der Elementanzahl des Eingabevektors vereinbart. Die Konstante wird mit dem Wert des Eingabevektors initialisiert. Die Variable hat den Typ string und wird in einer Schleife elementweise mit den in Zeichen konvertierten Elementwerten beschrieben: function str(x: std_logic_vector) return string is constant bv: std_logic_vector(1 to x’length) := x; variable s: string(1 to x’length); begin for idx in bv’range loop s(idx) := str(bv(idx)); end loop; return s; ⇒WEB-Projekt: Tuc/Ausgabe.vhdl end function;
Für reellwertige Zahlentypen ist die Textdarstellung, die das image-Attribut liefert, insbesondere die Anzahl der Nachkommastellen, vom Wert abhängig. Eine formatierte Darstellung mit einer festen Anzahl von Nachkommastellen kann z.B. dadurch erzeugt werden, dass je eine Zeichenkette für den ganzzahligen Wert der Vorkommastellen und eine Zeichenkette für die Nachkommastellen erzeugt wird. Für die Darstellung mit m Nachkommastellen wird der Wert mit 10m multipliziert und in eine ganze Zahl konvertiert. Der ganzzahlige Wert der Vorkommastellen ist der durch 10m geteilte Quotient. Die Zeichenkette der Nachkommastellen wird aus dem Betrag des Divisionsrests der Division durch 10m plus 10m gebildet. Die Addition von 10m stellt dabei eine führende »1« voran und verhindert das Abschneiden führender Nullen.
3.3 Ein- und Ausgabe
239
Vor dem Anhängen an die Zeichenkette der Vorkommastellen wird diese »1« abgeschnitten, indem das erste Zeichen weggelassen wird (Abb. 3.15). function str(x: real; m: positive := 2) return string is Zahlenbeispiel constant z: positive := 10**m; x = −25,7382... m=2 constant i: integer := integer(real(z)*x); z = 100 constant snk: string(1 to m+1) := str(z + abs(i rem z)); i = −2574 begin snk = 174 return str(i/z) & "," & snk(2 to snk’length); str(i/z) = ”-25” end function; R¨ uckgabewert: ”-25” & ’,’ & ”74” Abb. 3.15. Konvertierung von real nach string mit m Nachkommastellen
Für Zeiten stellt das image-Attribut den Wert in Femtosekunden (1 fs = 10−15 s) dar. Auch das ist meist nicht so erwünscht. In der nachfolgenden Prozedur wird die Maßeinheit in Abhängigkeit von der Größe des Wertes gewählt. Dabei ist zu beachten, dass der Quotient aus zwei Zeitwerten eine ganze Zahl mit einem Wertebereich von 2−31 bis 231 −1 ist. Wenn der Quotient zu groß ist, läuft der Wertebereich über. Beispielsweise ergibt (3 us)/fs nicht wie erwartet 3 · 109 , sondern 3 · 109 − 232 = −1294967296. Deshalb werden in der nachfolgenden Funktion die Zeitwerte größer 1 µs nicht mehr durch fs, sondern durch größere Zeitwerte dividiert: function str(t: time; m: natural := 2) return string is begin if abs(t)< 1 ps then return str(t/fs ) & " fs"; elsif abs(t) < 1 ns then return str(1.0E-3 * real(t/fs),m) & " ps"; elsif abs(t) < 1 us then return str(1.0E-6 * real(t/fs),m) & " ns"; elsif abs(t) < 1 ms then return str(1.0E-6 * real(t/ps),m) & " us"; elsif abs(t) < 1 sec then return str(1.0E-6 * real(t/ns),m) & " ms"; elsif abs(t) < 1 min then return str(1.0E-6 * real(t/us),m) & " sec"; else return str(1.0E-6 * real(t/60 us),m) & " min"; end if; ⇒WEB-Projekt: Tuc/Ausgabe.vhdl end function;
Eine Str-Funktion ist für alle eigenen Datentypen – Zahlentypen, Aufzählungstypen, physikalische Typen, Feld-Typen und Verbund-Typen – zu programmieren, für die Simulationsausgaben erforderlich sind. Die Entwicklung von Str-Funktionen ist eine gute Programmierübung. Denn zum einen sind Str-Funktionen mit einer vernünftigen Ausgabedarstellung nicht ganz einfach zu programmieren und zum anderen lassen sie sich gut testen (siehe Web-Projekt »Tuc/Test_Ausgabe.vhdl«).
240
3 VHDL im Detail
3.3.3 Texteingabe Das Einlesen von Eingabedaten aus einer Datei ist komplizierter als die Ausgabe. Das in der Informatik übliche Modell dafür ist ein Band mit Zeichen, das von einem Akzeptorautomaten abgeräumt wird. Abräumen bedeutet Lesen, auf Zulässigkeit testen, wenn zulässig verarbeiten und den Lesezeiger weiterstellen, sonst Fehler signalisieren und Zeiger zurückstellen. Die Zeichenfolge zur Darstellung einer vorzeichenfreien ganzen Zahl sei z.B. definiert als eine Ziffer, gefolgt von beliebig vielen Ziffern und einem Trennzeichen Zahl ⇒ z{z}t (z – Ziffer; t – Trennzeichen). Der zugehörige Akzeptorautomat räumt das erste Zeichen ab. Wenn es eine Ziffer ist, räumt er, solange er weitere Ziffern findet, auch diese ab und versucht danach ein Trennzeichen abzuräumen. Im Erfolgsfall terminiert er mit einem Band, das bis zum Ende oder bis zum nächsten Eingabewort abgeräumt ist, und einer gültigen Ausgabe. Falls das erste Zeichen keine Ziffer oder das auf die Ziffern folgende Zeichen kein Trennzeichen ist, terminiert er im Fehlerzustand, d.h. mit zurückgesetztem Zeiger und ungültiger Ausgabe (Abb. 3.16). Die Werteberechnung erfolgt im Beispiel rekursiv. Die erste Ziffer liefert den Ziffernwert und die weiteren erhöhen ihn auf w =B·w+z (w – Wert; B – Basis des Zahlensystems; z – Ziffernwert).
z
ja nein
z
ja nein
t
ja nein
Band . . . 1 3 5 ok
Start 1
err
Startzustand z Ziffer erwartet t Trennzeichen erwartet √ zul¨assiges Zeichen nein kein zul¨assiges Zeichen ok Wort erfolgreich abger¨aumt err Eingabefehler
Endzustand Start 2
Endzustand
5 8 k ...
erwartet √ z√ z√ z z (nein) √ t
Wert
ok √ z√ z z (nein) t (nein)
135
err
ung¨ ultig
1 13 135 135 5 58
Abb. 3.16. Einlesen einer vorzeichenfreien ganzen Zahl mit einem Akzeptorautomaten
Die Eingabedatei kann fehlerhaft sein. Die Fehlersuche verlangt aussagekräftige Fehlermeldungen, aus denen hervorgeht, bei welchem Wert des Lesezeigers das unzulässige Zeichen erkannt wurde und welche Zeichen oder Symbole an dieser Stelle hätten stehen müssen. Im Package »Tuc.Eingabe« ist der Datentyp für das abzuräumende Band als Verbund definiert:
3.3 Ein- und Ausgabe type tPString is record str: string(1 to ZL); pta, pte: positive; Status: tLesestatus; err_pos: positiv; err_msg: tString; end record;
–––––-
241
Zeichenkette Anfangs- und Endzeiger Status der letzen Operation Fehlerposition Fehlermeldung
(ZL – Zeilenlänge). Der Lesestatus ist ein Aufzählungstyp type tLesestatus is (X, ok, trunc, err);
(X – unbestimmt; ok – in Ordnung; trunc – gekürzt; err – Fehler). Mit der Prozedur procedure read(file f: text; w: out tPString) is variable pstr: tPString; begin std.textio.read(f, pstr.str, pstr.pte); if pstr.pte>pstr.str’length+1 then pstr.pte := pstr.str’length; pstr.Status := trunc; else pstr.Status := ok; end if; w := pstr; ⇒WEB-Projekt: Tuc/Eingabe.vhdl end procedure;
wird das Bandobjekt mit einer Zeile aus einer Datei gefüllt. Wenn die gelesene Zeile länger als die Zeichenkette, die sie aufnehmen soll, ist, wird als Rückgabestatus »trunc« (abgeschnitten) und sonst »ok« zurückgegeben. Der Zeiger »w.pta« zeigt nach der Ausführung der Prozedur auf den Bandanfang und der Zeiger »w.pte« auf das Bandende. Anschließend werden mit den überladenen Prozeduren procedure get(pstr:inout tPString; w: out tTyp ...) is ...;
(tTyp ∈{bit, boolean etc., siehe Anhang A.2.3) Werte der erwarteten Datentypen abgeräumt. Wenn die Zeichenfolge auf dem Band nicht den Syntaxregeln für die Wertedarstellung der Datenobjekte entspricht, stoppt die Abräumprozedur, setzt den Anfangszeiger auf den Wert zum Aufrufzeitpunkt zurück und setzt den Status auf »pstr.Status=err«. In den Fehlerzeiger »pstr.err_pos« wird die Position des ersten Zeichens, das die Syntaxregeln verletzt hat, und in »pstr.err_msg« wird eine Fehlermeldung geschrieben. Das Package stellt die Get-Prozeduren für alle standardisierten Datentypen bereit10 . Für eigene Typen müssen sie selbst geschrieben werden. Die bisher verwendeten Eingabeprozeduren nutzen die Get-Prozeduren. Sie sind alle nach folgender Schablone aufgebaut: 10
für ausgewählte Wertedarstellungen
242
3 VHDL im Detail procedure read(promt: string; w: out tTyp) is variable pstr: tPString; begin write(promt); loop read(input, pstr); CheckExit(pstr); get(pstr, w); exit when pstr.Status=ok; write(str(pstr.err_msg)); end loop; end procedure;
⇒WEB-Projekt: Tuc/Eingabe.vhdl
(tTyp – Datentyp, für den die Get-Prozedur definiert ist, siehe Anhang A.2.3). Zuerst wird der Ausgabetext ausgegeben und anschließend solange eine Zeile von der Standardeingabe gelesen, bis das zurückgegebene Band-Objekt den Status »pstr.Status=ok« hat oder der Prozeduraufruf von »CheckExit« die Simulation beendet. Bei einer unzulässigen Eingabe wird die im Band-Objekt gespeicherte Fehlermeldung ausgegeben. Dann springt der Kontrollfluss zum Schleifenanfang. Die Prozedur »CheckExit« kontrolliert, ob in dem BandObjekt »pstr« die Zeichenkette »Exit« oder ein anderes Symbol zur Beendigung der Simulation steht, und ruft, wenn das der Fall ist, zur Beendigung der Simulation die Prozedur »StopSim« aus dem Package »Tuc.StopSim_pack« auf. Die Unterprogramme für die Eingabe gehören zu den aufwändigsten und kompliziertesten Teilen eines VHDL-Simulationsmodells. 3.3.4 Zusammenfassung und Übungsaufgaben Die Ein- und Ausgaben erfolgen über Datei-Objekte, die in der Regel bei der Vereinbarung mit einer geöffneten Datei verbunden werden. Für jeden Dateiobjekt-Typ – im Buch wird nur text aus dem Package std.textio verwendet – wird implizit eine Lese- und eine Schreibprozedur sowie eine Testfunktion, ob das Dateiende erreicht ist, bereitgestellt. Darauf aufbauend wurde ein Konzept aus Typen und Unterprogrammen für die Textverarbeitung entwickelt. Das vorgeschlagene Bausteinkonzept besteht aus Unterprogrammen zum Lesen, Zusammensetzen und Schreiben von Textzeilen. Für jeden Datentyp, von dem Werte auszugeben sind, ist die Str-Funktion zu überladen und für jeden Datentyp, von dem Werte von der Standardeingabe oder aus einer Datei gelesen werden sollen, sind die Get- und die Read-Prozedur zu überladen. Der hierfür erforderliche Programmieraufwand ist, wie die Packages »Tuc.Eingabe« und »Tuc.Ausgabe« zeigen, erheblich. Aber eine leistungsfähige Textverarbeitung ist eine Grundvoraussetzung dafür, dass die Entwurfsfehler, die sich leider nie ganz vermeiden lassen, bei der Simulation erkannt werden und dass der Aufwand für ihre Lokalisierung erträglich bleibt. Weiterführende und ergänzende Literatur siehe [11, 36, 45].
3.3 Ein- und Ausgabe
243
Aufgabe 3.10 Schreiben Sie für den Aufzählungstyp type tZustand is (Idle, Start, Run, Ready);
eine Get-Prozedur und eine Str-Funktion. Nutzen Sie dazu die entsprechenden Unterprogramme aus den Packages »Tuc.Eingabe« und »Tuc.Ausgabe« als Vorbild oder als Bestandteile. Aufgabe 3.11 Schreiben Sie für den Untertyp subtype tByte is tUnsigned(7 downto 0);
eine Funktion function str_hex(x: tByte) return string;
die für ungültige Werte die Zeichenkette »XX« und sonst eine Textdarstellung durch zwei Hexadezimalziffern zurückgibt. Aufgabe 3.12 Schreiben Sie eine nebenläufige Prozedur procedure Protokoll(signal x: std_logic_vector; file f: text);
die zum Simulationsbeginn und bei jeder Werteänderung von »x« den Änderungszeitpunkt, gefolgt von einem Doppelpunkt und dem Wert von x in die geöffnete Datei des Dateiobjekts »f« schreibt. Aufgabe 3.13 Gegeben sei der Datentyp type tByteFeld is array (natural range <>) of std_logic_vector(7 downto 0);
a) Schreiben Sie eine Initialisierungsfunktion function Init(Dateiname: string; N: natural) return tByteFeld;
die aus einer Datei, in der in jeder Zeile eine Dezimalzahl im Bereich von 0 bis 255 steht, die ersten N Werte einliest, in einen 8-Bit-Vektor umwandelt, fortlaufend in ein Byte-Feld einträgt und zum Abschluss das Byte-Feld zurückgibt. b) Schreiben Sie eine Vereinbarung einer Konstanten »ROM« für ein 1024 Bytes großes Feld, das mit dem Inhalt einer Datei »Daten.txt« initialisiert wird.
244
3 VHDL im Detail
3.4 Beschreibungsschablonen für digitale Schaltungen Eine Schaltungsbeschreibung sollte leicht verständlich, kompakt und selbst erklärend sein. Das spart Programmier- und Testaufwand und mindert die Anzahl der Fehler, die bei der Modellentwicklung entstehen und die beim Testen möglicherweise übersehen werden. Ein Mittel, dieses Ziel zu erreichen, sind Beschreibungsschablonen. Dies sind mit Beispielinhalten dekorierte Empfehlungen für die Beschreibungsstruktur und Regeln, um die empfohlenen Strukturen mit abgewandelten Inhalten zu füllen. Der bisher verwendete Testrahmen, die Beschreibung von Abtastprozessen, die Strukturempfehlungen für die Str- und Get-Funktionen etc. waren alles solche Beschreibungsschablonen. In diesem Abschnitt werden Empfehlungen für die Strukturierung komplexer Entwurfsbeschreibungen mit Hilfe von Unterprogrammen, Schleifen und Packages herausgearbeitet. 3.4.1 Auslagerung kombinatorischer Funktionen in Packages Das Verhaltensmodell einer kombinatorischen Schaltung ist bildlich gesehen ein schwarzer Kasten mit Ein- und Ausgabesignalen, der bei jeder Änderung eines Eingabesignals alle Ausgabewerte neu berechnet und verzögert an die Ausgabesignale zuweist (vgl. Abschnitt 1.2.4). Im Synthesemodell entfallen die Angaben für die Verzögerungszeit sowie die Zuweisungen und Auswertungen von Pseudo-Werten (vgl. Abschnitt 2.1.2). In einem Entwurfsprojekt ist es zweckmäßig, den übereinstimmenden Teil im Simulations- und im Synthesemodell – die kombinatorische Funktion ohne Zeitverhalten und ohne PseudoWerte – in ein Package auszulagern. Das perfekte Beschreibungsmittel hierfür ist eine VHDL-Funktion. Mit ihr ist es zum einen nicht möglich, versehentlich ein Speicherverhalten zu beschreiben (vgl. Abschnitt 2.1.6). Zum anderen lassen sich VHDL-Funktionen sehr gut separat testen. Eine VHDL-Funktion kann beliebig viele Eingabeparameter haben, aber sie hat nur einen Rückgabewert. Vor der Funktionsdefinition muss deshalb oft ein Datentyp für den Rückgabewert – ein Verbund oder ein Feld – vereinbart werden, der alle Ausgabesignale der Schaltung zusammenfasst. Für die hierfür zu vereinbarenden Datentypen benötigt das Simulationsmodell wiederum Hilfsfunktionen und weitere Vereinbarungen (Str-Funktion, Get-Funktion, Test auf ungültig, ein Pseudo-Wert für ungültig etc.). Dafür ist ein Package zu programmieren. Für das Package muss ein Testrahmen geschrieben werden, der die enthaltenen Vereinbarungen und Unterprogramme vor ihrer weiteren Verwendung gründlich testet. Die eigentliche Beschreibung der kombinatorischen Schaltung ist, unabhängig von ihrer Größe und Komplexität, sowohl im Simulations- als auch im Synthesemodell ein Einzeiler (Abb. 3.17). Beispiel Prioritäts-Encoder Ein Beispiel für die Auslagerung der Beschreibung einer kombinatorischen Funktion in ein Package sei ein Prioritäts-Encoder. Der Beispiel-Prioritäts-
3.4 Beschreibungsschablonen für digitale Schaltungen x1 x2 ···
fkt(x1 , x2 ,...)
···
yTyp
y
Entwurfseinheit(en) - - Signalvereinbarungen signal x1: ...; signal x2: ...; signal y: yTyp; - - Simulationsmodell y <= XX yTyp after th, fkt(x1, x2, ...) after td; - - Synthesebeschreibung y <= fkt(x1, x2, ...);
245
Package zum Entwurfsprojekt package fkt pack is [sub]type yTyp is . . . - - kombinatorische Funktion function fkt(x1: ...; x2; ...; ...) return yTyp is ...; - - Str-Funktion function str(y: yTyp) return STRING is ...; - - Test, ob ung¨ ultig function is X(y: yTyp) return BOOLEAN is ...; - - Konstante f¨ ur ung¨ ultig constant XX yTyp: yTyp:= ...; end package; package body fkt pack is ...;
Abb. 3.17. Auslagerung kombinatorischer Funktionen in Packages
Encoder soll für einen Eingabevektor x das höchstwertigste Bit bestimmen, das »1« ist. Die Schaltungsausgabe soll aus einem Zahlenwert für die Bitnummer und einem Gültigkeitsbit bestehen. Das Gültigkeitsbit soll »1« sein, wenn mindestens ein Eingabebit »1« ist. Der Verbund für die Schaltungsausgabesignale besteht entsprechend aus zwei Teilobjekten: type tPEncOut is record w: tUnsigned(3 downto 0); g: std_logic; end record;
In der Definition der kombinatorischen Funktion kann die Größe des Eingabevektors variabel gehalten werden. Damit die Ausgabe alle Bitnummern darstellen kann, darf der größte Indexwert »15« nicht überschreiten. Eine AssertAnweisung beendet in diesem Fall die Simulation mit einer Fehlermeldung: function PrioEnc(x: std_logic_vector) return tPEncOut is variable y: tPEncOut := (w=>x"0", g=>’0’); begin Priorit¨ats- w x Encoder g assert x’high<16 report "Fehler" severity failure; for idx in x’high downto x’low loop x w g if x(idx)=’1’ then 0 0 0 0 0 0 0 000 0000 0 y.w := to_tUnsigned(idx, y.w’length); 1001 1 1- - - - - - - - 1000 1 01 - - - - - - - y.g := ’1’; 001 - - - - - - 0 1 11 1 return y; 000 1 - - - - - 0110 1 ... ... ... end if; - beliebig (don’t care) end loop; return y; ⇒WEB-Projekt: P3.4/PEnc_pack.vhdl end function;
Die Berechnung erfolgt in einer Schleife vom höchstwertigen zum niederwertigsten Bit des Eingabevektors. Wenn der Bitwert »1« ist, wird der Indexwert
246
3 VHDL im Detail
als Bitvektor in »y.w« geschrieben, das Gültigkeitsbit »y.g« gesetzt und die Funktion mit »y« als Rückgabewert beendet. Sonst wird der Initialwert von »y« (alle Bits »0«) zurückgegeben. Da die Funktion auch in der Synthesebeschreibung verwendet werden soll, darf sie keine technisch nicht darstellbaren Pseudo-Werte auswerten und zuweisen. Zusätzlich zu der VHDL-Funktion für das Zielverhalten werden für den neuen Typ »tPEncOut« eine Konstante für den Pseudo-Wert »ungültig«, eine Testfunktion auf Gültigkeit und eine Str-Funktion benötigt. Eine ungültige Ausgabe bedeutet, dass im ungünstigsten Fall alle Ausgabebits ungültig sind: constant XX_PEncOut: tPEncOut := (w=>"XXXX", g=>’X’);
Die Funktion zur Kontrolle auf Ungültigkeit, die bei der Abtastung für die Kontrolle der Vor- und Nachhaltebedingungen gebraucht wird, soll nur den Wert false (nicht ungültig) zurückgeben, wenn alle Bits gültig sind: function is_x(x: tPEncOut) return boolean is begin return is_x(x.w) or is_x(x.g); end function;
⇒WEB-Projekt: P3.4/PEnc_pack.vhdl
Die Str-Funktion setzt den Ausgabetext aus konstanten Textbausteinen und den Textdarstellungen der Werte der Elemente zusammen: function str(x: tPEncOut) return string is begin return "(w=" & str(x.w) & ", g=" & str(x.g) & ")"; end function; ⇒WEB-Projekt: P3.4/PEnc_pack.vhdl
Das Package muss nach seiner Entwicklung getestet werden. Das nachfolgende Beispiel beschreibt einen Dialogtest, bei dem in einer Endlosschleife je ein Eingabewert angefordert wird. Die Ausgabeanweisung gibt zusätzlich zum Eingabewert und zum Funktionswert der kombinatorischen Funktion den Funktionswert der Testfunktion »is_x(...)« aus: –- Vereinbarungen im Testprozess variable x: std_logic_vector(9 downto 0); variable y: tPEncOut; –- Anweisungsfolge im Testprozess loop read("Bitte 10-Bit-STD_LOGIC_VECTOR eingeben:", x); y := PrioEnc(x); write("x=" & str(x) & " is_x(x)=" & str(is_x(x)) & " y=" & str(y)); end loop; ⇒WEB-Projekt: P3.4/Test_PEncFkt.vhdl
3.4 Beschreibungsschablonen für digitale Schaltungen
247
Das Simulationsmodell des Prioritäts-Encoders besteht in seinem Kern nur noch aus einer nebenläufigen Signalzuweisung, die nach jeder Eingabeänderung nach der Haltezeit dem Ausgabesignal den Wert »ungültig« und nach der Verzögerungszeit den neuen gültigen Funktionswert zuweist: constant th: delay_length := 1 ns constant td: delay_length := 3 ns signal x: std_logic_vector(9 downto 0); signal y: tPEncOut; ... y <= XX_PEncOut after th, PrioEnc(x) after td ;
Im Synthesemodell entfallen die grau unterlegten Teile, die das Zeitverhalten und die Berechnung des Gültigkeitsfensters beschreiben (vgl. Abschnitt 2.1.2).
Anwendung auf Register-Transfer-Beschreibungen Eine Register-Transfer-Beschreibung beschreibt eine sequenzielle Schaltung als ein System aus Verarbeitungsfunktionen und Abtastregistern (vgl. Abschnitt 1.4.4). Auch hier sei empfohlen, die kombinatorischen Verarbeitungsfunktionen in Packages auszulagern und vor ihrer Einbindung in das übergeordnete Modell gründlich zu testen. Für die kombinatorischen Funktionen sind wieder Ausgabetypen und für diese die erforderlichen Hilfsfunktionen etc. zu entwickeln. In der übergeordneten Beschreibung werden dann die Eingabe- und die Register-Transfer-Funktionen der sequenziellen Schaltung durch Funktionsaufrufe in einem Abtastprozess und die Ausgabefunktionen durch Funktionsaufrufe in nebenläufigen Signalzuweisungen oder kombinatorischen Prozessen beschrieben. Abbildung 3.18 zeigt eine Beispielschaltung mit einer Eingabefunktion »f1(...)«, einer Register-Transfer-Funktion »f2(...)« und einer Ausgabefunktion »f3(...)«. Die im Package vereinbarten Typen der Ausgabesignale dieser Verarbeitungsfunktionen können Bit-, Bitvektor-, Zahlen-, Aufzählungs- und daraus zusammengesetzte Typen sein. Wichtig ist, dass die Typvereinbarungen alle Teilergebnisse, die von derselben Verarbeitungsfunktion berechnet werden, zu einem Datenobjekt zusammenfassen. Dann folgen die Funktionsdefinitionen für die Nachbildung der kombinatorischen Funktionen. Die Definitionen der Hilfsfunktionen für die neuen Typen (Str-Funktion etc.) und der Package-Körper mit den Beschreibungen der Unterprogramme – in Abb. 3.18 aus Platzgründen nicht dargestellt – dürfen natürlich nicht fehlen. Die eigentliche Schaltungsbeschreibung besteht nur noch aus einem Abtastprozess und einer nebenläufigen Signalzuweisung. Der Funktionsaufruf von »f1(...)« im Abtastprozess und die Zuweisung des Funktionswertes an das Signal z1 beschreibt die Eingabefunktion mit dem nachfolgenden Eingaberegister, die Zuweisung des Funktionswertes von »f2(...)« an das Signal z2 die
248
3 VHDL im Detail
x T
tX
f1
z1 tZ1
f2
tZ1
z2 tZ2
tZ2
Simulationsmodell Package package . . . is [sub]type tX is . . . [sub]type tZ1 is . . . [sub]type tZ2 is . . . [sub]type tY is . . . function f1(x: tX) return tZ1 is . . . function f2(x1: tZ2; x2: tZ2) return tZ2 is . . . function f3(x: tZ2) return tY is . . . end package;
tZ2
f3
tY
y
signal T: STD LOGIC ; signal x: tX; signal z1: tZ1; signal z2: tZ2; signal y: tY; ... Abtastprozess: process(T) begin if RISING EDGE (T) then z1 <= f1(x); z2 <= f2(z1, z2); end if; end process; Ausgabe: y <= f3(z2);
Abb. 3.18. Auslagerung der Beschreibungen der kombinatorischen Verarbeitungsfunktionen einer sequenziellen Schaltung in ein Package
Register-Transfer-Funktion mit dem nachfolgenden Zustandsregister. Die nebenläufige Zuweisung des Funktionswertes von »f3(...)« an das Ausgabesignal beschreibt die Ausgabefunktion. Ein wesentlicher Aspekt der hier empfohlenen Entwurfsstrukturierung ist der hierarchische Test. Jedes Entwurfsobjekt ist in der Beschreibungseinheit, in der es definiert ist, so gründlich zu testen, dass es bei seiner Nutzung auf der nächsthöheren Hierarchieebene genügt, nach Fehlern bei der Interaktion zwischen den Teilsystemen zu suchen [29]. Das gilt für die Tests der Unterprogramme in den Packages, für jedes Unterprogramm, das Unterprogramme nutzt, und für jede Teilschaltung, die in eine übergeordnete Schaltung eingebaut wird. Denn es ist viel einfacher, Fehler in einer kleineren Beschreibung zu finden, als in der übergeordneten größeren Beschreibung, und es ist viel einfacher, Fehler in einem imperativ arbeitenden Unterprogramm zu finden, als in der übergeordneten nebenläufig arbeitenden Schaltungsbeschreibung. Übergangsfunktionen als Bearbeitungsprozeduren Eine der wichtigsten Techniken, die Entwicklungskosten für große SoftwareProjekte bezahlbar zu halten, ist die objektorientierte Programmierung. Ein objektorientierter Entwurf beginnt mit der Strukturierung der zu verarbeitenden Daten. Gemeinsam zu verarbeitende Daten werden zu Objekten zusammengefasst, diese wiederum Klassen zugeordnet. Für jede Klasse werden Methoden definiert und auf Vorrat implementiert, mit denen Objekte dieser Klasse üblicherweise bearbeitet werden. Das übergeordnete System wird aus Objekten, die mit Methoden ihrer Klasse bearbeitet werden, zusammengesetzt. Der Entwurf zerfällt dadurch auf natürliche Weise in überschaubare und getrennt testbare Beschreibungseinheiten. Objektorientierung erfordert nicht
3.4 Beschreibungsschablonen für digitale Schaltungen
249
unbedingt eine objektorientierte Programmiersprache mit speziellen Beschreibungsmitteln für Klassen, Methoden und Vererbung. Notfalls genügt eine normale imperative Programmiersprache, wie sie in VHDL enthalten ist. Im digitalen Schaltungsentwurf kommt dieses Prinzip bei der Beschreibung komplexer Operationsabläufe zum Einsatz. Es werden Datenobjekte und Operationen, die auf diese Datenobjekte anzuwenden sind, definiert. Anschließend wird die Zielfunktion durch eine geeignete Operationsfolge nachgebildet. Bei der Festlegung der Datenobjekte und der auf sie anzuwendenden Operationen kommt genau die Denkwelt der objektorientierten Programmierung zum Tragen. Was für Daten sind darzustellen und mit welchen Methoden sind diese zu bearbeiten? Die Datenobjekte werden in der Schaltung dann später zu Registern und die Operationen zu Register-Transfer-Funktionen. Jede Register-Transfer-Funktion lässt sich auch als Prozedur beschreiben. Insbesondere Übergangsfunktionen, die aus Registerzuständen Folgezustände bilden, lassen sich anschaulicher mit einer Prozedur beschreiben, der die zu bearbeitenden Registerzustände als les- und veränderbare Signale übergeben werden, als mit einer Funktion, bei der die Registerzustände zum einen Eingabeparameter und zum anderen Teile des Rückgabewertes sind (Abb. 3.19).
function fz(x: tX; z: tZ) return tZ;
x
a)
procedure pz(x: tX; signal z: inout tZ);
b)
Register f¨ ur das Zustandssignal z
x
tX
fz (. . .)
pz (. . .) tX
tZ
oder
tZ x
tZ
tX
pz (. . .) tZ
tZ
Abb. 3.19. Beschreibung einer Übergangsfunktionen Funktion b) durch eine Bearbeitungsprozedur
a) durch eine VHDL-
Beispiele seien die Zählprozeduren für einen Sättigungszähler. Ein Sättigungszähler ist ein Zähler, der seinen Wert nicht über einen vorgegebenen Maximalwert erhöht und nicht unter einen vorgegebenen Minimalwert verringert. Das zu bearbeitende Objekt – der Zählwert – sei eine vorzeichenfreie Zahl vom Typ »tUnsigned«. Die Bearbeitungsmethoden für den Zählwert sind Erhöhung um »1«, solange der Maximalwert nicht erreicht ist procedure inc(signal x: inout tUnsigned; max: tUnsigned) is begin if x<max then x <= x + "1"; end if; end procedure;
und Verringerung um »1«, solange der Minimalwert nicht erreicht ist:
250
3 VHDL im Detail procedure dec(signal x: inout tUnsigned; min: tUnsigned) is begin if x>min then x <= x - "1"; end if; end procedure;
Ein Nachteil bei der Beschreibung von Übergangsfunktionen mit Prozeduren ist, dass für die zu bearbeitenden Datenobjekte der Objekttyp – »variable« oder »signal« – in der Prozedurvereinbarung festzulegen ist. Es ist nicht möglich, mit den beiden Prozeduren »dec(...)« und »inc(...)« Variablen zu bearbeiten. Umgekehrt wäre es bei einer Definition des Übergabeparameters für den Zählwert als Variable nicht möglich, die Prozedur auf Signale anzuwenden. Um wahlweise Variablen- oder Signalwerte bearbeiten zu können, ist für jeden der beiden Objekttypen eine eigene Prozedur zu programmieren. In den nachfolgenden Schaltungsbeschreibungen werden die zu bearbeitenden Register- und Blockspeicherzustände in der Regel Signale sein. Bei der objektorientierten Beschreibung von Algorithmen kann es vorkommen, dass auf einen Schaltungszustand nacheinander mehrere Bearbeitungsmethoden anzuwenden sind. Was passiert in diesem Fall? Wenn die zu bearbeitenden Datenobjekte Variablen sind, verhält sich das Modell wie ein normales Programm. Jeder Prozeduraufruf sieht das Ergebnis der vorherigen Prozeduraufrufe und bearbeitet den Zustand weiter. Bei Signalen als Bearbeitungsobjekte gibt es einen Unterschied. Signalwerte werden erst mit der nächsten Warteanweisung aktualisiert. Wenn im selben Zeitschritt hintereinander mehrere Bearbeitungsprozeduren aufgerufen werden, sehen alle Prozeduren denselben Bearbeitungsstand und überschreiben jeweils die zuvor zugewiesenen Änderungen. Zur Wahrung der Übersichtlichkeit und Verständlichkeit ist es deshalb zu empfehlen, die Bearbeitungsmethoden auf unterschiedliche Zeitschritte zu verteilen, damit zwischen jedem Prozeduraufruf eine Warteanweisung die Signalwerte aktualisiert. 3.4.2 Bäume statt Ketten Eine typische Form eines parametrisierten Schaltungsmodells ist eine assoziative Verknüpfung von n Operanden zu einem Ergebnis (Addition, UND, ODER, XOR etc.) y = xn−1 ◦ xn−2 ◦ . . . ◦ x1 ◦ x0 (◦ – assoziativer Operator). Assoziativ bedeutet, dass die Reihenfolge der Zusammenfassung keinen Einfluss auf das Ergebnis hat. Eine einfache Schleife arbeitet Ausdrücke gern von links nach rechts oder in umgekehrter Reihenfolge ab (Abb. 3.20). Die Synthese rollt die Schleife auf und extrahiert eine Kette von Teilschaltungen mit der Operatorfunktion. Der Nachteil einer Kette ist, dass der längste Schaltungspfad und damit die Verzögerung linear mit der Anzahl der Operanden zunimmt.
3.4 Beschreibungsschablonen für digitale Schaltungen type tVektortyp is (natural range <>) of tTyp; variable x, z: tVektortyp(n-1 downto 0); ... begin z(0) := x(0); for idx in z’low + 1 to z’high loop z(idx) := z(idx - 1) ◦ x(idx); end loop; beliebiger assoziativer Operator
x0
251 z0 z1
x1
z2
x2 ··· xn−1
zn−1
l¨ angster Signalpfad
Abb. 3.20. Schleife, die einen Ausdruck mit assoziativen Operatoren durch eine Kettenstruktur nachbildet
In einem Ausdruck mit einer festen Anzahl von assoziativen Operationen lässt sich der Berechnungsfluss durch eine geeignete Klammernsetzung optimieren (vgl. Abschnitt 2.2.2). In dem Ausdruck in Abb. 3.21 werden zuerst Paare von Eingabesignalen und anschließend jeweils Paare von Zwischenergebnissen so zusammengefasst, dass der Berechnungsfluss einen Baum bildet. Die korrespondierende Schaltung hat nur eine Verzögerungszeit der Ordnung O (log (n)). Ausdruck mit Klammern ((x0 ◦ x1 ) ◦ (x2 ◦ x3 )) ◦ ((x4 ◦ x5 ) ◦ x6 )
Berechnungsbaum
x0 x1 x2 x3 x4 x5 x6
Abb. 3.21. Überführung der Ketten- in eine Baumstruktur
Baumartige Berechnungsflüsse lassen sich auch mit einer Schleife beschreiben. Abbildung 3.22 zeigt hierfür einen Reduktionsalgorithmus, der solange paarweise 2 · i Eingabewerte zu i Zwischenwerten zusammenfasst, bis nur ein Zwischenwert, der Ausgabewert, übrig ist. Bei einer paarweisen Zusammenfassung von n Eingabesignalen entstehen n − 2 Zwischenergebnisse und ein Gesamtergebnis. In Abb. 3.22 wird für alle Werte zusammen ein Feld mit 2 · n − 1 Elementen vereinbart. In die ersten n Elemente werden die Eingabewerte geschrieben. Anschließend werden in jedem Schleifendurchlauf die Werte zweier benachbarter Feldelemente zusammengefasst und in das nächste freie Element geschrieben. Die ersten n − 2 Schleifendurchläufe berechnen die Zwischenergebnisse im Baum und der n − 1-te Schleifendurchlauf aus den letzten beiden Zwischenergebnissen das Endergebnis. Das Prinzip ist auch auf andere Reduktionsschemata anwendbar. Ein Carry-Save-Addierer fasst immer drei Summanden oder Zwischensummen zu zwei Zwischensummen zusammen. Die Iteration stoppt, wenn nur noch zwei nicht zusammengefasste Zwischensummen übrig sind, für deren Zusammenfas-
252
3 VHDL im Detail type tVektortyp is (natural range <>) of tTyp; variable x: tVektortyp(n-1 downto 0); z x0 0 z7 variable z: tVektortyp(2*n-2 downto 0); z10 z x1 1 z 2 z12 ... z8 x2 z x3 3 z11 z(x’range) := x; z4 x4 z9 z for idx in 0 to n-2 loop x5 5 z R¨ uckgabewert z(idx+n) := z(2*idx) ◦ z(2*idx+1); x6 6 end loop; zi Variable, in der der Wert gespeichert wird
Abb. 3.22. Schleife zur Beschreibung der Schaltung aus Abb. 3.20 mit einer Baumstruktur
sung ein normaler Addierer erforderlich ist (vgl. Abschnitt 2.5.5). Die Eingabewerte werden auch hier in ein initialisiertes Feld geschrieben. Nur entnimmt danach jeder Iterationsschritt drei Werte aus dem Feld und schreibt zwei Ergebniswerte in die nächsten freien Feldelemente (Abb. 3.23). x0 x1 x2 x3 x4 x5 x6 x7 x8 x9
z0 z1 z2 z3 z4 z5 z6 z7 z8 z9
CSA CSA CSA
s c s c s
z10 z11 z12 z13 z14 z15
c
CSA CSA
s c s c
z16 z17 z18 z19
CSA
s c
z20 z21
CSA
s c
z22 z23
CSA
s c
z24 z25
CSA Carry-Save-Addierer Pfad mit der l¨ angsten Verz¨ogerung zi Variable, in der der Wert gespeichert wird
Abb. 3.23. Reduktionsschema für einen Carry-Save-Addierer
3.4.3 Blockspeicher Digitale Schaltungen benötigen in der Regel größere Speicherblöcke zur Aufbewahrung von Daten. Das Modell eines adressierbaren Speichers ist ein adressierbares Feld von Bitvektoren: subtype tDaten is std_logic_vector(... downto 0); type tMem is array (natural range <>) of tDaten;
Die Adresse ist ein Bitvektor zur Darstellung vorzeichenfreier Zahlen: subtype tAdr is tUnsigned(... downto 0);
Die weiteren Details hängen vom Modellierungsstil, bzw. davon ab, wofür das Speichermodell zu entwickeln ist. Dieser Abschnitt beschreibt beispielhaft Modellschablonen für: •
die objektorientierte Modellierung von Schreib-Lese-Speichern in RegisterTransfer- und Synthesebeschreibungen,
3.4 Beschreibungsschablonen für digitale Schaltungen
• • •
253
die Kernfunktion eines Schreib-Lese-Speichers als Prozedur, eine synchrone Speicherschnittstelle und eine Initialisierungsfunktion für Nur-Lese-Speicher aus einer Datei.
Objektorientierte Modellierung Ein wahlfrei adressierbarer Schreib-Lese-Speicher hat zwei Bearbeitungsmethoden, das Beschreiben und das Lesen eines Speicherplatzes. Die Schreibmethode sei eine Prozedur. Die Adresse und die zu schreibenden Daten sind Eingabesignale und das Speicherobjekt sei ein les- und veränderbares Signal11 . Die Eingabedaten müssen denselben Typ wie die Elemente des Speicherobjekts haben. Die Adresse ist ein Bitvektor zur Darstellung vorzeichenfreier Zahlen und wird mit der Funktion »int« in einen Zahlenwert umgewandelt. Ein Schreibzugriff auf eine ungültige Adresse invalidiert den gesamten Speicher. Aus einer gültigen Adresse wird der Zahlenwert gebildet. Wenn er im Indexbereich des Speicherobjekts liegt, wird der adressierte Speicherplatz mit dem Eingabewert beschrieben, sonst passiert nichts. Falls alle darstellbaren Adresswerte im Indexbereich des Speicherobjekts liegen, kann die letzte Fallunterscheidung entfallen, die in der Hardware-Nachbildung zwei Vergleicher kostet: procedure write(signal Mem: inout tMem; adr: tAdr; x: tDaten) is constant cXX: tDaten := (others=>’X’); variable idx: natural; begin if is_x(adr) then for idx in Mem’range loop Mem(idx) <= cXX; end loop; else idx := int(adr); if idx>=Mem’low and idx<=Mem’high then Mem(idx) <= x; end if; end if; ⇒WEB-Projekt: P3.4/MemOOP_pack.vhdl end procedure;
Die Synthese verlangt eine vereinfachte Schreibmethode. Die Abfrage auf und die Zuweisung des Pseudo-Wertes für ungültig ist in synthesefähigen Beschreibungen nicht zulässig (vgl. Abschnitt 2.1):
11
Das zu bearbeitende Datenobjekt kann auch eine Variable sein. Eine Bearbeitungsmethode für Variablen als Datenobjekte kann jedoch keine Signalobjekte bearbeiten und umgekehrt. Deshalb ist eine Beschränkung auf einen Objekttyp zweckmäßig.
254
3 VHDL im Detail procedure write(signal Mem: inout tMem; adr: tAdr; x: tDaten) is variable idx: natural := int(adr); begin if adr>=ram’low and adr<=ram’high then Mem(idx) <= x; end if; end procedure;
Idealerweise liegen alle darstellbaren Adresswerte im Indexbereich des Speichers. Dann ist die zu bevorzugende Beschreibungsschablone für die Synthese eine einfache Signalzuweisung an das adressierte Feldelement: Mem(int(adr)) <= x;
Die Lesemethode ist als Funktion beschreibbar. Die Eingabeparameter sind das Speicherobjekt und die Adresse. Der Rückgabewert ist der gelesene Inhalt. Wenn die Adresse gültig ist und im zulässigen Adressbereich liegt, wird der adressierte Inhalt und sonst der Pseudo-Wert »ungültig« zurückgegeben: function read(Mem: tMem; adr: tAdr) return tDaten is constant cXX: tDaten := (others=>’X’); variable idx: natural; begin if is_x(adr) then return cXX; else idx := int(adr); if idx<Mem’low or idx>Mem’high then return cXX; else return Mem(idx); end if; end if; ⇒WEB-Projekt: P3.4/MemOOP_pack.vhdl end function;
In einer Synthesebeschreibung entfallen wieder die Abfragen auf und die Zuweisungen von »ungültig«. Wenn alle darstellbaren Adresswerte im Indexbereich des Speichers liegen, ist die zu bevorzugende Beschreibungsschablone ein einfacher indizierter Zugriff: x <= Mem(int(adr));
Die Kernfunktion eines Schreib-Lese-Speichers als Prozedur Eine Prozedur kann jedes mit einem Prozess beschreibbare Verhalten nachbilden, auch das von einem Schreib-Lese-Speicher. Die Vorteile, Prozessbeschreibungen als Prozeduren zu beschreiben und in ein Package auszulagern, sind, dass sich diese Teile dann als parametrisierte Modelle beschreiben und gut testen lassen, von der übrigen Beschreibung gekapselt sind, mehrfach verwendet werden können und dass die Beschreibungen auf den oberen Hierarchieebenen kleiner und übersichtlicher werden. Eine anschauliche Realisierung für einen adressierbaren Schreib-Lese-Speicher ist eine adressierbare Matrix aus Latches (Abb. 3.24 a). Die Kernfunktion
3.4 Beschreibungsschablonen für digitale Schaltungen
255
des Speichers hat einen Dateneingang, einen Adresseingang, ein Schreibsignal und einen Datenausgang. Zum Schreiben wird eine Adresse angelegt und das Schreibsignal aktiviert. Der 1-aus-n-Decoder aktiviert das Schreibsignal für genau eines der n Latches. Dieses übernimmt die Eingabedaten. Zum Lesen bleibt das Schreibsignal inaktiv und der Multiplexer verbindet das Ausgabesignal des adressierten Latches mit dem Speicherausgang. Jeder Speicherzugriff beschreibt oder liest genau einen adressierten Speicherplatz. x W
a)
1 aus n Decoder a s s0 00 0001 s a 01 0010 1 10 0100 s2 11 1000 s3
a x y a W
Eingabedaten Ausgabedaten Adresssignal Schreibsignal
thy tdy tsa tna
&
x E
&
x E
&
x E
&
x E
00 01 10 11
Lesehaltezeit Leseverz¨ogerungszeit Adressvorhaltezeit Adressnachhaltezeit
a W y
b)
1 0
tdy
y a W
c)
Leseablauf
x
thy
Schreibablauf tsa
1 0
tsx
tna twr tnx
twr Schreibdauer tsx Datenvorhaltezeit tnx Datennachhaltezeit
Abb. 3.24. Adressierbarer Schreib-Lese-Speicher a) Funktionsnachbildung mit Latches b) Leseablauf c) Schreibablauf
Die Zeitabläufe für die Ansteuerung leiten sich aus denen für ein Latch ab (vgl. Abschnitt 2.1.4). Zum Lesen muss das Schreibsignal dauerhaft inaktiv sein. Nach einer Adressänderung wird das Ausgabesignal frühstens nach einer Haltezeit thy ungültig und übernimmt spätestens nach einer Verzögerungszeit tdy den gelesenen Wert (Abb. 3.24 b). Für eine Schreiboperation muss zuerst eine gültige Adresse angelegt werden. Frühstens nach der Adressvorhaltezeit tsa darf das Schreibsignal aktiviert werden. Dann muss es mindestens für die Schreibdauer twr stabil anliegen. Die Adresse darf sich frühstens nach der Adresshaltezeit nach der Deaktivierung des Schreibsignals ändern. Die zu schreibenden Daten müssen ähnlich wie bei einem Latch um eine Vorhaltezeit tsx vor bis um eine Nachhaltezeit tnx nach der Deaktivierungsflanke des Schreibsignals stabil und gültig anliegen (Abb. 3.24 c). Bei einer Verletzung der Zeitbedingungen oder bei Glitches auf dem deaktivierten Schreibsignal wird der adressierte Inhalt ungültig. Richtige Blockspeicher haben zwar eine etwas andere Schaltungsstruktur (siehe später Abschnitt 4.4.1), das Funktionsprinzip ist jedoch dasselbe und das Zeitverhalten der Kernfunktion lässt sich mit demselben Modell beschreiben. Abbildung 3.25 beschreibt das skizzierte Verhalten als nebenläufige Prozedur. Die Anschlusssignale sind Übergabeparameter mit dem Objekttyp »si-
256
3 VHDL im Detail procedure ram(signal W: std_logic; signal a: tUnsigned; signal x: std_logic_vector; signal y: out std_logic_vector; thy, tdy, tsa, tna, twr, tsx: delay_length) is type tRAM is array(natural range <>) of std_logic_vector(x’range); constant XX: std_logic_vector(x’range) := (others=>’X’); variable RAM: tRAM(0 to 2**a’length-1); variable t: delay_length; begin loop if W=’0’ then –- Lesen y <= XX after thy; Leseablauf if not is_x(a) then a y <= transport RAM(int(a)) after tdy; W end if; thy tdy elsif rising_edge(w) then y t := now; y <= XX after thy; wait until falling_edge(W); –- Schreiben t := now - t; –- gemessene Schreibzeit if t>=twr and x’last_event>tsx and not is_x(a) then RAM(int(a)) := x; Schreibablauf elsif not is_x(a) then a tsa tna RAM(int(a)) := XX; W end if; twr wait for tna; tsx tnx x if a’last_event < tsa+t+tna then RAM := (others=>XX); tdy Leseverz¨ogerungszeit x Eingabedaten end if; tsa Adressvorhaltezeit y Ausgabedaten end if; tna Adressnachhaltezeit a Adresssignal wait on W, a; twr Schreibdauer W Schreibsignal end loop; tsx Datenvorhaltezeit thy Lesehaltezeit end procedure; ⇒Web-Projekt: P3.4/RAM pack.vhdl
Abb. 3.25. Kernfunktion eines Schreib-Lese-Speichers als nebenläufige Prozedur. Zur Modellvereinfachung wurde die Datennachhaltezeit tnx = 0 gesetzt.
gnal« und der entsprechenden Signalflussrichtung. Die Zeitparameter sind normale Eingabeparameter. Das Speicherobjekt selbst sei eine lokale Variable. Letzteres erlaubt es, mit einer einzigen Prozedur Speicher mit beliebigen Adress- und Datenvektorbreiten zu modellieren und es schützt gleichzeitig den Speicherinhalt vor unerlaubten Zugriffen. In der Prozedur wird aus der Bitanzahl der beim Aufruf zugeordneten Adress- und Datenvektoren die Feldgröße für die Speichermatrix bestimmt, dafür ein Typ und mit dem Typ die Variable für das Speicherobjekt vereinbart. Eine nebenläufige Prozedur, die ihren Zustand in einer lokalen Variablen speichert, darf den Kontrollfluss während der Simulation nicht abgeben12 . Das eigentliche Verhaltensmodell ist deshalb in 12
Denn bei einem Neuaufruf werden alle lokalen Variablen neu initialisiert.
3.4 Beschreibungsschablonen für digitale Schaltungen
257
eine Endlosschleife mit einer Warteanweisung auf eine Adress- oder Schreibsignaländerung am Ende des Schleifenkörpers eingebettet. Das Verhaltensmodell für einen Lesezugriff ähnelt dem einer kombinatorischen Schaltung. Wenn das Schreibsignal inaktiv ist und sich die Adresse ändert, wird das Datenausgabesignal nach einer Haltezeit thy ungültig und übernimmt nach Anliegen der neuen gültigen Adresse den gespeicherten Inhalt mit einer Verzögerungszeit tdy . Eine Fallunterscheidung verhindert den indizierten Feldzugriff mit einer ungültigen Adresse. Denn sonst würde sich die Simulation bei jedem ungültigen Adresswert mit einer Fehlermeldung beenden, statt nur das Speicherausgabesignal zu invalidieren. Die Zuweisung des gültigen Ausgabewertes erfolgt nach dem Transportverzögerungsmodell, damit die zuvor zugewiesene schwebende Änderung nach »ungültig« nicht gelöscht wird. Der Schreibvorgang ist komplizierter. Eine korrekte Datenübernahme setzt voraus, dass vor der Deaktivierung des Schreibsignals dieses, die Adresse und die Eingabedaten hinreichend lange stabil und gültig waren. In Abb. 3.25 wird die Schreibzeit gemessen. Wenn sie ausreichend lang, die Vorhaltezeit der Daten eingehalten und die Adresse gültig ist, wird der Speicherplatz mit dem Eingabewert beschrieben. Danach wird die Haltezeit der Adresse abgewartet und kontrolliert, dass sich die Adresse auch in dieser Zeit nicht geändert hat. Anderenfalls wird, wenn die Adresse während des Schreibvorgangs stabil und gültig war, nur der adressierte Speicherplatz und sonst der gesamte Speicher invalidiert. Die RAM-Beschreibung in der Entwurfseinheit besteht nur noch aus einem nebenläufigen Prozeduraufruf, in dem die Signale und Parameterwerte zugeordnet werden: signal Adr: tUnsigned(0 to 11); signal di, do: std_logic_vector(3 downto 0); signal W: std_logic; ... ram(W => W, a => Adr, x => di, y => do, thy => 2 ns, tdy => 5 ns, tsa => 2 ns, tna => 0 ns, twr => 7 ns, tsx => 4 ns); ⇒WEB-Projekt: P3.5/Test_RAM.vhdl
Die Prozedur übernimmt zum Simulationsbeginn den Kontrollfluss des einrahmenden Prozesses, legt die lokale Variable mit der Speichermatrix an, initialisiert die Speicherplätze mit dem Pseudo-Wert für »nicht initialisiert13 «, schreibt bei Schreibzugriffen die zu speichernden Werte in die Speichermatrix und weist bei Lesezugriffen die gelesenen Werte dem Ausgabesignal zu. Die Kernfunktion eines adressierbaren Speichers kann zur Anpassung an das Wunschverhalten in unterschiedliche Schnittstellenschaltungen eingebettet sein. Das komplette Verhaltensmodell besteht dann aus der nebenläufigen 13
Nach Zuschalten der Betriebsspannung haben die Speicherplätze eines SchreibLese-Speichers in der Regel keine bekannten Anfangswerte.
258
3 VHDL im Detail
Prozedur für die Kernfunktion und ein paar zusätzlichen Signalzuweisungen. Wir werden später in Abschnitt 4.4.1 auf diese Weise ein Simulationsmodell für einen kompletten Speicherschaltkreis konstruieren. Synchrone Speicherschnittstelle Für die Synthese ist es erforderlich, die Kernfunktion eines Schreib-LeseSpeichers in eine synchrone Schnittstelle so einzubetten, dass das externe Anschlussverhalten nicht laufzeitkritischer als bei anderen synchronen Schaltungen ist. Abbildung 3.26 a zeigt eine geeignete Schnittstellenbeschaltung mit einem Abtastregister für alle Eingabesignale und einem Latch für das Ausgabesignal. Das Eingaberegister hält die Eingabewerte immer für eine komplette Taktperiode konstant und das Ausgabe-Latch speichert die Ausgabedaten während der Schreibvorgänge. Gelesen wird bei R = 1 und W = 0. Das Abtastregister übernimmt die Steuersignale und die Adresse. Im Folgetakt ist das Ausgabe-Latch durchlässig und das Ausgabesignal wechselt nach der aktiven Taktflanke um die Haltezeit verzögert auf »ungültig« und übernimmt nach der Verzögerungszeit den gültigen gelesenen Wert. Bei einem Schreibzugriff (W = 1) werden die abgetasteten Eingabedaten im Folgetakt unter dem Abtastwert der Adresse gespeichert. Das Ausgabe-Latch speichert während dieser Zeit die zuletzt gelesenen Daten (Abb. 3.26 b). signal x, y: tDaten; signal a: tAdresse; type tRAM is array (natural range <>) of tDaten; signal RAM is tRAM(0 to 2**a’length-1); x signal T, W, R: std_logic; x RAM y x y a ... a E process (T) W W R begin & if rising_edge(T) then a) T if W=’1’ then ifVorhaltebedingung_ok_und_Adresse_gueltig then Schreiben Lesen Pause RAM(Int(a)) <= x; T 10 else x Speicherinhalt_invalidieren ; a
end if; 1 W 0 elsif R=’1’ then 1 R 0 y <= unguelt. after ˙ th ,RAM(Int(a)) after td ; y th ts end if; td c) end if; Im Synthese-Modell entfallen die grau unterlegten Beschreibungselemente b) end process; Abb. 3.26. Beschreibung eines synchronen SRAMs a) Schnittstellenschaltung b) VHDL-Beschreibung c) Anschlusssignalverläufe
3.4 Beschreibungsschablonen für digitale Schaltungen
259
Die synchrone Schnittstelle vereinfacht die Modellierung des Zeitverhaltens erheblich. Die Taktperiode muss mindestens so lang wie die Schreibdauer twr und die Leseverzögerung tdx sein. Das lässt sich mit einer Laufzeitanalyse vor der Simulation kontrollieren. Die Simulation muss nur die Vor- und Nachhaltebedingungen des Eingaberegisters und die Halte- und Verzögerungszeit für den Lesezugriff berücksichtigen (Abb. 3.26 b). Wenn die Eingabesignale von Register-Transfer-Funktionen bereitgestellt und die Ausgabesignale mit Register-Transfer-Funktionen weiterverarbeitet werden, lassen sich auch diese Zeitbedingungen mit einer Laufzeitanalyse, d.h. ohne Simulation, überwachen. Im Simulationsmodell entfallen dann die grau unterlegten Beschreibungselemente für die Berechnung des Zeitverhaltens und der Signalgültigkeit. Übrig bleibt ein Abtastprozess mit adressierten Schreib- und Lese-Zugriffen auf ein Bitvektorfeld14 : process (T) begin if rising_edge(T) then if W=’1’ then RAM(int(a)) <= x; elsif R=’1’ then y <= RAM(int(a)); end if; end if; end process;
Das ist gleichzeitig die Beschreibungsschablone für die Synthese. Die Schreibund Lesezugriffe können auch im objektorientierten Beschreibungsstil durch Unterprogrammaufrufe beschrieben werden. Eine wesentliche Funktionseinschränkung einfacher Blockspeicher ist, dass zu einem Zeitpunkt nur entweder ein Wert gelesen oder ein Wert geschrieben werden kann. Wenn die Synthese ein Feld findet, bei dem zeitgleich auf mehrere Elemente zugegriffen wird, und sei es nur zur Initialisierung, kann sie das Feld nicht durch einen einfachen Blockspeicher, sondern nur entweder durch viele einzelne Register oder Latches oder durch einen Mehrportspeicher nachbilden (Mehrportspeicher siehe später Abschnitt 4.4.2). Eine Initialisierungsfunktion für nur lesbare Blockspeicher Ein nur lesbarer Blockspeicher (ROM, read only memory) wird in einem Simulations- oder Synthesemodell als initialisierte Feldkonstante modelliert: subtype tDaten is std_logic_vector(... downto 0); type tMem is array (natural range <>) of tDaten; 14
Man könnte das Simulationsmodell in Abb. 3.26 auch aus der Kernfunktion in Abb. 3.25, einem Register, einem Latch und Gattern zusammensetzen. Aber das Modell wird dadurch weder einfacher noch besser.
260
3 VHDL im Detail subtype tAdr is tUnsigned(... downto 0); constant ROM: tMem := Anfangswert ;
Der Speicherinhalt ist der Anfangswert. Für einen kleinen Speicher können die Anfangswerte mit einer Zuordnungsliste festgelegt werden: constant ROM: tMem := (0=>x"0f", 1=>x"c3", 2=>x"a4", 3=>x"12", others=>x"00")
Für einen größeren Festwertspeicher ist eine Funktion, die den zu speichernden Inhalt – z.B. das Programm für einen Prozessor – aus einer Datei liest und als Anfangswert übergibt, wünschenswert. In dem nachfolgenden Beispiel geht es wieder im Wesentlichen um das Thema Textverarbeitung. Die Beispieltextdatei soll je Zeile eine beliebige Anzahl von Bytes enthalten, dargestellt durch zwei Hexadezimalziffern, gefolgt von Leerzeichen, dem Zeilenumbruch oder dem Dateiende: AF 09 87 D4 2A 95 12 BC C4 3C D6 E3 EC 79 45 C5 DE AB 24 38 8D A6 B3 ...
In dem in Abschnitt 3.3.3 skizzierten Package »Tuc.Eingabe« ist ein Datenobjekt für ein Leseband vereinbart, das mit der Funktion procedure read(file f: text; pstr: tPString)
mit einer Zeile aus einer Textdatei geladen und von dem mit Get-Prozeduren Zeichenfolgen für unterschiedliche Datenobjekte abgeräumt werden können. Für das Abräumen der zweistelligen Hexadezimalzahlen werden die Prozedur procedure get_char(pstr: inout tPString; pos: out natural; CharTab: string);
die ein Zeichen aus einer Zeichentabelle »CharTab« abräumt und deren Tabellenposition zurückgibt, und die Prozedur procedure skip(pstr: inout tPString; TrennzeichenTab: string);
die Trennzeichen abräumt, benötigt (siehe Anhang A.2.3). Daraus wird eine Prozedur zum Abräumen von Bytes zusammengesetzt, die nach demselben Schema wie die Abräumprozeduren für andere Datenobjekte aufgebaut ist: constant HexZiffern: string := "0123456789ABCDEF"; procedure get_Byte(pstr: inout tPString; w: out tByte) is constant ptr: positive := pstr.pta; variable z0, z1: natural; begin skip(pstr, " "); get_char(pstr, z1, HexZiffern); if pstr.Status=ok then get_char(pstr, z0, HexZiffern); if pstr.Status=ok then
3.4 Beschreibungsschablonen für digitale Schaltungen
261
w := std_logic_vector(to_tUnsigned(16*z1 + z0, 8)); return; end if; end if; ... –- Fehlerbehandlung ⇒WEB-Projekt: P3.4/ROM_pack.vhdl end procedure;
Die gesamte Funktion zum Einlesen der Datei öffnet die zu lesende Datei, legt eine Feld-Variable für den Rückgabewert an und liest in einer Schleife alle Bytewerte nacheinander ein. Das Lesebandobjekt wird zu Beginn mit der ersten Dateizeile und, immer wenn es leer ist, mit der Folgezeile gefüllt. Bei einem vorzeitigen Dateiende oder einem unzulässigen Zeichen beendet eine Assert-Anweisung die Simulation mit einer Fehlermeldung, aus der die Ursache und der Ort des Datenfehlers in der Eingabedatei hervorgeht: function ROM_init(datei_name:string; m:positive) return tByteMem is file f: text open read_mode is datei_name; variable Mem: tByteMem(1 to m); variable ByteNr, ZeilenNr: positive; variable pstr: tPString; begin read(f, pstr); S1: loop S2: while pstr.Status=ok and pstr.pta
Das Einprogrammieren aussagekräftiger Fehlermeldungen in ein Simulationsmodell, insbesondere auch beim Lesen aus Dateien, ist dringend zu empfehlen. Der nicht unerhebliche Programmieraufwand rentiert sich später bei der Fehlersuche. Mit der Initialisierungsfunktion »ROM_init(...)« ist die Vereinbarung einer initialisierten Konstante zur Nachbildung eines Festwertspeichers ein Einzeiler (siehe Web-Projekt: P3.4/Test_ROM.vhdl): constant ROM: tByteMem := ROM_init("ROM_init.txt", 8);
262
3 VHDL im Detail
3.4.4 Ein objektorientiertes FIFO-Modell Für einen einfachen Speicher lohnt sich der objektorientierte Ansatz noch nicht unbedingt, aber für ein Speichersystem mit interner Adressrechnung unter Umständen schon. Beispiel sei ein FIFO. Ein FIFO ist ein Speicher mit den drei Methoden Initialisieren, Schreiben und Lesen. Das Akronym FIFO steht für »f irst in f irst out« und beschreibt das Organisationsprinzip. Die Initialisierungsmethode stellt den Anfangszustand »leer« her, die Schreibmethode speichert einen Datenwert und die Lesemethode entnimmt und löscht den ältesten (f irst in) gespeicherten Datenwert und weist ihn an das Ausgabesignal zu. Intern besteht ein FIFO aus einer Speichermatrix mit wahlfreiem Zugriff, einem Lesezeiger, einem Schreibzeiger, einem Flag für »leer« und einem Flag für »voll« (Abb. 3.27). Der Schreibzeiger zeigt, wenn der FIFO nicht voll ist, auf den ersten freien und sonst auf den ersten frei werdenden Speicherplatz. Der Lesezeiger zeigt, wenn der FIFO nicht leer ist, auf den ältesten gespeicherten Wert und sonst auf den nächsten zu beschreibenden Speicherplatz. Im Package für das objektorientierte FIFO-Modell werden zuerst die Datentypen für die Adresszeiger und die Speichermatrix vereinbart. Die Flags haben einen Bittyp. Aus den Einzeltypen wird ein Verbund für alle zu manipulierenden Datenobjekte zusammengesetzt. constant N_Adr: positiv := ...; subtype tAdrIdx is range 0 to 2**N_Adr-1; subtype tDaten is ...; type tSpeicher is array (tAdrIdx) of tDaten; type tFIFO is record Mem : tSpeicher; sz, lz : tAdrIdx; leer, voll: boolean; end record;
⇒Web-Projekt: P3.4/FIFO pack.vhdl
lz sz voll sz lz voll leer
leer Mem Schreibzeiger Lesezeiger Flag, true, wenn voll Flag, true, wenn leer gespeicherte Daten
Abb. 3.27. Das Datenobjekt FIFO-Speicher (FIFO-Objekt)
Die Initialisierungsmethode setzt beide Zeiger auf die erste Speicheradresse, das Flag »leer« auf true und das Flag »voll« auf false. Die Speichermatrix selbst bleibt unverändert. Die Übergaberichtung des gesamten FIFOObjekts muss dennoch »inout« sein, weil die Prozedur bei der Übergaberichtung »out« auch dem Speicher einen Initialwert zuweisen würde. Bei der Schaltungsumsetzung müsste dann ein parallel initialisierbarer Speicher eingesetzt werden, eine Hardware-aufwändige Lösung, die aus funktionaler Sicht nicht erforderlich ist (siehe später Abschnitt 4.4.1):
3.4 Beschreibungsschablonen für digitale Schaltungen procedure init(signal fifo: inout tFIFO) is begin fifo.lz <= 0; fifo.sz <= 0; fifo.leer <= true; fifo.voll <= false; end procedure;
263 init FIFOObjekt
⇒WEB-Projekt: P3.4/FIFO_pack.vhdl
Die Schreibmethode übernimmt den Eingabewert nur, wenn der FIFO nicht voll ist. Bei Übernahme wird das Flag »leer« gelöscht und nach der Übernahme die Adresse weitergeschaltet. Die Adressweiterschaltung erfolgt zirkular mit Hilfe der Modulo-Operation. Nach der höchsten Adresse folgt die kleinste Adresse: function inc(x: tAdrIdx) return tAdrIdx is begin return (x+1) mod (tAdrIdx’high+1); end function;
⇒WEB-Projekt: P3.4/FIFO_pack.vhdl
Wenn die Schreib-Adresse dabei die Lese-Adresse einholt, wird das Flag »voll« gesetzt und der FIFO übernimmt bei nachfolgenden Schreiboperationen keine weiteren Daten. procedure write(x: tDaten; signal fifo: inout tFIFO) is x write begin if not fifo.voll then FIFOfifo.Mem(fifo.sz) <= x; Objekt fifo.leer <= false; fifo.sz <= inc(fifo.sz); if fifo.sz=fifo.lz then fifo.voll <= true; end if; end if; ⇒WEB-Projekt: P3.4/FIFO_pack.vhdl end procedure;
Die Lesemethode ist ähnlich aufgebaut. Sie hat statt des Eingabesignals das Ausgabesignal als Übergabeparameter. Wenn der FIFO nicht leer ist, wird der Wert, auf den der Lesezeiger zeigt, ausgegeben, das Flag »voll« gelöscht, der Lesezeiger weitergestellt und, falls der Lesezeiger den Schreibzeiger eingeholt hat, das Flag »leer« gesetzt. procedure read(signal y: out tDaten; signal fifo: inout tFIFO) is begin if not fifo.leer then read x y <= fifo.Mem(fifo.lz); fifo.voll <= false; FIFOfifo.lz <= inc(fifo.lz); Objekt if fifo.lz=fifo.sz then fifo.leer <= true; end if; end if; ⇒Web-Projekt: P3.4/FIFO pack.vhdl end procedure;
264
3 VHDL im Detail
Der Testrahmen für das FIFO-Objekt und seine Methoden vereinbart ein Signal für den FIFO, eines für die Ausgabe und eines für den Takt: signal f: tFIFO;
signal y: tDaten;
signal T: std_logic;
Der Test selbst erfolgt in einem Prozess. Nach jedem Aufruf einer Zugriffsmethode folgt eine Warteanweisung. Während dieser übernehmen die Signale ihre Werte. Die zu schreibenden Daten dürfen auch Konstanten sein15 : process begin TaktSchritt(T); init(f); TaktSchritt(T); write(x"f1", f); TaktSchritt(T); write(x"e2", f); TaktSchritt(T); read(y, f); TaktSchritt(T); ...
⇒WEB-Projekt: P3.4/Test_FIFO.vhdl
Die Prozedur »TaktSchritt(...)« legt nicht nur den Prozess für eine Taktperiode schlafen, sondern sie erzeugt auch das Taktsignal: procedure TaktSchritt( signal T: out std_logic) is begin wait for 5 ns; T <= ’0’; wait for 5 ns; T <= ’1’; end procedure; ⇒WEB-Projekt: P3.4/FIFO_pack.vhdl
Weiterhin soll der Testrahmen einen Ausgabeprozess erhalten, der bei jeder aktiven Taktflanke die aktuelle Simulationszeit, den Wert des Ausgabesignals und den FIFO-Zustand als Bildschirmtext ausgibt: process begin write(" Zeit | y | FIFO-Zustand"); –- Tabellenüberschrift loop write(rechts(str(now), 9) & "| " & str_hex(y) & " |" & str(f)); wait until rising_edge(T); end loop; ⇒WEB-Projekt: P3.4/Test_FIFO.vhdl end process;
Die Str-Funktion konvertiert im Beispiel den Wert des FIFO-Objekts in folgende Textdarstellung: [Lesezeiger :Schreibzeiger |[Wert{,Wert}][(leer)|(voll)]]
([...] – kann, aber muss nicht vorhanden sein; {...} – kann beliebig oft vorhanden sein; | – entweder der linke oder der rechte Wert}. Die Simulationsausgaben sehen wie folgt aus: 15
Die Übergabe der Daten an die Schreibmethode erfolgt mit dem Objekttyp »constant«, so dass wahlweise die Werte von Signalen, Variablen oder Konstanten übergeben werden dürfen (vgl. Abschnitt 3.1.7).
3.4 Beschreibungsschablonen für digitale Schaltungen Zeit 0 10.00 20.00 30.00 40.00 50.00 60.00
| fs| ns| ns| ns| ns| ns| ns|
y XX XX XX XX XX XX f1
| FIFO-Zustand |[0:0|XX,XX,XX,XX,XX,XX,XX,XX] |[0:0|(leer)] |[0:1|f1] |[0:2|f1,e2] |[0:3|f1,e2,d3] |[0:4|f1,e2,d3,c4] |[1:4|e2,d3,c4]
70.00 80.00 90.00 100.00 110.00 120.00 130.00 140.00
ns| ns| ns| ns| ns| ns| ns| ns|
e2 d3 c4 c4 c4 c4 c4 c4
265
|[2:4|d3,c4] |[3:4|c4] |[4:4|(leer)] |[4:4|(leer)] |[4:5|1f] |[4:6|1f,2e] |[4:7|1f,2e,3d] |[4:0|1f,2e,3d,4c]
Zum Simulationsbeginn haben alle Datenteilobjekte des FIFOs ihre Anfangswerte, die Zeiger den Wert null, die Flags den Wert false und die Daten im Blockspeicher den Wert »ungültig«. Im ersten Schritt wird der FIFO initialisiert, dann mit vier Werten beschrieben. Danach werden zwei Werte gelesen etc. Bei jeder Schreiboperation schaltet der Schreibzeiger weiter und der FIFO füllt sich. Bei jeder Leseoperation schaltet der Lesezeiger weiter und der FIFO leert sich. Für das Erkennen und die Lokalisierung potenzieller Entwurfsfehler ist eine übersichtliche Darstellung der Simulationsergebnisse – insbesondere für komplexe Datenobjekte wie einen FIFO – entscheidend. 3.4.5 UART – schrittweise Modellentwicklung Die Entwicklung einer Verhaltensbeschreibung für eine komplexe Schaltung ist ein langwieriger iterativer Prozess, der in überschaubare Schritte aufgeteilt werden muss. Nach jedem Schritt sind Zwischenkontrollen erforderlich. Der Bleistift-und-Papier-Entwurf beginnt mit Skizzen für Teilfunktionen, Beispielabläufe und Algorithmenfragmente. Zur Kontrolle der einzelnen Ideen und Funktionsskizzen ist es wichtig, bereits in frühen Entwurfsstadien Simulationsmodelle und Testbeispiele aufzustellen und abzuarbeiten. Dieser Abschnitt demonstriert, wie ein solcher Entwurf ablaufen könnte. Die Entwurfsobjekte sind der Sender und der Empfänger für eine universelle serielle Schnittstelle (UART – universal asynchronous receiver transmitter). Der Fahrplan für den Entwurfsablauf lautet • • • • • •
Ableitung von Algorithmen aus der Aufgabenstellung, Software-orientierte Modellierung mit Textein- und Textausgaben, Aufteilung der Berechnungsschritte in Zeitschritte, Auslagerung von Typdefinitionen und Unterprogrammen in Packages, Steuerung der Zeitabläufe mit Signalen (Takt, Initialisierung, Operationsstart etc.) und Umwandlung in eine synthesegeeignete Beschreibungsstruktur.
Jede Beschreibung, die in diesem Entwurfsablauf entsteht, benötigt einen Testrahmen für die Simulation, um potenzielle Fehler aufzudecken und zu beseitigen. Auch für die Zwischenergebnisse in einem solchen Prozess gibt es gewisse Beschreibungsschablonen, die zwar nicht bindend, aber vorteilhaft
266
3 VHDL im Detail
sind. Eine UART ist ein Stück komplexer als der serielle Addierer in Abschnitt 2.5.2 und der serielle Dividierer in Abschnitt 2.6.5, für die der Entwurf auch schon in Schritte unterteilt wurde. Die Schrittanzahl ist entsprechend größer. Zur Verbesserung der Übersichtlichkeit wird nach jedem Entwurfsschritt der Bearbeitungsstand zusammengefasst. Schritt 1: Ableitung des Algorithmus aus der Aufgabenstellung Die Aufgabe einer seriellen Schnittstelle ist der Datenaustausch zwischen räumlich getrennten Teilschaltungen über eine minimale Anzahl von Leitungen. Dazu wird die zu übertragende Information vom Sender in eine geeignete Darstellung umgewandelt und bitweise verschickt. Der serielle Datenstrom gelangt zum Empfänger, der daraus die übertragene Information zurückgewinnt. Sender und Empfänger sind nur über ein Signal (und die Leitung für das Bezugspotenzial) miteinander verbunden. Eine Hin- und Rückübertragung erfordert zwei Übertragungssignale und je einen Sender und einen Empfänger auf beiden Seiten (Abb. 3.28 a). Eine UART überträgt die Information byteweise. Vor der Übertragung und während der Übertragungspausen wird der Wert »1« – das Stoppbit – gesendet. Die Übertragung beginnt mit der fallenden Flanke zum Startbit. Nach dem Startbit folgen die acht Datenbits, optional ein Paritätsbit und vor der nächsten Byte-Übertragung mindestens ein Stoppbit. Das Paritätsbit ergibt sich aus der EXOR-Summe der Datenbits. Es erlaubt dem Empfänger, einzelne Bitverfälschungen zu erkennen. Das Startbit, die Datenbits und das Paritätsbit werden genau für eine Bitzeit tBit versendet, das Stoppbit zwischen den Übertragungen mindestens für eine Bitzeit (Abb. 3.28 b). ¨ serielle Ubertragung
System 1 Sender
TxD
Empf¨ anger
RxD
Pegelwandler
1 0
von 2 nach 1
x0 x1 x2 x3 x4 x5 x6 x7 P 0
b)
Pegelwandler
von 1 nach 2
RxD
Empf¨ anger
TxD
Sender
Bezugspotenzial (3. Leitung)
a)
RxD, TxD
System 2
1
2
3
4
5
6
7
8
9 10 11
TxD Sendesignal
x0 t tBit
Stoppbit Startbit Datenbits Parit¨atsbit
RxD Empfangssignal
Abb. 3.28. a) Verbindung zweier Systeme über eine serielle Schnittstelle b) Übertragung eines Bytes
3.4 Beschreibungsschablonen für digitale Schaltungen
267
Bearbeitungsstand: Das Anschlussverhalten und die Zielfunktion sind grob umrissen. Schritt 2: Sender – Algorithmus, Zeitablauf und Steuersignale Der Sender soll sich wie folgt verhalten. Er soll auf ein zu versendendes Byte warten, dieses in das 11-Bit-Datenpaket verpacken, das gesamte Datenpaket bitweise versenden und anschließend auf das nächste zu versendende Byte warten. Während der Übertragung ist ein Busy-Signal zu aktivieren, das der Schaltung, die die Sendedaten liefert, mitteilt, dass die zu versendenden Daten nicht verändert werden dürfen. Dieses verbal formulierte Verhalten ist in Abb. 3.29 a als Struktogramm beschrieben. Der Ablaufrahmen ist eine Endlosschleife, in der zu Beginn das Stoppbit versendet, das Busy-Signal deaktiviert und auf das Startsignal gewartet wird. Nach Aktivierung des Startsignals werden das Busy-Signal aktiviert und nacheinander genau für je eine Bitzeit das Startbit, die acht Datenbits und das Paritätsbit versendet. Das Versenden der acht Datenbits und die Berechnung der Parität erfolgt in einer Schleife. Das Stoppbit und die Deaktivierung des Busy-Signals folgen zum Beginn des nächsten Schleifendurchlaufs. In diesem Struktogramm steckt der kreative Teil des Entwurfs. Feinheiten wie, dass das Stoppbit als erstes und nicht als letztes versendet werden muss, sind in der Regel das Ergebnis mehrerer Nachbesserungsiterationen. procedure Sender(signal Start: std_logic; signal x: tByte; signal TxD, busy: out std_logic; tbit: delay_length) is variable P: std_logic; Wiederhole immer begin T xD <= 1; busy <= 0; loop warte eine Bitzeit und bis Start = 1 TxD <= ’1’; busy <= ’0’; T xD <= 0; busy <= 1; P := 0; wait for tbit; warte eine Bitzeit wait until Start=’1’; wiederhole f¨ ur idx = 0 bis 7 TxD <= ’0’; busy <= ’1’; P := ’0’; T xD <= x(idx); P := P xor x(idx); warte eine Bitzeit wait for tbit; T xD <= P ; for idx in x’reverse_range loop a) warte eine Bitzeit TxD <= x(idx); Schnittstellenverhalten P := P xor x(idx); Start 10 wait for tbit; end loop; x 10001011 TxD <= P; busy 10 wait for tbit; TxD 10 x0 x1 x2 x3 x4 x5 x6 x7 end loop; Startflanke Daten Parit¨at b) end procedure; c) ⇒Web-Projekt: P3.4/UART1 pack.vhdl Abb. 3.29. Sendermodell a) Struktogramm b) VHDL-Prozedur c) Signalverlauf
268
3 VHDL im Detail
In Abb. 3.29 b ist das Struktogramm durch eine nebenläufige Prozedur nachgebildet. Die Eingangssignale Start und x (zu versendendes Datenbyte) und die Ausgabesignale busy und TxD sind als Schnittstellensignale mit der entsprechenden Übergaberichtung vereinbart. Die Bitzeit wird als Konstante übergeben. Die Anweisungsfolge ist aus dem Struktogramm übernommen. Nach den Berechnungen für einen Zeitschritt legt eine Warteanweisung den Prozess, in dem die Prozedur abgearbeitet wird, für eine Bitzeit schlafen. Vor Versenden des Startbits wird zusätzlich auf die Aktivierung des Startsignals gewartet. Die Nachbildung des Struktogramms durch eine nebenläufige Prozedur hat offenbar Rezeptcharakter. Die Prozedur liefert nebenläufig in einem Testrahmen aufgerufen für die Eingabesignalverläufe in Abb. 3.29 c die dargestellten Ausgabesignalverläufe. Bearbeitungsstand: Die Funktion des Senders ist in einer simulierbaren Weise beschrieben. Sie sollte jetzt solange nachgebessert werden, bis sie die Zielfunktion ausreichend gut nachbildet. Schritt 3: Ein Verhaltensmodell für den Empfänger Für den Empfänger sind dieselben Schritte und Überlegungen wie für den Sender erforderlich. Abbildung 3.30 a zeigt den Empfangsablauf als Struktoprocedure Empfaenger(signal RxD: std_logic; signal y: out tByte; signal ready, Err: out std_logic; ready <= 0; Err <= 0; tbit: delay_length) is Wiederhole immer variable P: std_logic; warte bis RxD = 0 begin ready <= 0; Err <= 0; P := 0; warte 1,5 Bitzeiten ready <= ’0’; Err <= ’0’; wiederhole f¨ ur idx = 0 bis 7 loop y(idx) <= RxD; P := P xor RxD; wait until RxD=’0’; warte eine Bitzeit ready <= ’0’; Err <= ’0’; P := ’0’; ready <= 1 wait for 1.5*tbit; RxD = P ? nein ja for idx in y’reverse_range loop Err <= 1 y(idx) <= RxD; warte eine Bitzeit P := P xor RxD; RxD = 1? nein ja wait for tbit; Err <= 1 end loop; warte eine halbe Bitzeit a) ready <= ’1’; 1 x0 x1 x2 . . . x7 P if P/=RxD then Err <= ’1’; end if; RxD 0 wait for tbit; 0 1 2 3 4 8 9 10 11 t tBit if RxD/=’1’ then Err <= ’1’; end if; Startflanke Bit¨ ubernahme wait for 0.5*tbit; Parit¨atskontrolle Stoppbitkontrolle end loop; ¨ Ubertragungsende b) end procedure; c) ⇒Web-Projekt: P3.4/UART1 pack.vhdl Abb. 3.30. Verhaltensmodell für den Empfänger a) Struktogramm b) nebenläufige Prozedur c) Empfangssignal mit Abtastzeitpunkten
3.4 Beschreibungsschablonen für digitale Schaltungen
269
gramm, Abb. 3.30 b beschreibt ihn als eine nebenläufige Prozedur und Abb. 3.30 c zeigt noch einmal das Empfangssignal, aus dem das übertragene Byte zurückgewonnen wird. Der Empfänger erkennt den Übertragungsbeginn an der fallenden Flanke zwischen dem Stoppbit der letzten Übertragung und dem Startbit der aktuellen Übertragung. Von da aus wartet er zuerst 1,5 Bitzeiten bis zur Mitte des ersten Datenbits und dann jeweils eine Bitzeit auf den nächsten Bitübernahmezeitpunkt. Von den empfangenen Bits wird gleichfalls die EXOR-Summe gebildet und mit dem Wert des empfangenen Paritätsbits verglichen. Bei einer Abweichung oder, wenn zum Abtastzeitpunkt des Stoppbits der Wert des Empfangssignals RxD ungleich »1« ist, wird ein Fehlersignal aktiviert. Nach Empfang aller Bits wird das Ready-Signal, das nach Erkennen der Startflanke deaktiviert wurde, wieder aktiviert. Auch der Empfangsablauf ist in eine Endlosschleife eingebettet. Bearbeitungsstand: Auch für den Empfänger ist die Funktion jetzt in einer simulierbaren Weise beschrieben. Schritt 4: Simulation der seriellen Übertragung Die wichtigste Anforderungen an eine UART ist, dass die versendeten Daten korrekt empfangen werden. Dazu ist die gesamte UART mit dem Modell des Senders, dem Modell des Empfängers, einem Modell für die Übertragungsstrecke sowie Hilfsprozessen für die Bereitstellung der zu versendenden Daten und für die Kontrolle der empfangenen Daten zu simulieren. In dem nachfolgenden Testrahmen werden der Sender und der Empfänger durch die beiden nebenläufigen Prozeduren und die Übertragungsstrecke durch eine nebenläufige Signalzuweisung mit einer Transportverzögerung beschrieben. Die Bitzeit für den Empfänger wurde 1% kleiner als die des Senders gewählt, um den zufälligen Taktversatz und die unvermeidlichen geringen Abweichungen zwischen den Periodendauern des Sendertaktes und des Empfängertaktes ansatzweise zu berücksichtigen. Besser wäre eine geringfügige zufällige Variation der Taktperioden: signal x, y: tByte; signal Start, busy, TxD, RxD, ready, Err: std_logic; constant tbit: delay_length := 10 ns;
... Sender(Start=>Start, x=>x, TxD=>TxD, busy=>busy, tbit=>tbit); Empfaenger(RxD=>RxD,y=>y,ready=>ready,Err=>Err,tbit=>0.99*tbit); RxD <= transport TxD after 5 ns; –- Leitungsmodell ⇒WEB-Projekt: P3.4/Test_UART1.vhdl
Der nachfolgende Eingabeprozess stellt die Daten für zwei Übertragungszyklen bereit. Für jeden Übertragungszyklus werden ein Startimpuls erzeugt, die
270
3 VHDL im Detail
zu übertragenden Daten bereitgestellt und auf die Deaktivierung des BusySignals gewartet. Anschließend terminiert der Prozess mit einer Warteanweisung ohne Weckbedingung: Eingabe: process begin Start <= ’0’ after 2 ns, ’1’ after 20 ns, ’0’ after 30 ns; x <= XX after 15 ns, x"3f" after 18 ns; wait until falling_edge(busy); Start <= ’1’ after 28 ns, ’0’ after 73 ns; x <= XX after 7 ns, x"79" after 9 ns; wait until falling_edge(busy); wait; end process;
⇒WEB-Projekt: P3.4/Test_UART1.vhdl
Als Debug-Hilfe soll der Testrahmen zusätzlich einen Ausgabeprozess erhalten, der alle Signaländerungen in Tabellenform protokolliert: Ausgabe: postponed process(Start, x, busy, TxD, RxD, y, ready, Err) variable TabKopf: boolean := true; begin –- Ausgabe Tabellenkopf if TabKopf then write(" Zeit |Start| ... |Err"); TabKopf := false; end if; –- Ausgabe einer Tabellenzeile write(rechts(str(now),9) & "| " & str(Start) & " |" & ... ); end process; ⇒WEB-Projekt: P3.4/Test_UART1.vhdl
Zum Simulationsbeginn gibt der Prozess zusätzlich einen Tabellenkopf mit den Signalbezeichnungen aus. Der Funktionsaufruf »rechts(str(now),9)« bettet die Zeichenkette der Zeitangabe rechtsbündig in eine Zeichenkette der Länge neun mit führenden Leerzeichen ein: Zeit 0 0 2.00 5.00 15.00 18.00 ... 237.05
|Start| x |Busy|TxD|RxD| y |Ready|Err fs| U |UUUUUUUU| U | U | U |UUUUUUUU| U | U fs| U |UUUUUUUU| 0 | 1 | U |UUUUUUUU| 0 | 0 ns| 0 |UUUUUUUU| 0 | 1 | U |UUUUUUUU| 0 | 0 ns| 0 |UUUUUUUU| 0 | 1 | 1 |UUUUUUUU| 0 | 0 ns| 0 |XXXXXXXX| 0 | 1 | 1 |UUUUUUUU| 0 | 0 ns| 0 |00111111| 0 | 1 | 1 |UUUUUUUU| 0 | 0 ns|
0
|01111001|
0 | 1 | 1 |01111001|
1
(1) (2)
| 0
((1) – Ausgabe des zur Initialisierung geweckten Prozesses; (2) – Ausgabe nach allen Signaländerungen für den Simulationszeitpunkt null). Der Ausgabeprozess ist ein sog. Postponed-Prozess. Ein Postponed-Prozess wird für jeden Simulationszeitpunkt erst geweckt, wenn alle Nicht-Postponed-Prozesse und
3.4 Beschreibungsschablonen für digitale Schaltungen
271
alle verzögerungsfreien Signalzuweisungen abgearbeitet sind. Das hat im Beispiel den Vorteil, dass für jeden Simulationszeitpunkt nur eine Zeile ausgegeben wird. Ausnahme ist der Simulationsbeginn, zu dem der Ausgabeprozess einmal zur Initialisierung und einmal nach Abarbeitung aller verzögerungsfreien Signalzuweisungen geweckt wird. Ohne »postponed« bewirken die verzögerungsfreien Signalzuweisungen im Modell, dass auch für andere Simulationszeitpunkte mehrere Zeilen ausgegeben werden. Bearbeitungsstand: Simulation der seriellen Übertragung. Schritt 5: Takt und Initialisierung Die bis hierher entwickelten Simulationsmodelle dienen ausschließlich dazu zu kontrollieren, ob die Zeitabläufe und Berechnungsschritte in der angedachten Weise zufriedenstellend funktionieren. Wenn das der Fall ist, ist die Beschreibung so weiterzuentwickeln, dass sie technisch realisierbar ist. Die Zielstruktur ist jeweils für den Sender und den Empfänger eine getaktete Schaltung mit asynchroner Initialisierung. Die Modelle erhalten dazu zwei zusätzliche Eingabesignale, einen Takt T und ein Initialisierungssignal I. In den Funktionsmodellen sind die Zeitabläufe an den aktiven Taktflanken und den Änderungen des Initialisierungssignals auszurichten. Alle Warteanweisungen müssen dazu zum einen so umprogrammiert werden, dass statt für eine bestimmte Zeit auf die aktive Taktflanke gewartet wird. Das ist eine relativ einfache Änderung. Zusätzlich müssen die Warteanweisungen auch bei einer Aktivierung des Initialisierungssignals beendet werden. Nach einer solchen Beendigung ist die Endlosschleife, in die der Programmfluss eingebettet ist, zu beenden und zum Eintrittspunkt mit der Anfangsinitialisierung zu springen. Sender Der Trick, in einem Software-orientierten Ablaufmodell eine asynchrone Neuinitialisierung zu beschreiben, besteht im Wesentlichen in einer Einbettung der bisherigen äußeren Endlosschleife in eine zusätzliche Endlosschleife und in bedingten Abbruchanweisungen nach jeder Warteanweisung, mit denen die bisherige äußere Endlosschleife verlassen wird. Abbildung 3.31 zeigt die erforderlichen Änderungen für den Sender am Struktogramm. Die bisherige Endlosschleife hat hier den Schleifenbezeichner »S1« und die zusätzliche Schleife den Bezeichner »S0«. Alle grau unterlegten Anweisungen, in denen im bisherigen Modell für eine Bitzeit gewartet wird, werden durch zwei Anweisungen ersetzt. Die erste ist eine Warteanweisung auf die nächste aktive Taktflanke oder die Aktivierung des Initialisierungssignals und die zweite eine Abbruchanweisung für die Schleife »S1«, wenn I aktiv ist. Nach Verlassen der Schleife »S1« werden als Nächstes die Initialisierungsanweisungen vor der Schleife »S1« abgearbeitet. Im Beispiel wird nur auf die Deaktivierung von I gewartet. Die skizzierten Änderungen bilden eine allgemeine, auf beliebige Software-orientierte Abläufe anwendbare Beschreibungsschablone.
272
3 VHDL im Detail Funktionsmodell mit ”warte f¨ ur eine Bitzeit”
warte auf Abschluss der Initialisierung
S1: Wiederhole immer T xD <= 1; busy <= 0; warte eine Bitzeit und bis Start = 1
T xD <= 0; busy <= 1; P := 0; warte eine Bitzeit
S2: wiederhole f¨ ur i = 0 bis 7 T xD <= x(idx); P := P xor x(idx); warte eine Bitzeit
T xD <= P ; warte eine Bitzeit
I S0 S1 S2
Initialisierungssignal Initialisierungsschleife Endlosschleife Wiederholschleife ”f¨ ur alle Bits”
Erweitungen um Taktflankenausrichtung und asynchrone Neuinitalisierung S0: Wiederhole immer warte auf Abschluss der Initialisierung
S1: Wiederhole immer T xD <= 1; busy <= 0; warte auf Taktflanke und Start = 1 oder I = 1; Abbruch bei I = 1
T xD <= 0; busy <= 1; P := 0; warte auf Taktflanke oder I = 1 Abbruch bei I = 1
S2: wiederhole f¨ ur idx = 0 bis 7 T xD <= x(idx); P := P xor x(idx); warte auf Taktflanke oder I = 1 Abbruch bei I = 1
T xD <= P ; warte auf Taktflanke oder I = 1 Abbruch bei I = 1
Abb. 3.31. Erweiterung des Sendermodells zur Nachbildung einer synchronen Schaltung mit asynchroner Initialisierung
Die nebenläufige Prozedur für das Senderverhalten erhält zusätzlich den Takt und das Initialisierungssignal als Übergabeparameter. Dafür entfällt der Übergabeparameter für die Bitzeit. Intern kommt genau wie im Struktogramm die Initialisierungsschleife hinzu. Die Wartebedingungen werden entsprechend Abb. 3.31 ersetzt und die Schleifenabbruchanweisungen ergänzt. Der Rest des Kontrollflusses und die Signalzuweisungen sind aus dem ersten Simulationsmodell übernommen: procedure Sender(signal T, I, Start: std_logic; signal x: tByte; signal TxD, busy: out std_logic) is variable P: std_logic; begin S0: loop wait until falling_edge(I); S1: loop TxD <= ’1’; busy <= ’0’; wait until (rising_edge(T) and Start=’1’) or I=’1’; exit S1 when I=’1’; TxD <= ’0’; busy <= ’1’; P := ’0’; wait until rising_edge(T) or I=’1’; exit S1 when I=’1’; S2: for idx in x’reverse_range loop TxD <= x(idx); P := P xor x(idx); wait until rising_edge(T) or I=’1’; exit S1 when I=’1’; end loop; TxD <= P; wait until rising_edge(T) or I=’1’; exit S1 when I=’1’;
3.4 Beschreibungsschablonen für digitale Schaltungen end loop; end loop; end procedure;
273
⇒WEB-Projekt: P3.4/UART2_pack.vhdl
Empfänger Im Simulationsmodell des Empfängers sind die Wartebedingungen • • • •
warte warte warte warte
auf RxD = 0, für eineinhalb Bitzeiten, für eine Bitzeit und für eine halbe Bitzeit
an den Takt und an das Initialisierungssignal zu binden. Der Bezugszeitpunkt für die Abtastung des empfangenen Signals RxD ist die erste fallende Flanke und nicht zum Empfängertakt ausgerichtet. Der Empfänger wird deshalb mit einem Vielfachen der Frequenz des Senders getaktet und die Zeitdifferenz von der Startflanke bis zur Abtastung mit einem Zähler bestimmt. Üblicherweise verwendet der Empfänger einer UART die 16-fache Taktfrequenz des Senders und einen 4-Bit-Binärzähler (Abb. 3.32). Warteanweisungen für mehrere Bitzeiten lassen sich durch eine Prozedur mit einer Schleife, die entweder nach einer bestimmten Anzahl von aktiven Taktflanken oder der Aktivierung des Initialisierungssignals beendet wird, nachbilden: procedure Warte(signal T,I:std_logic; ct: inout tUnsigned(3 downto 0)) is begin loop ct := ct + "1"; wait until rising_edge(T) or I=’1’; exit when I=’1’ or ct="0000"; end loop; ⇒WEB-Projekt: P3.4/UART2_pack.vhdl end procedure;
Für eine Wartezeit von einer halben Bitzeit (acht Empfangstakte) muss der Zählstand vor dem Prozedur-Aufruf »1000« und für eine Bitzeit (sechzehn Empfangstakte) »0000« sein. Die überarbeitete Empfängerbeschreibung in Abb. 3.32 erhält genau wie die Senderbeschreibung die zusätzliche äußere Schleife »S0«, über die bei einer Abbruchanweisung für die Schleife »S1« ein Rücksprung zum Eintrittspunkt der Schleife »S1« erfolgt. Die erste Warteanweisung auf die fallende Flanke des Empfangssinals RxD wird zusätzlich an die steigende Taktflanke gebunden. Die nachfolgende Warteanweisung »warte für 1,5 Bitzeiten« wird durch zwei Aufrufe der Warteprozedur nachgebildet. Beim ersten Aufruf zählt der Zähler von "1000" bis "0000", so dass acht, beim zweiten Aufruf von "0000" bis "0000", so dass sechzehn Takte gewartet wird. Bei den weiteren Warteanweisungen wird jeweils sechzehn und bei der letzten Warteanweisung acht Takte gewartet. Nach jedem Aufruf der Warteprozedur folgt die optionale Abbruchanweisung für die Neuinitialisierung.
274
3 VHDL im Detail
procedure Empfaenger(signal T, I, RxD: std_logic; signal y: out tByte; signal ready, Err: out std_logic) is variable P: std_logic; variable ct: tUnsigned(3 downto 0); begin S0: loop wait until falling_edge(I); ready <= ’0’; Err <= ’0’; S0: Wiederhole immer warte auf Abschluss der Initialisierung S1: loop wait until (rising_edge(T) and RxD=’0’) ready <= 0; Err <= 0; S1: Wiederhole immer or I=’1’; exit S1 when I=’1’; warte auf Taktflanke und RxD = 0 ∗ ready <= ’0’; Err <= ’0’; P := ’0’; ready <= 0; Err <= 0; P := 0; ct := "1000"; warte 8 + 16 Taktperioden ∗ Warte(T, I, ct); exit S1 when I=’1’; S2: Wiederhole f¨ ur idx = 0 bis 7 Warte(T, I, ct); exit S1 when I=’1’; y(idx) <= RxD; P := P xor RxD; S2: for idx in y’reverse_range loop warte 16 Taktperioden ∗ y(idx) <= RxD; ready <= 1; P := P xor RxD; RxD = P ? nein ja Warte(T, I, ct); exit S1 when I=’1’; Err <= 1; end loop; warte 16 Taktperioden ∗ ready <= ’1’; RxD = 1? nein ja if P/=RxD then Err <= ’1’; end if; Err <= 1; Warte(T, I, ct); exit S1 when I=’1’; warte 8 Taktperioden ∗ if RxD/=’1’ then Err <= ’1’; end if; a) ct := "1000"; Warte(T, I, ct); end loop; ∗ oder bis I = 1; verlasse S1 bei I = 1 b) end procedure; ⇒Web-Projekt: P3.4/UART2 pack.vhdl Abb. 3.32. Funktionsmodell für einen synchronen seriellen Empfänger mit asynchroner Initialisierung a) Struktogramm b) VHDL-Prozedur
Erweiterung des Testrahmens Abbildung 3.33 zeigt den Signalflussplan des erweiterten Testrahmens. Die Bausteine aus dem ersten Testrahmen sind in modifizierter Form übernommen: • •
Die nebenläufigen Prozeduren zur Nachbildung des Senders und des Empfängers haben zusätzlich den Takt und das Initialisierungssignal als Eingabesignale. Der Prozess zur Erzeugung der Textausgaben soll auch die Werte der hinzugekommenen Signale aus dem Testrahmen mitprotokollieren.
Sender und Empfänger arbeiten zueinander asynchron. Auch das Start- und das Initialisierungssignal werden vom Testprozess ohne zeitliche Ausrichtung bereitgestellt. Asynchrone Eingabesignale müssen in der Regel vor ihrer internen Verarbeitung mit dem Systemtakt abgetastet werden, weil nur so für jeden möglichen Zeitversatz zwischen der Eingabeänderung und der aktiven Taktflanke für die korrekte Funktion des Systems garantiert werden kann (vgl.
3.4 Beschreibungsschablonen für digitale Schaltungen
275
x Start I
x UART- TxD Start’ Start Sender I’Send busy I TSend
Eingabeprozess
Taktgenerator Sender
TxD
Leitung
RxD
Busy
Init-Signalgenerator
RxD’ I’Empf TEmpf
y y RxD UARTEmpf¨ ang. ready Rdy Err Err I Taktgenerator Empf¨ anger
Ausgabeprozess
Abschnitt 1.5.1). Das gilt auch für das Empfangssignal RxD. Das Dateneingabesignal x für den Sender ist im Beispiel ausgenommen, weil der Eingabeprozess dessen Wert von der steigenden Startflanke bis zur fallenden BusyFlanke konstant hält. Das Initialisierungssignal muss sogar zweimal abgetastet werden, für den Sender mit dem Sendertakt und für den Empfänger mit dem Empfängertakt. Für weitere Details sei auf den Testrahmen in der Datei »P3.4/Test_UART2.vhdl auf der Projekt-Web-Seite [27] verwiesen.
...
Abb. 3.33. Signalflussplan für den Test der Sender- und Empfängerbeschreibung
Bearbeitungsstand: Das Soll-Verhalten ist bit- und taktgenau beschrieben und simuliert. Der dazu entwickelte Testrahmen mit den Testbeispielen kann für alle weiteren Beschreibungsversionen bis zur fertigen Schaltung genutzt werden. Schritt 6: Überführung in eine synthetisierbare Beschreibung In den bis zu diesem Entwurfsschritt entwickelten Ablaufmodellen fassen die Warteanweisungen jeweils die Signalzuweisungen, die im Kontrollfluss davor ausgeführt werden, zu einem Verarbeitungsschritt zusammen. Für die Synthese ist dieselbe Funktion mit einem Abtastprozess mit dem Takt und dem Initialisierungssignal in der Weckliste zu beschreiben. Dazu startet der Entwurf noch einmal von vorn mit der Nachbildung der Zielfunktion durch einen Operationsablaufgraphen. Die erforderlichen Operationen sind aus dem Programmablauf ablesbar. Für den Sender sind das • •
die Zuweisungen an das Sendesignal T xD und an das Busy-Signal, die EXOR-Verknüpfung für die Paritätsberechnung und das Weiterschalten des Schleifenzählers, der im Operationsablauf als Signal modelliert wird.
Der Operationsablauf ist gleichfalls ablesbar. Aus den Wartezuständen im Programmablauf werden Kontrollflusszustände, denen die Anweisungen davor als auszuführende Operationen zugeordnet sind. In dem Struktogramm für den Ablauf des Sendeautomaten in Abb. 3.34 gibt es vier verschiedene Wartezustände, denen im Ablaufgraph rechts die symbolischen Zustände »zStopp« für Stoppbit versenden, »zStart« für Startbit versenden etc. zugeordnet sind.
276
3 VHDL im Detail
Wiederhole immer T xD <= 1; busy <= 0;
T xD <= 1; idx <= 0;
zStopp P <= 0 busy <= 0;
T xD <= 0; idx <= 0;
Start=’1’ zStart P <= 0; busy <= 1;
warte auf Takt und Start = 1
T xD <= 0; busy <= 1; P := 0; warte auf Takt
wiederhole f¨ ur idx = 0 bis 7 T xD <= x(idx); P := P xor x(idx); warte auf Takt
T xD <= P ; warte auf Takt
Neuinitialisierung, wenn I = 1
zDaten T xD <= x(idx); P <= P xor x(idx); idx <= idx + 1; busy <= 1;
sonst
sonst
idx = 7 zParitaet T xD <= P ; P <= 0 idx <= 0; busy <= 1;
Abb. 3.34. Nachbildung der Software-orientierten Ablaufbeschreibung durch einen Operationsablaufgraphen für den seriellen Sender
Der Initialzustand ist »Stoppbit versenden«. In diesem Zustand verbleibt der Automat bis zur Aktivierung des Startsignals. Danach durchläuft er alle Zustände, versendet das Startbit, die acht Datenbits und das Paritätsbit und kehrt in den Initialzustand, in dem das Stoppbit versendet wird, zurück. Bei der Übernahme der Anweisungen aus dem Software-orientierten Ablaufmodell in den Operationsablaufgraphen gibt es kleine Erweiterungen. Das Paritätsbit P , das in der Schaltung zu einem Register wird, soll durch ein Signal beschrieben werden. Den vier Signalen T xD, P , idx und busy wird in allen, statt nur in ausgewählten Zuständen ein Wert zugewiesen. Hinter letzterem verbirgt sich eine Schaltungsoptimierung. Datenobjekte, denen in einem Zustand des Operationsablaufgraphen kein Wert zugewiesen wird, müssen ihren Wert in diesem Zustand speichern. Das erfordert zusätzliche Register oder zusätzliche Register-Transfer-Operationen vom Typ »Zustand speichern«. Wenn z.B. dem Busy-Signal wie links in dem Struktogramm nur in einem Teil der Wartezustände ein Wert zugewiesen wird, muss das Signal durch ein Register nachgebildet werden. Wenn wie rechts in jedem Zustand ein Wert zugeordnet ist, genügt eine kombinatorische Ausgabefunktion. Die Zuweisung des Wertes null an die Signale P und idx in allen Zuständen, in denen der Wert ohne Bedeutung ist, erspart den Registern in der späteren Schaltung die Freigabeeingänge. Ausgehend vom Operationsablaufgraphen erfolgt der weitere Entwurf, wie in Abschnitt 1.6.5 beschrieben. Die Übergangs- und die Ausgabefunktionen sollen dabei wie in Abschnitt 3.4.1 als VHDL-Funktionen in einem Package beschrieben werden. Für die Programmierung der Übergangsfunktion ist zuerst der Rückgabetyp – das ist der Zustand des Senders – zu vereinbaren. Der Senderzustand ist ein Verbund aus dem Kontrollflusszustand
3.4 Beschreibungsschablonen für digitale Schaltungen
277
type tZTyp is (zStopp, zStart, zDaten, zParitaet);
dem Bitzähler und dem Paritätsbit. Der Bitzähler sei ein 3-Bit-Vektor für die Darstellung vorzeichenfreier Zahlen: type tZustand is record z: tZTyp; idx: tUnsigned(2 downto 0); P: std_logic; end record;
Die Übergangsfunktion berechnet aus dem Ist-Zustand und aus den Werten der Eingabesignale Start und x den Folge-Zustand: function fz(Start:std_logic; x: tByte; z_in: tZustand) return tZustand is variable z: tZustand := z_in; begin case z.z is when zStopp => z.idx := "000"; z.P := ’0’; if Start=’1’ then z.z := zStart; end if; when zStart => z.idx := "000"; z.P := ’0’; z.z := zDaten; when zDaten => z.idx := z.idx + "1"; z.P := z.P xor x(int(z.idx)); if z.idx="000" then z.z := zParitaet; end if; when zParitaet => z.idx := "000"; z.P := ’0’; z.z := zStopp; end case; return z; end function;
Das zu versendende Signal T xD« wird aus dem Zustand und den zu versendenden Daten gebildet. Im Zustand »zStart« ist es »0«, im Zustand »zDaten« gleich dem von idx ausgewählten Datenbit, im Zustand »zParitaet« gleich dem berechneten Paritätsbit und sonst »1«: function f_TxD(x: tByte; z: begin case z.z is when zStart => return when zDaten => return when zParitaet => return when others => return end case; end function;
tZustand) return std_logic is
’0’; x(int(z.idx)); z.P; ’1’;
Das Busy-Signal ist im Zustand »zStopp« eins und sonst null: function f_Busy(z: tZustand) return std_logic is begin if z.z=zStopp then return ’1’; else return ’0’; end if; end function; ⇒WEB-Projekt: P3.4/UART2_pack.vhdl
278
3 VHDL im Detail
Die Beschreibung der Gesamtfunktion besteht aus einem Abtastprozess zur Modellierung des Zustandsregisters und der Übergangsfunktion sowie je einer nebenläufigen Signalzuweisung mit einem Funktionsaufruf für die Bildung der beiden Ausgabesignale. –- Vereinbarungen in der Entwurfseinheit signal z: tZustand; signal x: tByte; signal I_del, T, Start_del, Busy, TxD: std_logic; Sender:process(T, I_del) begin if I_del=’1’ then z <= (z=>zStopp, idx=>"000", P=>’0’); elsif rising_edge(T) then z <= fz(Start_del, x, z); end if; end process; –- nebenläufige Signalzuweisung für die Ausgabe TxD <= f_TxD(x, z); ⇒WEB-Projekt: P3.4/Test_UART3.vhdl Busy <= f_Busy(z);
An dieser Stelle soll die Demonstration des Entwurfsablaufs enden. Nach dem Entwurf des Operationsablaufgraphen, der Übergangsfunktion und der Ausgabefunktionen für den Sender würden als Nächstes dieselben Entwurfsschritte für den Empfänger folgen. Dann sind der Sender und der Empfänger als Entwurfseinheiten zu beschreiben, zu simulieren, zu synthetisieren, nochmal zu simulieren und als fertige Schaltungen zu testen. Nach dem berühmten Motto der Software-Technik Plan beats no Plan! ist auch für den Hardware-Entwurf eine Politik der kleinen Schritte ratsam. Zu Beginn des Entwurfsprozesses empfiehlt es sich, die Zielfunktion Softwareorientiert zu beschreiben, zu simulieren und solange nachzubessern, bis sie alle Anforderungen erfüllt. Dann ist schrittweise auf eine Hardware-orientierte Beschreibung bis hin zu einer synthesefähigen Beschreibung zuzuarbeiten. Nach jedem Teilschritt sollten Testschritte folgen und erkennbare Fehler beseitigt werden. Kleine überschaubare Schritte sind die Voraussetzung dafür, dass auch für größere Entwurfsprojekte der Entwurfsaufwand kalkulierbar bleibt und die entworfenen Systeme später zuverlässig arbeiten [29]. 3.4.6 Entwicklung eines CORDIC-Rechenwerks Der Sender und der Empfänger einer UART sind noch relativ kleine Schaltungen, die nur aus einigen Hundert Gattern bestehen. Das zweite Beispiel – die Entwicklung eines Rechenwerks, das für einen vorgegebenen Winkel φ
3.4 Beschreibungsschablonen für digitale Schaltungen
279
den Sinus und den Kosinus berechnet – ist eine Stufe komplexer und selbst für einen erfahrenen Entwickler kaum noch ohne Zwischenschritte mit simulierbaren Teilergebnissen zu lösen. Schritt 1: Ableitung des Algorithmus aus der Aufgabenstellung Der gebräuchliche Algorithmus für die Berechnung von Winkelfunktionen ist ein CORDIC [39]. Das Akronym CORDIC steht für Coordinate Rotation Digital Computer und beschreibt einen Algorithmus, der genau wie der Divisionsalgorithmus in Abschnitt 2.6.5 im Wesentlichen aus Additionen und Verschiebeoperationen besteht. Die Grundidee ist ein rotierender Zeiger in einem kartesischen Koordinatensystem. Wie aus Abb. 3.35 ablesbar ist, ergeben sich die Koordinaten eines rotierenden Vektors aus dem Produkt einer Rotationsmatrix mit dem Startvektor: ! ! ! xi+1 cos (φi ) − sin (φi ) xi = · yi+1 sin (φi ) cos (φi ) yi
1 yi+1 yi 0
φi α 0
xi+1 xi 1
xi = cos (α) yi = sin (α) xi+1 = cos (α + φi ) = cos (α) · cos (φi ) − sin (α) · sin (φi ) = xi · cos (φi ) − yi · sin (φi ) yi+1 = sin (α + φi ) = cos (α) · sin (φi ) + sin (α) · cos (φi ) = xi · sin (φi ) + yi · cos (φi )
Abb. 3.35. Rotation eines Zeigers der Länge eins in einem Koordinatensystem
Bei dem CORDIC-Algorithmus wird der Drehwinkel aus einer festen Anzahl konstanter Winkelschritte abnehmender Größe zusammengesetzt und jeweils der zugehörige Sinus- und Kosinus-Wert berechnet (Abb. 3.36). In jedem Schritt besteht die Möglichkeit, den nächstkleineren Winkelschritt zu addieren +φ0 −φ1 +φ2 +φ3 . . . φ
φz
+φ0 −φ1 −φ2 +φ3 . . . φ φz
0
1
2
3
Iterationsschritt
...
0
1
2
3
...
Iterationsschritt
Abb. 3.36. Schrittweise Annäherung an den Zielwinkel durch Addition oder Subtraktion konstanter Winkelschritte abnehmender Größe
280
3 VHDL im Detail
oder zu subtrahieren. Zur Berechnung von Sinus und Kosinus ist der Winkel φz vorgegeben und die Vorzeichen der Winkelkonstanten φi werden bei der Summation so gewählt, dass die Summe den Zielwinkel möglichst genau annähert. Mit einer ähnlichen Iteration werden auch die Exponentialfunktion, sinh(x) und praktisch alle anderen Winkelfunktionen berechnet. Eine ausführlichere Darstellung zum CORDIC-Algorithmus ist in [39] zu finden. Wenn ein Algorithmus in Hardware ausgelagert wird, werden vorher alle anderen Optimierungsmöglichkeiten ausgereizt. Denn anderenfalls genügt auch eine Software-Lösung. Der CORDIC führt parallel zu den n Additionen oder Subtraktionen der Winkelkonstanten φi die folgenden n Matrixmultiplikationen aus: ! ! ! ! xn cos (±φn−1 ) − sin (±φn−1 ) cos (±φ0 ) − sin (±φ0 ) 1 = · ... · · yn sin (±φn−1 ) cos (±φn−1 ) sin (±φ0 ) cos (±φ0 ) 0 Den Hauptaufwand verursachen die vier Multiplikationen mit den Konstanten cos (φi ) und sin (φi ) in jedem Iterationsschritt. Durch Ausklammern der Kosinus-Konstanten werden die Hälfte der Koeffizienten zu eins, so dass die Hälfte der Multiplikationen entfallen. Aus den Sinus-Konstanten werden Tangens-Konstanten. Der Kosinus ist eine gerade Funktion cos (φi ) = cos (−φi ) so dass das Produkt der Kosinus-Konstanten, unabhängig davon, ob die Winkel positiv oder negativ gezählt werden, auch eine Konstante ist: 0 xn yn
!
1
B n−1 C ! ! ! BY C 1 − tan (±φn−1 ) 1 − tan (±φ0 ) 1 B C =B cos (±φi ) C · · ... · · B C tan (±φn−1 ) 1 tan (±φ0 ) 1 0 @ i=0 A | {z } Konstante: SCS
Die Multiplikationen mit den Tangens-Konstanten werden durch VerschiebeOperatoren ersetzt, indem für die Tangens-Konstanten absteigende Zweierpotenzen gewählt werden: xn yn
!
1
= SCS ·
±2−n+1
∓2−n+1 1
! · ... ·
1 ∓2−1 ±2−1 1
! ·
1 ∓2−0 ±2−0 1
! ·
! 1 0
Daraus folgt für die Winkelkonstanten der Iterationsschritte: i
0
1
2
3
4
5
tan (φi ) φi
2−0 0,785
2−1 0,464
2−2 0,245
2−3 0,124
2−4 0,062
2−5 0,031
6
i>6
2−6 2−i 0,015 ≈ 2−i
3.4 Beschreibungsschablonen für digitale Schaltungen
281
Das Produkt der Kosinus-Konstanten für diese Winkelkonstanten beträgt: SCS =
n−1 Y
cos arctan 2−i
≈ 0,617
i=0
Der resultierende Algorithmus ist genial einfach. In einer Wiederholschleife für jeden Tabellenindex i wird, wenn der Zielwinkel größer als der aktuelle Winkel ist, • • •
zum x-Wert der um i Stellen vorzeichenerweitert nach rechts verschobene y-Wert addiert, vom y-Wert der um i Stellen vorzeichenerweitert nach rechts verschobene x-Wert subtrahiert und zum Winkel der Tabellenwert addiert.
Sonst, wenn der Zielwinkel nicht größer als der aktuelle Winkel ist, werden aus den beiden Additionen Subtraktionen und aus der Subtraktion eine Addition. Abschließend werden die so berechneten Zwischenergebnisse für den Sinusund den Kosinuswert mit der Konstanten SCS multipliziert (Abb. 3.37). signal φ, φz , x, y: tDaten; Wiederhole immer Eingabe φz ; φ <= 0; x <= 1; y <= 0; wiederhole f¨ ur i von 0 bis n − 1 φz > φ ? ja nein x <= x + (y sra i) x <= x − (y sra i) y <= y − (x sra i) y <= y + (x sra i) φ <= φ + AtanTab(i) φ <= φ - AtanTab(i) x <= SCS · x; y <= SCS · y; Ausgabe der Ergebnisse x sra i
Warte eine Taktperiode oder auf eine Ein- oder Ausgabe arithmetische Rechtsverschiebung, identisch mit x · 2−i
Abb. 3.37. Optimierter Algorithmus für die Berechnung von Sinus und Kosinus
Das Struktogramm in der Abbildung berechnet die Zwischenergebnisse in Signalen. Zwischen der Zuweisung an ein Signal und der Auswertung des zugewiesenen Wertes muss eine Warteanweisung liegen. Denn der Simulator aktualisiert Signalwerte nur, wenn der Prozess schläft. Der skizzierte Algorithmus benötigt deshalb nach der Eingabe, nach jedem CORDIC-Schritt und nach den Multiplikationen je einen Warteschritt. Zusätzlich wartet ein Rechenwerk im Allgemeinen auch auf die Bereitstellung neuer Eingabewerte und auf das Abholen der Ergebnisse. Die Warteanweisungen legen den Zeitablauf fest.
282
3 VHDL im Detail
Schritt 2: Ein erstes Simulationsmodell Das erste Simulationsmodell sei ein Software-orientiertes Ablaufmodell mit Texteingaben und -ausgaben und mit Warteanweisungen vom Typ »warte für eine Taktperiode«. Zu praktisch jedem Simulationsmodell gehört ein Package mit den Vereinbarungen der modellspezifischen Datentypen, Konstanten und Unterprogrammen. Der Beispielalgorithmus benötigt einen Datentyp für vorzeichenbehaftete Zahlenwerte im Bereich zwischen −2 und 2 und den zugehörigen Vektortyp für die Konstantentabelle. Für die ersten Simulationsexperimente genügen reellwertige Zahlentypen für die Wertedarstellung: subtype tDaten is real range -2.0 to 2.0; type tDatenArray is array (natural range <>) of tDaten;
Später ist der Zahlentyp durch einen geeigneten Bitvektortyp zu ersetzen. Der Verschiebeoperator »sra«16 für die n-fache Halbierung von Bitvektor-Werten ist für Zahlentypen durch die Division durch eine Zweierpotenz zu ersetzen: function "sra"(a: tDaten; b: natural) return tDaten is begin return a/(2.0**b); ⇒WEB-Projekt: P3.4/cordic1_pack.vhdl end function;
Für die Verständlichkeit, Änderungsfreundlichkeit und Fehlervermeidung ist es zu empfehlen, Konstanten durch Funktionen berechnen zu lassen, statt sie als Ziffernfolgen manuell einzutippen. Die Winkelfunktionen für reelle Zahlen, die für die Konstantenberechnungen benötigt werden, stellt das standardisierte Package ieee.math_real bereit: use ieee.math_real.cos; use ieee.math_real.atan; –- laut [11] arctan
Die Funktion zur Berechnung der Tabelle der Winkelkonstanten benötigt die Anzahl der Iterationsschritte N als Eingabeparameter und liefert ein Ergebnis vom Typ »tDatenArray«: function fAtanTab(N: positive) return tDatenArray is variable Tab: tDatenArray(0 to N-1); begin for idx in Tab’range loop Tab(idx) := atan(1.0 sra idx); end loop; return Tab; ⇒WEB-Projekt: P3.4/cordic1_pack.vhdl end function;
Die Konstante SCS wird aus der Konstantentabelle berechnet und hat den Typ »tDaten«: 16
arithmetische Rechtsverschiebung
3.4 Beschreibungsschablonen für digitale Schaltungen
283
function fSCS(Tab: tDatenArray) return tDaten is variable w: tDaten := 1.0; begin for idx in Tab’range loop w := w * cos(Tab(idx)); end loop; return w; ⇒WEB-Projekt: P3.4/cordic1_pack.vhdl end function;
Für die Untersuchung, ob der Algorithmus richtig funktioniert, genügt ein einzelner Prozess in einem Testrahmen, der zur Eingabe eines Winkels auffordert, den Sinus- und den Kosinuswert berechnet und der die Ergebnisse auf dem Bildschirm ausgibt: –- Vereinbarungen im Testrahmen signal x, y, phi: tDaten; constant tP: delay_length := 10 ns; constant cAtanTab: tDatenArray := fAtanTab(16); constant SCS: tDaten := fSCS(cAtanTab); ... process variable phi_z: tDaten; begin read("Winkel im Bereich -pi/4 bis pi/4 eingeben", phi_z); x <= 1.0; y <= 0.0; phi <= 0.0; wait for tP; –- Warte einen Takt nach der Eingabe for idx in cAtanTab’range loop if phi_z>phi then x <= x - (y sra idx); y <= y + (x sra idx); phi <= phi + cAtanTab(idx); else x <= x + (y sra idx); y <= y - (x sra idx); phi <= phi - cAtanTab(idx); end if; wait for tP; –- Warte einen Takt nach jedem CORDIC-Schritt end loop; x <= SCS * x; y <= SCS * y; wait for tP; –- Warte einen Takt nach den Multiplikationen write("Ergebnis phi=" & str(phi) & " cos=" & str(x ) & " sin=" & str(y)); write("Soll-Werte: phi=" & str(phi_z) & " cos=" & str(cos(phi_z) & " sin=" & str(sin(phi_z)); ⇒WEB-Projekt: P3.4/cordic1.vhdl end process;
Der Testrahmen gibt außer den berechneten Ergebnissen auch den Eingabewert des Winkels und die mit den Package-Funktionen aus ieee.math_real berechneten Kosinus- und Sinus-Werte aus. Zur Kontrolle der Zeitabläufe können die drei Signale des Simulationsmodells »x«, »y« und »phi« auch graphisch darstellt werden.
284
3 VHDL im Detail
Schritt 3: Ein Festkommaformat für die Wertedarstellung Für die Synthese muss der reellwertige Zahlentyp, mit dem im bisherigen Simulationsmodell die Daten dargestellt werden, durch einen geeigneten Bitvektortyp ersetzt werden. Alle Operanden und Zwischenergebnisse liegen bei dem gewählten Algorithmus im Bereich zwischen ±1,7. Es bietet sich ein Format für vorzeichenbehaftete ganze Zahlen mit einem gedachten Komma hinter den führenden zwei Bits an. Der damit darstellbare Wertebereich von 10,0...02 = −2 bis 01,1...12 = 2 − 2−14 ist ausreichend. Der Datentyp wird umdefiniert in subtype tDaten is tSigned(15 downto 0); type tDatenArray is array (natural range <>) of tDaten;
Für den Typ »tDaten« sind als Nächstes die erforderlichen Hilfsfunktionen und Prozeduren zu programmieren. Die Zahlenwerte sollen wie in dem ersten Modell als dezimale Festkommawerte auf dem Bildschirm angezeigt werden. Nach der Konvertierung in eine ganze und weiter in eine Gleitkommazahl ist der Wert vor der Str-Konvertierung mit 2−14 zu multiplizieren. Für vierzehn binäre Nachkommastellen genügen vier dezimale Nachkommastellen: function str_real(x: tDaten) return string is begin return str(real(int(x))/(2.0**14), 4); ⇒WEB-Projekt: P3.4/cordic2_pack.vhdl end function;
Für die Konvertierung der reellwertigen Konstanten in die Festkommadarstellung ist der Wert zuerst mit 214 zu multiplizieren, in eine ganze Zahl und dann weiter in einen 16-Bit-Vektor vom Typ »tSigned« zu konvertieren. Die Assert-Anweisung kontrolliert, dass die Eingabewerte im darstellbaren Bereich liegen: function to_tDat(w: real) return tDaten is begin assert w>=-2.0 and w<=1.9999 report "Wertebereichsverletzung" severity failure; return to_tSigned(integer(w * 2.0**14), 16); end function; ⇒WEB-Projekt: P3.4/cordic2_pack.vhdl
Mit Hilfe dieser Konvertierungsfunktion werden unter anderem auch die Konstanten in den Funktionen »fATanTab(...)« und »fSCS(...)« in ihre Festkommadarstellung konvertiert. Bei einer Multiplikation der Festkommazahlen vom Typ »tDaten« verdoppelt sich die Anzahl der Vorkommastellen auf vier und die der Nachkommastellen auf 28. Zur Konvertierung des Ergebnisses in das Darstellungsformat von »tDaten« werden die zusätzlichen Vorkommaund Nachkommastellen wieder abgeschnitten:
3.4 Beschreibungsschablonen für digitale Schaltungen
285
function mult(a, b: tDaten) return tDaten is constant y: tSigned(2 * tDaten’length-1 downto 0) := a * b; begin return y(y’high - 2 downto y’high-tDaten’length - 1); end function; ⇒WEB-Projekt: P3.4/cordic2_pack.vhdl
Im Testrahmen selbst ist das Package auszutauschen (siehe Web-Projekt CORDIC/cordic2.vhdl). Die Konstanten und Eingabewerte sind vor ihrer Verarbeitung in den neu definierten Bitvektortyp zu konvertieren. Die Multiplikationsoperatoren sind durch die Mult-Funktion und die Str-Funktionen für die Ausgabedaten durch die neu definierte Funktion »str_real(....)« zu ersetzen. Das überarbeitete Simulationsmodell verhält sich fast wie das erste. Nur die Ergebnisse sind bei einer Berechnung mit nur 16 Bit etwas ungenauer. Schritt 4: Ablaufoptimierung In der nächsten Nachbesserungsiteration sollen Hardware- und Zeitaufwand genauer betrachtet werden. Den größten Schaltungsaufwand erfordern, falls in Hardware realisiert, die beiden abschließenden 16-Bit-Multiplikationen. Da eine Multiplikation auch durch bitverschobene bedingte Additionen nachgebildet werden kann, bietet es sich im Beispiel an, die vorhandenen Addierer und Shifter auch für die Multiplikationen zu nutzen. Abbildung 3.38 zeigt die Struktogrammerweiterung um eine Schleife mit bedingten Additionen und den zugehörigen VHDL-Code. Der Wert der Konstanten SCS liegt zwischen 0,5 und eins. Das größte Bit, das eins ist, ist SCS (13) mit dem Stellenwert 0,5. Die Register für die Akkumulation der Produkte werden entsprechend mit dem halbierten ersten Faktor »x sra 1« bzw. »y sra 1« initialisiert. Dann wird in einer Schleife für alle niederwertigeren Bits von SCS, falls diese eins sind, der signal px, py: tDaten; constant SCS: tDaten := ... ... px <= x sra 1; py <= y sra 1; wait for tP; for idx in 12 downto 0 loop if SCS(idx)=’1’ then px <= px + (x sra (14-idx)); py <= py + (y sra (14-idx)); end if; wait for tP; end loop;
Wiederhole immer Eingabe, CORDIC-Schritte ... px <= x sra 1; py <= y sra 1; wiederhole f¨ ur idx = 0 bis 12 SCS(idx) = 1? ja
nein
px <= px + (x sla (14 − idx)); py <= py + (y sla (14 − idx)); Ausgabe warte f¨ ur einen Takt ⇒Web-Projekt: P3.4/cordic3.vhdl
Abb. 3.38. Nachbildung der abschließenden Multiplikationen mit der Konstanten SCS durch Additionen und Verschiebeoperationen
286
3 VHDL im Detail
um 14 − idx Stellen arithmetisch (vorzeichenerweitert) nach rechts verschobene erste Faktor addiert. Der Preis für die Einsparung der beiden Multiplizierer sind vierzehn zusätzliche Berechnungsschritte. Das heißt aber nicht unbedingt, dass die Berechnung so viel länger dauert. Denn ohne die Multiplizierer wird die Schaltung kleiner und einfacher und lässt sich möglicherweise mit einer höheren Taktfrequenz betreiben. Unser Entwurfsbeispiel endet hier, obwohl es noch nicht abgeschlossen ist. Es gibt weitere Optimierungsmöglichkeiten. Die Signale und Zeitabläufe für die Übernahme der Eingabedaten und die Übergabe der Ergebnisse sind noch zu definieren und in die Modellabläufe einzupassen. Die Warteanweisungen sind an Takten auszurichten etc. und zum Abschluss ist die ablauforientierte Funktionsbeschreibung in eine synthesefähige Beschreibung zu transformieren. Selbst für einen erfahrenen Entwickler ist ein Hardware-Entwurf mit der Komplexität eines CORDIC-Rechenwerks (mehrere Tausend Gatter) kaum noch ohne Zwischenschritte mit simulierbaren Teilergebnissen zu lösen. 3.4.7 Zusammenfassung und Übungsaufgaben Ein fehlerarmer Entwurf mit einem kalkulierbaren Aufwand verlangt ein planvolles Vorgehen. Komplexe Entwürfe sind sowohl strukturell als auch im zeitlichen Ablauf in separat zu testende Beschreibungen aufzuspalten. Für den zeitlichen Entwurfsablauf empfiehlt es sich, die Zielfunktionen zuerst wie eine normale Software zu beschreiben und zu simulieren, dann mit Warteanweisungen die zeitlichen Abläufe einzubauen und dann iterativ die Abläufe, die Operationen und die Datenstrukturen zu optimieren. Die so entwickelten Algorithmen werden anschließend über ihre Operationsablaufgraphen in synthesefähige Beschreibungen überführt, simuliert, synthetisiert, wieder simuliert und nach der Umsetzung in die Hardware getestet. Für die strukturelle Aufteilung der Beschreibungen in den Anfangsphasen des Entwurfsprozesses empfiehlt sich eine Auslagerung der Typvereinbarungen, der Beschreibungen von kombinatorischen Funktionen und der Beschreibungen von Testhilfen in Packages. Auch eine objektorientierte Modellentwicklung ist für Hardware in einem begrenzten Maße möglich und sinnvoll. Die Berechnungsabläufe für die kombinatorischen Funktionen sollten sich an der angestrebten Zielstruktur der Schaltungsnachbildung orientieren. So sollten Schleifen zur Beschreibung kombinatorischer Funktionen möglichst einen baumartigen statt einen kettenartigen Berechnungsfluss haben. Weiterführende und ergänzende Literatur siehe [11, 16, 39, 45]. Aufgabe 3.14 Entwerfen Sie eine VHDL-Funktion
3.4 Beschreibungsschablonen für digitale Schaltungen
287
function AnzahlEinsen(x: std_logic_vector, N: natural) return tUnsigned;
zur Nachbildung einer kombinatorischen Funktion, die für den Eingabevektor »x« die Anzahl der Einsen zählt und den Wert in einem N -Bit-tUnsignedVektor zurückgibt, a) mit einem kettenartigen Berechnungsfluss und b) mit einem baumartigen Berechnungsfluss17 . c) Skizzieren Sie die aus den Berechnungsflüssen in den Aufgabenteilen a und b resultierenden Datenflüsse für N = 7. Schreiben Sie an alle Signale die tatsächlich erforderliche Bitbreite. Aufgabe 3.15 In Abschnitt 4.4.3 wird später eine Schaltungsstruktur für Assoziativspeicher behandelt. Ein Assoziativspeicher besitzt wie ein normaler RAM eine Leseund eine Schreibmethode. Zusätzlich besitzt er eine Suchmethode function Suche(Mem: tMem; x: tDaten) return tSuchergebnis;
mit type tSuchergebnis is record hit: std_logic; –- ’1’ wenn der Wert enthalten ist adr: tAdr; –- Speicheradresse mit dem Wert end record;
die, wenn der Wert von x im Speicher enthalten ist, in hit den Wert »1« und in adr die Adresse des ersten Speicherplatzes, in dem der Wert steht, und sonst hit = 0 und den Adresswert null zurückgibt. Entwickeln Sie eine Funktionsbeschreibung für diese Methode. Aufgabe 3.16 Bestimmen Sie mit dem Testrahmen »P3.4/cordic1.vhdl« aus Abschnitt 3.4.6 für den Winkelwert φ = 1,2 die Abweichungen zwischen den berechneten und den tatsächlichen Winkel-, Kosinus- und Sinus-Werten für eine Iterationsschrittanzahl von N =4 , N = 8, N = 12 und N = 16. 17
Die Summanden und Teilsummen sollen alle dieselbe Bitbreite N wie der Rückgabewert haben.
288
3 VHDL im Detail
3.5 Methoden und Funktionsbausteine für den Test
Bereitstellung der Testeingaben
Ein Test ist ein Funktionsablauf, bei dem das Testobjekt mit Eingabewerten stimuliert wird und seine Ausgaben auf Richtigkeit kontrolliert werden (Abb. 3.39). Die Testeingaben können für den gezielten Nachweis von erwarteten Fehlern berechnet, nach einem Vollständigkeitskriterium ausgewählt oder zufällig ausgewählt sein. Vorab berechnete Testeingaben werden in der Regel in einer Datei bereitgestellt. Für die beiden anderen Arten der Testauswahl werden die Testeingaben im Testrahmen algorithmisch erzeugt.
Auswertung der Simulationsergebnisse
Testobjekt K Testablaufsteuerung
Fehlermeldungen und Warnungen K
Kontrollfunktion
Abb. 3.39. Allgemeiner Signalfluss in einen Testrahmen
Die berechneten Signalverläufe werden bei der Simulation aufgezeichnet und können anschließend visualisiert und manuell ausgewertet werden. Eine manuelle Inspektion ist jedoch nur für einen Grobtest mit wenigen Testbeispielen geeignet. Gründliche Tests mit Tausenden oder Millionen von Einzeltests verlangen eine automatische Testauswertung. Dafür gibt es folgende Prinzipien: • • • •
Soll-Ist-Vergleich, Mehrversionsvergleich, Probe und Plausibilitätstests.
Ein Soll-Ist-Vergleich ist ein Vergleich der Testausgaben mit vorab berechneten Soll-Werten. Das setzt bekannte Testeingaben voraus. Bei sehr vielen Testbeispielen werden die Soll-Werte genauso wie die zugehörigen Testeingaben in der Regel aus einer Datei bereitgestellt. Mehrversionsvergleich bedeutet, dass mehrere unterschiedliche Beschreibungsversionen derselben Zielfunktion mit denselben Eingaben stimuliert und ihre Ausgaben miteinander verglichen werden. Die Referenzschaltung bzw. ihre Beschreibung kann genau wie das Testobjekt Fehler enthalten. Zur Vermeidung übereinstimmender falscher Ergebnisse, die ein Mehrversionsvergleich prinzipbedingt nicht erkennt, sollte das Referenzobjekt entweder seine Ergebnisse nach einem anderen Algorithmus bilden oder es sollte unabhängig vom Testobjekt und von einer anderen Person entworfen worden sein. Bei dem
3.5 Methoden und Funktionsbausteine für den Test
289
Mehrversionsvergleich in Abschnitt 3.4.6 berechnet z.B. das Testobjekt die Sinus- und Kosinus-Werte mit dem CORDIC-Algorithmus und die Vergleichsergebnisse werden mit der Sinus- und der Kosinusfunktion aus dem Package ieee.math_real gebildet. Eine Probe ist ein Kontrollalgorithmus, der feststellen kann, ob ein Ergebnis richtig ist. Im Abschnitt 3.4.5 könnte die Testauswertung für den UARTSender z.B. so automatisiert werden, dass die gesendeten Daten mit dem Empfänger zurückgewonnen und mit den ursprünglichen Werten verglichen werden. Ein Plausibilitätstest ist eine Kontrolle der Ergebnisse auf Zulässigkeit. Bei einer Verletzung eines Plausibilitätskriteriums ist das Ergebnis immer falsch. Wenn alle Plausibilitätskriterien erfüllt sind, ist das Ergebnis zulässig, aber nicht unbedingt richtig. Der Test auf Gültigkeit, der in vielen der bisherigen Testrahmen durchgeführt wird, ist z.B. ein Plausibilitätstest. Die Auswertung und Weiterverarbeitung ungültiger Signalwerte ist immer ein Fehler. Gültige Werte können, aber müssen nicht richtig sein. Auch die Kontrolle des empfangenen Paritätsbits und des empfangenen Stoppbits in Abschnitt 3.4.5 sind Plausibilitätstests, die einen Teil der potenziellen Übertragungsfehler erkennen, aber nicht alle. 3.5.1 Pseudo-Zufallstest und Mehrversionsvergleich Eine gebräuchliche Lösung für den gründlichen Test von Schaltungen und Entwurfsbeschreibungen ist die Stimulierung mit Tausenden oder Millionen von zufälligen Eingaben und ein Mehrversionsvergleich für die Ausgabe (Abb. 3.40). Der Testrahmen benötigt außer dem Testobjekt und dem Referenzmodell einen Pseudo-Zufallsgenerator, Signalquellen für den Takt- und das Initialisierungssignal und einen Funktionsbaustein, der die Testeingaben und die Testausgaben, bei denen Vergleichsfehler auftreten, in eine Datei oder auf den Bildschirm schreibt. Die testspezifischen Funktionsbausteine sollen als nebenläufige Prozeduren entwickelt werden. Das Beispieltestobjekt sei die Beschreibung des 7-Segment-Decoders durch logische Ausdrücke aus Abschnitt 2.2.4 und das Referenzobjekt die Beschreibung derselben Schaltung mit einer Auswahlanweisung.
x PseudoZufallsgenerator
Testobjekt Referenzobjekt
yist ysoll
Vergleich und Protokollierung der Vergleichsfehler
T, I T, I T, I Signalquelle f¨ ur den Takt und das Initialisierungssignal
T I x yist ysoll
Takt Initialisierungssignal Eingabesignal Ist-Ausgabe Soll-Ausgabe
Abb. 3.40. Testrahmen für einen Pseudo-Zufallstest mit Mehrversionsvergleich
290
3 VHDL im Detail
Pseudo-Zufallsgenerator-Prozedur Pseudo-zufällig bedeutet, dass die Auswahl Zufallscharakter hat, aber dass bei einer Wiederholung exakt dieselben Signalverläufe erzeugt werden. Eine zufällige Testauswahl hat unter anderem den Vorteil, dass die so ausgewählten Testbeispiele vorrangig die Fehler finden, die die Verlässlichkeit am stärksten beeinträchtigen [29]. Reproduzierbarkeit ist wichtig für die sich an den Test anschließende Fehlerlokalisierung, falls die Ausgaben des Testobjekts und des Referenzobjekts voneinander abweichen. Die vordefinierte VHDL-Prozedur zur Erzeugung von Pseudo-Zufallszahlen ist procedure uniform(seed1, seed2: inout positive; x: out real);
aus dem standardisierten Package math_real (vgl. Abschnitt 3.1.5). Sie erzeugt aus den zwei ganzzahligen positiven Parametern »seed1« und »seed2«, die den Zustand des Pseudo-Zufallsgenerators beschreiben, zwei pseudo-zufällige Folgewerte und einen Ausgabewert x im Bereich 0 ≤ x ≤ 1. Die zu entwerfende Prozedur soll ein Takteingabesignal haben und ein pseudo-zufälliges Bitvektorsignal vom Typ std_logic_vector mit einem erst beim Aufruf festzulegenden Indexbereich erzeugen. Zusätzlich zu den beiden Signalen sollen der Prozedur über ihre Aufrufschnittstelle optional drei Parameter übergeben werden, ein Wichtungsvektor w und zwei positive Zahlenwerte. Der Wichtungsvektor hat den Typ subtype tWeight is real range 0.0 to 1.0; type tWeightArray is array (natural range <>) of tWeight;
und legt die Auftrittshäufigkeit des logischen Wertes »1« für die einzelnen Ausgabebits fest18 . Die beiden positiven Zahlenwerte bilden den Anfangszustand des internen mit der Prozedur »uniform(...)« beschriebenen PseudoZufallsgenerators. Die Prozedurbeschreibung lautet wie folgt: procedure RTPG(signal T: std_logic; signal y: out std_logic_vector; w: tWeightArray := (0=>0.5); seed1, seed2: positive := 1) is variable ww: tWeightArray(y’range) := (others=>0.5); variable yy: std_logic_vector(y’range); variable s1: positive := seed1; variable s2: positive := seed2; variable p: tWeight; begin if w’length=y’length then ww := w; end if; loop for idx in y’range loop math_real.uniform(s1, s2, p); if p<ww(idx) then yy(idx) := ’1’; 18
Die Wichtung ist ein Mittel, um die Fehlerüberdeckung eines PseudoZufallstestsatzes bei gleicher Testsatzlänge deutlich zu erhöhen [22, 49].
3.5 Methoden und Funktionsbausteine für den Test else yy(idx) := ’0’; end if; end loop; y <= yy; wait until rising_edge(T); end loop; end procedure;
PseudoZufallsgenerator (RTPG)
291
y
T ⇒Web-Projekt: P3.5/MVTest pack.vhdl
Nebenläufig aufgerufen legt die Prozedur intern Variablen für den Gewichtsvektor, für den auszugebenden Bitvektor für den mit uniform(...) verarbeiteten Zustand an. Der Gewichtsvektor hat einen Standardwert. Falls dieser nicht überschrieben wird, werden alle Bits mit 0,5 gewichtet. Das heißt, es wird für jedes Bit mit einer Wahrscheinlichkeit von 0,5 eine »1« und mit derselben Wahrscheinlichkeit eine »0« ausgewählt. Falls beim Aufruf ein Wichtungsvektor der richtigen Länge vorgegeben wird, überschreibt dieser den Initialwert und legt für jedes Ausgabebit des Pseudo-Zufallsgenerators eine individuelle Wichtung fest. Der Anweisungsteil besteht aus einer Endlosschleife, in der in jedem Schleifendurchlauf ein Pseudo-Zufallsvektor berechnet, ausgegeben und auf die nächste aktive Taktflanke gewartet wird. Taktgenerator Simulationsmodelle für Taktgeneratoren wurden in den vergangenen Abschnitten bereits mehrfach verwendet. Der hier benötigte Taktgenerator soll für eine Simulationszeit tsim ein Taktsignal mit der Periode TP erzeugen. Der Beschreibungsrahmen sei wieder eine nebenläufige Prozedur, diesmal mit dem Takt als Ausgabesignal und der Simulationszeit und der Taktperiode als Eingabeparameter: procedure TaktGen(signal T: out std_logic; tP, tsim: delay_length) is begin T <= ’0’; while now
Nebenläufig aufgerufen setzt die Prozedur den Takt zum Simulationsbeginn auf »0« und weist ihm dann in einer Schleife bis zum Simulationsende immer nach der halben Taktperiode abwechselnd die Werte »1« und »0« zu. Die Prozedur hat kein Eingabesignal, so dass, wenn sie den Kontrollfluss abgibt, der einrahmende Prozess terminiert.
292
3 VHDL im Detail
Vergleicher mit Protokollierung der Vergleichsfehler Der Vergleicher soll synchron zum Takt arbeiten. Er soll bei jeder aktiven Taktflanke kontrollieren, dass das überwachte Ausgabesignal des Testobjekts während der Vorhaltezeit ts stabil war. Wenn der Ausgabewert des Referenzobjekts gültig ist, soll er zusätzlich kontrollieren, dass der Ausgabewert des Referenzobjekts mit dem des Testobjekts übereinstimmt. Verletzte Vorhaltebedingungen und Vergleichsfehler sind zu protokollieren. Für die Prozedur zur Nachbildung dieses Verhaltens sind der Takt, das gemeinsame Eingabesignal und die getrennten Ausgabesignale des Test- und des Referenzobjekts alles Eingabesignale. Die zu kontrollierende Vorhaltezeit ist ein normaler Eingabeparameter: procedure Vergleicher(signal T: std_logic; signal x, y_ist, y_soll: std_logic_vector; ts: delay_length) is variable TNr: positive; begin write("Verletzte Vorhaltebedingungen und Vergleichsfehler:"); loop wait until rising_edge(T); if y_ist’last_event
3.5 Methoden und Funktionsbausteine für den Test
295
if pstr.Status/=ok then write("Lesefehler Zeile: " & str(TNr) &" Spalte: " & str(pstr.err_pos) & " " & str(pstr.err_msg)); exit; end if; –- Takt weiterschalten wait for tp/2; T <= ’0’; wait for tp/2; T <= ’1’; –- Kontrolle der Vorhaltezeit if y’last_event
296
3 VHDL im Detail
Nach dieser Schablone lässt sich praktisch jede kombinatorische Schaltung mit einer beliebigen Anzahl von Ein- und Ausgabebits und auch jede sequenzielle Schaltung mit einem zusätzlichen Takt- und Initialisierungseingang testen. Dazu müssen im Wesentlichen nur der Entity- und der ArchitectureBezeichner des Testobjekts und die Datei mit den Testdaten ausgetauscht werden. 3.5.3 Spezifikationstest Die Entwicklung größerer Systeme beginnt mit der Spezifikation der Anforderungen. Kontrollierbare Anforderungen sind gleichzeitig Kontrollkriterien für den Test. Für wenige Testbeispiele lassen sich die spezifizierten Anforderungen manuell kontrollieren, für gründliche Tests mit Tausenden von Testbeispielen sind die Kontrollen mit in den Testrahmen einzuprogrammieren. Spezifikationstests sind in der Regel nur Plausibilitätstests, die die berechneten Signalverläufe auf Zulässigkeit, aber nicht auf Richtigkeit kontrollieren [29]. Im nachfolgenden Beispiel wird die Steuerung einer Fußgängerampel spezifiziert. Aus der Spezifikation werden drei Arten von Plausibilitätstests abgeleitet: • • •
die Kontrolle der Ausgabewerte, die Kontrolle der Dauer der Ampelphasen und die Kontrolle der Vorgänger der Ausgabewerte
auf Zulässigkeit. Anschließend werden alle diese Plausibilitätstests zu einer nebenläufigen Überwachungsprozedur zusammengefasst. Eine Fußgängerampelsteuerung steuert zwei Autoampeln und zwei Fußgängerampeln. Die Autoampeln vor und nach dem Fußgängerübergang haben je eine rote, eine gelbe und eine grüne Lampe, die paarweise von denselben Signalen angesteuert werden. Auch die roten und grünen Lampen der Fußgängerampeln auf beiden Straßenseiten werden gleich angesteuert. Die Steuerung muss insgesamt einen 5-Bit-Ausgabevektor liefern. Der Datentyp der zu überwachenden Ausgabe sei: subtype tAusgabe is std_logic_vector(4 downto 0);
(Zuordnung von links nach rechts: Autos rot, gelb, grün; Fußgänger rot, grün). Es wird die einfachste Form einer Ampelsteuerung ohne Phasen für »rot-gelb« und »grün-gelb« betrachtet. Mit dieser Vereinfachung sind von den 25 darstellbaren Ausgabewerten nur die fünf in Abb. 3.43 als Konstanten definierten Werte zulässig. Die Kontrolle erfolgt mit einer Wertebereichsüberwachung. Abbildung 3.44 zeigt einen parametrisierten Zeitablauf der Ampelphasen. Die Autos haben im Normalfall grün und die Fußgänger rot. Wenn ein Fußgänger die Taste an der Ampel drückt, schaltet die Ampel nacheinander die Ausgabe von grün-rot über gelb-rot, rot-rot nach rot-grün um. Parameter
3.5 Methoden und Funktionsbausteine für den Test gn_rt: ge_rt: rt_rt: rt_gn: Start:
tAusgabe := "00110"; tAusgabe := "01010"; tAusgabe := "10010"; tAusgabe := "10001"; tAusgabe := "00000";
rt ge gn
gn rt ge rt rt rt rt gn Start
Fußg. Auto
constant constant constant constant constant
297
rt gn
y4 y3 y2 y1 y0
Abb. 3.43. Zulässige Ausgaben der Ampelsteuerung
sind die Dauer der einzelnen Ampelphasen. In Abb. 3.44 sind dafür Oberund Untergrenzen spezifiziert. Ferner soll gelten, dass Autofahrer nicht länger als für eine Zeit tWAmax und Fußgänger nach Betätigen der Fußgängertaste nicht länger als eine Zeit tWFmax auf grün warten sollen. Die Überwachung der zulässigen Zeitfenster ist der zweite Typ von Plausibilitätstests, die durchgeführt werden sollen. Weiterhin lässt sich aus den Beispielabläufen ablesen, dass jede Ampelausgabe eine begrenzte Menge zulässiger Vorgänger hat. Das sind gleichfalls überprüfbare Plausibilitätskriterien. Taste Autos
1 0
gn rt gr¨ un
Fußrot g¨ anger Mindestdauer tA Maximaldauer Startzustand
ge rt gelb
rt rt rot
rt gn rot
rt rt rot
rot
rot
gr¨ un
rot
rot
rot
tmin tmax
tmin tmax
0,9 · tF 1,1 · tF
tmin tmax
tmin tmax
t
tWF ≤ tWFmax
ge rt gn rt gelb gr¨ un
tWA ≤ tWAmax
Auto Fußg. Ausgabewert zulässige Vorgänger Mindestdauer Maximaldauer tA
-
01010 (ge_rt) gn_rt, rt_rt, Start
tmin
tmax
10010 (rt_rt)
ge_rt, rt_gn
tmin
tmax
rt_rt
0,9 · tF
1,1 · tF
-
-
tmax
grün
rot 00110 (gn_rt)
gelb
rot
rot
rot
rot -
grün 10001 (rt_gn) -
00000 (Start)
ge_rt
Abb. 3.44. Spezifikationsvorgaben für die Ampelsteuerung
Eine unerwünschte, aber auch nicht zu vermeidende Eigenschaft jeder digitalen Schaltung und damit auch der Ampelsteuerung ist, dass die Ausgabesignale bei jedem Wechsel zwischen zwei zulässigen Werten kurzzeitig ungültig sein können. Die Spezifikation kann nur die zulässige Dauer tX , die die Werte ungültig sein dürfen, auf einen hinreichend kleinen Wert begrenzen. Ungültig bedeutet, dass nichts über den Werteverlauf vorhersagbar ist, nicht welches Bit sich zuerst ändert, nicht, ob Glitches auftreten und auch nicht welche Zwischenwerte der Signalvektor annehmen kann. Das erschwert die
298
3 VHDL im Detail
Programmierung der Plausibilitätstests für eine Werte- und Zeitüberwachung erheblich. Ein einfacher Workaround ist eine nebenläufige Signalvektorzuweisung mit dem Standardverzögerungsmodell und der Verzögerungszeit tX vor der Überwachung: yy <= y after tX;
Diese Zuweisung löscht alle Impulse mit einer Länge ≤ tX und weist allen Bits des verzögerten Signalvektors ihre Änderungen zeitgleich zu. Das so aufbereitete Auswertesignal ist für korrekte Testobjektausgaben frei von ungültigen Zwischenwerten und dadurch deutlich einfacher zu überwachen. Die nächste Aufgabe ist die Entwicklung der nebenläufigen Überwachungsprozedur. Eingabesignale sind das verzögerte Ausgabesignal der Steuerung und das Tastensignal. Zusätzliche Eingabeparameter, die als Konstanten übergeben werden, sind die Zeitparameter aus der Tabelle in Abb. 3.44. Die Überwachungsprozedur soll eine Endlosschleife enthalten, damit sie den Kontrollfluss während der Simulation nicht mehr abgibt und so Zwischenergebnisse in lokalen Variablen speichern kann. In der Variablen tWA wird der letzte Zeitpunkt gespeichert, zu dem die Autos grün hatten, in tWF die Zeit seit der ersten Tastenbetätigung nach der letzten Phase »Fußgänger grün«, in tz der Anfangszeitpunkt der aktuellen Ampelphase, in vgy der Vorgänger des aktuellen Ausgabewertes und in Flag, ob seit der letzten Phase »Fußgänger grün« die Taste bereits betätigt wurde oder nicht. Die Anweisungsfolge innerhalb der Endlosschleife wird zum Simulationsbeginn und nach jeder Eingabesignaländerung erneut abgearbeitet. Nach einer Änderung des verzögerten Ampelsignals y wird in einer ersten Fallunterscheidung die Dauer der vorherigen Phase auf Zulässigkeit überprüft. Die zweite Fallunterscheidung kontrolliert für den aktuellen Ausgabewert, ob der vorherige Ausgabewert ein zulässiger Vorgänger war. Der aktuelle Ausgabewert wird dabei automatisch mit auf Zulässigkeit überprüft. Beim Wechsel zur Ausgabe »Autos grün« wird zusätzlich die Zeit seit der letzten Phase »Autos grün« und bei einem Wechsel in die Phase »Fußgänger grün« die Zeit seit der ersten Tastenbetätigung nach der letzten Phase »Fußgänger grün« auf Überschreitung des zulässigen Maximalwertes hin getestet. Alle Spezifikationsverletzungen – auch unzulässige Werte – werden als Textausgaben protokolliert. I procedure SpecTest_Ampel(signal y: tAusgabe; AmpelAuto T signal Taste: std_logic; ta, tF, tmin, steuerung Taste tmax, twa_max, twf_max: delay_length) is variable twa, twf, tz: delay_length; SpecTest variable vgy: tAusgabe; –- Vorgängerausgabe Fußg. Ampel(...) variable Flag: boolean; –- Tastendruck erkannt begin Protokollierung vgy := y; verletzter Spezifiwait on y; kationsvorgaben loop if not Flag and rising_edge(Taste) then Flag:=true; twf:=now; end if;
3.5 Methoden und Funktionsbausteine für den Test
299
if y’event then tz := tz-now; case vgy is –- Zeitfensterkontrolle der Ampelphasen when gn_rt => twa := now; if tz
if tztmax then write(str(now) & " Dauer rt_gn: " & str(tz)); end if; when rt_rt => if tztmax then write(str(now) & " Dauer rt_gn: " & str(tz)); end if; when rt_gn => Flag := false; if tz<0.9*tF or tz>1.1*tF then write(str(now) & " Dauer rt_gn: " & str(tz)); end if; when Start => if tz>tmax then write(str(now)&" Dauer Start:"& str(tz));end if; when others => null; end case; case y is –- Kontrolle der Vorgänger und Wartezeit auf grün when gn_rt => if not(vgy=ge_rt) then write(str( now ) & " Vorgänger von gn_rt: " & str(vgy)); end if; if twa-now>twa_max then write(str( now)&" Autowartezeit auf gn_rt:"&str(twa)); end if; when ge_rt => if not(vgy=gn r _t or vgy=rt r _t or vgy=Start) then write(str(now)&" Vorgänger von ge_rt: "&str(vgy)); end if; when rt_rt => if not(vgy=ge_rt or vgy=rt_gn) then write(str(now)&" Vorgänger von rt_rt: "&str(vgy)); end if; when rt_gn => if not(vgy=rt r_t) then write(str(now)&" Vorgänger von rt_gn: "&str(vgy)); end if; if twf-now>twf_max then write(str(now) & " Fussgängerwartezeit auf rt_gn:" & str(twf)); end if; when Start => null; when others => write(str(now)&" unzulässiger Ausgabewert"); end case; vgy := y; end if; if not Flag and rising_edge(Taste) then Flag:=true; twf:=now; end if; tz := now; wait on y, Taste; end loop; ⇒WEB-Projekt: P3.5/SpecTest_pack.vhdl end procedure;
Die entwickelte Prozedurbeschreibung zeigt, dass die Programmierung spezifikationsbasierter Plausibilitätstests recht aufwändig sein kann. Aber die Alternative, alle diese Plausibilitätskriterien für Tausende von Testbeispielen manuell zu kontrollieren, ist noch viel aufwändiger. Gründliches Testen hat einen hohen Preis.
300
3 VHDL im Detail
3.5.4 Zusammenfassung und Übungsaufgaben Ein gründlicher Test erfordert Tausende oder Millionen von Testbeispielen. Das verlangt eine Automatisierung der Bereitstellung der Testeingaben und eine automatische Überwachung der Ausgabesignale während des Tests. Nach einem kurzen Überblick über die Möglichkeiten, die es dafür gibt, wurde für die Testlösungen • • •
Pseudo-Zufallstest mit Mehrversionsvergleich, Test mit Eingaben und Soll-Ausgaben aus einer Datei und spezifikationsbasierte Plausibilitätstests am Beispiel der Kontrolle der Ansteuersignale für eine Ampel
gezeigt, wie sie in VHDL programmiert werden können. Weiterführende und ergänzende Literatur siehe [12, 29]. Aufgabe 3.17 Was ist der Unterschied zwischen einer Probe und einem Plausibilitätstest? Aufgabe 3.18 Für den Test eines digitalen Filters ist eine Prozedur zur Erzeugung eines sinusförmigen Testeingabesignals mit der Aufrufschnittstelle procedure GenSin(signal T: std_logic; signal y: out tSigned; A: natural; tpsin: delay_length; phi: real);
zu entwickeln. Diese soll nebenläufig aufgerufen bei jeder steigenden Taktflanke den Wert des Sinusterms tnow to_tSigned A · sin 2π · +ϕ tPsin an das Ausgabesignal y zuweisen (A – Amplitude des zu erzeugenden Sinussignals; tpsin (tpsin) – Periode; ϕ (phi) – Phasenverschiebung; tnow – aktuelle Simulationszeit). Für die Berechnung der Sinuswerte ist die Funktion math_real.sin(...) und für π die Konstante math_real.math_pi zu verwenden. Wenn der übergebene Parameter für die Amplitude größer als der größte mit dem Ausgabesignal y darstellbare Wert ist, soll eine AssertAnweisung die Simulation beim Aufruf der Prozedur beenden.
4 Vom Transistor zur Schaltung
Dieses Kapitel behandelt den Bottom-Up-Entwurf digitaler Grundschaltungen und die Zusammenhänge zwischen der Schaltungsstruktur, der Geschwindigkeit und der Schaltungsgröße. Die Schaltungstechnik der heutigen hochintegrierten Schaltkreise ist fast ausschließlich CMOS und darauf wird sich auch dieses Kapitel beschränken.
4.1 Entwurf und Modellierung von CMOS-Gattern Ein CMOS-Gatter besteht im Wesentlichen aus zwei Arten von Schaltelementen: • •
Low-Side-Schalter (NMOS-Transistoren) und High-Side-Schalter (PMOS-Transistoren).
Aus diesen werden nach einfachen Konstruktionsregeln Gatter, Speicherzellen und andere Grundschaltungen zusammengesetzt, die ihrerseits als Bausteine für komplexere Funktionseinheiten dienen. 4.1.1 MOS-Transistoren als Schalter Ein MOS-Transistor ist ein Halbleiterbauteil mit den vier Anschlüssen Drain (D), Source (S), Gate (G) und Substrat (bzw. Bulk, B). Zwischen Drain und Source – zwei hoch dotierten Halbleitergebieten – befindet sich das Gate, das die Leitfähigkeit des darunter liegenden Kanals steuert. Das Gate ist durch eine dünne Isolationsschicht und das Substrat durch gesperrte pn-Übergänge vom Source, vom Drain und vom Kanal getrennt (Abb. 4.1). Bei einem NMOSTransistor sind die Source- und Drain-Gebiete und der eingeschaltete Kanal n-leitfähig und bei einem PMOS-Transistor p-leitfähig. Das Substrat hat jeweils den anderen Leitfähigkeitstyp. N-leitfähig heißt, dass die beweglichen Ladungsträger negativ geladene bewegliche Elektronen, und p-leitfähig, dass G. Kemnitz, Technische Informatik, eXamen.press, DOI 10.1007/978-3-642-17447-6_4, © Springer-Verlag Berlin Heidelberg 2011
302
4 Vom Transistor zur Schaltung
die beweglichen Ladungsträger positiv geladene Löcher sind. Aus dem fast identischen Aufbau resultiert ein fast gleiches elektrisches Verhalten. Nur die Vorzeichen aller Ströme und Spannungen sind umgekehrt. B
S
p+
n+
G D
D
n+
p+
p NMOS-Transistor
G S p+
B n+
n PMOS-Transistor
G S D B
Gate Source Drain Bulk n-Kanal p-Kanal
p-Substrat
p stark dotiert (p+)
Polysilizium (Gate)
n-Wanne
n stark dotiert (n+)
Isolator (SiO2 )
Abb. 4.1. Aufbau und Anschlüsse eines NMOS- und eines PMOS-Transistors
Das elektrische Verhalten eines MOS-Transistors basiert auf dem Feldeffekt. Stellvertretend wird der NMOS-Transistor betrachtet. Wenn das Gate negativ aufgeladen wird, lädt sich die Schicht unter dem Gate-Isolator positiv auf und umgekehrt. Bei einer negativen oder schwach positiven Gate-SubstratSpannung ist das Halbleitergebiet unter dem Kanal p-leitfähig. Source und Drain sind über gesperrte pn-Übergänge vom Substrat und voneinander isoliert (Abb. 4.2 a). Bei einer Gate-Source-Spannung größer der Einschaltspannung UTN diffundieren bewegliche Elektronen vom Source in den Kanal1 . Es entsteht eine leitfähige Verbindung (Abb. 4.2 b). Wenn der Kanal eingeschaltet ist, nimmt die Ladungsträgerdichte im Kanal proportional mit der GateKanal-Spannung zu. Bei einem Stromfluss durch den Kanal ist die Gate-Kanal-Spannung ortsabhängig. Es wird zwischen zwei Arbeitsbereichen unterschieden. Im aktiven Bereich ist auch die Gate-Drain-Spannung größer als die Einschaltspannung UTN . Der leitfähige Kanal reicht bis zum Drain. Der Drain-Strom ist sowohl eine Funktion der Gate-Source-Spannung UGS als auch von der Drain-SourceSpannung UDS : u2DS iD = βN · (uGS − UTN ) · uDS − (4.1) 2 (iD – Drain-Strom; βN – Leitfähigkeitsparameter). Bei einem großen Spannungsabfall zwischen Drain und Source unterschreitet die Gate-Drain-Span1
Der Source-Anschluss ist bei einem NMOS-Transistor das angrenzende hochdotierte n-Gebiet mit dem niedrigeren Potenzial und bei einem PMOS-Transistor das angrenzende hochdotierte p-Gebiet mit dem höheren Potenzial. Die Zuordnung von Source und Drain hängt folglich nicht vom Aufbau, sondern von der elektrischen Beschaltung ab und kann sich während des Betriebs ändern. Wenn der Kanal wie in Abb. 4.2 b nicht eingeschnürt ist, diffundieren auch vom Drain bewegliche Ladungsträger in den Kanal.
4.1 Entwurf und Modellierung von CMOS-Gattern S uGB < UTN
uGS
G
S
D
+
n+
a)
uDS = uGS − uGD G > UTN uGD
n+ Akkumulationsp-Substrat schicht B uGB Gate-Substrat-Spannung uGS Gate-Source-Spannung uGD Gate-Drain-Spannung uDS Drain-Source-Spannung UTN Einschaltspannung
n p-Substrat
b) +
+
303 D
+
n Verarmungsschicht Inversionsschicht B
Isolator Gateladung Aufladung mit beweglichen Ladungstr¨agern Aufladung mit ortsfesten Ionen
Abb. 4.2. Feldeffekt am NMOS-Transistor a) kleine b) große Eingangsspannung
nung uGD = uGS − uDS die Einschaltspannung. Der leitfähige Kanal endet kurz vor dem Drain. Die Breite des eingeschnürten Bereichs regelt sich so ein, dass der Drain-Strom ab dem Arbeitsbereichswechsel bei uDS = uGS − UTN nicht weiter mit der Drain-Source-Spannung zunimmt: iD =
βN · (uGS − UTN ) 2
2
(4.2)
Ein PMOS-Transistor funktioniert genauso, nur dass die Vorzeichen aller Ströme, Spannungen und Transistorparameter genau umgekehrt sind. Die Einschaltspannung wird in der digitalen Schaltungstechnik betragsmäßig auf etwa 20% der Versorgungsspannung UV eingestellt. Parameter
Sperrbereich
aktiver Bereich
Einschnürbereich
NMOS
UTN ≈ 0,2 · UV , βN > 0
uGS ≤ UTN
uGS > UTN und uGS − uDS > UTN
sonst
PMOS
UTP ≈ −0,2 · UV , βP < 0
uGS ≥ UTP
uGS < UTP und uGS − uDS < UTP
sonst
Schaltverhalten In den im Weiteren betrachteten elektrischen Modellen für Logikgatter schalten die NMOS-Transistoren die Verbindung zwischen dem Gatterausgang und dem Bezugspunkt und entladen die Lastkapazität CL . Die Spannung ux des Eingabesignals liegt zwischen Gate und Source an. Die Spannung uy des Ausgabesignals wird zwischen Drain und Source abgegriffen (Abb. 4.3 a). Die PMOS-Transistoren schalten die Verbindung zum positivsten Schaltungspunkt, dem Versorgungsanschluss, und laden die Lastkapazität CL auf. Die
304
4 Vom Transistor zur Schaltung
Gate-Source-Spannung, die die Leitfähigkeit des Kanals steuert, ist die Versorgungsspannung UV abzüglich der Eingangsspannung ux (Abb. 4.3 b). Die Lastkapazität CL selbst ist kein eigenständiges Bauteil, sondern setzt sich aus den Kapazitäten der pn-Isolationen der Drain-Gebiete, den Leitungskapazitäten und den Eingangskapazitäten der nachfolgenden Gatter zusammen. iD x ux = a) uGS
G
D B S
uy = uDS
ux Eingangsspannung uy Ausgangsspannung iD Drainstrom
uGS
y
x
CL
ux = b) UV + uGS
G
S B D
uGS Gate-Source-Spannung uDS Drain-Source-Spannung UV Versorgungsspannung
uDS
iD
uy = UV + uDS
y CL
UV
CL Lastkapazit¨ at
Abb. 4.3. Symbole und elektrische Beschaltung a) NMOS-Transistor als Low-SideSchalter b) PMOS-Transistor als High-Side-Schalter
Bei einem Logikgatter darf der Ausgang im stationären Betrieb nur entweder über ein eingeschaltetes NMOS-Netzwerk mit Masse oder über ein eingeschaltetes PMOS-Netzwerk mit der Versorgungsspannung verbunden sein. Die Transistoren sind entweder ausgeschaltet oder sie arbeiten im aktiven Bereich mit uDS = 0. In beiden Fällen fließt fast kein Strom. CMOS-Schaltungen benötigen praktisch nur Umladeströme. Der Stromverbrauch verhält sich etwa proportional zur Schalthäufigkeit. Die Schaltungsgeschwindigkeit ergibt sich aus der Größe der Lastkapazitäten und der Größe der Umladeströme. Für das logische Verhalten sei definiert, dass • •
große Spannungen und eingeschaltete Drain-Source-Strecken durch den Signalwert »1« und kleine Spannungen und ausgeschaltete Drain-Source-Strecken durch den Signalwert »0«
dargestellt werden. Ein NMOS-Transistor schaltet mit dieser Zuordnung bei einer »1« am Gate ein und einer »0« am Gate aus. Das entspricht einer Identität. Ein PMOS-Transistor schaltet bei einer »0« am Gate ein und bei einer »1« aus, d.h., er invertiert. In Gatterschaltungen werden MOS-Transistoren gern in der vereinfachten Form ohne Substratanschluss dargestellt (Abb. 4.4). Das vereinfachte Symbol für den PMOS-Transistor besitzt zur Unterscheidung von dem des NMOS-Transistors einen Negationspunkt am Gate, der das invertierende Verhalten symbolisiert. Eine weitere alternative Darstellung, insbesondere später für Reihen- und Parallelschaltungen von geschalteten Transistoren, ist ein Zweipol mit dem logischen Ausdruck, bei dem er einschaltet. Geschaltete Zweipole für PMOS-Netzwerke werden zur Unterscheidung von denen für NMOS-Netzwerke mit einer schwarzen Ecke gekennzeichnet.
4.1 Entwurf und Modellierung von CMOS-Gattern logische Funktion PMOSTransistor
NMOSTransitor
G 0 1
S→D 1 0
G 0 1
S→D 0 1
komplett
Schaltsymbol vereinfacht Zweipol S
S ¯ G
D
D
D
D
D
D
S UV G
G
G
G
305
G S
S
S
Abb. 4.4. MOS-Transistoren als Schalter
4.1.2 Geschaltete Transistornetzwerke Logische Verknüpfungen werden durch Reihen- und Parallelschaltungen von Transistoren realisiert. Eine Reihenschaltung mehrerer Transistoren ist insgesamt eingeschaltet (Schaltzustand »1«), wenn alle Transistoren eingeschaltet sind. Das entspricht einer UND-Verknüpfung. Eine Parallelschaltung mehrerer Transistoren ist eingeschaltet, wenn mindestens ein Transistor eingeschaltet ist. Das ist eine ODER-Verknüpfung. Zur Realisierung von UND-ODERVerknüpfungen werden Reihen- und Parallelschaltungen miteinander kombiniert. Transistornetzwerke aus NMOS-Transistoren bilden dabei eine UNDODER-Verknüpfung der direkten und PMOS-Netzwerke eine UND-ODERVerknüpfung der negierten Signale an den Gate-Anschlüssen (Abb. 4.5). Zu beachten ist, NMOS-Transistoren eignen sich nur zur Weiterleitung einer »0« und PMOS-Transistoren nur zur Weiterleitung einer »1«. Deshalb dürfen NMOS- und PMOS-Transistoren z.B. auch nicht innerhalb eines Schalternetzwerks gemischt werden. In der Zweipoldarstellung ist der
Reihenschaltung
Parallelschaltung
x1
gemischte x3 Reihen- und Parallelschaltung x1
NMOS-Netzwerk Struktur Funktion
PMOS-Netzwerk Struktur Funktion
x1
x1
x2
x1 ∧ x2
x2
x1 ∨ x2
(x1 ∨ x2 ) ∧ x3 x2
x1
x2
x ¯1 ∧ x ¯2
x2
x3 x1
x ¯1 ∨ x ¯2
(¯ x1 ∨ x ¯2 ) ∧ x ¯3 x2
Abb. 4.5. Geschaltete Transistornetzwerke
306
4 Vom Transistor zur Schaltung
logische Ausdruck für ein NMOS-Netzwerk eine UND-ODER-Verknüpfung von direkten Eingabesignalen und für ein PMOS-Netzwerk eine UND-ODERVerknüpfung invertierter Eingabesignale. In einem Gatter werden die Schaltzustände der Transistornetzwerke wieder in Spannungen umgesetzt (Abb. 4.6). Der Gatterausgang wird über ein eingeschaltetes NMOS-Netzwerk mit dem Bezugspotenzial (Masse), das den logischen Wert »0« repräsentiert, oder über ein eingeschaltetes PMOS-Netzwerk mit der Versorgungsspannung UV , die den logischen Wert »1« repräsentiert, verbunden. Eingeschaltet bildet das NMOS-Netzwerk eine Quelle mit dem Signalwert »0«, ausgeschaltet mit dem Signalwert »Z« (Z – hochohmig). Das PMOS-Netzwerk liefert entweder »1« oder »Z«. Der logische Wert am Gatterausgang wird nach folgenden Regeln gebildet: • •
»0« oder »1« setzen sich gegenüber »Z« durch und gleichzeitig »0« und »1« darf nur kurzzeitig während der Schaltvorgänge auftreten und verursacht einen ungültigen Signalwert (»X«). Pull-Up-Netzwerk (PMOS) 1 fp yp fp
0 1
Z 1
Modell des Gatterausgangs yp ∈ {Z, 1}
yp yn
y
Z Z Z(1) Z 0 0 yn ∈ {Z, 0} CL 1 Z 1 Pull-Down-Netzwerk (NMOS) 1 0 X(2) yn fn yn (1) hochohmig; CL wird (fast) fn nicht umgeladen 0 Z (2) unbestimmt, nur w¨ ahrend der 1 0 0 Schaltvorg¨ange zul¨assig yp
y
Abb. 4.6. Der Ausgang eines CMOS-Gatters als Signal mit mehreren Quellen
Abbildung 4.7 a zeigt ein Gatter, in dem das PMOS-Netzwerk aus einer Reihenschaltung und das NMOS-Netzwerk aus einer Parallelschaltung von zwei Transistoren besteht. Die Schaltfunktionen der beiden Transistornetzwerke sind zueinander komplementär. Das NMOS-Netzwerk schaltet ein, wenn mindestens eines der beiden Eingabesignale »1« ist. Das PMOS-Netzwerk schaltet ein, wenn keines der Eingabesignale »1« ist. Wie aus der Wertetabelle ablesbar ist, handelt es sich um ein NOR-Gatter. In Abb. 4.7 b erzeugt das PMOS-Netzwerk bei x1 = 0 am Ausgang eine »1« und das NMOS-Netzwerk bei x1 = x2 = 1 eine »0«. Bei der übrigen Eingabebelegung x1 = 1 und x2 = 0 ist der Ausgang hochohmig (»Z«). Ein hochohmiger Ausgang speichert in seiner Lastkapazität den letzten Ausgabewert noch für eine gewisse Zeit, bevor sich die Kapazität umlädt und der Ausgang einen ungültigen Wert annimmt.
4.1 Entwurf und Modellierung von CMOS-Gattern UV
1 x ¯1 ∧ x ¯2
x1 x2
yp
yp
y
yn x1
a)
yn
x1
yp
yp
y
yn
x2
x1 ∨ x2
x2
1 x ¯1
UV y
yn
y
x1 ∧ x2
x1
0
0
x2 x1
fp fn
yp yn
y
x2 x1
fp fn
yp yn
y
0 0 1 1
1 0 0 0
1 Z Z Z
1 0 0 0
0 0 1 1
1 0 1 0
1 Z 1 Z
1 Z 1 0
0 1 0 1
0 1 1 1
Z 0 0 0
b)
307
0 1 0 1
0 0 0 1
Z Z Z 0
Abb. 4.7. CMOS-Gatter a) NOR-Gatter b) Gatter, dessen Ausgang auch hochohmig gesteuert werden kann
4.1.3 Signale mit mehreren Quellen In allen zuvor behandelten VHDL-Modellen hatte ein Signal genau eine Quelle. In dem gerade eingeführten Gatter-Modell hat das Signal für den Gatterausgang zwei oder mehr Quellen. Ein Signal mit mehreren Quellen benötigt eine Auflösungsfunktion (engl. resolution function), die aus den einzelnen Quellenwerten den Signalwert der Leitung berechnet. In VHDL ist diese Auflösungsfunktion an den Datentyp gebunden. Die Zuordnung erfolgt in einer speziellen Untertypvereinbarung: subtype tAflTyp is AufloesFkt tTyp;
(tTyp – Basistyp; AufloesFkt – Auflösungsfunktion; tAflTyp – Untertyp mit Auflösungsfunktion). Die Auflösungsfunktion berechnet aus einem oder mehreren Eingabewerten vom Basistyp einen Rückgabewert vom Basistyp. Der Typ des Eingabeparameters der Auflösungsfunktion ist entsprechend ein Vektor mit dem Basistyp als Elementtyp und variablem Indexbereich: type tTyp_Vector (natural <>) of tTyp; function AufloesFkt(qv: tTyp_Vector) return tTyp is ...;
Der vom Standard empfohlene Bit-Typ std_logic ist ein solcher Untertyp. Der Basistyp ist std_ulogic, dessen Vektortyp ist std_ulogic_vector und die Auflösungsfunktion heißt hier resolved(...): type std_ulogic is (’U’,’X’,’0’,’1’,’Z’, ...); type std_ulogic_vector is array (natural <>) of std_ulogic; subtype std_logic is resolved std_ulogic;
Die Auflösungsfunktion resolved(...) fasst in einer Schleife immer paarweise zwei Quellenwerte zu einem gemeinsamen Signalwert zusammen:
308
4 Vom Transistor zur Schaltung function resolved (s: std_ulogic_vector) return std_ulogic is variable result: std_ulogic := s(s’low); begin for idx in s’low + 1 to s’high loop result := resolution_table(result, s(idx)); end loop; return result; end function;
Die Regeln für die paarweise Zusammenfassung sind in einer zweidimensionalen Konstantentabelle festgelegt, die jedem Paar von std_ulogic-Werten ein std_ulogic-Ergebnis zuordnet2 . type stdlogic_table is array (std_ulogic, std_ulogic) of std_ulogic; constant resolution_table: stdlogic_table := ( -------------------------------------------- ... X 0 1 Z ... | | ------------------------------------------( ..., ..., ..., ..., ..., ... ), -- | U | ( ..., ’X’, ’X’, ’X’, ’X’, ... ), -- | X | ( ..., ’X’, ’0’, ’X’, ’0’, ... ), -- | 0 | ( ..., ’X’, ’X’, ’1’, ’1’, ... ), -- | 1 | ( ..., ’X’, ’0’, ’1’, ’Z’, ... ), -- | Z | ... );
Wenn beide Signalquellen denselben Wert liefern, ist der Tabelleneintrag der übereinstimmende Wert. Bei Abweichungen setzt sich »X«, der Pseudo-Wert für ungültig, gegenüber allen anderen Werten und »0« oder »1« gegenüber »Z« durch. Mit dem Typ std_logic lässt sich der Ausgang eines Gatters auch in VHDL als ein Signal mit mehreren Quellen beschreiben. Für das NOR-Gatter aus Abb. 4.7 a ergeben sich die logischen Ausgabewerte der Transistornetzwerke über folgende Fallunterscheidungen: Z für x1 ∨ x2 = 0 1 für x1 ∨ x2 = 0 yn = 0 für x1 ∨ x2 = 1 yp = Z für x1 ∨ x2 = 1 X sonst X sonst Die Auflösungsfunktion berechnet daraus den Gatterausgabewert. In einem ereignisgesteuerten Simulationsmodell sind die beiden Fallunterscheidungen in einen kombinatorischen Prozess einzurahmen, der die Werte von yn und yp bei jeder Änderung von x1 oder x2 neu berechnet und zuweist. Der Gatterausgang wird mit zwei nebenläufigen Signalzuweisungen an y beschrieben: 2
Auch Aufzählungstypen sind als Indexbereiche für Felder zugelassen (vgl. Abschnitt 3.2.5).
4.1 Entwurf und Modellierung von CMOS-Gattern
309
yp <= ’1’ after tdp1; else yn <= ’X’ after tdnX; yp <= ’X’ after tdpX; end if; end process; y <= yn; y <= yp;
signal x1,x2,yn,yp,y:std_logic; ... process(x1, x2) begin if (x1 or x2)=’1’ then yn <= ’0’ after tdn0; yp <= ’Z’ after tdpZ; elsif (x1 or x2)=’0’ then yn <= ’Z’ after tdnZ;
⇒WEB-Projekt: P4.1/NOR2nq.vhdl
Mit dem Datentyp std_ulogic würden die beiden nebenläufigen Zuweisungen an y eine Fehlermeldung verursachen. Mit std_logic als Signaltyp werden sie in einen Aufruf der Auflösungsfunktion übersetzt, die in einer nebenläufigen Signalzuweisung mit der Konkatenation der Quellenwerte yn und yp bei jeder Änderung von yn oder yp den neuen Wert von y bestimmt und zuweist: y <= resolved(yn & yp);
Die Modellierung eines Gatters durch einzelne geschaltete Transistornetzwerke erlaubt eine wirklichkeitsnahe Annäherung des Zeitverhaltens (siehe später Abschnitt 4.2.5). 4.1.4 FCMOS-Gatter Das »FC« von FCMOS steht für vollständig komplementär (f ull complementary). Es bedeutet, dass im stationären Zustand, d.h. nach Abklingen aller Umladevorgänge, entweder der PMOS- oder das NMOS-Zweipol eingeschaltet ist. Der PMOS-Zweipol hat die logische Funktion des Gatters und der NMOSZweipol die hierzu inverse Funktion (Abb. 4.8): fp = f fn = f¯ UV
(4.4) fn (x) fp (x)
fp (x) x = (. . . , x1 , x0 )
(4.3)
0 0 1 1
y fn (x) (1)
0 1 0 1
y verboten(1) 1 0 verboten(1)
im station¨ aren Zustand
Abb. 4.8. Struktur und Konstruktionsregel für FCMOS-Gatter
310
4 Vom Transistor zur Schaltung
Das einfachste Gatter ist der Inverter. Die beiden Netzwerke eines Inverters bestehen aus je einem Transistor und haben die zueinander komplementären Funktionen (Abb. 4.9 a) fn = x fp = x ¯ Ein NAND-Gatter hat die Zielfunktion y (x) = x1 x2 Das NMOS-Netzwerk muss die invertierte Funktion haben. Die beiden Negationen heben sich gegenseitig auf: fn (x) = x1 x2 = x1 x2 Das Ergebnis ist eine UND-Verknüpfung der Eingabesignale, nachzubilden durch eine Reihenschaltung von zwei NMOS-Transistoren. Der Ausdruck für das PMOS-Netzwerk ist mit der ersten De Morgan’schen Regel aus Tabelle 2.1 in die erforderliche Struktur umzuformen, die ODER-Verknüpfung der negierten Eingabesignale, die durch eine Parallelschaltung von zwei PMOSTransistoren nachzubilden ist (Abb. 4.9 b): fp (x) = x1 x2 = x ¯1 ∨ x ¯2 Für eine NOR-Verknüpfung lauten die Gleichungen y (x) = x1 ∨ x2 fn (x) = x1 ∨ x2 fp (x) = x ¯1 x ¯2 Für das NMOS-Netzwerk ergibt sich eine Parallel- und für das PMOSNetzwerk eine Reihenschaltung (Abb. 4.9 c).
UV x a)
y
x1
x2 x1
b)
x2
x1
UV y
x2 x1 c)
x2
UV y
d)
x1
x2
x3
x4
x1
x3
x2
x4
UV y
Abb. 4.9. FCMOS-Gatter a) Inverter b) NAND-Gatter c) NOR-Gatter d) Komplexgatter
4.1 Entwurf und Modellierung von CMOS-Gattern
311
Ganz allgemein lässt sich jede logische Funktion, deren negierte Funktion sich durch UND- und ODER-Verknüpfungen nicht negierter Signale beschreiben lässt, durch ein einzelnes FCMOS-Gatter nachbilden. Beispiel sei ein Komplexgatter mit der Funktion y (x) = x1 x2 ∨ x3 x4 fn (x) = x1 x2 ∨ x3 x4 fp (x) = (¯ x1 ∨ x ¯2 ) (¯ x3 ∨ x ¯4 ) Das NMOS-Netzwerk ist durch eine Parallelschaltung von je zwei in Reihe geschalteten Transistoren und das PMOS-Netzwerk durch eine Reihenschaltung von je zwei parallel geschalteten Transistoren nachzubilden. (Abb. 4.9 d). Die Zuordnung zu Reihen- und Parallelschaltungen ist im NMOS- und im PMOSNetzwerk immer genau umgekehrt. Eine Grundregel des Gatterentwurfs ist, die gegebene Zielfunktion vor der Nachbildung durch ein Gatter so weit wie möglich zu vereinfachen. In der Zielfunktion y = (x1 x2 x3 ) ∨ x1 ∨ x2 = x1 ∨ x2 (4.5) ist z.B. der komplette erste UND-Term überflüssig, weil er nur dann eins wird, wenn die ODER-Verknüpfung mit x1 ohnehin »1« ist. Übrig bleibt ein NOR-Gatter mit zwei Eingängen. In einem FCMOS-Gatter kostet jeder Gattereingang einen NMOS- und einen PMOS-Transistor. Die Vereinfachung in Gleichung 4.5 verringert den Schaltungsaufwand von zehn auf vier Transistoren. In der Zielfunktion y = x1 x2 ∨ x1 x3 ∨ x2 x3 = x1 (x2 ∨ x3 ) ∨ x2 x3
(4.6)
kann x1 ausgeklammert werden, so dass das Gatter ein Transistorpaar weniger benötigt (Abb. 4.10 a). Ein Beispiel für eine Zielfunktion, die nicht mit einem einzelnen FCMOSGatter nachgebildet werden kann, ist die EXOR-Funktion (Abb. 4.10 b): y = x1 ⊕ x2 Die Funktion des NMOS-Netzwerks enthält die Eingabesignale einmal direkt und einmal negiert:
a)
x1
x2 x3
x2
x3
x2
x3
x1
UV
UV x1
x2 x3
x ¯1 UV
y x2 b)
x ¯2
x2 x ¯1
x ¯2 x1
x2 x1
x ¯2 x ¯1
UV y
Abb. 4.10. a) FCMOS-Gatter mit der Funktion aus Gleichung 4.6 b) EXOR-Gatter
312
4 Vom Transistor zur Schaltung
fn = x1 ⊕ x2 = x1 x2 ∨ x ¯2 x ¯1
(4.7)
Dasselbe gilt für die Zielfunktion des PMOS-Netzwerks: fp = x1 ⊕ x2 = x ¯ 1 x2 ∨ x1 x ¯2
(4.8)
Zur Nachbildung der EXOR-Funktion über die Gleichung 4.7 und 4.8 werden zwei zusätzliche Inverter benötigt, die die negierten Eingabesignale bereitstellen. 4.1.5 Bussignale und deaktivierbare Treiber Bussignale sind zentrale Informationsknoten, die unterschiedliche Teilsysteme miteinander verbinden. Sie haben oft mehrere Quellen, die den Bus nacheinander nutzen. In Abb. 4.11 schickt erst die Quelle A und anschließend die Quelle B Daten über den Bus. Die jeweils andere Quelle ist während der Übertragung inaktiv. Wenn alle Signalquellen inaktiv sind, ist der Signalwert auf dem Bus hochohmig (»Z«). Die gleichzeitige Aktivierung mehrerer Quellen ist verboten und kann zur Zerstörung der Treiber führen.
Quelle A Quelle B
yA yB
yA y
Signalwert ung¨ ultig Treiber inaktiv
Z
yB y
wA2
wA1
wA1
wA1
Z wB1
wB2
wB1
wB2
t
Abb. 4.11. Signalübertragung über ein Bussignal mit mehreren Quellen
Ein Bustreiber benötigt außer dem Eingabe- und dem Ausgabesignal für die Daten ein Freigabesignal E für die Ausgabeaktivierung. Auf der Transistorebene lässt sich ein deaktivierbarer Treiber genau wie alle bisherigen Gatter aus einem NMOS- und einem PMOS-Netzwerk konstruieren. Das PMOSNetzwerk soll genau dann einschalten, wenn das Freigabe- und das Datensignal »1« sind: fp = E ∧ x Das NMOS-Netzwerk soll einschalten, wenn das Freigabesignal »1« und das Datensignal »0« ist: fn = E ∧ x ¯ Das Freigabesignal wird für das PMOS-Netzwerk invertiert und für das NMOS-Netzwerk direkt benötigt, das Datensignal nur invertiert (Abb. 4.12). Für die Synthese wird ein deaktivierbarer Treiber durch eine bedingte Zuweisung des Signalwertes »Z« beschrieben:
4.1 Entwurf und Modellierung von CMOS-Gattern UV
¯ E x
y
x ¯
x E
y E∧x ¯
E
a)
UV E∧x
b)
0 0 1 1
fp fn
y
0 0 0 1
Z 0 Z 1
0 1 0 1
0 1 0 0
x E
313
y
c)
Abb. 4.12. Deaktivierbarer Treiber a) Transistorschaltung b) Logiktabelle c) Symbol signal x, y, E: std_logic; ... if E=’1’ then y <= x; else y <= ’Z’; end if;
4.1.6 Gatter mit Pull-Up- und Pull-Down-Elementen Ein Pull-Up-Element ist eine Schaltung, die eine schwache logische Eins liefert. Ein Pull-Down-Element liefert eine schwache logische Null. Im einfachsten Fall werden schwache Signalwerte mit einem hochohmigen Widerstand zur Versorgungsspannung (UV ) oder zur Masse (⊥) erzeugt (Abb. 4.13). Bei einem Signal mit mehreren Quellen überschreibt ein schwacher logischer Wert den Wert »Z« (hochohmig) und wird selbst von den starken Werten »0«, »1« und »X« (unbestimmt) überschrieben. Die Folgegatter, die den Signalwert weiterverarbeiten, unterscheiden dabei nicht, ob eine Null oder eine Eins von einer schwachen oder einer starken Quelle erzeugt wird. Ein Pull-Up-Element ersetzt auf diese Weise in einem Gatter das geschaltete PMOS-Netzwerk und ein Pull-Down-Element das geschaltete NMOS-Netzwerk. UV (1)
H a)
yn ∈ {Z, 0}
y
yn
y
0 Z
0 H
yp ∈ {Z, 1} L b)
Abb. 4.13. a) Gatter mit Pull-Up-Widerstand Widerstand
y
yp
y
Z 1
L 1
0
b) Gatter mit Pull-Down-
Der std_logic-Wert für eine schwache »0« ist »L« (abgeleitet von low), für eine schwache »1« »H« (abgeleitet von high) und für einen schwachen ungültigen Wert »W« (abgeleitet von weak). Abbildung 4.14 zeigt die komplette Tabellenkonstante, mit der die Auflösungsfunktion des Typs std_logic die Treiberwerte paarweise zusammenfasst (vgl. Abschnitt 4.1.3). Bei gleichen schwachen Werten ist das Ergebnis der übereinstimmende Wert. Unterschiedlichen schwachen Werten ordnet die Tabellenfunktion »W« (schwach ungültig) zu.
314
4 Vom Transistor zur Schaltung constant resolution_table: stdlogic_table := ( ---------------------------------------------------------- U X 0 1 Z W L H | | --------------------------------------------------------( ’U’, ’U’, ’U’, ’U’, ’U’, ’U’, ’U’, ’U’, ’U’ ), -- | U | ( ’U’, ’X’, ’X’, ’X’, ’X’, ’X’, ’X’, ’X’, ’X’ ), -- | X | ( ’U’, ’X’, ’0’, ’X’, ’0’, ’0’, ’0’, ’0’, ’X’ ), -- | 0 | ( ’U’, ’X’, ’X’, ’1’, ’1’, ’1’, ’1’, ’1’, ’X’ ), -- | 1 | ( ’U’, ’X’, ’0’, ’1’, ’Z’, ’W’, ’L’, ’H’, ’X’ ), -- | Z | ( ’U’, ’X’, ’0’, ’1’, ’W’, ’W’, ’W’, ’W’, ’X’ ), -- | W | ( ’U’, ’X’, ’0’, ’1’, ’L’, ’W’, ’L’, ’W’, ’X’ ), -- | L | ( ’U’, ’X’, ’0’, ’1’, ’H’, ’W’, ’W’, ’H’, ’X’ ), -- | H | ( ’U’, ’X’, ’X’, ’X’, ’X’, ’X’, ’X’, ’X’, ’X’ ) -- | - |);
Abb. 4.14. Komplette Zusammenfassungstabelle für std_ulogic-Werte (aus dem Package ieee.std_logic_1164.vhd)
Abbildung 4.15 zeigt ein Beispielgatter. Das PMOS-Netzwerk ist durch ein Pull-Up-Element ersetzt. Das NMOS-Netzwerk besteht aus zwei parallel geschalteten Transistoren, die eine ODER-Verknüpfung bilden. Ist einer der Transistoren im NMOS-Netzwerk eingeschaltet, zieht er den Gatterausgang auf »0«. Sonst setzt sich der schwache Wert des Pull-Up-Widerstands durch. Die nachfolgenden Gatter unterscheiden nicht zwischen schwachen und starken Signalwerten. Die Gesamtschaltung realisiert eine NOR-Verknüpfung. UV H yn x1
x2
H y
yn x1 ∨ x2 0
y
x2 x1
fn
0 0 1 1
0 1 1 1
0 1 0 1
H H H H
yn
y
Z 0 0 0
H (1) 0 0 0
Abb. 4.15. NOR-Gatter mit Pull-Up-Widerstand
Ein Pull-Up-Widerstand hat den Nachteil, dass bei der Ausgabe einer »0« auch dann ein Strom fließt, wenn kein Schaltvorgang stattfindet. Eine Alternative ist eine kleine aufgeladene Kapazität. In Abb. 4.16 wird die Lastkapazität CL des Gatters vor der Auswertung des Ausgabesignals mit einer kurzzeitigen »0« auf der Taktleitung aufgeladen. Wenn das NMOS-Netzwerk in der Auswertephase sperrt, bleibt der Ausgabewert »1«. Sonst entlädt sich die Lastkapazität und der Ausgabewert wechselt auf »0«.
4.1 Entwurf und Modellierung von CMOS-Gattern UV Pull-Up- T Element
CL
E A
y
yn xn−1
xn−2
...
x0
T yn
Z 0
y
1 0
E
A
E
315
A E
1 0
A Aufladen von CL E Entladen von CL , wenn (xn−1 ∨ . . . ∨ x0 ) = 1
Abb. 4.16. NOR-Gatter mit einer aufgeladenen Kapazität als Pull-Up-Element
Im Beispiel ist das NMOS-Netzwerk eine Parallelschaltung aus n NMOSTransistoren. Im Vergleich zu einem FCMOS-Gatter ersetzt die Pull-UpSchaltung hier eine Reihenschaltung aus n PMOS-Transistoren. Der Nachteil ist der zusätzlich erforderliche Takt. Eine weitere Anwendung für Pull-Up- oder Pull-Down-Elemente ist der Anschluss mehrerer Signalquellen an einen Bus (Abb. 4.17). Wenn alle Quellen hochohmig sind, setzt sich die schwache Eins des Pull-Up-Elements durch. In allen anderen Fällen ist der Ausgabewert Null. Eine solche Schaltung wird als verdrahtetes UND (engl. wired-AND) bezeichnet.
Bus
aktive Quelle xi yq 0 Z 1 0
xi ∈ {0, 1}
≥1
Pull-Up-Element H
inaktive Quellen
Fehlersignal: aktiv, wenn ein zweiter aktiver Treiber am Bus erkannt wird
0
Z
y
Z 0
yq Treiberwert der aktiven Quelle
Abb. 4.17. Bussignal mit Pull-Up-Element
Die inaktiven Quellen müssen den Wert »Z« liefern. Wenn unerlaubterweise gleichzeitig eine weitere Quelle aktiv ist, überschreibt eine »0« der zweiten Quelle das »Z« der ersten Quelle. Die Ausgabe wird verfälscht. Örtlich getrennte Signalquellen an einem Bus besitzen oft eine Fehlererkennungsschaltung. Das ist im einfachsten Fall ein NOR-Gatter, das kontrolliert, dass, wenn die aktive Quelle »Z« sendet, auf dem Bus insgesamt eine »1« ankommt. 4.1.7 Transfergatter und Multiplexer Ein Transfergatter ist die Nachbildung eines Schalters zur Weiterleitung einer »0« oder einer »1«. Es besteht aus der Parallelschaltung eines NMOSund eines PMOS-Transistors. Da PMOS-Transistoren invertieren, benötigt ein Transfergatter sowohl das direkte als auch das invertierte Steuersignal (Abb. 4.18). Die logische Funktion ähnelt der des deaktivierbaren Treibers aus Abschnitt 4.1.5.
316
4 Vom Transistor zur Schaltung UV s¯ x
y
fp = s¯
x
y
fn = s
s
x s
y
- 0 0 1 1 1
Z 0 1
- Wert beliebig (don’t care)
Abb. 4.18. Transfergatter
Transfergatter dienen z.B. zur Konstruktion von Multiplexern. Ein 2:1Multiplexer hat die Funktion ( x1 wenn s = 0 y= (4.9) x2 wenn s = 1 und besteht aus zwei gegenläufig angesteuerten Transfergattern. Das Transfergatter zur Weiterleitung von x1 ist bei s = 0 ein- und sonst ausgeschaltet. Das Transfergatter zur Weiterleitung von x2 ist bei s = 1 ein- und sonst ausgeschaltet (Abb. 4.19 a). Ein Transfergatter zur Weiterleitung der Konstanten »1« braucht keinen NMOS- und ein Transfergatter zur Weiterleitung einer »0« keinen PMOS-Transistor (Abb. 4.19 b und c). Ein Multiplexer, der bei s = 0 eine »1« und bei s = 1 eine »0« weiterleitet, hat nicht nur dieselbe Funktion, sondern auch dieselbe Schaltung wie ein CMOS-Inverter (Abb. 4.19 d). x1 x2
0 1
y
x2 a)
x2
0 1
y
x1 0
s s¯ s¯ s
1 y x2 b)
0 1
1
y
0
s
s
s
x1
1
s s¯ s
x1 y c)
0
0 1
y
s s s¯ s
1
s
y
y d)
0
s
Abb. 4.19. Multiplexer zur Umschaltung zwischen zwei Eingabesignalen a) zwei variable Eingabesignale b) eine »1« und ein variables Eingabesignal c) ein variables Eingabesignal und eine »0 « c) Umschalter zwischen »1« und »0« (Inverter)
Mit Multiplexern werden z.B. binäre Entscheidungsdiagramme nachgebildet (vgl. Abschnitt 2.3.3). Abbildung 4.20 zeigt, wie für ein vorgegebenes binäres Entscheidungsdiagramm zuerst eine Multiplexerschaltung und dann weiter eine Transistorschaltung entworfen wird. In Abb. 4.20 b sind die drei oberen Entscheidungsknoten aus Abb. 4.20 a durch Multiplexer nachgebildet. Der untere Entscheidungsknoten liefert den Wert von s3 . Im nächsten Schritt
4.1 Entwurf und Modellierung von CMOS-Gattern 0 s2 0 0
a)
1 0 s3
0 1
s3 0 b)
s1
1
s¯1
s2
s1 s¯2
1 1
e)
1 1
z1
1 0 M1 s2 1 z2 0 M2 s2
s¯2 s2
1 0
y M3
M1 z1
s¯2
s3
s¯2
z2
c)
0
s¯2
y
s2 s3 1
s1
M2
s¯2 s2
y
s¯2
s3
s¯2
s¯1
s2
s1
UV
M3 s¯1 s1
317
s2 d)
0
s¯2
z1p z2p z1n z2n
s¯1 s1 s1
y
s¯1
Abb. 4.20. Nachbildung eines binären Entscheidungsdiagramms durch eine Transistorschaltung a) Funktionsvorgabe als reduziertes binäres Entscheidungsdiagramm (ROBDD) b) Nachbildung durch eine Multiplexerschaltung c) Nachbildung der Multiplexer durch Transistorschalter d) Aufteilung in eine Teilschaltung aus NMOSund eine Teilschaltung aus PMOS-Transistoren e) fertige Transistorschaltung
sind die Multiplexer durch ihre einzelnen Transferschalter ersetzt. Die Verbindungen nach »1« von Multiplexer M1 benötigt nur den PMOS-Transistor und die Verbindung nach »0« von Multiplexer M2 nur den NMOS-Transistor (Abb. 4.20 c). Auf dem Halbleiter werden die NMOS-Transistoren und die PMOS-Transistoren in der Regel räumlich zusammengefasst. Die Verbindungen zwischen z1n und z1p sowie zwischen z2n und z2p haben keinen Einfluss auf die logische Funktion und sind damit überflüssig3 (Abb. 4.20 d). Abbildung 4.20 e zeigt die fertige Transistorschaltung. Binäre Entscheidungsdiagramme eignen sich nicht nur für die Schaltungsoptimierung und als Normalformen für logische Ausdrücke. Sie lassen sich auch nach einem einfachen Formalismus durch Transistorschaltungen nachbilden. Dabei entstehen ganz andere Schaltungen als bei der Nachbildung der logischen Funktion mit FCMOS-Gattern. 4.1.8 Geometrische Beschreibung Eine integrierte Schaltung wird durch eine zweidimensionale Anordnung und Verdrahtung von Transistoren auf einem Halbleiterchip realisiert. Die Halbleitergebiete und Verbindungen werden für den Entwurf als Flächenelemente, meist Rechtecke, dargestellt. Die dritte Dimension, die Schichtfolge und die 3
Im PMOS-Netzwerk müssen nur die Einsen und im NMOS-Netzwerk nur die Nullen weitergeleitet werden.
318
4 Vom Transistor zur Schaltung
Schichtabmessungen in der Tiefe, sind durch den Fertigungsprozess vorgegeben. Abbildung 4.21 zeigt einen NMOS- und einen PMOS-Transistor in der 3DDarstellung und in der Draufsicht. In der Draufsicht bestehen die Transistoren hier im Buch aus vier Arten von Flächenelementen4 , nämlich aus • • • •
den schwach dotierten Substrat-Wannen der PMOS-Transistoren, den hochdotierten n-Gebieten für die Source- und Drain-Anschlüsse der NMOS-Transistoren und die Substrat-Anschlüsse der PMOS-Transistoren, den hochdotierten p-Gebieten für die Source- und Drain-Anschlüsse der PMOS-Transistoren und die Substrat-Anschlüsse der NMOS-Transistoren und den Polysilizium-Streifen als Gate-Anschlüsse und Verbindungen.
B p+
NMOS-Transistor S G D n+
n+
p+
p
a)
S
D G
p+
n+
n
NMOS-Transistor B
PMOS-Transistor G S B
D
PMOS-Transistor D
S G
B
G S D B
Gate Source Drain Bulk
p-Substrat n-Wanne p stark dotiert (p+) n stark dotiert (n+) Polysilizium (Gate)
b)
Isolator (SiO2 )
Abb. 4.21. NMOS- und PMOS-Transistor a) 3D-Darstellung b) Draufsicht
Die Polysilizium-Streifen dienen bei der Fertigung gleichzeitig als Maske zur Unterbrechung der Source- und Drain-Gebiete. Wenn in der Draufsicht auf beiden Seiten eines Polysilizium-Streifens ein hochdotiertes Gebiet liegt, beschreibt der Streifen ein Transistor-Gate mit einem Kanal darunter. Sonst ist ein Polysiliziumstreifen eine normale vom Halbleiter isolierte Verbindung. Polysilizium ist ein relativ schlechter Leiter und wird nur für kurze Verbindungen genutzt. Längere Verbindungen werden aus Metall, in der Regel Aluminium, hergestellt. Die Metalllagen befinden sich oberhalb der Halbleiterstrukturen und sind durch Isolationsschichten voneinander, von der Polysiliziumschicht und vom Halbleiter getrennt. Eine Verbindung zwischen zwei 4
Es gibt andere Realisierungen, z.B. solche, bei denen auch oder nur die NMOSTransistoren in Wannen angeordnet sind.
4.1 Entwurf und Modellierung von CMOS-Gattern
319
leitfähigen Schichten erfordert eine Durchkontaktierung. Das ist ein in die Isolationsschicht geätztes Loch, das beim Auftragen der darüberliegenden leitfähigen Schicht mit leitfähigem Material gefüllt wird. Sowohl die Durchkontaktierungen als auch die Metallbahnen werden in der Entwurfsansicht als Flächenelemente dargestellt. Abbildung 4.22 zeigt eine beispielhafte geometrische Anordnung der Halbleitergebiete, Polysilizium-Streifen, Durchkontaktierungen und Metallleiterbahnen für einen Inverter.
G2 x
UV y
G1
UV x
G1
G2 y
n-Wanne p+ -Gebiet n+ -Gebiet Polysilizium-Streifen Metallleiterbahn Durchkontaktierung
Abb. 4.22. Geometrischer Entwurf eines Inverters
Die unvermeidlichen geometrischen Fertigungsstreuungen verlangen Mindestgrößen und Mindestabstände, die in Form von geometrischen Entwurfsregeln vorgegeben sind. Bezugsmaß ist das Technologiemaß. Das ist die minimal fertigbare Strukturbreite. Die heutigen Fertigungsprozesse für digitale Schaltkreise haben ein Technologiemaß von deutlich unter einem Mikrometer. Jedes Flächenelement muss bestimmte Mindestabmessungen und Mindestabstände zu anderen Objekten haben. Die Gate-Streifen über den Source-Drain-Streifen müssen z.B. an beiden Enden überstehen. Sonst besteht die Gefahr, dass aufgrund von Fertigungsstreuungen bei einigen Transistoren am Rand eine ständig leitende Verbindung zwischen Source und Drain übrig bleibt5 . Ein Ziel des geometrischen Entwurfs besteht darin, die geometrischen Objekte möglichst platzsparend anzuordnen. Eine Reihenschaltung ist ein Streifen eines hochdotierten Gebiets, das von mehreren Gate-Streifen unterbrochen ist. Auch eine Parallelschaltung lässt sich platzsparend mit einer Streifenstruktur realisieren. Alle Verbindungen, die nicht durch hochdotierte Diffusionsgebiete oder Polysilizium-Streifen realisiert werden, sind über Durchkontaktierungen in den darüber liegenden Metallebenen entlangzuführen. Ein weiteres Prinzip zur Erzielung einer hohen Packungsdichte ist die Zusammenfassung der PMOS-Netzwerke auch mehrerer Gatter in einer Wanne. Abbildung 4.23 zeigt als Beispiel einen geometrischen Entwurf eines Komplexgatters mit Ausgabeinverter. Die Platzierung und Verdrahtung erfolgt rechnergestützt, oft vollautomatisch. Nach Abschluss des geometrischen Entwurfs werden aus der Größe und Anordnung der Flächenelemente der Transistorbestandteile und Verbindungen die relativen Transistorbreiten und Lastkapazitäten extrahiert, aus denen 5
Nicht mit einem Gate abgedeckte Verbindungen zwischen zwei hochdotierten Gebieten werden bei der Fertigung auch hoch dotiert.
320
4 Vom Transistor zur Schaltung
x1
G10
G8
x1
x2
x3
x4
n+ -Gebiet Polysilizium-Streifen
G9
G4
n-Wanne p+ -Gebiet
G9
G3
x4
y
G3 G4
G7
x3
G10
G2
G1
G7
G6
x1
G2
x3
UV
G8
G1
x2
G6
x4
G5
x2
G5
y
Metallleiterbahn Durchkontaktierung
Abb. 4.23. Geometrischer Entwurf eines Komplexgatters mit Ausgabeinverter
die Zeitparameter für die abschließenden Laufzeitanalysen und Simulationen berechnet werden. 4.1.9 Zusammenfassung und Übungsaufgaben Frei strukturierte CMOS-Gatter verwenden NMOS-Transistoren als geschaltete Verbindungen nach »0« und PMOS-Transistoren als geschaltete Verbindungen nach »1«. Reihenschaltungen bilden eine UND-Verknüpfung und Parallelschaltungen eine ODER-Verknüpfung. Am Gatterausgang setzen sich starke Signalwerte gegenüber schwachen Signalwerten und schwache Signalwerte gegenüber »hochohmig« durch. Aufsetzend auf diesen wenigen Grundregeln gibt es mehrere Schaltungsstrukturen für Logikgatter, z.B. FCMOS-Gatter und Gatter mit passiven oder geschalteten Pull-Up-Elementen. In einem FCMOS-Gatter realisiert das PMOS-Netzwerk die Zielfunktion mit einer Invertierung an allen Eingängen und das NMOS-Netzwerk die Zielfunktion mit einer Invertierung am Ausgang. Nach diesem Prinzip lassen sich alle logischen Funktionen mit UNDund ODER-verknüpften Eingabevariablen und invertierter Ausgabe nachbilden. Es ist auch möglich, ein Gatter so zu entwerfen, dass der Ausgang für bestimmte Eingaben hochohmig ist. Zur Einsparung von Transistoren kann das PMOS-Netzwerk durch ein Pull-Up- oder das NMOS-Netzwerk durch ein Pull-Down-Element ersetzt werden. Ein alternatives Konstruktionsprinzip ist die Nachbildung der Zielfunktion durch Multiplexer. Auf der geometrischen Beschreibungsebene werden die Transistoren und ihre Reihen- und Parallelschaltungen durch Flächenelemente für die hoch- und niedrigdotierten Halbleitergebiete, Gate-Streifen, Leiterbahnen und Durchkontaktierungen nachgebildet. Erst nach der Platzierung und Verdrahtung lassen sich die tatsächlichen Verzögerungen und der tatsächliche Flächenbe-
4.1 Entwurf und Modellierung von CMOS-Gattern
321
darf digitaler Schaltungen ermitteln. Weiterführende und ergänzende Literatur siehe [30, 32, 39, 46]. Aufgabe 4.1 Für den Datentyp type tZ01 is (’Z’, ’0’, ’1’);
soll ein Untertyp mit der Auflösungsfunktion »AufloesZ01(...)« definiert werden. Die Auflösungsfunktion soll sich wie folgt verhalten: • • •
Wenn alle Treiber »Z« liefern, sei der Gesamtwert »Z«. Wenn nur ein Treiber einen Wert ungleich »Z« liefert, sei der Wert dieses Treibers der Gesamtwert. Wenn mehrere Treiber einen Wert ungleich »Z« liefern, soll die Auflösungsfunktion die Simulation mit einer Fehlermeldung beenden.
Aufgabe 4.2 Entwerfen Sie ein FCMOS-Gatter mit der logischen Funktion y = (x1 ∧ x3 ) ∨ ((x1 ∨ x3 ) ∧ (x2 ∨ x4 )) Aufgabe 4.3 Gegeben ist die Transistorschaltung in Abb. 4.24. a) Füllen Sie die Wertetabelle in der Abbildung aus. Zur Vereinfachung der Rechnung sei unterstellt, dass ein eingeschalteter NMOS-Transistor mit einer Eins am Drain eine schwache Eins zum Source und ein eingeschalteter PMOS-Transistor mit einer Null am Drain eine schwache Null zum Source weitergibt. (Der Source-Anschluss ist bei einem NMOS-Transistor nach Abschnitt 4.1.1 der Kanalanschluss mit dem niedrigeren und bei einem PMOS-Transistor der Kanalanschluss mit dem höheren Potenzial). b) Welche logische Funktion besitzt das Gatter? T2 x1 T1
x2
UV x2 x1 z
z T3
yT3 T5 yT5
T4
yT4 T6 yT6
1 1
0 →H
0
→L
y
0 0 1 1
yT3 yT4 yT5 yT6
y
0 1 0 1
zu unterstellendes Verhalten f¨ ur einen NMOSTransistor, der eine Eins, und einen PMOSTransistor, der eine Null weiterleitet
Abb. 4.24. Schaltung zu Aufgabe 4.3
322
4 Vom Transistor zur Schaltung
Aufgabe 4.4 Entwerfen Sie eine Gatterschaltung mit der Funktion x E
0 0
0 1
1 0
1 1
y
1
Z
0
Z
Aufgabe 4.5 Gegeben sind die Skizze einer CMOS-Transistorschaltung und der Eingabesignalverlauf in Abb. 4.25. a) Zeichnen Sie die komplette Transistorschaltung. b) Skizzieren Sie den Ausgabesignalverlauf für den vorgegebenen Eingabesignalverlauf. c) Bei welcher Taktflanke muss das Ausgabesignal ausgewertet werden, damit das Gatter eine sinnvolle Funktion hat, und welche Funktion realisiert das Gatter dann? UV T x
4
T y
fn = x1 ∨ x2 ∨ x3 x4 T
1 0
0011 1000 0010
x y
1 0
mit x = x4 x3 x2 x1
Abb. 4.25. Schaltungsskizze und Eingabesignalverlauf zu Aufgabe 4.5
4.2 Zeitverhalten Die Simulation einer digitalen Schaltung soll außer dem logischen Verhalten auch das Zeitverhalten berechnen. Während digitale Simulationsmodelle das logische Verhalten in der Regel exakt beschreiben, wird das Zeitverhalten nur mehr oder weniger genau angenähert. Wir hatten bisher drei unterschiedlich genaue Modelle behandelt, die verzögerungsfreie Simulation, die das Zeitverhalten komplett ignoriert, die Simulation mit Verzögerungszeiten, bei der nur Richtwerte für die Zeitverläufe berechnet werden, und die Simulation mit Halte- und Verzögerungszeiten, die auch die Gültigkeitsfenster mitberechnet. Die Zeitparameter wurden einfach als vorgegeben betrachtet. In diesem Abschnitt werden zeitgenauere und aufwändigere Simulationsmodelle, die auf der Transistorebene aufsetzen, untersucht. Lernziel ist ein Grundverständnis über
4.2 Zeitverhalten
323
die Simulationsmöglichkeiten auf der Transistorebene, welche Parameter dafür benötigt und wie diese Parameter gewonnen werden. Der Abschnitt endet mit einigen Anmerkungen zur Optimierung des Verhältnisses aus Flächenbedarf und Geschwindigkeit für Logikgatter. 4.2.1 Simulation des elektrischen Verhaltens Das genauste Verfahren zur Bestimmung der Signalverläufe in einer digitalen Schaltung ist die zeitdiskrete Simulation des elektrischen Verhaltens. Eine zeitdiskrete Simulation berechnet die Signalverläufe in einem System, das durch Differentialgleichungen beschrieben ist, indem sie die Zeit in kleine diskrete Schritte unterteilt und so die Ableitungen nach der Zeit durch Differenzen und die Integrale über die Zeit durch Summationen annähert. Das durch Differenzialgleichungen beschriebene System kann die Umgebung einer digitalen Schaltung, aber auch das elektrische Modell der Schaltung selbst sein. Berechnet werden analoge Werteverläufe, in einer Schaltung üblicherweise Ströme und Spannungen [30]. In diesem Abschnitt soll an einem Beispiel die zeitdiskrete Simulation in VHDL vorgeführt werden. Das Beispiel ist ein Ringinverter aus CMOSGattern. Das Beispiel wird zeigen, dass die Programmierung von Differenzengleichungen in VHDL auch nicht schwieriger als in anderen Programmiersprachen ist. Das ist nützlich zu wissen, wenn es die Aufgabenstellung erfordert, im Testrahmen die Umgebung einer digitalen Schaltung, z.B. eine vor- oder nachgelagerte analoge Schaltung oder das Verhalten eines angesteuerten technischen Prozesses mitzusimulieren. Denn deren Modelle werden nach demselben Schema programmiert. Das elektrische Simulationsmodell des Ringinverters und das Ergebnis der Simulation werden weiterhin dazu dienen, ein Parametermodell für die ereignisgesteuerte Simulation von CMOS-Schaltungen zu entwickeln. Abbildung 4.26 zeigt die Beispielschaltung, einen Ring aus fünf Invertern. Jeder Inverter besteht aus zwei Transistoren und hat eine Lastkapazität CL am Ausgang. Die Lastkapazität ist kein Bauteil, sondern sie setzt sich aus den Kapazitäten der gesperrten pn-Übergänge am Gatterausgang, den Leitungskapazitäten und den Gate-Kapazitäten der angesteuerten Gattereingänge zusammen. Der Laststrom ist in der Abbildung gleich null gesetzt, weil an den Eingängen eines CMOS-Gatters nur kapazitive Umladeströme fließen und die Kapazitäten der Gattereingänge in der Lastkapazität CL mit berücksichtigt sind. Für eine Kapazität gilt, dass sich die Spannung proportional zum Integral über den Strom verhält: Z t 1 uC (t) = uC (t0 ) + · iC (τ ) · dτ (4.10) C t0 Bei einer zeitdiskreten Annäherung verändert sich die Spannung in jedem Zeitschritt um einen zum Strom und zur Größe des Zeitschritts ∆t = tn+1 − tn
324
4 Vom Transistor zur Schaltung uGS
uGS UV uGS uDS CL
G
G
iDP
iL = 0 x1
iDN
D S
UV
uDS
S D
uDS
x2
x3
x4
x0
iC CL
Versorgungsspannung Gate-Source-Spannung Drain-Source-Spannung Lastkapazit¨ at
iC iDN iDP iL
Umladestrom Drain-Strom NMOS-Transistor Drain-Strom PMOS-Transistor Laststrom
Abb. 4.26. Ringinverter
proportionalen Wert: uC (n + 1) ≈ uC (n) +
∆t · iC (n) C
(4.11)
Der Strom, der die Lastkapazität umlädt, setzt sich bei einem CMOS-Inverter als Signalquelle aus dem Drain-Strom des NMOS-Transistors und dem DrainStrom des PMOS-Transistors zusammen. Diese ergeben sich wiederum aus den Anschlussspannungen des Inverters und den Transistorparametern. Für den NMOS-Transistor gilt, dass für eine Gate-Source-Spannung kleiner oder gleich der Einschaltspannung UTN kein Strom fließt. Bei einer Gate-SourceSpannung uGS > UTN ist der Transistor eingeschaltet. Wenn die Differenz der Gate-Source-Spannung und der Drain-Source-Spannung größer als die Einschaltspannung ist uGS − uDS > UTN , arbeitet er im aktiven Bereich (Gleichung 4.1) und sonst im Einschnürbereich (Gleichung 4.2): uGS ≤ UTN 0 2 iDN = βN · (uGS − UTN ) · uDS − uDS uGS > UTN und uGS − uDS > UTN 2 2 0,5 · (uGS − UTN ) sonst (4.12) (UTN – Einschaltspannung, βN – Leitfähigkeitsparameter des NMOS-Transistors). Der PMOS-Transistor verhält sich genau wie der NMOS-Transistor, nur dass alle Ströme, Spannungen und Parameter umgekehrte Vorzeichen haben und dass alle Größenrelationen vertauscht sind. Darüber hinaus ist die GateSource-Spannung des PMOS-Transistors nicht die Eingangsspannung des Inverters, sondern die Differenz aus der Eingangsspannung und der Versorgungsspannung. Die Gate-Drain-Spannung ist in Analogie dazu die Differenz aus der Ausgangsspannung des Inverters und der Versorgungsspannung [30]: uGS ≥ UTP 0 u2DS iDP = βP · (uGS − UTP ) · uDS − 2 uGS < UTP und uGS − uDS < UTP 2 0,5 · (uGS − UTP ) sonst (4.13)
4.2 Zeitverhalten
325
(UTP – Einschaltspannung, βP – Leitfähigkeitsparameter des PMOS-Transistors). Der gesamte Berechnungsalgorithmus der zeitdiskreten Simulation des Ringinverters besteht aus einer Anfangswertzuweisung für die Spannungen über den Lastkapazitäten und einer Schleife »wiederhole für alle Zeitschritte«. In dieser Schleife ist eine Schleife »wiederhole für alle Inverter« eingebettet. Im innersten Schleifenkörper werden jeweils die beiden Drain–Ströme und aus ihnen der Spannungszuwachs über der Kapazität berechnet (Abb. 4.27). Anfangswertzuweisung an die Spannungen uC f¨ ur alle Inverter Wiederhole f¨ ur jeden Zeitschritt n Wiederhole f¨ ur alle f¨ unf Inverter Berechne iDN (n) Berechne iDP (n) Berechne uC (n + 1)
Abb. 4.27. Zeitdiskrete Simulation des Ringinverters aus Abb. 4.26
Die Zeitschrittweite ∆t einer zeitdiskreten Simulation ist so zu wählen, dass sich in einem Zeitschritt die Spannungen über den Kapazitäten nur geringfügig ändern. Bei einer Gatterverzögerung von 100 ps ist für die Zeitschrittweite ein Wert im Bereich von etwa 1 ps bis 10 ps zu wählen. Bei einer simulierten Zeit von 1 ns mit ∆t = 1 ps sind 1000 Zeitschritte zu simulieren. In jedem Zeitschritt sind im Beispiel für fünf Inverter insgesamt zehn Drain-Ströme und fünf neue Spannungswerte zu berechnen. Um das Verhalten einer Schaltung mit 1000 Gattern für eine ganze Sekunde zu simulieren, wäre der Rechenaufwand 1012 mal höher. Das bringt auch die schnellsten der heutigen Rechner an ihre Grenzen. Bei einer ereignisgesteuerten Simulation derselben Schaltung ist der mittlere zeitliche Abstand für die Neuberechnung eines Gatterausgabewertes deutlich größer als die Gatterverzögerungszeit, so dass der Rechenaufwand um mehrere Zehnerpotenzen geringer ist. Datentypen für die physikalischen Berechnungen VHDL unterstützt spezielle physikalische Datentypen (vgl. Abschnitt 3.2.3). Das nahe liegendste wäre, diese zu verwenden. Die physikalischen Typen haben jedoch einen entscheidenden Nachteil. Es sind ganzzahlige Typen. In den Gleichungen zur Berechnung der Drain-Ströme sind bis zu drei Faktoren zu multiplizieren. Angenommen die Spannungswerte werden in Millivolt gezählt, für den Leitfähigkeitsparameter β ist die Basiseinheit 1 µA V2 und der Zahlenbereich ist auf integer6 beschränkt, dann gäbe es z.B. bei folgender Produktbildung einen Zahlenbereichsüberlauf: 6
32-Bit-Zweierkomplementdarstellung
326
4 Vom Transistor zur Schaltung
1
mA · 1 V · 3 V 7→ 1000 · 1000 · 3000 ≥ 231 V2
Das Ergebnis ist nicht wie erwartet 3 · 109 , sondern 3 · 109 − 232 ≈ −1,3 · 109 . Theoretisch müsste vor jeder Multiplikation und Division die gedachte Kommaposition so korrigiert werden, dass keine Zahlenbereichsüberläufe auftreten können und gleichzeitig die maximal mögliche Anzahl von gültigen Stellen berechnet wird. Die Alternative ist, eine Gleitkommadarstellung zu verwenden. Gleitkommazahlen passen die Kommaposition automatisch an ihren Wert an und wurden genau für solche physikalischen Berechnungen entwickelt (vgl. Abschnitt 2.4.4). Um nicht auf die automatische Maßeinheitenkontrolle, die die physikalischen Typen bieten, verzichten zu müssen, werden für alle benötigten physikalischen Größen – Spannung, Strom, Kapazität und Leitfähigkeitsparameter – eigene reellwertige Datentypen, die dann als Gleitkommazahlen simuliert werden, definiert: type type type type
tSpannung is range -10.0 to 10.0; tStrom is range -0.1 to 0.1; tKapazitaet is range 0.0 to 1.0E-10; tBeta is range -0.1 to 0.1;
––––-
in in in in
Volt Ampere Farad A pro V2
⇒WEB-Projekt: P4.2/PhysSimPack.vhdl
Gleitkommatypen haben auch den Vorteil, dass die Bezugsmaßeinheit nicht die kleinste unterstützte Maßeinheit sein muss, sondern die Grundeinheit (Volt, Ampere etc.) sein darf. Berechnung der Drain-Ströme Abbildung 4.28 zeigt eine Package-Funktion zur Nachbildung von Gleichung 4.12 zur Berechnung des Drain-Stroms eines NMOS-Transistors. Aufrufparameter sind die Gate-Source-Spannung uGS , die Drain-Source-Spannung uDS , die Einschaltspannung UTN und der Leitfähigkeitsparameter βN . Rückgabewert ist der Drain-Strom. Im Invertermodell soll diese Funktion mit der Eingangsspannung ux als Gate-Source-Spannung und der Ausgangsspannung uy als Drain-Source-Spannung aufgerufen werden. Bei einer negativen Ausgangsspannung uy < 0 müssen Drain und Source ihre Rollen tauschen. Das realisiert die erste Fallunterscheidung, die bei einer negativen DrainSource-Spannung diese von der Gate-Source-Spannung abzieht, das Vorzeichen der Drain-Source-Spannung ändert und sich den Faktor »-1.0« für die Stromrichtungsumkehrung merkt. Danach folgt die Fallunterscheidung nach den Arbeitsbereichen und die Berechnung des Drain-Stroms mit der zugehörigen Gleichung. Die arithmetischen Operationen für Zahlen verlangen typgleiche Operanden. Um z.B. einen Leitfähigkeitsparameter mit einer Spannung zu multiplizieren, müssen beide Faktoren vorher in denselben Typ umgewandelt werden.
4.2 Zeitverhalten
327
function ID_NMOS(uGS, uDS, UTN: tSpannung; beta_N: tBeta) iDN return tStrom is variable i: tStrom := 1.0; uy = variable vuGS: tSpannung := uGS; ux = uDS variable vuDS: tSpannung := uDS; uGS begin if uDS<0.0 then –- Rollentausch Drain und Source vuGS := vuGS - vuDS; vuDS := -vuDS; i := -i; end if; if vuGS<=UTN then –- Sperrbereich return 0.0; elsif vuGS-vuDS
Zahlentypen sind untereinander eng verwandt, so dass für sie immer die implizit vereinbarten Konvertierungsfunktionen, die den Namen des Zieltyps haben, zur Verfügung stehen (vgl. Abschnitt 3.2.1). Im Beispiel erfolgt die Konvertierung aller Eingabegrößen in den Rückgabetyp der Funktion »tStrom«. Ein PMOS-Transistor verhält sich genau wie ein NMOS-Transistor, nur dass die Vorzeichen der Spannungen und Ströme umgekehrt sind. Die nachfolgende Funktion für die Berechnung von iDP ruft entsprechend nur die Funktion für die Berechnung des Drain-Stroms für einen NMOS-Transistor mit negierten Parametern auf und gibt den negierten Funktionswert zurück: function ID_PMOS(uGS, uDS, UTN: tSpannung; beta_P: tBeta) return tStrom is begin return -ID_NMOS(-uGS, -uDS, -UTP, -beta_P); end function;
Das Simulationsmodell des Inverters Das zeitdiskrete Funktionsmodell eines Inverters ist eine Entwurfseinheit mit einem Eingangssignal und einem Ausgabesignal, beide vom Typ »tSpannung, mit der Versorgungsspannung UV , der Zeitschrittweite ∆t für die Simulation, den Einschaltspannungen UTN und UTP , den Leitfähigkeitsparametern βN und βP , der Lastkapazität CL und dem Anfangswert für die Ausgangsspannung Uinit als Parameter (Abb. 4.29). Zum Simulationsbeginn wird an das Ausgabesignal der Anfangswert zugewiesen und einen Zeitschritt gewartet.7 Nach dem ersten Zeitschritt und je7
Der Warteschritt ist erforderlich, damit die Anfangswertzuweisung wirksam wird.
328
4 Vom Transistor zur Schaltung entity Inverter is generic ( UV: tSpannung := 5.0; dt: delay_length UTN: tSpannung := 1.0; beta_N: tBeta UTP: tSpannung := -1.0; beta_P: tBeta CL: tKapazitaet := 2.0E-14; U_init: tSpannung port(ux: in tSpannung; uy: out tSpannung); end entity;
:= 2 ps; := 1E-3; := -1E-3; := 0.0;
architecture PhySim of Inverter is UV uGS uDS begin iDP process x y variable vuy:tSpannung := U_init; iDN iC u u x y variable iDN, iDP: tStrom; uDS CL uGS constant eps: tSpannung := 1.0E-3; begin uy <= vuy; wait for dt; iDP := ID_PMOS(ux-UV, vuy-UV, UTP, beta_P); iDN := ID_NMOS(ux, vuy, UTN, beta_N); vuy:=vuy - tSpannung(real(iDP + iDN) * real(dt/fs) * 1.0E-15/real(CL)); end process; end architecture; ⇒Web-Projekt: P4.2/Inverter.vhdl Abb. 4.29. Physikalisches Simulationsmodell für einen Inverter
dem weiteren Zeitschritt werden die Drain-Ströme und mit ihnen die neue Ausgangsspannung berechnet. Die Operanden der arithmetischen Operationen werden in diesem Beispiel alle nach real und das Ergebnis nach »tSpannung« konvertiert. Das Simulationsmodell ließe sich noch dahingehend verbessern, dass die Wartezeit bis zum nächsten Berechnungsschritt aus der Größe des Umladestroms und der Lastkapazität berechnet wird. Der komplette Ringinverter Ein Ringinverter besteht aus einer ungeraden Anzahl von Invertern, die zu einem Ring verschaltet sind (Abb. 4.30 a). Das Simulationsmodell in Abb. 4.30 b ist eine Strukturbeschreibung (vgl. Abschnitt 1.2.5). Für jeden der Inverter G1 bis G5 wird eine Instanz erzeugt. Die verbindenden Signale x0 bis x4 haben den Typ »tSpannung«. Eines der Gatter – im Beispiel G1 – muss einen etwas anderen Anfangswert haben, damit der Schwingungsvorgang bei der Simulation in Gang kommt8 . Ein Spannungsanstieg am Eingang des ersten Inverters bewirkt einen verzögerten Abfall am Eingang des zweiten Inverters, dieser einen verzögerten Anstieg am Eingang des dritten Inverters etc. Der Ausgang des letzten Inverters reagiert nach fünf Gatterverzögerungen mit einem Spannungsabfall, der rückgekoppelt auf den Eingang des ersten Inverters 8
In der richtigen Schaltung genügen die Bauteilstreuungen oder das thermische Rauschen.
4.2 Zeitverhalten
329
nach fünf weiteren Gatterverzögerungen wieder einen Anstieg der Ausgabespannung zur Folge hat. Nach der Einschwingzeit sind an allen Invertereingängen und -ausgängen zeitversetzte periodische Schwingungen zu beobachten (Abb. 4.30 c). Zur Beendigung der Simulation nach einer definierten Zeit wird der Simulator mit einer Abbruchzeit aufgerufen: ghdl -r Ringinverter --wave=Ringinverter.ghw --stop-time=500ps
entity Ringinverter is end entity; G1
x0 architecture PhySim of Ringinverter is signal x0,x1,x2,x3,x4: tSpannung; a) begin G1: entity work.Inverter(PhySim) generic map(u_init=>1.0) port map(x0, x1); G2: entity work.Inverter(PhySim) port map(x1, x2); x0 G3: entity work.Inverter(PhySim) x1 port map(x2, x3); x2 G4: entity work.Inverter(PhySim) x3 port map(x3, x4); x4 G5: entity work.Inverter(PhySim) 0 port map(x4, x0); b) end architecture; c)
x1
G2
x2
G3
t in ps
x3
G4
x4
G5
300
⇒Web-Projekt: P4.2/Ringinverter.vhdl
Abb. 4.30. Zeitdiskrete Simulation des gesamten Ringinverters a) Schaltung b) Strukturbeschreibung c) Simulationsergebnis
4.2.2 Verzögerungsparameter Eine ereignisgesteuerte Simulation benötigt Zeitparameter, die aus dem elektrischen Verhalten der Logikschaltungen zu bestimmen sind. Das Lernziel in diesem Abschnitt ist, eine Vorstellung zu vermitteln, wie heuristische Modelle zur Berechnung von Zeitparametern aussehen und konstruiert werden. Ausgangspunkt ist das Simulationsergebnis für den Ringinverter aus dem Beispiel zuvor. Das entwickelte Modell dient anschließend auch wieder als Beispiel für weitere Untersuchungen. Abbildung 4.31 zeigt die Spannungsverläufe am Ein- und am Ausgang des untersuchten Inverters aus dem vorherigen Abschnitt. Im grau unterlegten Bereich ist der Signalwert ungültig, darunter »0« und darüber »1«. An diesem Zeitverlauf lassen sich mehrere Arten von Zeitparametern definieren: Richtwerte, Verzögerungszeiten, Haltezeiten und Umschaltzeiten. Der Richtwert für die Verzögerung ist die Zeit zwischen einer 50%-igen Eingabeänderung und einer 50%-igen Ausgabeänderung. Die Haltezeit ist die Zeit, die der alte gültige
330
4 Vom Transistor zur Schaltung
Signalwert am Ausgang nach einer Eingabeänderung erhalten bleibt. Die Verzögerungszeit ist die Zeit, die nach Erreichen neuer gültiger Eingaben vergeht, bis der Ausgang seinen neuen gültigen Wert annimmt. Die Umschaltzeit ist die Zeit, die der Signalwert bei einem Wechsel zwischen »0« und »1« ungültig ist. Wie aus der Abbildung hervorgeht, sind die Halte- und die Verzögerungszeiten von einem Inverter etwa so groß wie die Richtwerte. Die Umschaltzeiten verhalten sich überschlagsweise proportional dazu. Für die Entwicklung eines empirischen Modells genügt es deshalb vorerst, nur die Richtwerte zu betrachten. UV ux x
UV
0
1 0
y 10 UV uy
x ux
td.ein tein th.ein tx.ein
td.aus taus th.aus tx.aus
td.ein , td.aus tein , taus th.ein , th.aus tx.ein , tx.aus
0 0
100
200
t in ps
CL
y uy
Verz¨ogerungszeiten Richtwerte Haltezeiten Umschaltzeiten ung¨ ultig Sicherheitsbereich (Wert ung¨ ultig)
Abb. 4.31. Zeitparameter für das Verzögerungsverhalten
Aus den Gleichungen für die zeitdiskrete Simulation lassen sich einige grundlegende Beziehungen ableiten. Aus Gleichung 4.11 ist erkennbar, dass sich die Änderungsgeschwindigkeit der Gatterausgangsspannung proportional zur Größe der Umladeströme und umgekehrt proportional zur Größe der Lastkapazität verhalten. Die Umladeströme verhalten sich sowohl im aktiven Bereich als auch im Einschnürbereich proportional zu den Leitfähigkeitsparametern βN bzw. βP der eingeschalteten Transistoren (Gleichungen 4.12 und 4.13). Die Leitfähigkeitsparameter verhalten sich wiederum proportional zur relativen Transistorbreite bN bzw. bP . Die relative Transistorbreite ist das Verhältnis aus der Breite des schaltbaren Kanals zu seiner Länge (Abb. 4.32). Die Kanallängen lN und lP werden in der Regel so kurz wie möglich gewählt. Die absoluten Breiten wN und wP und mit ihnen auch die relativen Breiten bN bzw. bP sind wählbare Entwurfsparameter zur Einstellung des Zeitverhaltens. Die Ein- und die Ausschaltzeiten der Gatter verhalten sich wiederum umgekehrt proportional zu den Änderungsgeschwindigkeiten der Gatterausgangsspannung. Insgesamt gilt tein ≈ kP ·
CL bP
(4.14)
4.2 Zeitverhalten p-Substrat B
n-Wanne p stark dotiert (p+)
S lN D
wN
wP D
G
n stark dotiert (n+)
lP
S
331
B
G
Polysilizium (Gate) bN =
relative Transistorbreite:
wN lN
bP =
wP lP
Abb. 4.32. Relative Transistorbreite
taus ≈ kN ·
CL bN
(4.15)
Die Proportionalitätsfaktoren kP , kN sind messtechnisch oder durch eine zeitdiskrete Simulation zu bestimmen. Inverter mit einer großen relativen Breite laden ihre Lastkapazität schneller um, haben aber aufgrund ihrer größeren Fläche auch größere Ein- und Ausgangskapazitäten. Wegen der etwa doppelt so großen Beweglichkeit der Elektronen im Kanal von NMOS-Transistoren gegenüber der Löcherbeweglichkeit im Kanal von PMOS-Transistoren ist bei gleicher Versorgungsspannung, gleicher Temperatur etc. der Proportionalitätsfaktor kp für die Einschaltzeit etwa doppelt so groß wie der Proportionalitätsfaktor kN für die Ausschaltzeit: kP ≈ 2 · kN
(4.16)
Damit die Einschaltzeit etwa gleich der Ausschaltzeit ist, muss der PMOSTransistor eines Inverters etwa doppelt so breit sein wie der NMOS-Transistor [25, 30, 39]. Die Lastkapazität eines Gatters setzt sich aus der Kapazität am Gatterausgang CA , der Leitungskapazität CLtg und den Eingangskapazitäten CE aller angesteuerten Gattereingänge zusammen. Eingesetzt in die Gleichungen 4.14 und 4.15 und unter Einbeziehung von Gleichung 4.16 resultiert daraus für die Ein- und Ausschaltzeit ! NL X 2 · kN tein ≈ · CA + CLtg + ·CE.i (4.17) bP i=1 taus
kN ≈ · bN
CA + CLtg +
NL X
!
CE.i
(4.18)
i=1
(NL – Lastanzahl, Anzahl der Gattereingänge, die der Gatterausgang ansteuert). Die in der Praxis eingesetzten Modelle fassen die Produkte der unterschiedlichen Kapazitäten mit dem Proportionalitätsfaktor kN zu Zeitkonstanten zusammen, die die eigentlichen Modellparameter bilden:
332
4 Vom Transistor zur Schaltung
tein
taus
2 ≈ · bP 1 ≈ · bN
τA + τLtg +
NL X
! τL.i
i=1
τA + τLtg +
NL X
(4.19) !
τL.i
(4.20)
i=1
(τA – Grundverzögerung; τLtg – leitungsabhängige Verzögerung; τL.i – lastabhängige Verzögerungen; bN , bP – relative Transistorbreite des NMOS- bzw. des PMOS-Transistors, über den die Lastkapazität umgeladen wird). 4.2.3 Parameterbestimmung mit Ringinvertern Die Grundverzögerung, die leitungsabhängigen Verzögerungen und die lastabhängigen Verzögerungen aus den Gleichungen 4.19 und 4.20 lassen sich entweder aus gemessenen oder aus mit einer zeitdiskreten Simulation berechneten Signalverläufen bestimmen. Das Messen der Signalverläufe ist jedoch problematisch, allein schon, weil ein angeschlossenes Messgerät die Lastkapazitäten so stark vergrößern würde, dass das Messergebnis keinen Aussagewert mehr hätte. Eine praxistaugliche Alternative ist die indirekte Messung mit Hilfe spezieller Teststrukturen. Die einfachste Schaltung zur experimentellen Bestimmung der Verzögerungsparameter ist der Ringinverter aus Abschnitt 4.2.1. Ein Ringinverter besteht ganz allgemein aus einer ungeraden Anzahl invertierender Gatter, die zu einem Ring verschaltet sind, und ein periodisches Rechtecksignal erzeugen. Die Periode des Rechtecksignals ist gleich der Summe der Ein- und Ausschaltzeiten aller Inverter im Ring: TP =
N Inv X
tein.i + taus.i
(4.21)
i=1
Damit die Eingangskapazität des Messgerätes für die Schwingungsperiode das Messergebnis nicht verfälscht, ist ein weiteres Gatter zur Entkopplung der Ausgabe erforderlich (Abb. 4.33). Im Beispiel in Abb. 4.33 a besteht der Ring aus drei identischen Invertern mit einem gleichfalls identischen Inverter zur Entkopplung der Ausgabe. Die relative Kanalbreite der NMOS-Transistoren sei eins und die der PMOSTransistoren zwei. Die leitungsabhängigen Verzögerungszeiten sollen vernachlässigt werden. Unter diesen Annahmen vereinfachen sich die Gleichungen 4.19 und 4.20 zu tein ≈ taus ≈ τA + NL · τL (4.22) (NL – Lastanzahl). Die ersten beiden Inverter haben eine und der dritte Inverter hat zwei Lasten. Eingesetzt in Gleichung 4.21 beträgt die Dauer einer Schwingungsperiode
4.2 Zeitverhalten x1
x2 t...1
a) x1
x3 t...2
x2
y t...3
t...4
x3
x1 x2 x3
1 0 1 0 1 0
y
1 0
y
b)
333
taus.1 tein.2 taus.3 tein.1 taus.2 tein.3 tein.4
c)
taus.4
tein.4
TP
Abb. 4.33. Ringinverter a) mit minimaler Last b) mit doppelter Last c) Signalverläufe
TP1 ≈ 2 · (τA + τL ) + 2 · (τA + τL ) + 2 · (τA + 2 · τL ) | {z } | {z } | {z } tein.1 +taus.1
tein.2 +taus.2
= 6 · τA + 8 · τL
tein.3 +taus.3
(4.23)
Eine getrennte Bestimmung der beiden Modellparameter τA und τL erfordert zwei Ringinverter, für die sich die Schwingungsperiode in unterschiedlicher Weise aus den Modellparametern berechnet. In Abb. 4.33 b treibt jedes Gatter die doppelte Anzahl von Lasten. Die Dauer einer Schwingungsperiode des geänderten Ringinverters beträgt TP2 ≈ 6 · τA + 16 · τL
(4.24)
Die Gleichungen 4.23 und 4.24 bilden ein lösbares Gleichungssystem. Die gesuchten Modellparameter errechnen sich wie folgt: 1 · (2 · TP1 − TP2 ) 6 1 τL = · (TP2 − TP1 ) 8
τA =
(4.25) (4.26)
(TP1 – gemessene Schwingungsdauer des Ringinverters mit einfacher Last; TP2 – gemessene Schwingungsdauer des Ringinverters mit doppelter Last). Ein Verzögerungsmodell mit mehr als zwei Parametern würde entsprechend die Fertigung einer Testschaltung mit mehr als zwei Ringinvertern erfordern, um alle Modellparameter getrennt voneinander zu bestimmen. 4.2.4 Gatter mit mehreren Eingängen Bisher wurde nur das einfachste Gatter – der Inverter – betrachtet. Gatter mit mehreren Eingängen verlangen eine geringfügige Modellerweiterung. In einem Gatter mit mehreren Eingängen wird die Lastkapazität über Parallel- und Reihenschaltungen mehrerer Transistoren umgeladen. Eine Parallelschaltung und eine Reihenschaltung eingeschalteter MOS-Transistoren lässt sich in einen funktionsgleichen eingeschalteten Einzeltransistor umrechnen.
334
4 Vom Transistor zur Schaltung
Parallelschaltung In einer Parallelschaltung von MOS-Transistoren mit gleicher Einschaltspannung UT addieren sich die Drain-Ströme. Die Gate-Source-Spannungen, die Drain-Source-Spannungen und auch alle anderen spannungsabhängigen Terme in den Gleichungen 4.12 und 4.13 sind gleich. Eine Parallelschaltung von zwei eingeschalteten MOS-Transistoren verhält sich entsprechend wie ein Einzeltransistor mit einem Ersatzparameter (Abb. 4.34) βErs = β1 + β2
(4.27)
Der Ersatztransistor hat die relative Transistorbreite bErs = b1 + b2
Parallelschaltung D
funktionsgleiche Ersatzschaltung
iD = (β1 + β2 ) · f (uGS , uDS )
D
iD1 = β1 · f (uGS , uDS ) iD2 = β2 · f (uGS , uDS )
G
(4.28)
G uGS
uDS
iD = βErs · f (uGS , uDS ) uDS
S
uGS S
Abb. 4.34. Ersatzschaltung für zwei eingeschaltete parallele MOS-Transistoren
Reihenschaltung Bei der Reihenschaltung addieren sich die Kanallängen. Die resultierende Stromgleichung lässt sich über eine physikalische Betrachtung herleiten. In Reihe geschaltete Transistoren sind räumlich durch ein gut leitendes hochdotiertes Gebiet, das für den einen Transistor den Drain- und für den anderen Transistor den Source-Anschluss darstellt, und optional durch Leiterbahnen getrennt. Der Spannungsabfall über diesen Verbindungen kann vernachlässigt werden, so dass sich elektrisch kein Unterschied ergibt, wenn beide eingeschalteten Kanäle gedanklich direkt hintereinander angeordnet werden (Abb. 4.35). Wenn die Transistoren gleich breit sind w1 = w2 verhält sich der Gesamttransistor wie ein Transistor, dessen Kanallänge gleich der Summe der Einzelkanallängen ist:
4.2 Zeitverhalten S
UDS
D
S
UDS
UGS Gate 2 n+
U (y)
G Gate
ID n+
n+
identisch, wenn UT und w gleich sind
UDS
0
l1
l1
Kanal U (y)
p-Substrat
U (y) 0
D
UGS
Gate 1 Kanal
n+
335
l1 + l2
y
ID n+ p-Substrat
UDS U (y) 0
l1 + l2
0
y
Abb. 4.35. Modelltransformation zur Bestimmung des elektrischen Verhaltens einer Reihenschaltung gleichbreiter eingeschalteter MOS-Transistoren
lErs = l1 + l2
(4.29)
Bei einer Reihenschaltung unterschiedlich breiter Transistoren ist diese einfache Addition nicht zulässig. Man kann aber gedanklich die Kanalbreite und die Kanallänge im selben Verhältnis vergrößern oder verkleinern, ohne dass sich laut Modell die Strom-Spannungs-Beziehung am Transistor ändert. Der gleichbreite zweite Ersatztransistor hat die Kanallänge l2∗ = l2 ·
w1 w2
(4.30)
Eingesetzt in Gleichung 4.29 ergibt sich, dass der Kehrwert der relativen Transistorbreite des Ersatztransistors gleich der Summe der Kehrwerte der relativen Transistorbreiten der Einzeltransistoren ist: 1 bErs
=
l1 + l2 · w1
w1 w2
=
1 1 + b1 b2
bErs =
b1 · b2 b1 + b2
(4.31)
Bei einer Parallelschaltung eingeschalteter MOS-Transistoren addieren sich die relativen Transistorbreiten und bei einer Reihenschaltung die Kehrwerte der relativen Transistorbreiten. 4.2.5 Simulation mit geschalteten Transistorbreiten In einem Gatter mit mehreren Eingängen verhalten sich die geschalteten NMOS- und PMOS-Netzwerke wie Ersatztransistoren, deren relative Breite davon abhängt, welche Transistoren eingeschaltet sind, welche möglicherweise eingeschaltet sind und welche nicht eingeschaltet sind. Ein ausgeschalteter Transistor hat die relative Breite null. Für einen eingeschalteten Transistor sei die relative Transistorbreite ein bekannter Wert b. Wenn der Eingabewert ungültig ist, schaltet der Transistor vielleicht ein. Für die relative Transistorbreite lässt sich in diesem Fall nur ein Bereich angeben. Er ist mindestens
336
4 Vom Transistor zur Schaltung
null und maximal gleich der tatsächlichen relativen Breite b. Bei einer Parallelschaltung addieren sich die relativen Transistorbreiten und damit auch die Minima und Maxima. Bei einer Reihenschaltung gilt dasselbe für die Kehrwerte (Abb. 4.36). Konvertierung
Parallelschaltung
x, b ⇒
bmin bmax
b1.min b1.max
bmin
x: 0 0
1 b
X 0
b1.min + b2.min
bmax
0
b
b
b1.max + b2.max
b2.min b2.max
Reihenschaltung b1.min b1.max
x
b2.min b2.max
b1.min ·b2.min b1.min +b2.min b1.max ·b2.max b1.max +b2.max
b bmin bmax
Logikwert am Gate (0,1,X) relative Transistorbreite minimale relative Breite maximale relative Breite
Abb. 4.36. Basisoperationen zur Berechnung der relativen Transistorbreite eines geschalteten Transistornetzwerks aus den logischen Eingabewerten
Der Datentyp für die relative Transistorbreite sei ein Verbund aus dem Maximalwert und dem Minimalwert: type tRelB is record bmax: real; bmin: real; end record;
Die Berechnung der relativen Transistorbreiten basiert auf drei verschiedenen Operationen: • • •
der Konvertierung eines logischen Eingabewertes für einen Einzeltransistor in einen Bereich der relativen Transistorbreite, dem ODER-Operator für die Parallelschaltung und dem UND-Operator für die Reihenschaltung.
Die Konvertierungsfunktion setzt bei einer »0« am Eingang die relative Breite auf null, bei einer »1« auf b und sonst, wenn die Eingabe ungültig ist, auf einen Bereich zwischen null und b. Der ODER-Operator addiert jeweils die Minimalund die Maximalwerte der relativen Transistorbreiten und der UND-Operator die Kehrwerte (Abb. 4.37). Die Haltezeit, die das Ausgabesignal des Gatters seinen aktuellen gültigen Wert nach einer Eingabeänderung mindestens noch behält, ergibt sich abschätzungsweise über die Gleichungen 4.19 und 4.20 mit den Maximalwerten für die relative Transistorbreite – für den Einschaltvorgang der des PMOSNetzwerks und für den Ausschaltvorgang der des NMOS-Netzwerks: th.ein ≈
2·τ bP.max
;
th.aus ≈
τ bN.max
mit τ = τA + τLtg +
NL X i=1
τL.i
(4.32)
4.2 Zeitverhalten –- Logikwert ⇒ relative Transistorbreite function To_RelB(x: std_logic; b: real) return tRelB is begin x ⇒ case x is when ’0’ => return (bmax => 0.0, bmin => 0.0); when ’1’ => return (bmax => b, bmin => b); when others => return (bmax => b, bmin => 0.0); end case; end function; –- Operator für die Parallelschaltung function "or"(x1, x2: tRelB) return tRelB is begin return (bmax=>x1.bmax+x2.bmax, bmin=>x1.bmin+x2.bmin); end function;
337
bmin , bmax
x1
x2
–- Hilfsfunktion function KehrwertAdd(a, b: real) return real is begin if a=0.0 or b=0.0 then return 0.0; end if; return a*b/(a+b); end function; –- Operator für die Reihenschaltung x1 x2 function "and"(x1, x2: tRelB) return tRelB is begin return (bmax => KehrwertAdd(x1.bmax, x2.bmax), bmin => KehrwertAdd(x1.bmin, x2.bmin)); end function; ⇒Web-Projekt:P4.2/bRel pack.vhdl Abb. 4.37. Funktionen und Operatoren zur Berechnung der relativen Transistorbreite geschalteter Transistornetzwerke
Der Folgewert »1« wird nur sicher erreicht, wenn das NMOS-Netzwerk ausgeschaltet ist. Die maximale Einschaltverzögerung verhält sich dabei umgekehrt proportional zur minimalen relativen Transistorbreite des PMOS-Netzwerks. Analog dazu verhält sich die Ausschaltverzögerung unter der Voraussetzung, dass das PMOS-Netzwerk sperrt, umgekehrt proportional zur Mindestbreite des eingeschalteten NMOS-Netzwerks:
td.ein ≈
2·τ bP.min
td.aus ≈
τ bN.min
mit τ = τA + τLtg +
NL X
τL.i
(4.33)
i=1
Als Beispiel soll das Simulationsmodell für das komplexe FCMOS-Gatter in Abb. 4.38 entwickelt werden. Die relativen Transistorbreiten der Einzeltransistoren und die Zeitkonstante τ seien Parameter der Entwurfseinheit. Die Eingabesignale und das Ausgabesignal sollen den Typ std_logic erhalten, um auch den Pseudo-Wert für ungültig darstellen zu können. Das Verhalten selbst wird durch einen kombinatorischen Prozess mit allen Eingabesignalen
338
4 Vom Transistor zur Schaltung entity Komplexgatter is generic(tau: delay_length := 100 ps; bn1, bn2, bn3, bn4, bn5: real := 1.0; bp1, bp2, bp3, bp4, bp5: real := 1.0); port(x1, x2, x3, x4, x5: in std_logic; y: out std_logic); end entity;
x1
x2
x4
UV
x3 x5 architecture Sim of Komplexgatter is y x5 x4 signal yy; std_logic; begin x2 process(x1, x2, x3, x4, x5) x1 x3 variable n, p: tRelB; begin –- relative Transistorbreite p := ((To_RelB(not x1, bn1) or To_RelB(not x2, bn2)) and To_RelB(not x3, bn3)) or (To_RelB(not x4, bn4) and To_RelB(not x5, bn5)); n := ((To_RelB(x1, bn1) and To_RelB(x2, bn2)) or To_RelB(x3, bn3)) and (To_RelB(x4, bn4) or To_RelB(x5, bn5)); –- Zuweisung Folgewert ’X’ nach th if yy=’0’ and p.bmax>0.0 then yy <= ’X’ after (2.0/p.bmax) * tau; elsif yy=’1’ and n.bmax>0.0 then yy <= ’X’ after (1.0/n.bmax) * tau; end if; –- Zuweisung des Folgewerts ’1’ bzw. ’0’ nach td if n.bmax=0.0 and p.bmin>0.0 then yy <= transport ’1’ after (2.0/p.bmin) * tau; elsif p.bmax=0.0 and n.bmin>0.0 then yy <= transport ’0’ after (1.0/n.bmin) * tau; end if; y <= yy; end process; end architecture; ⇒Web-Projekt: P4.2/Komplexgatter.vhdl
Abb. 4.38. Simulation mit geschalteten Transistorbreiten
in der Weckliste beschrieben. Aus den Reihen- und Parallelschaltungen sind für die beiden Netzwerke folgende logische Funktionen ablesbar: n = ((x1 x2 ) ∨ x3 ) (x4 ∨ x5 ) p = ((¯ x1 ∨ x ¯2 ) x ¯3 ) ∨ (¯ x4 x ¯5 ) Im Simulationsmodell werden die logischen Eingabewerte zuerst in relative Transistorbreiten konvertiert und dann mit den überladenen UND- und ODER-Operatoren in derselben Weise wie in den logischen Ausdrücken zusammengefasst. Die Berechnung der Wertänderung für das Ausgabesignal be-
4.2 Zeitverhalten
339
darf mehrerer Fallunterscheidungen, bei denen auch der aktuelle Ausgabewert des Gatters zu berücksichtigen ist. Der Ausgabesignalverlauf wird deshalb in einem internen Signal der Entwurfseinheit berechnet und nebenläufig an das Ausgabesignal zugewiesen. Wenn der Ausgabewert »0« und die maximale relative Transistorbreite des PMOS-Netzwerks größer null oder der Ausgabewert »1« und die maximale relative Transistorbreite des NMOS-Netzwerks größer null ist, wird das Ausgabesignal nach der Haltezeit, die sich aus Gleichung 4.32 ergibt, invalidiert. Die Zuweisung eines neuen gültigen Wertes erfolgt nur, wenn das eine Netzwerk garantiert ausgeschaltet ist und das andere Netzwerk eine berechnete Mindestbreite größer null hat. Die Zuweisung erfolgt nach dem Transportverzögerungsmodell, damit schwebende Änderungen nach »ungültig« nicht gelöscht werden, und mit einer Verzögerungszeit nach Gleichung 4.33. Das Beispiel hat gezeigt, dass eine Simulation mit Laufzeittoleranzen auch auf der Transistorebene möglich ist. Eine Simulation mit geschalteten Transistorbreiten ist genauer und aufwändiger als die in Abschnitt 1.3.3 eingeführte Simulation mit nur einer Halte- und einer Verzögerungszeit je Gatter oder Funktionsblock. Größere Genauigkeit bedeutet, dass die berechneten Ungültigkeitsfenster, in denen über den Signalwert nichts ausgesagt werden kann, schmaler sind. Dadurch lässt sich auch noch für wesentlich laufzeitkritischere Schaltungen feststellen, ob sie zuverlässig sind oder ob sie möglicherweise ungültige Daten weiterverarbeiten. 4.2.6 Reduktion auf zwei Zeitparameter In den Simulationsmodellen der vergangenen Abschnitte wurden die Zeitparameter immer als vorgegeben betrachtet. Das Modell der geschalteten Transistorbreiten erlaubt es nun endlich, einen Algorithmus zur Berechnung dieser Parameter anzugeben. In dem in Abschnitt 1.3.3 eingeführten Simulationsmodell mit Laufzeittoleranzen hat jedes Gatter zwei Zeitparameter: • •
eine Haltezeit th , die nach jeder Eingabeänderung mindestens vergeht, bis sich der Ausgabewert ändert, und eine Verzögerungszeit td , nach der garantiert nach Anlegen eines neuen gültigen Eingabewertes der neue gültige Ausgabewert verfügbar ist.
In dem Modell der geschalteten Transistorbreiten ist die Haltezeit des Gatters die kürzest mögliche Haltezeit, d.h. der Quotient der Zeitkonstanten des Gatters durch die maximale relative Breite, über die die Lastkapazität umgeladen werden kann. Wegen der geringeren Löcherbeweglichkeit gehen die relativen Breiten der Pfade durch das PMOS-Netzwerk nur mit dem halben Wert ein: th ≈
τ max bN.i ,
bP.i 2
340
4 Vom Transistor zur Schaltung
Die Verzögerungszeit des Gatters ist in Analogie dazu der Quotient der Zeitkonstanten durch die minimale relative Breite, über die die Umladung erfolgen kann, wobei auch hier die relativen Breiten der Pfade durch das PMOSNetzwerk nur mit dem halben Wert zu berücksichtigen sind: td ≈
τ min bN.i ,
bP.i 2
Die Zeitkonstante τ ist dabei, wie in Abschnitt 4.2.2 hergeleitet, die Summe aus einer Grundverzögerung, einer leitungsabhängigen Verzögerung und einer lastabhängigen Verzögerung. Die Halte- und die Verzögerungszeiten hängen folglich nicht nur vom Gattertyp, sondern auch erheblich von der Anzahl und Art der nachfolgenden Gatter und der Leitungsführung ab. In Abb. 4.39 soll die Gatterhalte- und die Gatterverzögerungszeit für das Verarbeitungsgatter bestimmt werden. Die relativen Transistorbreiten der NMOS-Transistoren seien alle gleich bN und die der PMOS-Transistoren gleich bP . Die Lastkapazität wird im ungünstigsten Fall über eine Reihenschaltung aus drei PMOS-Transistoren und im günstigsten Fall über eine Reihenschaltung von drei mal zwei parallelen PMOS-Transistoren aufgeladen. Die Einschaltzeit liegt entsprechend im Bereich 3·τ 6·τ ≤ tein ≤ bP bP Die Entladung der Lastkapazität erfolgt im ungünstigsten Fall über eine einzige Reihenschaltung aus zwei NMOS-Transistoren und im günstigsten Fall über drei parallele Reihenschaltungen aus zwei NMOS-Transistoren: 2·τ 2·τ ≤ taus ≤ 3 · bN bN Für die weitere Rechnung wird eine Angabe über das Verhältnis der relativen Transistorbreiten der NMOS- und der PMOS-Transistoren benötigt. Für die Festlegung dieses Verhältnisses gibt es eine allgemeine Richtlinie. Sie lautet, dass für ein optimales Verhältnis aus Gattergeschwindigkeit und Flächenverbrauch die Maxima der Einschaltzeit und der Ausschaltzeit etwa denselben Wert haben sollten: Verarbeitungsgatter x1
x2
x4
x3
x2
x5
x1 x2
x4 x3
G1 y x2 x5
CA
Inverter zur Minderung der Verz¨ogerung und des Fl¨ achenbedarfs UV G2 G3 CE
angesteuerte Gatter (Lasten)
Abb. 4.39. Beispielschaltung zur Abschätzung der Halte- und der Verzögerungszeit
4.2 Zeitverhalten
341
6·τ 2·τ ≈ bP bN Für das Beispiel folgt daraus bP /bN = 3. Eine sinnvolle Wahl wäre bN = 1;
bP = 3
Mit diesen Festlegungen ergeben sich für die Ein- und die Ausschaltzeit die Bereiche 2 τ ≤ tein ≤ 2 · τ ; · τ ≤ taus ≤ 2 · τ 3 Die Gatterhaltezeit th ist davon der Minimalwert 23 · τ und die Gatterverzögerungszeit td der Maximalwert 2 · τ . Zusammenfassend erfolgt die Extraktion der Halte- und Verzögerungszeiten für ein Gatter nach einem überschaubaren Formalismus, der auch die Optimierung der Transistorbreiten beinhalten kann. In einem Verarbeitungsgatter, in dem die Lastkapazität über eine unterschiedliche Anzahl von Transistoren umgeladen wird, beträgt die Haltezeit nur ein Bruchteil der Verzögerungszeit. Die bisherige Annahme, dass die Breite der Ungültigkeitsfenster bei der Verarbeitung mit jedem Gatter stark zunimmt, ist damit empirisch belegt. 4.2.7 Gepufferte CMOS-Gatter Der letzte Abschnitt hat gezeigt, dass der Gatterentwurf mit dem Entwurf der Transistorschaltung noch nicht abgeschlossen ist. Die Optimierung der Transistorbreiten ist mindestens genauso wichtig und schwierig wie der reine Logikentwurf. Ein einziger Aspekt aus diesem Bereich soll noch behandelt werden, weil er in späteren Beispielschaltungen eine Rolle spielt. Das ist das Einfügen von Puffern. Puffer sind wie die Gatter G2 und G3 in Abb. 4.39 Inverter oder Inverterketten im Signalfluss zwischen Verarbeitungsgattern zur Minderung des Flächenbedarfs und der Verzögerung. Dahinter verbirgt sich folgende Gesetzmäßigkeit: Die Verzögerungszeit eines CMOS-Gatters hängt erheblich vom Produkt aus der Stockungstiefe des treibenden Gatters und seiner Lastanzahl ab. Die Stockungstiefe ist die maximale Anzahl der in Reihe geschalteten Transistoren, die Lastanzahl die Anzahl der am Gatterausgang angeschlossenen Eingänge nachfolgender Gatter. Die zwischengeschalteten Inverter spalten dieses Produkt in Summanden mit einer Stockungstiefe größer eins und einer Last und Summanden mit der Stockungstiefe eins und mehreren Lasten auf [39]. Abbildung 4.40 zeigt ein NAND mit 10 Eingängen mit bP = 0,2 · bN , das einmal ohne und einmal mit Puffer NL = 100 Lasten treibt. Die relative Transistorbreite ist im ungünstigsten Fall ein Zehntel der Breite eines einzelnen NMOS-Transistors. Die maximale Verzögerung ohne Puffer ist insgesamt td ≈
τA + 100 · τL τL ≈ 103 · 0,1 · bN bN
342
4 Vom Transistor zur Schaltung
(τL – lastabhängige Verzögerung; bN – relative Transistorbreite der NMOSTransistoren im NAND-Gatter). In der Schaltung mit Puffer hat das NANDGatter nur den ersten Inverter als Last. Dieser hat eine maximale relative Transistorbreite von bN und einen Inverter der zehnfachen Breite als Last. Das entspricht NL = 10 Lasten. Der zweite Inverter mit der zehnfachen relativen Transistorbreite treibt 100 Lasten. Insgesamt beträgt die Verzögerung für alle drei Gatter zusammen td1 +td2 +td3 ≈
τA + τL τA + 10 · τL 10∗ · τA + 100 · τL τA τL + + ≈ 12· +30· 0,1 · bN bN 10 · bN bN bN {z } | {z } | {z } |
1. Inverter
2. Inverter
3. Inverter
(∗ – Die größere Transistorbreite vergrößert auch die Kapazität am Gatterausgang und im selben Maße auch die Grundverzögerung). In der ungepufferten Lösung ohne die beiden Inverter müssen entweder die Transistoren ungefähr 20-mal so breit gewählt werden oder das Gatter hat etwa die 20-fache Verzögerung9 . td x0
bN 5
x0 x.1 .. x9 a)
bN 5
· · · x9 bN bN bN
NL = 100
td2
td1
UV x0
bN 5
x0 x.1 .. x9
τ = τA + 100 · τL bN.min = 0,1 · bN
b)
bN 5
· · · x9
2 · bN
td3
UV 20 · bN
NL = 100
10 · bN bN τ = τA + 100 · τL bN.min = 10 · bN τ = τA + 10 · τL bN bN.min = bN τ = τA + τL bN.min = 0,1 · bN bN bN
Abb. 4.40. NAND10 mit 100 Lasten a) ungepuffert b) gepuffert
Es ist offenbar zweckmäßig, Verarbeitungsgattern, die mehrere Lasten treiben, Inverter nachzuschalten. Die Treiber für sehr viele Lasten – Takttreiber, Spalten- und Zeilentreiber für Blockspeicher etc. – sind Inverterketten mit exponentiell zunehmender Transistorbreite. Das Lernziel war in diesem Abschnitt, dass Puffer Inverter (-Ketten) sind, die die Verzögerung und den Flächenbedarf mindern. 4.2.8 Zusammenfassung und Übungsaufgaben Das Zeitverhalten einer digitalen Schaltung wird von einer Simulation nur mehr oder weniger genau angenähert. Das genauste und rechenzeitaufwändigste Verfahren – eine zeitdiskrete Simulation des elektrischen Verhaltens – ist auch in VHDL zumindest für kleine Schaltungen und kurze Zeitintervalle kein Problem. Das üblicherweise genutzte Verfahren – die ereignisgesteuerte 9
Die Abschätzung basiert auf dem Richtwert τA ≈ 2 · τL aus [39].
4.2 Zeitverhalten
343
Simulation – erfordert jedoch einen um Zehnerpotenzen geringeren Rechenaufwand. Die Parameter der Simulationsmodelle werden zu einem Teil mit Experimenten an Teststrukturen, zum Teil mit elektrischen Simulationen und zum überwiegenden Teil aus der Schaltungsgeometrie bestimmt. Damit sind diese Simulationen erst nach Abschluss des geometrischen Entwurfs quasi als Endkontrolle möglich. Weitere wesentliche Aussagen waren: • • •
Auch auf der Transistorebene kann ein Simulationsmodell entweder so geschrieben werden, dass es nur Richtwerte für die Zeitverläufe bestimmt, oder so, dass es auch die Gültigkeitsinformationen mitberechnet. Die Zeitparameter für Gatterschaltungen werden aus den Zeitparametern der Simulationsmodelle der Transistorschaltungen abgeschätzt. Die Modelle für die Abschätzung der Verzögerungszeiten dienen auch zur Gatteroptimierung (Stichworte: Festlegung der Transistorbreiten und Puffer).
Weiterführende und ergänzende Literatur siehe [30, 39]. Aufgabe 4.6 In dem NAND4-Gatter aus Abb. 4.41 ist die relative Transistorbreite der NMOS-Transistoren bN = 2. Wie groß ist die relative Transistorbreite der PMOS-Transistoren zu wählen, damit die maximale Einschaltzeit gleich der maximalen Ausschaltzeit ist? x0
x1
x2
x3 x0 x1 x2 x3
UV y CL
Abb. 4.41. Schaltung zu Aufgabe 4.6
Aufgabe 4.7 Für die beiden Ringinverter in Abb. 4.42, die alle beide aus den NAND4Gattern aus Aufgabe 4.6 aufgebaut sind, wurde jeweils die Schwingungsperiode gemessen. Wie groß sind die Grundverzögerung τA und die lastabhängige Verzögerung τL , wenn die leitungsabhängige Verzögerung vernachlässigt werden kann?
344
4 Vom Transistor zur Schaltung 1
1
G1 &
G2
1
1
G3
&
G4 &
&
1 0
TP1 = 1,6 ns G5
G6
G7
G8
&
&
&
&
1 0
TP2 = 2,2 ns
Abb. 4.42. Ringinverter zu Aufgabe 4.7
4.3 Speicherzellen, Latches und Register Speicherzellen, Latches und Register sind Grundschaltungen zur Abtastung und Datenaufbewahrung. 4.3.1 Dynamische Speicherzellen Eine kurzzeitige Speicherwirkung hat praktisch jeder Gatterausgang in Form der Umladezeit der Lastkapazität. Die Umladezeit lässt sich durch Deaktivierung des Gatters (hochohmig schalten) erheblich verlängern. Abbildung 4.43 zeigt einen deaktivierbaren Treiber mit eingezeichneter Lastkapazität. Wenn der Treiber aktiviert ist, folgt der Signalverlauf am Ausgang dem Eingabesignalverlauf mit einer geringen Verzögerung td . Wenn der Treiber deaktiviert ist, ändert sich das Potenzial der signalführenden Leitung nur noch langsam. Der Signalwert bleibt für eine längere Haltezeit – typisch mehrere Millisekunden – gespeichert. Die Änderungsgeschwindigkeit und der stationäre Fall, gegen den das Potenzial einer Leitung mit deaktiviertem Treiber strebt, ist in der Regel nicht genau bekannt. Nach der garantierbaren Haltezeit thZ ist das Signal als ungültig zu betrachten. Dynamische Speicherzellen benötigen von allen Speicherzellen die wenigsten Transistoren und die geringste Chipfläche. Sie werden hauptsächlich in Schreib-Lese-Speichern für große Datenmengen eingesetzt, in denen der Vorteil der geringen Größe gegenüber y
x E a)
b)
& CL
E
x
y
0 0 1 1
0 1 0 1
Z Z 0 1
x E
1 0 1 0
ϕ(y)
Sicherheits-
y 10 c)
1 X 0
bereich
X
td
td
thZ
Abb. 4.43. Dynamische Speicherzelle a) Leitung mit deaktivierbarem Treiber b) Wertetabelle des Treibers c) Beispielsignalverläufe
4.3 Speicherzellen, Latches und Register
345
dem Nachteil, dass die Daten in einem Zeitabstand von wenigen Millisekunden aufgefrischt werden müssen, überwiegt (siehe später Abschnitt 4.4.4). 4.3.2 RS-Flipflop Eine statische Speicherzelle besteht aus einer bistabilen Logikschaltung. Die Grundschaltung ist ein Ring aus zwei Invertern (Abb. 4.44 a). Im Zustand y = 0 ist der Eingabewert des ersten Inverters »0« und der Eingabewert des zweiten Inverters »1«. Beide Inverter halten sich gegenseitig in diesem Zustand. Dasselbe gilt für den Zustand y = 1, nur mit den umgekehrten Signalwerten. Der gespeicherte Wert bleibt solange erhalten, bis ein neuer Wert eingestellt oder die Versorgungsspannung abgeschaltet wird. td1 y y¯ a)
b) R S
c)
0 0 1 1
0 1 0 1
S R
td2
≥1
≥1
f)
&
¯ R S¯
&
y¯ y
S
y Speichern 1 0 Vermeiden
y¯ y
R R
y
R
S
y¯
y¯
d)
S
e) y
td1 td2
td1 td2
td1 td2
td1 td2
Abb. 4.44. a) Bistabiler Inverterring b) Erweiterung zum NOR-RS-Flipflop c) Ansteuertabelle d) Schaltsymbol e) Signalverläufe f) NAND-RS-Flipflop
Zur Änderung ihres Zustands benötigt eine Speicherzelle Eingänge. In Abb. 4.44 b sind die beiden Inverter durch NOR-Gatter ersetzt. Die beiden Eingänge werden mit S (set, Setzen) und R (reset, Rücksetzen) bezeichnet. Die Tabelle in Abb. 4.44 c zeigt die Betriebsarten, Abb. 4.44 d das Symbol und Abb. 4.44 e Beispielsignalverläufe an den Anschlüssen eines RS-Flipflops. Eine »1« am Setzeingang bewirkt nach einer Verzögerung td1 am invertierten Ausgang eine »0« und nach einer weiteren Verzögerung td2 am nicht invertierten Ausgang eine »1«. Eine »1« am Rücksetzeingang bewirkt nach einer Verzögerung td2 am direkten Ausgang eine »0« und nach einer weiteren Verzögerung td1 am invertierten Ausgang eine »1«. Mit einer »0« an beiden Eingängen wird der zuletzt übernommene Wert gespeichert. Eine »1« an beiden Eingängen ist zu vermeiden, weil dann der direkte und der negierte Ausgang »0« sind, ein Zustand, der nicht gespeichert werden kann. Abbildung 4.44 f zeigt eine ähnliche Schaltung mit NAND-Gattern. Nur sind hier der Setz- und der Rücksetzeingang low-aktiv. Ein RS-Flipflop ist laufzeitempfindlich. Zu kurze Setz- und Rücksetzimpulse, ein ungültiger Wert am Setzeingang im zurückgesetzten Zustand oder ein
346
4 Vom Transistor zur Schaltung
ungültiger Wert am Rücksetzeingang im gesetzten Zustand invalidieren den gespeicherten Wert. Eine zuverlässige Datenspeicherung verlangt eine spezielle Ansteuerschaltung, die unzulässige Signalverläufe an den Eingängen – insbesondere Glitches – zuverlässig unterbindet. Hier im Buch werden RS-Flipflops nur in statischen Schreib-Lese-Speichern verwendet (siehe später Abschnitt 4.4.1). 4.3.3 D-Flipflops und Latches Ein D-Flipflop ist eine Speicherzelle mit einem Daten- und einem Freigabeeingang. Wenn das Signal am Freigabeeingang aktiv ist, wird der Wert am Dateneingang übernommen. Sonst wird der aktuelle Zustand gespeichert. In Abb. 4.45 wird der Inverterring mit einem Multiplexer aufgetrennt. Bei E = 1 bilden die Inverter eine Kette, die den Eingabewert übernimmt. Bei E = 0 ist der Inverterring rückgekoppelt und behält seinen Zustand. Im Vergleich zum RS-Flipflop ist bei einem D-Flipflop nur der Signalverlauf am Freigabeeingang zeitkritisch. Das Datensignal braucht nur hinreichend lange vor der Deaktivierung des Freigabesignals gültig und stabil zu sein. Die übrige Zeit ist der Werteverlauf des Datensignals ohne Bedeutung. Aber allein die Bereitstellung zeitgenauer, Glitch-freier Freigabesignale ist zumindest in synthesebasierten Entwürfen ein Problem (vgl. Abschnitt 2.1.4). Schaltung
Speichern (E = 0)
0 1 x E Symbol x E
y y¯
y y¯
Daten¨ ubernahme (E = 1) x E
y y¯
x
y y¯
Abb. 4.45. D-Flipflop
Erweiterung um einen Initialisierungseingang Eine Speicherzelle hat nach Zuschalten der Versorgungsspannung einen unbestimmten Anfangszustand. Zur Initialisierung mit einem definierten Anfangswert haben D-Flipflops oft zusätzlich einen Setz- oder einen Rücksetzeingang. Schaltungstechnisch wird dazu einer der Inverter wie bei einem RS-Flipflop durch ein NAND- oder ein NOR-Gatter ersetzt. Abbildung 4.46 zeigt eine Beispielschaltung und Beispielsignalverläufe. Das Setzen oder Rücksetzen mit dem Initialisierungssignal I hat schaltungsbedingt Vorrang vor der Datenübernahme. Der Signalverlauf für die Initialisierung ist genauso laufzeitkritisch wie
4.3 Speicherzellen, Latches und Register
347
bei einem RS-Flipflop. Der Initialisierungsimpuls muss ausreichend lang sein. Im Betrieb nach der Initialisierung muss das Initialisierungssignal inaktiv und Glitch-frei sein (vgl. Abschnitt 1.5.3). thx , tdx tdi
0 1
x
≥1 E
a)
y
I
b)
U nicht initialisiert
I
1 0
x
1 0
E
1 0
y
1 0
U
Zeitfenster, in dem x g¨ ultig sein muss
Abb. 4.46. D-Flipflop mit Rücksetzeingang a) Schaltung b) Signalverläufe
Latches Ein Latch ist eine Zusammenfassung von mehreren D-Flipflops mit gemeinsamem Freigabesignal und optional auch mit gemeinsamem Initialisierungssignal (Abb. 4.47). Das Verhaltensmodell wurde bereits in Abschnitt 2.1.4 eingeführt. Es unterscheidet sich von dem eines D-Flipflops nur darin, dass die Datenanschlüsse Bitvektoren beliebiger Breite sind, die in einer abstrakten Beschreibung auch durch Zahlentypen, Aufzählungstypen oder zusammengesetzte Typen dargestellt sein dürfen. Das Freigabe- und das Initialisierungssignal sind bitorientierte Signale.
x
n
x0
xn−1 a)
E I
x E I x E I
y0
n
y x E I
··· y n−1
n
x E I
n
y
b)
Abb. 4.47. Latch mit Initialisierungseingang a) Schaltung b) Symbol
4.3.4 Übernahme an der Taktflanke Ein Latch kann zu einem Register, das seine Eingabedaten mit der aktiven Taktflanke übernimmt, erweitert werden, indem an der aktiven Taktflanke ein Übernahmeimpuls gebildet und als Freigabesignal verwendet wird. In Abb. 4.48 ist die Schaltung zur Erzeugung des Übernahmeimpulses ein UND-Gatter
348
4 Vom Transistor zur Schaltung
mit dem direkten und dem verzögerten negierten Takt als Eingabesignale. Bei einer steigenden Taktflanke sind beide Eingänge des UND-Gatters für eine kurze Zeit td1 gleichzeitig »1«, so dass das nachfolgende UND-Gatter den gewünschten Impuls erzeugt. Für T = 1, T = 0 und auch nach der fallenden Taktflanke bleibt das Freigabesignal inaktiv. thL , tdL td1 T
z
a) ts tn thr tdr
x & td2
Vorhaltezeit Nachhaltezeit Registerhaltezeit Registerverz¨ogerung
n
E
x
n
y
E
Wert ung¨ ultig Abtastfenster wi Datenwert
b)
T
1 0
z
1 0
E
1 0
td1 td2
td2
ts = 0 tn = td1 w1 x tdR = tdL Bezugszeit der ⇒ ¨ thR = thL Ubernahme y w0 w1
Abb. 4.48. Gepulstes Latch a) Schaltung b) Zeitverhalten
Eine korrekte Datenübernahme ohne Glitches am Latch-Ausgang setzt voraus, dass das Eingabesignal während der gesamten Dauer, die das Freigabesignal aktiv ist, d.h. im Zeitfenster td2 bis td1 + td2 , stabil und gültig ist. Zur Beschreibung der Gesamtschaltung mit dem Registermodell aus Abschnitt 1.4.1 ist es notwendig, den Bezugszeitpunkt für die Datenübernahme auf die um td2 verzögerte steigende Flanke des Übernahmeimpulses zu verschieben. Das äquivalente Registermodell hat dann eine Vorhaltezeit ts = 0 und eine Nachhaltezeit tn = td1 . Die Registerverzögerung ist gleich der LatchVerzögerung und die Registerhaltezeit gleich der Latch-Haltezeit. Die Registernachbildung mit einem gepulsten Latch hat schaltungsbedingt eine Nachhaltezeit größer null. Das hat für die Register-Transfer-Funktionen zwischen den Registern den Nachteil, dass eine Mindesthaltezeit in der Größenordnung der Nachhaltezeit zu fordern ist (Abb. 4.49). Die Synthese unterstützt die Zusicherung von Mindesthaltezeiten normalerweise nicht, so dass TP tn x a) E c)
x E
thr , tdr x’
thf , tdf y = f (x)
thr + thf > tn
E
ts = 0, tn > 0 thr , tdr y
x E
tdr + tdf < TP
TP
1 0
tdr
thr x’ w0
y’
w1 thf
b)
y f (w0 )
w2 tdf tn
f (w1 )
Abb. 4.49. Register-Transfer-Funktion mit gepulsten Latches a) Schaltung b) Signalverläufe c) zuzusichernde Zeitbedingungen
4.3 Speicherzellen, Latches und Register
349
manuelle Nacharbeit bei der Festlegung der Schaltungsstruktur und der Leitungsführung erforderlich werden kann. 4.3.5 Register aus Master-Slave-Flipflops Ein Master-Slave-Flipflop ist eine taktflankengesteuerte Speicherzelle, die aus zwei taktzustandsgesteuerten D-Flipflops zusammengesetzt ist. Das erste DFlipflop – der Master – übernimmt die Daten vor der Übernahmeflanke und speichert sie in der Takthälfte nach der Übernahmeflanke. Das zweite DFlipflop – der Slave – übernimmt den zwischengespeicherten und damit stabilen Wert aus dem Master in der zweiten Takthälfte (Abb. 4.50). Das Eingabesignal des gesamten Master-Slave-Flipflops, das über den Master und den Slave zum Ausgang weitergeleitet wird, muss nur während eines kleinen Zeitfensters vor der Übernahmeflanke einen gültigen und stabilen Wert haben. Die Nachhaltezeit ist null. Die Gesamtschaltung ist zwar deutlich aufwändiger als ein funktionsgleiches gepulstes D-Flipflop. Dafür sind Master-Slave-Flipflops laufzeitunkritisch und Schaltungen mit ihnen einfacher zu entwerfen.
x T T
1 0
x
1 0
z
1 0
y
1 0
Master Slave z x x E E
Symbol y
x
y T
¨ Ubernahmeflanke Abtastfenster Master speichert Slave speichert
Abb. 4.50. Master-Slave-Flipflop
Ein Register fasst mehrere Master-Slave-Flipflops mit einem gemeinsamen Takteingang zusammen. Die einzelnen Speicherzellen sind räumlich voneinander getrennt, so dass die aktiven Taktflanken sie mit einem geringen Taktversatz erreichen (vgl. Abschnitt 1.4.5). Die Eingabedaten müssen um die Vorhaltezeit vor dem frühestmöglichen Zeitpunkt, zu dem die aktive Taktflanke eine Registerzelle erreichen kann, bis zur Nachhaltezeit nach dem spätestmöglichen Zeitpunkt, zu dem die aktive Taktflanke eine Registerzelle erreichen kann, stabil anliegen. Die Registerausgabe ändert sich frühstens nach der Zellenhaltezeit nach dem frühestmöglichen Zeitpunkt der Taktflanke. Der neue Wert ist spätestens nach der Zellenverzögerungszeit nach dem spätestmöglichen Zeitpunkt der Taktflanke am Registerausgang verfügbar (Abb. 4.51). Bei einem Taktversatz ist irgendein Zeitpunkt innerhalb des Intervalls, in dem die aktive Taktflanke die Zellen erreichen kann, als Bezugszeitpunkt zu definieren. Mit dem frühestmöglichen Zeitpunkt als Bezugszeitpunkt sind die Vorhaltezeit und die Haltezeit des Registers gleich der Vorhaltezeit und der
350
4 Vom Transistor zur Schaltung tsZ
thZ , tdZ
x0
y0
x1
y1
t∆T xn−1
t∆T T x
yn−1
y
T thR , tdR
n
n
x a)
T
y
wi (tnZ = 0)
tsZ tsR
···
tsR , tnR
1 0
wi−1 thZ
b) Abtastfenster modellierter Abtastzeitpunkt
thR
tnR wi tdR tdZ
t..R Zeitparameter Register t..Z Zeitparameter Zelle t∆T maximaler Taktversatz
Abb. 4.51. Register mit Taktversatz a) Schaltung b) Zeitverläufe
Haltezeit der einzelnen Speicherzellen. Die Nachhaltezeit ist gleich dem maximalen Taktversatz10 und die maximale Verzögerungszeit gleich der Summe aus dem Taktversatz und der Verzögerungszeit einer Speicherzelle: tsR = tsZ tnR = t∆T
thR = thZ tdR = t∆T + tdZ
(4.34)
(tsR – Vorhaltezeit des Registers; tsZ – Vorhaltezeit einer Speicherzelle; thR – Haltezeit des Registers; thZ – Haltezeit einer Speicherzelle; tnR – Nachhaltezeit des Registers; t∆T – maximaler Taktversatz; tdR – Verzögerungszeit des Registers; tdZ – Verzögerungszeit einer Speicherzelle). Eine Nachhaltezeit ungleich null lässt sich schlecht simulieren (vgl. Abb. 1.47 in Abschnitt 1.4.2). Mit einem Taktversatz kleiner der Haltezeit lässt sich der Bezugszeitpunkt um den Taktversatz nach hinten verschieben, ohne dass die Haltezeit einen nicht simulierbaren negativen Wert annimmt. Die Vorhaltezeit vergrößert sich, die Halte- und die Verzögerungszeit verringern sich gegenüber Gleichung 4.34 um den maximalen Taktversatz: tsR = tsZ + t∆T tnR = 0
thR = thZ − t∆T tdR = tdZ
(4.35)
Die Nachhaltezeit ist null. Das ist das bevorzugte Verhaltensmodell für ein Register. Freigabe- und Initialisierungseingang Abbildung 4.52 zeigt die Synthesebeschreibung für ein Register mit einem Initialisierungs- und einem Freigabeeingang (vgl. Abschnitt 2.1.3). Beide Zu10
plus der Zellennachhaltezeit, die für Master-Slave-Flipflops null ist
4.3 Speicherzellen, Latches und Register
351
satzfunktionen werden oft benötigt. Das rechtfertigt eine genauere schaltungstechnische Betrachtung. Die zustandsgesteuerte Übernahme des Anfangswertes wird bei einem Register aus Master-Slave-Flipflops mit initialisierbaren Slaves nachgebildet. Die Setz- und Rücksetzeingänge der Slave-Flipflops haben Vorrang vor der Übernahme vom Dateneingang (vgl. Abschnitt 4.3.3, Abb. 4.46), so dass die Initialisierung sofort und unabhängig vom Takt, d.h. asynchron, erfolgt. Die zusätzliche Freigabesteuerung kann unterschiedlich realisiert werden. In Abb. 4.52 b wird die bedingte Übernahme mit einem Multiplexer nachgebildet, der wahlweise den Ist-Zustand oder das abzutastende Signal an den Registereingang weiterleitet. In Abb. 4.52 c blockiert das inaktive Freigabesignal des Registers die Datenweitergabe vom Master zum Slave. Das Freigabesignal E ∗ muss während der gesamten Slave-Übernahmephase stabil und gültig sein. Dazu wird es in Abb. 4.52 c in dieser Taktphase in einem zusätzlichen D-Flipflop gespeichert. Das zusätzliche D-Flipflop wird für das gesamte Register nur einmal benötigt, während in Abb. 4.52 b jedes Bit seinen eigenen Multiplexer haben muss. signal x, y: std_logic_vector(n-1 downto 0); signal E, I, T: std_logic; ... process(I, T): begin if I=’1’ then y <= aw; elsif E=’1’ and rising_edge(T) then y <= x; end if; a) end process;
x E T b) I
Master x E
x T E c)
Master x E
0 1
I
x E
E∗
Slave x E I
&
Slave x E I
y
y
Abb. 4.52. Register mit Freigabe- und Initialisierungseingang a) SyntheseBeschreibung b) Schaltung mit Eingabemultiplexer c) Schaltung mit Übernahmeblockierung für den Slave
4.3.6 Taktversorgung Taktsignale legen die Zeitpunkte und Zeitfenster für die Datenübernahme in die Register und Latches fest. Sie müssen zeitgenau und Glitch-frei sein. In einer synchronen Schaltung werden alle Taktsignale von einem Haupttakt abgeleitet, der mit einem frequenzstabilen Oszillator erzeugt wird. Oszillator Ein Oszillator ist eine Schaltung, die ein periodisches Signal bereitstellt. Prinzipiell ist diese Funktion mit dem Ringinverter aus Abschnitt 4.2.3 realisierbar. Aber die Periodendauer des von einem Ringinverter erzeugten Signals ist
352
4 Vom Transistor zur Schaltung
meist zu ungenau. Zeitgenaue Oszillatoren verwenden einen Quarz oder einen anderen mechanischen Schwinger als frequenzbestimmendes Element. Abbildung 4.53 a zeigt eine gebräuchliche Oszillatorschaltung mit einem Quarz. Der Quarz verhält sich elektrisch wie ein Schwingkreis. Er bildet die Rückkopplung des ersten Inverters, der hier als Verstärker arbeitet. Die beiden Kapazitäten dienen zur Phasenverschiebung. Der zweite Inverter verbessert die Signalform. Die relative Abweichung zwischen der Ist- und der Soll-Frequenz eines solchen Oszillators liegt in der Größenordnung von 10−5 bis 10−4 . Das ist für den Takt einer digitalen Schaltung ausreichend genau. T Quarz C1 a)
Oszillatorschaltkreis UV
Quarz
digitaler Schaltkreis xtl in
C1
C2
C1 = C2 ≈ 20 pF
b)
C2
xtl out
Abb. 4.53. Quarzstabilisierter Taktgenerator a) Schaltung b) Schaltkreis mit Quarz- oder Oszillatoranschlüssen
Quarze und andere mechanische Schwinger gibt es als Einzelbauteile oder komplett mit Beschaltung als Oszillatorschaltkreise. Hochintegrierte Schaltkreise, die einen frequenzstabilen Takt benötigen, haben meist die Inverter für den Oszillator mit auf dem Chip, so dass extern nur noch der mechanische Schwinger angeschlossen werden muss. Alternativ kann an dem Schaltkreisanschluss, der auf den Eingang des ersten Inverters führt, auch ein externes Taktsignal von einem Oszillatorschaltkreis oder einer anderen Taktquelle eingespeist werden (Abb. 4.53 b). Taktnetze Taktsignale müssen die Takteingänge von Hunderten oder Tausenden von angeschlossenen Speicherzellen treiben. Die große Anzahl von Lasten und der geringe zulässige Taktversatz erfordern spezielle Treiber und spezielle Leitungsführungen (Abb. 4.54). In programmierbaren Logikschaltkreisen gibt es spezielle Taktverteilernetze. In der Spartan- und Virtex-Familie erfolgt die Einspeisung von Taktsignalen in diese Netze mit BUFG-Treibern. Der Entwurf der Taktnetze erfordert einen weit tieferen Einblick in das elektrische Verhalten der Gatter und Leitungen als alle anderen Entwurfsaufgaben in der Digitaltechnik zusammen. Taktteiler Takte mit einer geringeren Frequenz werden mit Taktteilern erzeugt. Ein Taktteiler ist ein autonomer Automat mit dem Ausgabetakt als Zustandssignal
4.3 Speicherzellen, Latches und Register
353
Treiber, Ausgleich von Laufzeitunterschieden Oszillator (Basistakt)
···
Ableitung zeitlich ausgerichteter Takte
···
Abb. 4.54. Schaltung zur Taktversorgung
und einem Zähler. In jedem Zustand wird nach einer bestimmten Anzahl von Zählschritten der Zustand gewechselt und der Zähler zurückgesetzt. Bei einem Tastverhältnis von eins zu eins genügt eine Übergangsfunktion, die das Zustandssignal immer nach der entsprechenden Taktanzahl invertiert. Abbildung 4.55 zeigt die VHDL-Beschreibung, den Operationsablaufgraph und die Signalverläufe für einen Taktteiler mit dem Teilerverhältnis eins zu sechs. Die Abtastung, die eine Definition des Taktes als Zustandssignal einschließt, ist notwendig, um Glitches auf dem Ausgabetakt auszuschließen, und minimiert gleichzeitig den Taktversatz zum Basistakt. signal ct: tUnsigned(1 downto 0); signal I, T, Q: std_logic; ... process(T, I) begin if I=’1’ then Q <= ’0’; ct <= "00"; elsif rising_edge(T) then if ct="10" then ct <= "00"; Q <= not Q; else ct <= ct + "1"; end if; end if; a) end process;
sonst ct <= ct + 1 Q=0 ct = 10 ct <= 00
b)
sonst ct <= ct + 1
ct = 10 ct <= 00 Q=1
1
T0 ct Q 10 c)
00 01 10 00 01 10 00 01 10 00
⇒Web-Projekt: P4.3/Taktteiler.vhdl
Abb. 4.55. Taktteiler a) VHDL-Beschreibung b) Operationsablaufgraph c) Signalverläufe
354
4 Vom Transistor zur Schaltung
Phasenregelkreise (PLL) Komplexe Taktversorgungsschaltungen benutzen Phasenregelkreise (PLL – phase locked loop). Ein Phasenregelkreis besteht aus einer Quelle für den Referenztakt, einem spannungsgesteuerten Oszillator (VCO – voltage controlled oscillator), einem Phasenvergleicher, einem Integrator und optional zwei Frequenzteilern (Abb. 4.56). Der Referenztakt TRef muss eine konstante Frequenz haben. Bei einem spannungsgesteuerten Oszillator lässt sich die Frequenz über das Steuerpotenzial ϕCtl einstellen. Der Phasenkomparator vergleicht die aktiven Flanken der beiden Takte und steuert einen Integrator an. Gemeinsam mit dem Integrator erhöht bzw. verringert der Phasenkomparator das Steuerpotenzial ϕCtl so, dass sich nach einer gewissen Zeit die Flanken der heruntergeteilten Takte T1 und T2 zeitlich angleichen. Man spricht dann vom Einrasten des Regelkreises, der von dem Phasenkomparator mit einem Ready-Signal quittiert wird. Im eingerasteten Zustand erzeugt der spannungsgesteuerte Oszillator einen Takt TVCO mit einer Frequenz von exakt fVCO =
Q · fRef P
(4.36)
(P – Teilerwert für den Referenztakt; Q – Teilerwert für den VCO-Takt; fRef – Frequenz des Referenztaktes). Schaltungen mit Phasenregelkreisen in der Taktversorgung sind erst betriebsbereit, wenn der Regelkreis eingerastet ist. Nach dem Zuschalten der Versorgungsspannung dauert das einige Hundert Takte. Erst dann kann der Rest der Schaltung initialisiert werden und seinen normalen Betrieb aufnehmen. Wenn diese Reihenfolge verletzt wird, kann es zu Fehlfunktionen kommen. TVCO VCO TRef
Taktteiler T1 1:Q Taktteiler T2 1:P t∆T12
↑ Phasenvergleicher ↓ Rdy
Integrator
ϕCtl
Ready
Abb. 4.56. Takterzeugung mit einem Phasenregelkreis
Phasenregelkreise werden zur Frequenzvervielfachung und zur Korrektur von Taktversätzen eingesetzt. Zur Erzeugung von Takten mit einem ganzzahligen Vielfachen der Frequenz des Basistaktes wird der P-Teiler nicht benötigt (P = 1). Mit dem Q-Teiler wird der gewünschte Vervielfachungsfaktor eingestellt. Mit P > 1 und Q > 1 lassen sich nicht ganzzahlige Vielfache der Basistaktfrequenz einstellen. Eine andere Anwendung von Phasenregelkreisen ist die Korrektur oder Erzeugung von Taktversätzen. Abbildung 4.57 a zeigt einen Phasenregelkreis mit einem Johnson-Zähler als Taktteiler. Der Schaltungstakt
4.3 Speicherzellen, Latches und Register
355
T0 , der am Eingang des Johnson-Zählers abgegriffen wird, regelt sich so ein, dass er in Frequenz und Phase mit dem Referenztakt übereinstimmt. An den übrigen Ausgängen des Johnson-Zählers können frequenzgleiche, je um eine Achtelperiode phasenversetzte Takte abgegriffen werden (Abb. 4.57 b). Einer dieser phasenversetzten Takte dient in der Beispielschaltung als Ausgabetakt Tout . Zu ihm soll das Ausgabesignal yout eine vorgegebene Vorhaltezeit ≥ ts haben (Abb. 4.57 c). Die Vorhaltezeit ist in der Beispielschaltung die Differenz zahlreicher unterschiedlicher Verzögerungszeiten. Eine Synthese kann normalerweise ein Vorhaltezeit-Constraint dieser Art schwer einhalten. In der Beispielschaltung ist das aber kein Problem. Denn es ist hier vorgesehen, nach der fertigen Platzierung und Verdrahtung die Vorhaltezeit durch eine geringfügige lokale Verdrahtungsänderung, nach der der Ausgabetakt vom richtigen Zählerausgang abgegriffen wird, zu korrigieren. Johnson-Z¨ ahler T0 Power-on-Reset TRef TIst
a) TVCO T0 T1 T2 b) T3
Phasenvergleicher, Integrator und VCO
x I
TVCO
T2
x I
T3
x I
eigentliche Schaltung x I
Rdy
Taktr¨ uckf¨ uhrung 1 0 1 0 1 0 1 0 1 0
x I
T1
x I
Treiber(1) TIst = TRef yout
1 0
Tout
1 0
c)
yout Tout
≥ ts
Abb. 4.57. PLL-basierte Erzeugung phasenverschobener und phasenkorrigierter Taktsignale a) Schaltung b) phasenverschobene Ausgabesignale des JohnsonZählers c) Datenausgabe mit einer vorgegebenen Vorhaltezeit zum Ausgabetakt ((1) – Treiber für eine große Anzahl von Lasten)
4.3.7 Zusammenfassung und Übungsaufgaben Daten werden in einer digitalen Schaltung bitweise gespeichert, entweder in einer kleinen Kapazität oder in einer bistabilen Schaltung. Die Signalwerte in Kapazitäten müssen nach der garantierten Haltezeit aufgefrischt werden. Eine bistabile Schaltung – ein Ring aus zwei invertierenden Gattern – behält den gespeicherten Wert bis zum nächsten Schreibvorgang oder bis zum Abschalten der Versorgungsspannung. Für das Schreiben der Daten gibt es unterschiedliche Ansteuerschemata. Das klassische RS-Flipflop ist laufzeitkritisch und nicht
356
4 Vom Transistor zur Schaltung
für zu synthetisierende Entwürfe geeignet. Auch D-Flipflops und Latches, die ihre Daten zustandsgesteuert übernehmen, sind für die Synthese problematisch. Das ideale taktflankengesteuerte Speicherelement ist das Master-SlaveFlipflop, das aus zwei D-Flipflops besteht. Der Master übernimmt die Daten vor der aktiven Taktflanke und speichert sie nach der aktiven Taktflanke. Der Slave übernimmt die im Master gespeicherten Daten und speichert, während der Master die nächsten Daten übernimmt. Bei einem Master-Slave-Flipflop mit initialisierbarem Slave hat die Initialisierung Vorrang vor der Datenübernahme und das Initialisierungssignal muss zum Takt ausgerichtet sein. Der Takt einer digitalen Schaltung wird in der Regel mit einem Quarz oder einem anderen mechanischen Schwinger stabilisiert, der eine hohe Zeitgenauigkeit sichert. Auch Taktteiler, Taktvervielfachungsschaltungen und Schaltungen zur Phasenkorrektur sind Spezialschaltungen zur Bereitstellung zeitgenauer Signale. Bei einer Taktversorgung mit Phasenregelkreisen ist auf die Initialisierungsreihenfolge zu achten. Weiterführende und ergänzende Literatur siehe [20, 21, 30, 36, 47, 52]. Aufgabe 4.8 Gegeben sind die Transistorschaltung und der Eingabesignalverlauf in Abb. 4.58. a) Welche Funktion haben die Gatter G1 bis G4? b) Beschreiben Sie die Funktionsweise der Schaltung und skizzieren Sie die Signalverläufe der Zwischensignale und des Ausgabesignals für den gegebenen Eingabesignalverlauf. Die Verzögerungszeiten seien in der Skizze vernachlässigbar klein und die Haltezeit, wenn der Gatterausgang hochohmig ist, sei viel größer als die Taktperiode. c) Um welchen Typ von Speicherzelle handelt es sich?
G1
G2
x T
T¯
G3 z1
G4 z2
UV y
ung¨ ultig
T x T¯ z1 z2 y
1 0 1 0 1 0 1 0 1 0 1 0
Abb. 4.58. Schaltung und Eingabesignalverlauf zu Aufgabe 4.8
Aufgabe 4.9 Wie müssen die Teilerverhältnisse P und Q für den Phasenregelkreis in Abb. 4.56 gewählt werden, um mit einem quarzstabilisierten 50MHz-Referenztakt einen 300MHz-Takt zu erzeugen?
4.4 Schreib-Lese-Speicher
357
4.4 Schreib-Lese-Speicher Für die Speicherung größerer Informationsmengen in digitalen Schaltungen werden Blockspeicher eingesetzt. Blockspeicher sind bottom-up entworfene, hochgradig optimierte Schaltungen mit einer wesentlich höheren Transistordichte, als der, die mit einer freistrukturierten Schaltung mit Latches und Registern erzielbar ist. Blockspeicher bestehen aus einer zweidimensionalen Speichermatrix, die den Hauptteil der Fläche einnimmt, umgeben von einer Ansteuerschaltung (Abb. 4.59). Schreib-Lese-Steuerung und Spaltenauswahl . .. bj+1 .. bj . wi Zelle Zelle SchnittZeilen··· ··· (i, j) (i, j + 1) stellenauswahl signale wi+1 Zelle Zelle ··· ··· (i + 1, j) (i + 1, j + 1) wi Wortleitungen .. .. . . b Bitleitungen j
Abb. 4.59. Blockspeicher
Die Funktion eines Blockspeichers wird in erster Linie von der Funktion der Zellen bestimmt. Abbildung 4.60 zeigt eine Übersicht über die wichtigsten Speicherarten. Es wird zwischen Festwertspeichern und Schreib-LeseSpeichern unterschieden. Festwertspeicher können nur einmal oder nur mit großem Zeitaufwand beschrieben werden, behalten ihre Daten auch ohne Versorgungsspannung über Jahre und werden in einem separaten Abschnitt behandelt. Schreib-Lese-Speicher haben eine Schreibzeit in der Größenordnung der Lesezeit. Statische Speicher behalten die gespeicherten Daten, bis die Versorgungsspannung abgeschaltet wird. Dynamische Speicher verwenden Kapazitäten als Speichermedium. Sie haben eine sehr hohe Speicherdichte, verlieren ihre Daten aber ohne Auffrischen nach wenigen Millisekunden. Die meisten Blockspeicher sind so aufgebaut, dass in jedem Zeitschritt nur der Zugriff auf einen Speicherplatz möglich ist. Mehrportspeicher, bei denen mehrere Speicherzellen zeitgleich wahlfrei adressiert werden können, benötigen spezielle Speicherart Festwertspeicher vom Hersteller programmiert
mehrmals programmierbar
(ROM)
(EEPROM)
Schreib-Lese-Speicher statisch Einport (SRAM) Mehrport
Abb. 4.60. Einteilung der Blockspeicher
dynamisch assoziativ
358
4 Vom Transistor zur Schaltung
Speicherzellen. Das gilt auch für Assoziativspeicher, die in einem Zugriffsschritt für ein Eingabedatenwort die Adresse, unter der es gespeichert ist, suchen und ausgeben können. 4.4.1 SRAM Die Abkürzung RAM ist ein Akronym für random access memory (Speicher mit wahlfreiem Zugriff) und wird als Bezeichnung für Schreib-LeseSpeicher mit wahlfreiem Zugriff verwendet11 . Das vorangestellte »S« steht für »statisch« und bedeutet, dass die Speichermatrix aus statischen Speicherzellen besteht. Ein SRAM besteht aus einer Matrix bistabiler Speicherzellen umgeben von einer Ansteuerschaltung für die Spalten- und Zeilenauswahl und die SchreibLese-Steuerung. Die kleinste bistabile les- und beschreibbare Speicherzelle benötigt sechs Transistoren. Vier Transistoren bilden den Inverterring (vgl. Abschnitt 4.3.2). Die übrigen beiden Transistoren T5 und T6 dienen zur Zellenauswahl. Wenn die Wortleitung wi einer Speicherzelle i, j aktiv ist, schalten beide Auswahltransistoren ein. Das Steuersignalpaar r¯j , s¯j wählt dann zwischen vier möglichen Aktionen: setzen (»1« schreiben), rücksetzen (»0« schreiben), lesen und keine Operation (Abb. 4.61). Spaltenauswahl
Zeilenauswahl
x W
a)
bj Schreibschaltung r¯j
&
s¯j Zelle i, j
wi T5
T6
LeseSchaltung x W wi Z∗
y
setzen r¨ ucksetzen lesen keine Operation
x 1 0 -
W 1 1 0
-
-
bj 1 1 1
r¯i 1 0 Z∗
s¯i 0 1 Z∗
0 Z∗ Z∗
b)
Dateneingang bj Bitleitung y Datenausgang Schreibsignal Wortleitung - beliebig (don’t care) Spaltentreiber deaktiviert
Abb. 4.61. SRAM a) Schaltung b) Ansteuerung der Speicherzellen
In Abb. 4.61 erfolgt die Zeilenauswahl über die Wortleitungen wi und die Spaltenauswahl über die Bitleitungen bj . Bei jedem Zugriff sind genau eine Wortleitung und eine Bitleitung aktiv. In allen anderen Zeilen sind die Auswahltransistoren ausgeschaltet und in allen anderen Spalten sind die Treiber der Schreibschaltung für die Steuersignale r¯j , s¯j hochohmig und die Leseschaltungen deaktiviert, so dass keine Operation ausgeführt wird. Für die ausgewählte Zelle schaltet die Wortleitung wi die Auswahltransistoren ein. Die Bitleitung aktiviert, wenn das Schreibsignal aktiv ist, die Treiber der Steuersignale r¯j , s¯j . Diese übernehmen den direkten und invertierten 11
Auch die meisten Nur-Lese-Speicher sind wahlfrei adressierbar.
4.4 Schreib-Lese-Speicher
359
Eingabewert. Bei einer zu schreibenden »1« wird der Zellenzustand über das low-aktive Setzsignal s¯j auf »1« und bei einer zu schreibenden »0« wird er über das low-aktive Rücksetzsignal r¯j auf »0« gesetzt. Wenn das Schreibsignal W inaktiv ist, bleiben die Spaltentreiber inaktiv. Die Signale r¯j , s¯j übernehmen die schwachen Werte der ausgewählten Speicherzelle und leiten sie an die Leseschaltung weiter, die den Datenwert übernimmt und zum Ausgang des Schaltkreises durchstellt. Abbildung 4.62 zeigt die komplette Transistorschaltung der 6-TransistorZelle und ein Beispiel für eine mögliche geometrische Anordnung. Die Transistoren haben Minimalabmessungen und werden auf minimaler Fläche angeordnet. Die zeilen- und spaltenweise Verdrahtung erfolgt in mehreren Ebenen von Metallleiterbahnen. Zur Erzeugung einer Zellenmatrix aus vielen Zeilen und Spalten wird die Zelle einfach Tausende oder Millionen Male kopiert. r¯j
s¯j
r¯j
UV
xij
n+ -Gebiet Polysilizium
T6
n-Wanne p+ -Gebiet
T3
b)
x ¯ij T1
a)
T4
T4 T6 x ¯ij T3
T2
T2 T5 xij T1
T5
wi
s¯j
UV
wi Metall 1 Durchk. 1
Metall 2 Durchk. 2
Abb. 4.62. SRAM-Zelle a) Transistorschaltung b) geometrische Anordnung
Eine wichtige Kenngröße eines Schreib-Lese-Speichers ist die Zugriffszeit. Das ist die Dauer eines Speicherzugriffs. Sie liegt bei großen Schreib-LeseSpeichern mit mehreren Megabit Speicherkapazität in der Größenordnung von 10 ns bis 100 ns und ist um Zehnerpotenzen größer als die Verzögerungszeit eines einzelnen Gatters. Benötigt wird diese lange Zeit für die Adressdecodierung und das Treiben der Zeilen- und Spaltenleitungen, die eine linear mit der Spalten- bzw. Zeilenanzahl zunehmende Lastkapazität haben. Aus Letzterem folgt, dass die Zugriffszeit eines Speichers mit der Anzahl der Speicherzellen zunimmt. Kleine Blockspeicher sind deutlich schneller als große. Für die Simulation wurde das RAM-Modell in Abschnitt 3.4.3 in eine Kernfunktion und eine Schnittstellenfunktion unterteilt. Die Kern-Lesefunktion ähnelt der einer kombinatorischen Schaltung. Wenn das Schreibsignal inaktiv ist, wird das Ausgabesignal nach einer Adressänderung nach der Haltezeit thy ungültig. Wenn die neue gültige Adresse anliegt, wird nach der Verzögerungszeit tdy der gespeicherte Inhalt ausgegeben. Ein korrekter Schreibvorgang setzt voraus, dass die Adresse hinreichend lange vor und nach der Aktivierung des Schreibsignals stabil und gültig ist. Der Schreibimpuls muss eine Mindestlän-
360
4 Vom Transistor zur Schaltung
ge haben und die zu schreibenden Daten müssen hinreichend lange vor der Deaktivierung des Schreibsignals stabil und gültig sein (Abb. 4.63). Bei einer Verletzung der Zeitbedingungen werden ungültige Werte gespeichert.
a
Lesen LA1
Lesen LA2
Schreiben Schreiben SA1 SA2 twr tsa tna
W x y
tsx
tnx X1
tdy
X2
thy Y1
LAi Leseadresse SAi Schreibadresse
Y2 Yi gelesener Wert Xi zu schreibender Wert
a x W y thy tdy tsa tna tsx tnx twr
Adresssignal Eingangsdaten Schreibsignal Ausgabedaten Lesehaltezeit Leseverz¨ogerungszeit Adressvorhaltezeit Adressnachhaltezeit Datenvorhaltezeit Datennachhaltezeit Schreibzeit
Abb. 4.63. Signalverläufe zum Lesen und Schreiben für die SRAM-Kernfunktion
Simulationsmodell für einen Speicherschaltkreis Große Blockspeicher sind oft eigenständige Schaltkreise. In diesem Abschnitt wird ein vereinfachtes Simulationsmodell für einen asynchronen SRAM-Schaltkreis entwickelt. Ziel ist die Vermittlung eines skizzenhaften Einblicks in diese Art der Modellbildung und die Entwicklung eines Beispielmodells für den darauf folgenden Unterabschnitt. Der Beispielschaltkreis ist ein IS61LV25616AL [8]. Er hat achtzehn Adresseingänge, sechzehn bidirektionale Datenanschlüsse, eine Speicherkapazität von vier Megabit und eine Zugriffszeit von 10 ns. Die Operationsauswahl soll ausschließlich über die drei Steuersignale • • •
Ausgabeaktivierung: oe (output enable, low-aktiv), Schreibaktivierung: wr (write, low-aktiv) und Schaltkreisauswahl: cs (chip select, low-aktiv)
erfolgen. Die hier nicht berücksichtigten Byte-Auswahlsignale seien ständig aktiv. Die bidirektionalen Datenanschlüsse bilden einen Bus, über den bei einem Schreibzugriff die zu schreibenden Daten zum Schaltkreis geschickt und bei einem Lesezugriff die gelesenen Daten abgeholt werden. Intern hat der Speicherschaltkreis, ohne dass das explizit im Datenblatt steht, eine Matrixstruktur wie in Abb. 4.61, die in eine Schnittstellenschaltung eingebettet ist. Letztere erzeugt aus den im Datenblatt beschriebenen Anschlusssignalverläufen die Signalverläufe für die Kernfunktion. Mit diesem Hintergrundwissen kann das Simulationsmodell aus dem bereits behandelten Simulationsmodell für die Kernfunktion und einem noch zu entwickelnden
4.4 Schreib-Lese-Speicher
361
Simulationsmodell für die Schnittstellenschaltung zusammengesetzt werden. Für die Schnittstellenschaltung lässt sich aus dem Datenblatt Folgendes rekonstruieren. Das Ausgabesignal y der Kernfunktion ist über einen deaktivierbaren Treiber mit den bidirektionalen Datenanschlüssen dat des Schaltkreises verbunden. Das Freigabesignal E des Treibers wird nur aktiviert, wenn das Schaltkreisauswahlsignal cs und das Ausgabefreigabesignal oe aktiv und das Schreibsignal wr inaktiv ist: E = cs ∧ oe ∧ wr Das Schreibsignal W der Kernfunktion wird nur aktiviert, wenn das Schaltkreisauswahlsignal cs und das Schreibsignal wr des Schaltkreises aktiv ist (Abb. 4.64): W = cs ∧ wr Im nächsten Schritt ist das Zeitverhalten nachzubilden. Aus den im Datenblatt für den Lesezyklus dargestellten Signalverläufen ist ablesbar, dass die Leseverzögerung tdy ≤ 10 ns und die Lesehaltezeit thy ≥ 2 ns beträgt. tZO , tOZ oe cs wr dat adr a)
& &
E
y W x a
Kernfunktion Leseschaltung Schreibtreiber (deaktivierbar) Spaltenauswahl Zeilenauswahl
Schaltkreisanschl¨ usse: oe Ausgabefreigabesignal cs Schaltkreisauswahlsignal wr Schreibsignal adr Adresssignal dat bidirektionales Datensignal Verz¨ogerung des Ausgabetreibers: tOZ Deaktivierungsverz¨ogerung tZO Aktivierungsverz¨ogerung (im Beispiel unten 2 × 4 ns)
signal adr: tUnsigned(17 downto 0); signal dat, y: std_logic_vector(15 downto 0); signal n_oe, n_cs, n_wr, W, E: std_logic; ... W <= not n_cs and not n_wr; E <= ’X’, not n_oe and not n_cs and n_wr after 4 ram(W=>W, a=>adr, x=>dat, y=>y, thy=>2 ns, tdy=>10 ns, tsa=>0 ns, tna=>0 ns, twr=>8 ns, process(oe, y) begin if E=’1’ then dat <= y; elsif E=’0’ then dat <= (others=>’Z’); else dat <= (others=>’X’); end if; b) end process;
–- 1. Gatter ns; –- 2. Gatter –- Kernfunktion tsx=>6 ns); –- Ausgabetreiber
⇒Web-Projekt: P4.4/IS61.vhdl
Abb. 4.64. Verhaltensmodell eines asynchronen SRAM-Schaltkreises a) Ersatzschaltung aus Kernfunktion und Schnittstellenschaltung b) VHDL-Beschreibung
362
4 Vom Transistor zur Schaltung
Die Aktivierung und Deaktivierung des Ausgabetreibers dauert zwischen null und 4 ns. Die Adresse benötigt keine Vor- und keine Nachhaltezeit. Für die Eingabedaten sind eine Vorhaltezeit von tdx ≥ 6 ns und eine Nachhaltezeit tnx ≥ 0 gefordert. Das Schreibsignal wr muss mindestens für 8 ns aktiv sein und die Adresse für mindestens 10 ns stabil anliegen. Die Aktivierungs- und Deaktivierungsverzögerung des Treibers sind in Abb. 4.64 b als Halte- und Verzögerungszeit des Gatters, das das Freigabesignal E erzeugt, modelliert. Die übrigen Zeitparameter werden der nebenläufigen Prozedur »ram(...)« aus Abschnitt 3.4.3, Abb. 3.25 übergeben, die das Verhalten der Kernfunktion nachbildet. Zum Aufdecken möglicher Modellschwächen sind Testbeispiele aus dem Datenblatt abzuleiten. Das entwickelte Schaltkreissimulationsmodell ist noch nicht ganz exakt. Für die Adresse kontrolliert die Prozedur »ram(...)« nur, dass sie bei einem Schreibzugriff für die Summe aus der Mindestschreibimpulsbreite, der Adressvorhaltezeit und der Adressnachhaltezeit twr + tsa + tna = 8 ns + 0 + 0 stabil anliegt. Laut Datenblatt soll sie aber mindestens für 10 ns stabil anliegen. Das erfordert eine kleine Änderung in der Prozedur »ram(...)«. Laut Simulationsmodell hat das Signal cs dieselbe zeitliche Wirkung auf den deaktivierbaren Ausgabetreiber wie das Signal oe, was auch nicht genau den Datenblattangaben entspricht. Für unsere Zwecke soll das Simulationsmodell in Abb. 4.64 b jedoch so genügen, wie es ist. Speichercontroller Der Schaltkreis aus dem vergangenen Abschnitt soll in eine synthesefähige Beschreibung eingebettet werden. Dazu muss er in Register eingerahmt werden, die die Eingabe- und Ausgabesignale abtasten und dabei zeitlich am Takt ausrichten. Das abgetastete Eingabedatensignal ist über einen deaktivierbaren Treiber an die bidirektionalen Datenanschlüsse des Schaltkreises anzuschließen. Für die Einstellung der geforderten Zeitversätze und Zeittoleranzfenster der Steuersignale in Bezug auf die abgetasteten Adress- und Datensignale gibt es zwei Lösungsansätze: • •
Einstellung mit abgestimmten Verzögerungszeiten und Erzeugung mit einer sequenziellen Schaltung.
In Abb. 4.65 werden die Steuersignalverläufe mit abgestimmten Verzögerungszeiten eingestellt. Aus den Abtastwerten der externen Steuersignale W (write, schreiben) und R (read, lesen) werden mit einer kombinatorischen Schaltung die drei Schaltkreisansteuersignale cs, wr und oe sowie ein Steuersignal EW für die Aktivierung des Schreibtreibers und ein Freigabesignal ER für das Leseregister erzeugt. Die kombinatorischen Funktionen für die Erzeugung der fünf Steuersignale sind einfach, aber ihre Gatternachbildungen müssen genau
4.4 Schreib-Lese-Speicher
363
aufeinander abgestimmte Einschalt- und Ausschaltverzögerungen haben. Die Ausschaltverzögerung der Schaltung für die Bereitstellung des Schreibsignals wr muss z.B. mindestens so groß wie die Adressverzögerung und seine Einschaltverzögerung darf maximal so groß wie die Adressverzögerung sein. In dieser Rechnung sind alle Verzögerungen, auch die auf der Baugruppe, zu berücksichtigen. Während eines Adresswechsels zwischen zwei Schreibvorgängen ist normalerweise das Schreibsignal zu deaktivieren. Der Beispielschaltkreis hat jedoch eine Adressvorhalte- und eine Adressnachhaltezeit null. Wenn sich alle Adressbits am Schaltkreis fast gleichzeitig ändern, kann auf die Deaktivierung verzichtet werden. Wie groß »fast gleichzeitig« ist, steht leider nicht im Datenblatt. Das Zeitfenster dafür ist offenbar sehr klein. Enge Zeittoleranzen erfordern, auch wenn die Schaltung nur aus wenigen Gattern besteht, einen professionellen Handentwurf. Für die Synthese der umgebenden Schaltung sind die manuell gefundene Schaltung sowie ihre Platzierung und Verdrahtung mit Constraints festzuschreiben (vgl. Abschnitt 2.1.5). Der Entwurf ist viel schwieriger, als es die Schaltung in Abb. 4.65 vermuten lässt. x a W R
x’ a’
EW
W’ R’
≥1 &
adr RAM- dat wr Schaltcs kreis oe
x
ER
y
E
T a x y W R
Adresssignal Eingabedaten Ausgabedaten Schreibsignal Lesesignal
T Takt ur Schreibdaten EW Freigabesignal f¨ ¨ f¨ ur gelesene Daten ER Ubernahmesignal individuell abzugleichende Verz¨ogerung laufzeitkritische Teilschaltung
Abb. 4.65. Speichercontroller für einen asynchronen Schreib-Lese-Speicher mit individuell angepassten Verzögerungszeiten
Die Alternative zu einem Handentwurf der laufzeitkritischen Schaltungsteile ist die Erzeugung der Steuersignale mit einer sequenziellen Schaltung. Abbildung 4.66 a zeigt die Prinzipschaltung. Die Adresse und die Daten werden genauso wie in der ersten Lösung abgetastet und die abgetasteten Eingabedaten werden genauso über einen deaktivierbaren Treiber an die bidirektionalen Datenanschlüsse des Schaltkreises angeschlossen. Neu ist der MooreAutomat, der die Steuer- und Freigabesignale erzeugt. Für den Automatenentwurf sind als erstes die Zeitverläufe der zu erzeugenden Steuersignale zu spezifizieren. In Abb. 4.66 b ist jeder Speicherzugriff in vier Taktphasen unterteilt. Die abgetastete Adresse a’ soll jeweils in allen vier Phasen stabil anliegen und das Schaltkreisauswahlsignal cs soll in allen vier Phasen aktiv sein. Um bei einem Schreibzugriff eine ausreichende
364
4 Vom Transistor zur Schaltung x’ a’
x a
EE
W R a)
T
E
fs
fy
EW adr RAM- dat wr Schaltcs kreis oe
E ER
Moore-Automat
T Phase
1 0
Stop W1 W2 W3 W4 L1 L2 L3 L4 Stop
a’ cs wr oe
LA1
SA1
bw
Stopp 111 100
tsa
twr
tna
tdy
sonst br
W1 011 000
R1 011 000
W2 001 010
R2 010 000
W3 001 010
R3 010 000
br W4 011 100 bw sonst
1 0 1 0 1 0
EE 10 EW 10 ER 10 b)
x
y
R4 010 101 br sonst
bw
¨ Ubergangsbedingungen: bw = (W = 1) br = (W = 0) ∧ (R = 1) Zusammensetzung des Ausgabevektors: cs & wr & oe EE & EW & ER c)
Abb. 4.66. Speichercontroller für einen asynchronen Schreib-Lese-Speicher mit einem Automaten zur Erzeugung der Steuersignale a) Gesamtschaltung b) zu erzeugende Steuersignalverläufe c) Zustandsgraph der Steuerung
Adressvorhalte- und Adressnachhaltezeit zu garantieren, wird das Schreibsignal wr erst einen Takt nach der Adressabtastung aktiviert und einen Takt vor der nächsten Adressabtastung deaktiviert. Das Freigabesignal EW für den Schreibtreiber soll erst ab der zweiten Taktphase aktiviert werden, damit der Ausgabetreiber im RAM-Schaltkreis nach einem Lesezyklus genügend Zeit hat, sich zu deaktivieren. Das Ausgabefreigabesignal des Speicherschaltkreises oe und das Freigabesignal ER für das Leseregister bleiben während des gesamten Schreibzugriffs inaktiv. Bei einem Lesezugriff bleiben das Speicherschreibsignal wr und das Freigabesignal EW für den Schreibtreiber durchgängig inaktiv. Das Ausgabefreigabesignal des Schaltkreises oe soll, damit der Treiber für die zu schreibenden Daten genügend Zeit zur Deaktivierung hat, erst im zweiten Takt aktiviert werden. Das Freigabesignal ER für das Leseregister ist nur im letzten Takt vor Abschluss der Leseoperation zu aktivieren. Im nächsten Entwurfsschritt wird aus den Steuersignalverläufen in Abb. 4.66 b der Zustandsgraph in Abb. 4.66 c konstruiert. Nach der Initialisierung und wenn kein Speicherzugriff erfolgt, soll sich der Automat im Zustand »Stopp« befinden, in dem alle Steuersignale außer dem Freigabesignal EE für das Eingaberegister inaktiv sind. Wenn das externe Schreibsignal W im Stopp-Zustand oder am Ende eines Schreib- oder Lesezylusses aktiv ist, wird anschließend die Zustandsfolge »W1« bis »W4« durchlaufen, die die Steuersignale für den Schreibvorgang erzeugt. In Analogie dazu wird in diesen Zuständen, wenn das Schreibsignal W inaktiv und das Lesesignal R aktiv ist,
4.4 Schreib-Lese-Speicher
365
die Zyklusfolge »R1« bis »R4« für die Erzeugung der Steuersignalfolge für den Lesevorgang durchlaufen. Im letzten Schreib- und im letzten Lesezustand wird jeweils wie im Stopp-Zustand das Steuersignal EE für die Abtastung der Eingabeadresse und der Eingabedaten aktiviert, damit sich sofort der nächste Schreib- oder Lesezyklus anschließen kann. Der nächste Entwurfsschritt ist die Abschätzung der minimalen Taktperiode. Die kritischste Zeitbedingung für den Beispielschaltkreis ist, dass der zwei Takte lange Schreibimpuls eine Mindestbreite von twr ≥ 8 ns haben muss. Daraus folgt für die Taktperiode eine Mindestlänge von TP ≥ 4 ns. Relativ kritisch ist auch die Einhaltung der Datenvorhaltezeit von tsx ≥ 6 ns für die in Summe mit der Aktivierungsverzögerung des Eingabetreibers auch nur zwei Taktperioden eingeplant sind. Wenn der Eingabetreiber des zu entwerfenden Controllers eine längere Aktivierungsverzögerung als 2 ns hat, muss die Taktperiode entsprechend länger als 4 ns gewählt werden. Der weitere Entwurf vom Zustandsgraphen bis zur fertigen Simulationsund Synthesebeschreibung erfolgt in der bereits mehrfach vorgeführten Weise und hat Rezeptcharakter (siehe Abschnitt 1.6). Auch wenn die Schaltung in Abb. 4.66 wesentlich komplizierter aussieht als die in Abb. 4.65, ist die zweite Schaltung einfacher zu entwerfen. Denn sie ist nicht laufzeitkritisch und damit synthesefähig. Allerdings ist die zweite Schaltung mit dem Automaten deutlich aufwändiger und langsamer als die erste. 4.4.2 Mehrportspeicher Ein zeitgleicher Zugriff auf mehrere Speicherplätze eines Blockspeichers verlangt Zellen, die über mehrere Sätze von Steuerleitungen unabhängig voneinander gelesen und beschrieben werden können. In Abb. 4.67 a ist die 6Transistor-Zelle aus Abb. 4.62 um einen zweiten Port erweitert. Der zweite Port besteht auf der Zellenebene aus dem zusätzlichen Auswahltransistorpaar T7 und T8. Jeder Port besitzt eigene Zeilen- und Spaltenauswahlsignale, die von einer Port-eigenen Ansteuerschaltung erzeugt werden (Abb. 4.67 b). Nach außen hin verhalten sich die einzelnen Ports eines Mehrportspeichers wie separate Speicher, nur dass der Zugriff auf dieselbe Speichermatrix erfolgt. Ein s¯1.j s¯2.j
z2.i
a)
z1.i
T5
x T1
Port1
T4 T8 x ¯ T6 T3
S-Decoder
b)
Port2
Z-Decoder
T7 T2
Z-Decoder
r¯2.j r¯1.j UV
S-Decoder
Abb. 4.67. 2-Port-Speicher a) Speicherzelle b) Gesamtstruktur (S-Decoder – Schreib-Lese-Steuerung und Spaltenauswahl; Z-Decoder – Zeilenauswahl)
366
4 Vom Transistor zur Schaltung
gleichzeitiges Beschreiben derselben Zelle mit unterschiedlichen Werten invalidiert den gespeicherten Inhalt und ist nach Möglichkeit durch eine geeignete Ansteuerschaltung auszuschließen. Eine typische Anwendung für Mehrportspeicher ist die Kopplung von Rechnern. Synthesebeschreibungen mit 1-Port- und Mehrportspeichern Abbildung 4.68 a zeigt noch einmal die synthesefähige Beschreibung für den 1-Port-Speicher aus Abb. 3.26 in Abschnitt 3.4.3. Wenn das Schreibsignal aktiv ist, wird das adressierte Feldelement beschrieben. Sonst, wenn das Lesesignal aktiv ist, wird der Wert des adressierten Feldelements gelesen. Für beide Operationen steht maximal die Dauer einer Taktperiode zur Verfügung. ts signal RAM is tRAM(0 to 2**a’length - 1); signal T, W, E: std_logic; x signal x, y: tDaten; a W signal a: tAdresse; E ... process (T) T begin b) if rising_edge(T) then T if W=’1’ and E=’1’ then x RAM(Int(a)) <= x; a elsif E=’1’ then W y <= RAM(Int(a)); E end if; y end if; a) end process; c)
t h , td
&
x RAM y a w
x E
y
& 1 0
Schreiben Lesen Pause
1 0 1 0
ts
th
td
Abb. 4.68. Synthesebeschreibung eines 1-Port-Speichers a) VHDL-Beschreibung b) erforderliche Beschaltung der Kernfunktion c) Ansteuerung
Abbildung 4.68 b zeigt die zugehörige Beschaltung der Kernfunktion. Die Adress-, Steuer- und Eingabedatensignale müssen, damit der gesamte Blockspeicher nach außen laufzeitrobust ist, abgetastet auf die Kernfunktion geführt werden. Ein nachgeschaltetes Ausgabe-Latch lässt die Lesewerte bei einem Lesezugriff passieren und hält die Ausgabe während der Schreibzugriffe konstant. Die internen Verzögerungszeiten sind so laufzeitkritisch, dass der Speicher gemeinsam mit seiner Schnittstelle von der Synthese nur als fertig vorentworfener und verdrahteter Block verwendet werden kann. Die Vorhalteund die Nachhaltezeiten zur aktiven Taktflanke für die Adresse, die Eingabedaten und die Steuersignale sind die des eingangsseitigen Abtastregisters und damit sehr klein. Die Haltezeit th und die Verzögerungszeit td eines Lesezugriffs sind etwas größer als die der Speicherkernfunktion und damit sehr groß (Abb. 4.68 c).
4.4 Schreib-Lese-Speicher
367
Die Synthesebeschreibung eines Mehrportspeichers ist sehr ähnlich zu der eines 1-Port-Speichers. Die Speichermatrix und die einzelnen Schreib- und Lesezugriffe werden genauso beschrieben. Der einzige Unterschied ist, dass in der Beschreibung eines Mehrportspeichers die Möglichkeit existiert, dass auf mehrere Speicherelemente zeitgleich zugegriffen wird. Schaltungstechnisch ist bei einem synchronen Mehrportspeicher jeder Port wie der des 1-PortSpeichers in Abb. 4.68 b zu beschalten. Im nachfolgenden Beschreibungsbeispiel wird in jedem Takt auf den Speicherplatz mit der Adresse a geschrieben und gleichzeitig der Inhalt der Vorgängeradresse gelesen: process (T) begin if rising_edge(T) then RAM(Int(a)) <= x; y <= RAM(Int(a) - 1); end if; end process;
Das erfordert einen 2-Port-Speicher. Eine weitere Leseoperation würde die erforderliche Port-Anzahl auf drei erhöhen. Auch wenn zwei zeitlich nicht zueinander ausgerichtete Prozesse auf dasselbe Bitvektorfeld schreibend oder lesend zugreifen, muss der Speicher mindestens zwei getrennte Ports haben. Umgekehrt ist es eine wichtige Optimierungsrichtlinie, in der Synthesebeschreibung die maximal mögliche Anzahl der zeitgleichen Speicherzugriffe, die die Portanzahl festlegt, so gering wie möglich zu halten. FIFO mit 1- und 2-Port-Speicher Abbildung 4.69 zeigt eine Schaltung zur Rechnerkopplung mit einem FIFOSpeicher12 . Das Initialisierungssignal I stellt den Anfangszustand »FIFO leer« her. Rechner 1 schreibt Daten in den FIFO und Rechner 2 holt die Daten in derselben Reihenfolge aus dem FIFO ab. In Abschnitt 3.4.4 wurde ein FIFO als ein Datenobjekt aus einem Blockspeicher, einem Lesezeiger, einem Schreibzeiger, einem Flag für »voll«, einem Flag für »leer« und je einer Bearbeitungsmethode zum Initialisieren, zum Schreiben eines Datenobjekts und zum Lesen eines Datenobjekts modelliert. Die Methode »init« setzt die beiden Zeiger auf null, das Flag »leer« auf true und das Flag »voll« auf false. Die Schreibmethode wird nur ausgeführt, wenn der FIFO nicht voll ist. Sie löscht das Flag »leer«, schreibt die Daten auf den nächsten freien Platz und stellt den Schreibzeiger weiter. Die Lesemethode entnimmt, wenn der FIFO nicht leer ist, den ältesten Wert und stellt den Lesezeiger weiter: 12
Ein FIFO-Speicher bildet eine Warteschlange nach, in die nacheinander Daten geschrieben und aus der die Daten in derselben Reihenfolge wieder abgeholt werden können (vgl. Abschnitt 3.4.4).
368
4 Vom Transistor zur Schaltung
Rechner 1 a) x y W R I leer voll
I T
x FIFO y W leer voll R I
Eingabedaten Ausgabedaten Schreibsignal Lesesignal Initialisierungssignal Flags f¨ ur leer und voll
Rechner 2
T I W R x
1 0 1 0 1 0 1 0
w1 w2 w3
y (voll) 4 3 F¨ ullstand 2 1 (leer) 0 b)
w4 w5 w6 w1 w2
w3
w3 w3 w4 w4 w1 w1 w2 w2 w3 w4 w5 w5 w1 w2 w3 w3 w3 w4 w5 w6 w6
Abb. 4.69. FIFO-Speicher a) Einsatz zur Kopplung von zwei Rechnern b) Funktionsweise an einem Beispielablauf
–- Package-Vereinbarungen für ein FIFO-Modell constant N_Adr: positiv := ...; –- Anzahl der Adressbits subtype tAdrIdx is range 0 to 2**N_Adr - 1; subtype tDaten is ...; type tSpeicher is array (tAdrIdx) of tDaten; type tFIFO is record Speicher: tSpeicher; –- Speichermatrix sz, lz : tAdrIdx; –- Schreib- und Lesezeiger leer, voll : boolean; –- Statussignale end record; procedure init(signal fifo: inout tFIFO); procedure write(x: tDaten; signal fifo: inout tFIFO); procedure read(signal y: out tDaten; fifo: inout tFIFO); ⇒WEB-Projekt: P3.4/FIFO_pack.vhdl
Im folgenden Verhaltensmodell wird der FIFO in jedem Taktschritt genau wie in Abb. 4.69 nur entweder beschrieben oder gelesen: signal x, y: tDaten; signal fifo: tFIFO; ... if init=’1’ then init(fifo); elsif rising_edge(T) then if W=’1’ and not voll then write(x, fifo); elsif R=’1’ and not leer then read(y, fifo); end if; end if;
Da in keinem Takt auf mehrere Speicherplätze zugegriffen wird, würde die Synthese für den Blockspeicher des FIFO-Objekts einen 1-Port-Speicher wählen. Bei einer geringfügigen Modifikation, die ein zeitgleiches Lesen und Schreiben nicht mehr ausschließt,
4.4 Schreib-Lese-Speicher
369
... elsif rising_edge(T) then if W=’1’ and not voll then write(x, fifo); end if; if R=’1’ and not leer then read(y, fifo); end if; end if;
muss die Synthese einen synchronen 2-Port-Speicher einsetzen. 4.4.3 Assoziativspeicher Ein Assoziativspeicher ist ein normal beschreibbarer und lesbarer Speicher mit einer Zusatzfunktion für den parallelen Vergleich. Bei der Vergleichsoperation werden die Eingabedaten in einem Schritt mit den gespeicherten Bitmustern aller Speicherzeilen verglichen. Abbildung 4.70 zeigt die Schaltung einer Zelle. Die Transistoren T1 bis T6 bilden eine normale RAM-Zelle und die Transistoren T7 bis T10 bilden die Schaltung für den bitweisen Vergleich. Bei der Vergleichsoperation werden die Spaltenleitungen wie beim Schreiben angesteuert: r¯j = dj ; s¯j = d¯j (4.37) (dj – Bitwert für Spalte j). Die Zeilenauswahlsignale bleiben jedoch alle inaktiv, so dass der Zelleninhalt nicht verändert wird. Das Netzwerk aus den Transistoren T7 bis T10 hat die Funktion fij = (¯ rj ∧ x ¯ij ) ∨ (¯ sj ∧ xij ) = (dj ∧ x ¯ij ) ∨ d¯j ∧ xij
(4.38)
Die Parallelschaltung der beiden Transistorpaare sperrt genau dann, wenn der Zellenwert mit dem Eingabewert übereinstimmt. Die Vergleichsschaltungen aller Spalten j einer Zeile i sind parallel geschaltet und damit ODER-verknüpft: dj UV vi
r¯j
s¯j
d¯j+1
r¯j+1
s¯j+1 H
T8 T5 T7 xij
zi = 0
dj+1
d¯j
T2 T1
T4
T10
T3
T9
x ¯ij
T6
xi,j+1 x ¯i,j+1
H
vi+1
xi+1,j x ¯i+1,j
xi+1,j+1 x ¯i+1,j+1
Abb. 4.70. Aufbau der Speichermatrix eines Assoziativspeichers
370
4 Vom Transistor zur Schaltung
fi =
NS _
fij
(4.39)
j=1
(NS – Spaltenanzahl). Das Gesamtnetzwerk einer Zeile i ist genau dann gesperrt (fi = 0), wenn die Eingabebits aller Spalten übereinstimmen. Ein PullUp-Element erzeugt in diesem Fall den Vergleichswert vi = 1. Die Gesamtausgabe besteht in der Regel aus zwei Informationseinheiten. Das ist zum einen ein binäres Signal »hit«, das aktiviert wird, wenn das Suchmuster im Speicher steht, und zum anderen die Adresse der ersten Zeile, in der das Suchmuster gefunden wird. Für die Modellierung eines synchronen Assoziativspeichers bietet sich genau wie für den FIFO im Vorabschnitt eine objektorientierte Modellierung an. Das Modell der Speichermatrix und die Schreib- und die Lesemethode unterscheiden sich nicht von der eines einfachen Schreib-Lese-Speichers und seien wie die in Abschnitt 3.4.3 definiert: subtype tDaten is std_logic_vector(... downto 0); type tMem is array (natural range <>) of tDaten; subtype tAdr is tUnsigned(... downto 0); function read(Mem: tMem; adr: tAdr) return tDaten; procedure write(x: tDaten, adr: tAdr, signal Mem: inout tMem);
Zusätzlich benötigt das Modell eines Assoziativspeichers eine Suchmethode zur Bestimmung der Adresse der ersten Speicherzeile, in der der Eingabewert steht: procedure search(x: tDaten; signal adr: out tAdr; signal hit: out std_logic; signal Mem: inout tMem) is begin for idx in Mem’range loop if Mem(idx)=x then hit <= ’1’; adr <= to_tUnsigned(idx, adr’length); return; end if; end loop; hit <= ’0’; adr <= (others=>’0’); ⇒WEB-Projekt: P4.4/AsRAM_pack.vhdl end procedure;
Auch wenn der Vergleich in der Hardware parallel erfolgt, muss er im Simulationsmodell sequenziell nachgebildet werden.
4.4 Schreib-Lese-Speicher
371
4.4.4 Dynamische Speicher (DRAM) Dynamische Speicher (DRAM – dynamic random access memory) besitzen die kleinsten Speicherzellen und die höchste Speicherdichte. Jede Speicherzelle besteht aus einer winzigen Kapazität CS , die über einen NMOS-Transistor mit einer Bitleitung verbunden ist (Abb. 4.71 a). Der Preis des einfachen Aufbaus und des geringen Flächenbedarfs der Zellen ist eine deutlich kompliziertere Funktionsweise und Ansteuerung. Auswahlleitung x
Bitleitung
a)
uCS
CS
ux
D∗
b)
UV
S∗
CS
uGS
uCS c)
D∗ S∗
UV
uCS
∗
Der Source ist bei einem NMOS-Transistor immer der Kanalanschluss mit dem niedrigeren und der Drain der mit dem höheren Potenzial. Abb. 4.71. a) DRAM-Zelle b) Schreiben einer »0« c) Schreiben einer »1«
Beschreiben einer Speicherzelle Zum Beschreiben der Speicherzelle wird auf der Bitleitung die Spannung zur Darstellung des Logikwertes angelegt ( 0 für x = 0 ux = (4.40) UV für x = 1 und der Transistor eingeschaltet. Beim Schreiben einer »0« arbeitet der Transistor ganz normal als Low-Side-Schalter (Abb. 4.71 b). Der Source, d.h. der Kanalanschluss mit dem niedrigeren Potenzial, ist der Leseleitungsanschluss und hat das Potenzial 0 V. Die Lesegeschwindigkeit errechnet sich nach demselben Modell wie die Ausschaltzeit taus eines Inverters (Gleichung 4.20). Beim Aufladen der Lastkapazität hat die Kapazitätsseite des Kanals das niedrigere Potenzial und bildet den Source. Die Gate-Drain-Spannung ist null, so dass der Transistor während des gesamten Aufladevorgangs im Abschnürbereich arbeitet (Abb. 4.71 c). Die Spannung über der Speicherkapazität strebt nicht gegen die Versorgungsspannung, sondern nur gegen uCS ≤ UV − UTN
(4.41)
(UTN – Einschaltspannung des Auswahltransistors). Der Aufladestrom ist deutlich kleiner als beim Aufladen über einen PMOS-Transistor mit vergleichbaren Parametern, so dass das Schreiben einer »1« vergleichsweise lange dauert [30].
372
4 Vom Transistor zur Schaltung
Lesen Der Lesevorgang ist noch komplizierter. Vor dem Lesen wird die Ladung auf der Bitleitung gelöscht: (−) QCx = Cx · Ux(−) = 0 Die Speicherkapazität hat vor dem Lesen die Ladung ( 0 für eine gespeicherte »0« (−) QCS = CS · UV − UTN für eine gespeicherte »1«
(4.42)
Anschließend wird der Transistor geöffnet. Es kommt zum Ladungsausgleich. Die gespeicherte Ladung geht dabei nicht verloren, sondern verteilt sich auf beide Kapazitäten: (−) QCS + QCx = QCS (4.43) Im stationären Zustand nach dem Einschalten des Transistors sind die Spannungsabfälle über beiden Kapazitäten gleich: (+)
UCS = Ux(+)
(4.44)
Die Ausgabespannung auf der Bitleitung strebt gegen ( 0 für eine gespeicherte »0« CS (+) Ux = · CS + Cx UV − UTN für eine gespeicherte »1«
(4.45)
Die Kapazität Cx der Bitleitung ist um mindestens zwei Zehnerpotenzen größer als die Speicherkapazität CS , so dass der Potenzialunterschied zwischen einer gelesenen »0« und einer gelesenen »1« nur wenige Millivolt beträgt (Abb. 4.72 a). Die Auswertung der Lesepotenziale auf den Bitleitungen erfolgt nach den Grundprinzipien des Analogentwurfs »Symmetrie und Kompensation«. In einer vollkommen symmetrischen Anordnung werden immer paarweise zwei Lese-Schreib-Steuerung Zwischenspeicher Leseverst¨arker
Ladungsausgleich UV a)
uCS
CS
Cx
ux (wenige Millivolt)
Leseablauf: Vorladen (L = 1) Ladungsausgleich (zi = 1) Vergleich und Speichern Zur¨ uckschreiben
zi Cs
b)
xj
xref 1: positiv 0: negativ
Cx
L
1 Cs 2
Cx
Abb. 4.72. Lesen einer DRAM-Zelle a) Ersatzschaltung für den Ladungsausgleich b) symmetrische Anordnung zur Auswertung der Lesepotenziale
4.4 Schreib-Lese-Speicher
373
Zellen gelesen, eine richtige und eine aufgeladene Dummy-Zelle mit der halben Kapazität. Wenn nach dem Ladungsausgleich das Potenzial der Leseleitung der richtigen Zelle größer als das der Leseleitung der Dummy-Zelle ist, wird eine »1« erkannt, sonst eine »0« (Abb. 4.72 b). Beim Lesen wird der gespeicherte Wert zerstört, so dass jede Zelle nach dem Lesevorgang neu beschrieben werden muss. Der komplette Lesezyklus besteht praktisch aus vier Schritten: • • • •
Entladen der Leseleitungen und Aufladen der Dummy-Zellen, Ladungsausgleich, Bestimmung der Logikwerte auf den Leseleitungen und Übernahme in den Zwischenspeicher und Zurückschreiben der gelesenen Inhalte.
Ein DRAM hat noch mindestens eine weitere Betriebsart, das Auffrischen (engl. refresh). Die Daten in den Speicherzellen bleiben nur wenige Millisekunden gültig (vgl. Abschnitt 4.3.1). Deshalb ist es notwendig, dass jede Speicherzelle innerhalb von wenigen Millisekunden einmal gelesen und der gelesene Inhalt zurückgespeichert wird. Damit das zeitlich möglich ist, erfolgt das Auffrischen und damit auch das Lesen nicht zellen-, sondern zeilenweise. Die Zeilenanzahl bestimmt die Bitleitungskapazität Cx . Sie darf, damit die Potenzialunterschiede auf den Bitleitungen beim Lesen noch sicher ausgewertet werden können, die Größenordnung Hundert bis Tausend nicht überschreiten. Dadurch gilt für alle DRAMs unabhängig von ihrer Organisation und Speichergröße, dass mindestens alle Hundert bis Tausend Speicherzugriffe ein Auffrischzyklus einzufügen ist. Ansteuerung DRAMs haben heute praktisch alle eine programmierbare synchrone Schnittstelle, die die komplexen internen Zeitabläufe verbirgt. Die Ansteuerung erfolgt über Befehle. Abbildung 4.73 zeigt einen vereinfachten Operationsablaufgraphen für die Ansteuerung eines DDR2-RAMs. Nach Zuschalten der Versorgungsspannung ist die Schnittstellenschaltung des Speichers zu initialisieren, bevor sie für Datenzugriffe bereit ist. Das beinhaltet die Initialisierung der Regelkreise für die interne Takterzeugung und dauert einige Hundert Takte (vgl. Abschnitt 4.3.6). Im Zustand »bereit« akzeptiert die Schnittstellenschaltung zwei Kommandos: • •
ACT (activate): Lesen einer Speicherzeile in den Zwischenpuffer und SFR (refresh): Lesen, Verstärken und Zurückschreiben.
Das Activate-Kommando benötigt zusätzlich die Zeilenadresse, das RefreshKommando entnimmt die Zeilenadresse einem internen Zähler, der nach jedem Refresh weitergeschaltet wird. Beide Operationen dauern mehrere Takte.
374
4 Vom Transistor zur Schaltung Anfangsinitialisierung bereit ACT RD
SFR Zeile auffrischen
Zeile gelesen RD WR WR Lesezyklus Schreibzyklus RDA letzter Lesezyklus
PR
WRA letzter Schreibzyklus
Zeile zur¨ uckspeichern, Vorladen(1)
SFR ACT RD RDA WR WRA PR (1)
Zeile auffrischen Zeile aktivieren Lesen abschließendes Lesen Schreiben abschließendes Schreiben Seite schließen Vorbereitung f¨ ur das Lesen der n¨achsten Zeile Zustands¨ ubergang ohne Befehl
Abb. 4.73. Vereinfachter Operationsablaufgraph für die Ansteuerung eines DDR2SDRAMs
Im Zustand »Zeile gelesen« wird nur noch mit dem Zwischenpuffer gearbeitet. Dieser verhält sich ähnlich wie ein wahlfrei adressierbarer Schreib-LeseSpeicher. Wenn eine Zeile im Zwischenpuffer steht, kann der Speicherschaltkreis folgende Kommandos ausführen: • • •
RD, RDA: Lesen, abschließendes Lesen, WR, WRA: Schreiben, abschließendes Schreiben und PR (precharge) Seite schließen.
Mit den Schreib- und Lesebefehlen ist zusätzlich die Spaltenadresse zu übergeben. Nach den Abschlussoperationen RDA, WRA und PR werden die Werte aus dem Zwischenspeicher zurück in die Speicherzeile geschrieben, die Bitleitungen entladen und die Dummy-Zellen aufgeladen. Danach ist der Schaltkreis wieder für eine Auffrischoperation oder die Aktivierung einer anderen Zeile bereit. Die Lese- und Schreibzugriffe auf einen DRAM erfolgen in der Regel Burstorientiert. Bei jedem Zugriff werden die Daten für z.B. vier oder acht aufeinanderfolgende Adressen übertragen. In Abb. 4.74 wird zuerst mit dem ActivateBefehl und der Übergabe der Zeilenadresse der Lesevorgang einer Speicherzeile gestartet. Das dauert typisch zwei bis drei Takte. Anschließend wird mit einem Lesebefehl und der Übergabe einer Spaltenadresse eine Leseoperation gestartet. Danach vergehen weitere zwei bis drei Takte, bevor der Schaltkreis eine Datenfolge schickt. Die Daten wechseln sowohl nach der fallenden als auch nach der steigenden Flanke, so dass in jeder Taktperiode zwei Datenworte übertragen werden. Letzterem verdankt das Schnittstellenprotokoll seinen Namen (DDR – double date rate). Der Zeitablauf einer Schreiboperation ist ähnlich. Nach Übersenden des Schreibkommandos und der Spaltenadresse vergeht eine definierte Anzahl von Takten, nach denen die zu schreibenden Daten mit der doppelten Taktrate auf den Datenbus zu legen sind.
4.5 Festwertspeicher 0
Takt Befehl
ACT
Adresse
za
1
2
3
NOP
RD
5
6
NOP
sar tACT
Daten
4
7 WR
8
9
375
10
NOP
saw tRD
Z NOP kein Befehl (no operation) za Zeilenadresse sar Spaltenadresse Leseoperation
tWR o0 o1 o2 o3
i0 i1 i2 i3
saw Spaltenadresse Schreiboperation oi gelesene Daten ii zu schreibende Daten
Abb. 4.74. Datenaustausch über eine DDR2-Schnittstelle
Ein einzelner Lese- oder Schreibzugriff auf einen DRAM-Schaltkreis dauert mehrere Takte, aber es ist im günstigsten Fall fast möglich, in jedem Takt zwei Datenworte zu übertragen. Der hohe Datendurchsatz lässt sich jedoch nur nutzen, wenn die Daten in größeren Blöcken gelesen und gespeichert werden. Schaltungen mit DRAMs als Speicher für große Datenmengen besitzen deshalb meist schnelle Zwischenspeicher, die aus den DRAM-Schaltkreisen blockweise geladen und deren Daten einzeln weiterverarbeitet werden. Umgekehrt werden die Daten in den schnellen Zwischenspeichern einzeln berechnet und blockweise in die DRAMs zurückgeschrieben. In Rechnern sind diese Zwischenspeicher die Cache-Speicher. Weiterführende und ergänzende Literatur siehe [36, 47].
4.5 Festwertspeicher Festwertspeicher, abgekürzt ROM (read only memory), können nur einmal oder nur mit großem Zeitaufwand beschrieben werden und behalten ihre Daten auch ohne Versorgungsspannung über Jahre. Das Speicherelement ist heute meist ein einzelner Transistor, der entweder ein- und ausschaltbar ist oder nur einen der beiden Schaltzustände besitzt. Bei einem NOR-ROM sind die Transistoren, die die Speicherzellen bilden, parallel geschaltet. Die deaktivierten Transistoren schalten nicht ein. Die Zeilenauswahlschaltung steuert pro Spalte nur einen Transistor mit »1« und alle anderen mit »0« an. Die gesamte Parallelschaltung ist leitend, wenn der ausgewählte Transistor einschaltet. Die Pull-Up-Elemente der Spalten wandeln die Schaltzustände in Logikwerte um (Abb. 4.75). Es gibt noch weitere Organisationsformen. Die Deaktivierung der Transistoren kann bei der Herstellung oder beim Anwender erfolgen, nur einmal oder mehrmals möglich sein etc. [30]. Das Verhaltensmodell eines Festwertspeichers ist eine initialisierte Bitvektor-Konstante, deren Elemente mit der Adresse als Index gelesen werden (vgl. Abschnitt 3.4.3). Die Zugriffszeit ist wie bei anderen Blockspeichern um Zehnerpotenzen größer als die Verzögerung eines Logikgatters. Denn auch nur lesbare
376
4 Vom Transistor zur Schaltung Spaltenauswahl und Pull-Up-Elemente
a
0 1
T1.1 Z
T1.2 Z
T1.3 Z
T2.1{0,Z}
T2.2{0,Z}
T2.3{0,Z}
T3.1 Z
T3.2 Z
T3.3 Z
a
0
a) a
Zeilenauswahl
Zeilenauswahl
Spaltenauswahl und Pull-Up-Elemente H H H
T1.1
T1.2
T1.3
T2.1
T2.2
T2.3
T3.1
T3.2
T3.3
b)
progr. Transistoren Adresse
Polysilizium-Streifen n+ -Gebiet
Metallleiterbahn Durchkontaktierung
Abb. 4.75. Festwertspeicher a) Schaltung b) geometrische Anordnung
Speicher besitzen Zeilen- und Spaltenleitungen mit großen Lastkapazitäten, die von verhältnismäßig schmalen Transistoren angesteuert werden.
4.6 Programmierbare Logikschaltkreise Ein programmierbarer Logikschaltkreis besteht aus programmierbaren Logikblöcken, programmierbaren Verbindungsnetzwerken und programmierbaren Eingabe-Ausgabe-Schaltungen (Abb. 4.76). Größere programmierbare Logikschaltkreise enthalten zusätzlich konfigurierbare Taktversorgungsschaltungen, Blockspeicher, Rechenwerke und Prozessorkerne. Die Funktion wird über Konfigurationsspeicher festgelegt. Kostengünstige programmierbare Logikschaltkreise können inzwischen Schaltungen nachbilden, die in nicht programmierbaren Schaltkreisen aus Millionen von Transistoren bestehen. Eingesetzt werden programmierbare Logikschaltkreise in Prototypen, Kleinserien, studentischen Praktika und auch zunehmend, um Hardware umprogrammieren zu können.
IO
PLB
PLB
PLB
IO IO
PLB
PLB IO
programmierbarer Logikblock programmierbares Verbindungsnetzwerk programmierbare Eingabe-Ausgabe-Schaltung
Abb. 4.76. Grundstruktur eines programmierbaren Logikschaltkreises
4.6 Programmierbare Logikschaltkreise
377
4.6.1 Programmierbare Tabellenfunktionen Eine programmierbare Tabellenfunktion ordnet jedem Eingabewert einen individuell einstellbaren Ausgabewert zu. Auf diese Weise lässt sich jede kombinatorische Funktion nachbilden. Die Schaltung ähnelt einem Speicher mit wahlfreiem Zugriff, einem Festwertspeicher oder einem RAM. Der Zeilendecoder – eine UND-Matrix – aktiviert für jede Eingabevariation genau eine der 2n Zeilenauswahlsignale. Die Zeilenauswahlsignale werden als Produktterme bezeichnet. Die ODER-Matrix, die für jedes Ausgabebit auswählt, bei welchen aktivierten Produkttermen eine »1« ausgegeben wird, ist programmierbar (Abb. 4.77). xn−1
···
x2 x1 x0
2n Produktterme
UND-Matrix ODER-Matrix (1 aus 2n - Decoder) · · · (programmierbar) ym−1
···
y2 y1 y0
Abb. 4.77. UND-ODER-Matrix zur Programmierung von Tabellenfunktionen
Abbildung 4.78 zeigt eine mögliche Realisierung. Die schwarzen Querstriche an den Kreuzungspunkten der Zeilen- und Spaltenleitungen in der UNDMatrix sind normale NMOS-Transistoren, die bei einer »1« am Gate einschalten. An den anderen Kreuzungspunkten befindet sich entweder kein oder ein deaktivierter Transistor, der auch bei einer »1« am Gate ausgeschaltet bleibt (vgl. Abschnitt 4.5). Das PMOS-Netzwerk ist durch Pull-Up-Elemente ersetzt. Die Treiber zwischen den Ausgängen der UND-Matrix und den Eingängen der ODER-Matrix sind Puffer zur Verringerung der Verzögerungszeiten (vgl. Abschnitt 4.2.7). Die ODER-Matrix besteht aus programmierbaren Transistoren. Um bei der Auswahl einer Zeile am Ausgang eine »0« auszugeben, ist x2
x1
x0
ODER-Matrix H
H
H p0 = x ¯2 x ¯1 x ¯0
H
p1 = x ¯2 x ¯1 x0
H ··· H
···
UND-Matrix
y 1 = p0 ∨ p2
p2 = x ¯2 x1 x ¯0 ··· p7 = x2 x1 x0 y 0 = p0 ∨ p7
H
Pull-Up-Element Treiber normaler Transistor programmierbarer Transistor st¨andig gesperrt einschaltbar
Abb. 4.78. Logikschaltung mit programmierter ODER-Matrix
378
4 Vom Transistor zur Schaltung
der zugeordnete Transistor zu deaktivieren. Die Inverter an den Ausgängen bewirken, dass bei einem eingeschalteten (ausgewählten, nicht deaktivierten) Transistor eine »1« ausgegeben wird. Eine Tabellenfunktion mit n Eingängen und m Ausgängen benötigt m · 2n Programmierelemente. Eine einzelne Tabellenfunktion ist entsprechend nur für die Nachbildung von Schaltungen mit wenigen Eingängen geeignet. Größere Schaltungen werden aus mehreren Tabellenfunktionen zusammengesetzt. Das erfordert zusätzlich ein programmierbares Verbindungsnetzwerk. Dieses ist eine Matrix aus deaktivierbaren Treibern oder Transfergattern mit Programmierstellen an den Steuereingängen. Abbildung 4.79 zeigt als Beispiel die Aufspaltung der fünfstelligen Logikfunktion y = x1 x2 (x3 ∨ x4 ∨ x5 )
(4.46)
in zwei kleinere Tabellenfunktionen mit je nur drei Eingängen: Tabellenfunktion 1 : z = x3 ∨ x4 ∨ x5 Tabellenfunktion 2 : y = x1 x2 z
(4.47)
Bei der Technologieabbildung mit Tabellenfunktionen als Bausteine werden die minimierten logischen Ausdrücke oder Entscheidungsdiagramme in Teilfunktionen mit der Eingangsanzahl des Tabellentyps und einem Ausgang aufgeteilt. x3 x4 x5
Tabellenfunktion 1 z = x3 ∨ x4 ∨ x5
x3 x4 x5 x1 x2 y
x5 x4 x3 0 0 0 0 0 1 0 1 0 0 1 1 1 0 0 1 0 1 1 1 0 1 1 1
z 0 1 1 1 1 1 1 1
Tabellenfunktion 2 y = x1 x2 z
z
Verdrahtungskanal genutzte Verbindung
z x1 x2
x2 x1 0 0 0 0 0 1 0 1 1 0 1 0 1 1 1 1
z 0 1 0 1 0 1 0 1
y 0 0 0 0 0 0 0 1
y
programmierbare Verbindung programmierte Verbindung
Abb. 4.79. Programmierte Logikschaltung mit zwei Tabellenfunktionen
4.6.2 Programmierbare UND-Matrix Statt der ODER-Matrix kann auch die UND-Matrix programmierbar ausgeführt sein. Die ODER-Matrix ist dann die ODER-Verknüpfung aller Pro-
4.6 Programmierbare Logikschaltkreise
379
duktterme (Abb. 4.80). Die nachzubildende Funktion wird mit Hilfe von KVDiagrammen oder des Verfahrens von Quine und McCluskey minimiert (vgl. Abschnitt 2.2). Die begrenzende Ressource für die Größe der programmierbaren Funktionen ist hier die Eingangsanzahl der UND-Matrix (Größenordnung 8 . . . 16) und die maximale Anzahl der Produktterme (Größenordnung 4 . . . 16). xn−1 H
x2
x1
...
H
x0 ODER-Verkn¨ upfung aller Produktterme H p0 = x ¯2
H
Treiber normaler Transistor
p1 = x ¯n−1 x ¯0
H
p2 = x0
H
Pull-Up-Element
p3 = x ¯n−1 x2 x0 programmierbare UND-Matrix y = p3 ∨ p 2 ∨ p 1 ∨ p 0
programmierbarer Transistor st¨andig gesperrt einschaltbar
Abb. 4.80. Logikschaltung mit programmierter UND-Matrix
4.6.3 Weitere programmierbare Elemente Eine typische Erweiterung einer Logikfunktion mit programmierbarer UNDMatrix ist eine programmierbare Ausgabeinvertierung. Sie besteht aus einem EXOR-Gatter mit einer Programmierstelle am zweiten Eingang (Abb. 4.81). Bei einer programmierten »0« liefert die EXOR-Verknüpfung den Wert selbst und bei einer »1« den invertierten Wert der programmierten Logikfunktion. Auf diese Weise kann die nachzubildende Funktion wahlweise nach den Nullen oder Einsen entwickelt werden (vgl. Abschnitt 2.2.4).
Logikschaltung mit programmierter UND-Matrix Programmierstelle zur Ausgabeinvertierung
=1
x I
0 1
Programmierstelle ¨ zur Uberbr¨ uckung Speicherzelle programmierbarer Takt- und Initialisierungsanschluss
Abb. 4.81. Erweiterte programmierbare Funktionseinheit
Digitale Schaltungen benötigen immer eine größere Anzahl von Speicherzellen für die Abtastung und Zwischenspeicherung von Daten. In der üblichen Schaltungsarchitektur ist hinter jeder programmierbaren Logikfunktion eine
380
4 Vom Transistor zur Schaltung
überbrückbare taktflankengesteuerte Speicherzelle angeordnet, die bei Bedarf in den Signalfluss eingefügt und mit Steuersignalen verbunden werden kann. Konfigurierbare Blockspeicher haben eine bestimmte Speicherkapazität und eine bestimmte Portanzahl. Konfigurierbar sind z.B. die genutzte Datenbreite, die genutzte Speichertiefe und die Anzahl der genutzten Ports. Bei konfigurierbaren Taktversorgungsschaltungen mit Phasenregelkreisen sind die Teilerverhältnisse und die Phasenverschiebungen der Ausgabetakte einstellbar (vgl. Abschnitt 4.3.6). Bei konfigurierbaren Rechenwerken und Prozessorkernen gibt es Bitbreitenoptionen und die Möglichkeiten, nur Teile davon zu nutzen. 4.6.4 Zusammenfassung Programmierbare Logikschaltkreise bestehen aus programmierbaren Logikblöcken, programmierbaren Verbindungsnetzwerken, programmierbaren Eingabe-Ausgabe-Schaltungen und weiteren programmierbaren Elementen wie überbrückbaren Speicherzellen, programmierbaren Taktversorgungsschaltungen und programmierbaren Blockspeichern. Für sie wird ein Schaltungsentwurf nicht in Fertigungs-, sondern in Konfigurationsdaten übersetzt, mit denen die einzelnen Konfigurationsbits – Tabellenwerte für Logikfunktionen, Multiplexerauswahlwerte für die Überbrückung oder Einfügung von Speicherzellen etc. – programmiert werden. Auf diese Weise lässt sich die Funktion von Hardware genauso schnell und unkompliziert ändern wie die von Software. Weiterführende und ergänzende Literatur siehe [14, 18, 31, 43].
5 Komplexe Beispielentwürfe
In den vergangenen Kapiteln wurde gezeigt, wie die unterschiedlichen Bestandteile einer digitalen Schaltung realisiert, modelliert und simuliert werden. Dieses Kapitel führt anhand von drei Beispielen in den Entwurf komplexer Systeme ein. Zuvor werden zwei strukturbestimmende Aspekte für die Nachbildung rechenzeitintensiver Algorithmen in Hardware behandelt.
5.1 Pipeline-Verarbeitung und Speicherengpass Eine wesentliche Zielgröße einer digitalen Schaltung ist ihre Verarbeitungsleistung. Sie wird durch die Anzahl der Operationen, die pro Zeit fertiggestellt werden, beschrieben. Um die Verarbeitungsleistung über die der bisher betrachteten sequenziellen Verarbeitung zu erhöhen, gibt es zwei Möglichkeiten: • •
Parallelverarbeitung mit mehr Hardware und Pipeline-Verarbeitung.
Die Pipeline-Verarbeitung ist eine Art zeitversetzte Parallelverarbeitung, bei der statt zusätzlicher Hardware die vorhandene Hardware besser ausgelastet wird. Es genügt dabei jedoch nicht, nur die Verarbeitungswerke zu betrachten. Denn den Engpass bilden in der Regel die Blockspeicher, aus denen die zu verarbeitenden Eingabedaten bereitgestellt und in die die Ergebnisse geschrieben werden. 5.1.1 Prinzip der Pipeline-Verarbeitung Pipeline-Verarbeitung heißt auf Deutsch Fließbandverarbeitung. Eine umfangreiche Aufgabe wird in eine Folge von nacheinander auszuführenden Arbeitsschritten mit vergleichbar großem Arbeitsaufwand aufgeteilt. Das zu bearbeitende Objekt fährt auf dem Fließband von einer Arbeitsstation zur nächsten. An jeder Station wird ein Arbeitsschritt ausgeführt. Die Fertigstellung G. Kemnitz, Technische Informatik, eXamen.press, DOI 10.1007/978-3-642-17447-6_5, © Springer-Verlag Berlin Heidelberg 2011
382
5 Komplexe Beispielentwürfe
eines Einzelproduktes geht dadurch nicht schneller. Aber es wird gleichzeitig an NP Objekten gearbeitet. In jedem Fließbandtakt rücken die zu bearbeitenden Objekte eine Station weiter. Am Anfang kommt ein neues unbearbeitetes Objekt auf das Fließband und am Ende verlässt ein fertiges Produkt das Fließband. Bei einer Aufteilung in NP Teilaufgaben mit derselben Bearbeitungsdauer erhöht sich die Verarbeitungsleistung auf das NP -fache (Abb. 5.1). Arbeitsstationen 1
2
3
fertig 4 Arbeits- 3 station 2 1
4
Fließband Werkst¨ uck Dauer eines Fließbandtakts
tP
Fließbandtakt: 1 · tP Fertigungszeit: 4 · tP
1
2
3
4
5
t tP
Abb. 5.1. Prinzip der Pipeline-Verarbeitung
In einer digitalen Schaltung ist das zu fertigende Produkt das Ergebnis einer Berechnung. Die Arbeitsstationen sind die nacheinander auszuführenden Operationen. Nach jeder Operation wird das Zwischenergebnis von einem Register abgetastet und an die nächste Operation weitergereicht. Die Teilung der Gesamtoperation in Schritte reduziert die Ausführungszeit der einzelnen Register-Transfer-Operationen. Die Gesamtschaltung kann idealerweise mit der mehrfachen Taktfrequenz arbeiten. Genau wie bei einem richtigen Fließband bleibt die Ausführungszeit insgesamt mindestens so groß wie bei einer sequenziellen Verarbeitung. Aber es werden mehr Ergebnisse pro Zeit fertig (Abb. 5.2). tdS1
tdR x
x’
tdR
T
z
tsR y
f2 (z)
y’
f (x’) = f2 (f1 (x’))
T
x
f1 (x’)
tdS2
x’
tdS1 f1 (x’)
tsR tdR z
z’
tdS2 f2 (z’)
tsR y’
tdR tdS1 tdS2 tsR x z y ...’
Registerverz¨ogerungszeit Verz¨ogerungszeit von f1 (...) Verz¨ogerungszeit von f2 (...) Registervorhaltezeit Eingabesignal Zwischensignal Ausgabesignal abgetastetes Signal
minimale Taktperiode ohne Pipeline y” tdR + tsR + tdS1 + tdS2 mit Pipeline tdR + tsR + max(tdS1 , tdS2 )
Abb. 5.2. Aufteilung einer Register-Transfer-Funktion in zwei Pipeline-Phasen
5.1 Pipeline-Verarbeitung und Speicherengpass
383
In VHDL wird eine Pipeline durch eine Folge von Signalzuweisungen in einem Abtastprozess beschrieben. Für die Schaltung in Abb. 5.2 unten lautet die Prozessbeschreibung z.B. process(T) if rising_edge(T) then x_del <= x; z_del <= f1(x_del); y_del2 <= f2(z_del); end if; end process;
Die erste Signalzuweisung beschreibt die Abtastung des Eingabesignals. Der Funktionsaufruf in der zweiten Signalzuweisung beschreibt die erste kombinatorische Verarbeitungsschaltung und die Zuweisung selbst das nachfolgende Zwischenregister. Die dritte Signalzuweisung beschreibt die Verarbeitung des abgetasteten Zwischensignals durch die zweite kombinatorische Teilschaltung und die Abtastung mit dem Ausgaberegister. 5.1.2 Ausbalancieren einer Pipeline Das Black-Box-Modell einer Pipeline ist eine Register-Transfer-Operation, deren Ergebnis erst nach NP Verarbeitungsschritten für die Weiterverarbeitung oder die Ausgabe zur Verfügung steht (NP – Pipeline-Tiefe). Die Mindestlänge der Taktperiode, mit der eine Pipeline betrieben werden kann, und damit ihre maximale Verarbeitungsleistung, wird von der längsten RegisterRegister-Verzögerung bestimmt. Zur Minimierung der maximalen Verzögerung müssen die Verzögerungszeiten der Operationen zwischen den Registern aneinander angeglichen werden. Dieser Angleich wird als Ausbalancieren der Pipeline bezeichnet. Zum Ausbalancieren werden alle Register, die ihre Daten nach derselben Anzahl von Abtastschritten in Bezug auf den Operationsstart übernehmen, mit einer Zeitschnittlinie verbunden. Dann werden die Register in dem Datenflussgraphen so verschoben, dass die maximale Verzögerung zwischen zwei aufeinanderfolgenden Zeitschnittlinien ein Minimum oder einen hinreichend kleinen Wert erreicht. In Abb. 5.3 a bilden die Abtastregister ein Schieberegister am Ausgang der Verarbeitungsfunktion. Die Verzögerung vom Eingang bis zum ersten Register td1a ist sehr groß und die Verzögerungen zwischen den angehängten Abtastregistern sind fast null. Die zusätzlichen Register verzögern die Ergebnisfertigstellung, ohne die Verarbeitungsleistung zu erhöhen. Abbildung 5.3 b zeigt eine Schaltung mit demselben Anschlussverhalten und einer ausbalancierten Pipeline, in der die Verzögerungen zwischen den Registern nur etwa ein Drittel der ursprünglichen maximalen Verzögerung td1a in Abb. 5.3 a betragen. Die geänderte Schaltung kann etwa mit der dreifachen Frequenz getaktet werden und besitzt dann die dreifache Verarbeitungsleistung.
384
5 Komplexe Beispielentwürfe x1 x2 a) x1 x2 b)
f1 f2
td1a
z1 z2
f3
f2
z2
f5
z4
f4
Zeitebene 0 z f1 1
td2a
z3
y
Zeitebene 1 z’1
z’3
f3
z’2
f4
td1b
z’4
unausbalanciert td1a ≫ td2a ≈ td3a
td3a
y’
y”
y”’
Zeitebene 2
z”3 f5
y”
ausbalanciert td1b ≈ td2b ≈ td3b y”’
z”4
Zeitschnittlinie td3b
td2b
Pipeline-Register
Abb. 5.3. Pipeline-Einbau a) Anhängen von Abtastregistern an den Ausgang b) Ausbalancieren der Pipeline
5.1.3 Engpass Speicher Für eine schnelle Verarbeitung und Übertragung müssen die Eingabedaten mit der Verarbeitungsgeschwindigkeit bereitgestellt und die Ergebnisse nach ihrer Berechnung abgeholt werden. Die Datenquellen und die Empfänger für die Ergebnisse sind in der Regel Blockspeicher. Die Zugriffszeit auf einen größeren Blockspeicher übersteigt die Verzögerung von Logikgattern um Zehnerpotenzen und wächst mit der Datenkapazität (vgl. Abschnitt 4.4.1) [23]. Eine Vergrößerung der transferierten Datenmenge verlangt mehrere parallele Speicher, Mehrportspeicher, eine Erhöhung der Datenwortbreite oder andere schaltungsaufwändige Maßnahmen. Der Entwurf von schnellen Verarbeitungswerken beginnt deshalb in der Regel mit der Planung der Speicherstruktur, einer Ablaufplanung der Speicherzugriffe und einer Planung der VerarbeitungsPipelines. Die folgenden Abschnitte demonstrieren das an drei komplexen Beispielentwürfen.
5.2 FIR-Filter mit Blockspeichern Ein FIR-Filter ist ein Filter mit endlicher Impulsantwort (FIR – f inite impulse response) und ein wichtiger rechenzeitintensiver Algorithmus der digitalen Signalverarbeitung. Typische Anwendungen sind die Trennung der Spektralanteile in analogen Signalen, die Echo-Unterdrückung bei der Signalübertragung über lange Leitungen und die Bildverarbeitung. Trotz der vielfältigen Anwendungen ist die Funktion eines FIR-Filters einfach. Jeder Ausgabewert yn ist eine gewichtete Summe von M aufeinanderfolgenden Eingabewerten: yn =
M −1 X m=0
cm · x k
mit k = n − m
(5.1)
5.2 FIR-Filter mit Blockspeichern
385
(xk – Eingabewert; yn – Ausgabewert; n – Nummer des Zeitschritts; cm – Koeffizient; M – Länge der Impulsantwort, Größenordnung 100). Die Filterfunktion – Hochpass, Tiefpass, Glättungsfilter etc. – legt der Vektor der Koeffizienten cm fest1 . Es gibt Anwendungen in der Signalverarbeitung, in denen viele Millionen Ergebnisse pro Sekunde zu berechnen sind. Jede Ergebnisberechnung erfordert 2 · M Speicherzugriffe mit den zugehörigen Adressrechnungen, M Multiplikationen und M − 1 Additionen. Für Anwendungen mit einem kontinuierlichen Rechenleistungsbedarf von 108 und mehr Operationen pro Sekunde ist eine Hardware-Lösung eine interessante Alternative zu einer Software-Lösung. 5.2.1 Planung der Speicherstruktur und der Pipeline-Abläufe Abbildung 5.4 a zeigt eine Skizze für den Berechnungsfluss, den Gleichung 5.1 beschreibt. Für jeden Ausgabewert yn werden M Eingabewerte mit M Koeffizienten multipliziert und die Produkte aufsummiert. Es fehlt der Datenspeicher, aus dem die Eingabedaten und Koeffizienten gelesen werden. Wie könnte der Speicher organisiert sein? Ein M -Port-Blockspeicher scheidet wegen der Größe von M hier aus. Eine nahe liegende Lösung wäre ein Schieberegister der Länge M , das die Eingabewerte abtastet und an seinen Ausgängen die um einen bis M Schritte verzögerten Eingabewerte bereitstellt (Abb. 5.4 b). An jedem Schieberegisterausgang ist ein Multiplizierer anzuschließen, der das Produkt cm · xk bildet. Der zweite Faktor ist eine Konstante, die fest codiert sein oder in einem Konfigurationsregister stehen kann. Die Teilprodukte werden mit einem Addiererbaum zusammengefasst. Die Umsetzung in eine VHDL-Beschreibung ist relativ einfach, aber die Schaltung wird sehr groß2 .
a)
xn c0 xn−1 c1 xn−2 c2 xn−3 c3
*
+
* * *
+
yn
+ b)
x+ n
xn
c0
xn−1
c1
xn−2
c2 c3 xn−3
*
+
* *
+
yn
+
*
Abb. 5.4. Datenfluss für eine FIR-Operation mit M = 4 a) ohne Operandenspeicher b) mit einem Schieberegister als Operandenspeicher 1
2
Der Koeffizientenvektor wird auch als Impulsantwort bezeichnet. Denn er ist gleich mit der Ausgabefolge für einen Impuls der Form x0 = 1,0 und xn6=0 = 0 als Eingabefolge. Man würde zumindest versuchen, die Multipliziereranzahl durch Einbau von Pipeline-Stufen in die Multiplizierer drastisch zu verringern. Das erfordert eine vollkommen andere Schaltungsstruktur.
386
5 Komplexe Beispielentwürfe
Wir wollen hier statt eines Schieberegisters, das für einen größeren Filtergrad M recht schaltungsaufwändig ist, einen wesentlich kompakteren Blockspeicher verwenden. Aus einem (1-Port-) Blockspeicher kann in jedem Zeitschritt nur ein Datenwort gelesen werden. Das ist der Flaschenhals für die Berechnung. Jetzt folgt ein Puzzle, in dem die Operationen auf Rechenwerke und Berechnungsschritte aufgeteilt werden. Die Berechnung für jeden Ausgabewert besteht aus M Teilberechnungen. Fast jede der Teilberechnungen beginnt mit der Erhöhung der Koeffizientenadresse um eins und der Erhöhung der Datenadresse um eins. Danach werden ein Koeffizient und ein Datenwert aus dem Speicher gelesen. Anschließend werden beide Werte miteinander multipliziert und das Ergebnis zur bisherigen Teilsumme addiert. Mit den Koeffizienten und Daten im selben Speicher müsste der Speicher entweder zwei Ports haben oder die beiden Werte wären nacheinander zu lesen. Die eine Lösung ist aufwändig und die andere halbiert die Verarbeitungsleistung. Die nahe liegende Alternative ist der Einsatz von zwei Blockspeichern, einem Schreib-Lese-Speicher für die Daten und einem Nur-Lese-Speicher für die Koeffizienten. Das Zwischenergebnis der Überlegungen bis hierher ist ein Berechnungsfluss, der mit zwei parallelen Adressrechnungen beginnt. Danach folgen die zwei parallelen Speicherzugriffe. Die gelesenen Werte werden multipliziert und das Produkt zum bisher akkumulierten Wert addiert. Das ist die Kernoperation, die für jeden zu berechnenden Ausgabewert mit geringfügigen Modifikationen M -mal hintereinander ausgeführt werden muss. cAdr +1 xAdr +1 AR
cDat cROM a ROM y xRAM xDat x RAM y a w OF
Register cAdr Koeffizientenadressregister xAdr Datenadressregister cDat Koeffizientenregister xDat Datenregister
Prod mult
Mult
Akku +
Add
Ergebnis Pipeline-Phasen AR Adressrechnung OF Operanden lesen Mult Multiplikation Add Addition
Prod Produktregister Akku Akkumulator (Summationsregister) Blockspeicher cROM Koeffizientenspeicher xRAM Datenspeicher
Abb. 5.5. Blockspeicher-Pipeline-Struktur der Kernfunktion des FIR-Filters
Als Nächstes soll die Kernoperation in Pipeline-Phasen unterteilt werden. Die Blockspeicherzugriffe, die den zeitlichen Engpass bilden, bekommen eine eigene Pipeline-Phase. Dasselbe gilt für die Multiplikation, die auch relativ viel Zeit benötigt3 . Die verbleibenden Adressrechnungen zu Beginn des Be3
Wenn die Multiplikation länger als der Speicherzugriff dauert, kann sie auch auf mehrere Pipeline-Phasen aufgeteilt werden.
5.2 FIR-Filter mit Blockspeichern
387
rechnungsflusses und die Akkumulation der Produkte nach der Multiplikation erhalten gleichfalls je eine eigene Pipeline-Phase. Das Zwischenergebnis des Entwurfsprozesses ist die 4-stufige Verarbeitungs-Pipeline in Abb. 5.5. In der ersten Pipeline-Phase »AR« (Adressrechnung) werden die Adressen weitergeschaltet. In der zweiten Phase »OF« (operand f etch) werden der Datenwert und der Koeffizient gelesen. In der nächsten Phase »Mult« (Multiplikation) wird multipliziert und in der letzten Phase »Add« (Addition) werden die Produkte aufsummiert. 5.2.2 Der gesamte Operationsablauf Nach der Skizzierung der Kernoperation ist der Operationsablauf zu präzisieren. Nach der Initialisierung der Adressregister sind die ersten M − 1 Eingabewerte x0 bis xM −2 in den Datenspeicher zu schreiben. Mit dem Schreiben des Eingabewertes xM −1 beginnt die Berechnung des ersten Ausgabewertes. Dieser Wert wird gleichzeitig in das Datenregister »xDat« und der zugehörige Koeffizient c0 in das Koeffizientenregister »cDat« übernommen. Im nächsten Schritt wird das Produkt des ersten Koeffizient-Wert-Paares gebildet und gleichzeitig das zweite Koeffizient-Wert-Paar aus den Blockspeichern gelesen. Danach wird das erste Produkt in das Akkumulatorregister übernommen, das zweite Produkt gebildet und das nächste Koeffizient-Wert-Paar aus dem Speicher gelesen etc. (Abb. 5.6). Die Koeffizientenadresse zählt immer zyklisch weiter. Die Daten stehen in absteigender Reihenfolge im Speicher und die Datenadresse zählt außer im letzten Schritt eines jeden Zyklus vorwärts. Nach dem letzten Schritt bleibt die Datenadresse unverändert und der älteste Wert wird mit dem neusten Wert überschrieben. Nach dem Startzyklus steht immer, während der Koeffizient »c2« gelesen wird (»cAdr=2«), ein abholbereites Ergebnis im Akkumulator. cAdr 1 2 3 0 1 2 3 0 1 2 3 0 3 2 1 0 1 2 3 3 0 1 2 2 xAdr xRAM W(x0 ) W(x1 ) W(x2 ) W(x3 ) R(x2 ) R(x1 ) R(x0 ) W(x4 ) R(x3 ) R(x2 ) R(x1 ) W(x5 ) c0 c1 c2 c3 c3 c0 c1 c2 cDat x3 x2 x1 x1 x0 x4 x3 x2 xDat c0 · x3 c1 · x2 c2 · x1 c3 · x0 c0 · x4 c1 · x3 c2 · x2 Prod P1 P1 P2 c0 · x3 y0 c0 · x4 Akku ... ... ... Zustand Init Normalzyklus Startzyklus Normal... Register cAdr Koeffizientenadressreg. xAdr Datenadressregister cDat Koeffizientenregister
xDat Datenregister Prod Produktreg. Akku Akku.-Reg.
Blockspeicher cROM Koeffizientenspeicher xRAM Datenspeicher
Speicheroperationen W(...) Speichern R(...) Lesen
Abb. 5.6. Beispielablauf der FIR-Operation für M = 4
Bearbeitungsstand: Halbformale Beschreibung der Blockspeicher-Pipeline-Struktur und des Operationsablaufs, aus denen in den weiteren Entwurfsschritten das Simulationsmodell zu entwickeln ist.
388
5 Komplexe Beispielentwürfe
5.2.3 Datentypen und Bearbeitungsmethoden Eine komplexe Funktionsbeschreibung sollte immer in überschaubaren Schritten, nach denen jeweils eine Zwischenkontrolle erfolgt, entwickelt werden. In Anlehnung an die objektorientierte Programmierung empfiehlt es sich, mit der Definition der Typen der zu verarbeitenden Datenobjekte und den Unterprogrammen zu ihrer Verarbeitung zu beginnen (vgl. Abschnitt 3.4.1). Der VHDL-Beschreibungsrahmen für Datentypen und Unterprogramme ist ein Package und der Beschreibungsrahmen für Testabläufe ein Testrahmen. Das erste Simulationsmodell einer zu entwerfenden Funktionseinheit dient in der Regel zur Kontrolle des Algorithmus (vgl. Abschnitt 3.4.5 und Abschnitt 3.4.6). Für die Darstellung der zu verarbeitenden Daten genügen in dieser Entwurfsphase wertebereichsbeschränkte reelle Zahlen und für die Adressen wertebereichsbeschränkte natürliche Zahlen: –- Typvereinbarungen zur Beschreibung eines FIR-Filters constant M: positive := 4; –- Filtergrad subtype tAdr is natural range 0 to M-1; constant max_abs: real := 1000.0; –- Wertebereichsgrenze type tDaten is real range -(max_abs) to max_abs; type tMem is array (natural range <>) of tDaten;
Die zwei Adressregister und die vier Datenregister der Pipeline in Abb. 5.5 sollen zur gemeinsamen Übergabe an die Bearbeitungs-Prozeduren zu einem Verbund zusammengefasst werden: –- Datentyp für den Pipeline-Zustand type tPipeline is record cAdr, xAdr: tAdr; cDat, xDat, Prod, Akku: tDaten; end record;
Der Gesamtzustand des FIR-Filters besteht aus dem Datenspeicherzustand und dem Pipeline-Zustand. Eine Aufgabe des Simulationsmodells ist die Kontrolle, dass bei den arithmetischen Operationen keine Wertebereichsüberläufe oder andere Situationen, die die Ergebnisse invalidieren, auftreten. Im Beispiel wird für die Einprogrammierung dieser Kontrollen der kleinste Wert des Zahlenbereichs als PseudoWert für »ungültig« reserviert und als Initialwert, als Operationsergebnis für ungültige Operanden sowie bei einem Über- und Unterlauf des Zahlenbereichs zugewiesen. Die Operatoren für die Addition und die Multiplikation werden entsprechend überladen. Sie bilden zuerst aus den nach real konvertierten Operanden das Ergebnis und entscheiden dann anhand der Gültigkeit der Operanden und des Ergebniswertes, ob der Pseudo-Wert für »ungültig« oder das nach »tDaten« zurückkonvertierte Ergebnis zurückgegeben wird:
5.2 FIR-Filter mit Blockspeichern
389
tDaten
tDaten function "+"(a, b: tDaten) return tDaten is + constant sum: real := real(a) + real(b); tDaten begin if a=tDaten’low or b=tDaten’low or (abs sum)>max_abs then return tDaten’low; –- Pseudo-Wert für ungültig else return tDaten(sum); end if; end function; tDaten
tDaten function "*"(a, b: tDaten) return tDaten is * constant prod: real := real(a) * real(b); tDaten begin if a=tDaten’low or b=tDaten’low or (abs prod)>max_abs then return tDaten’low; –- Pseudo-Wert für ungültig else return tDaten(prod); end if; ⇒WEB-Projekt: P5.2/FIR1_pack.vhdl end function;
Als Nächstes sind alle Register-Transfer-Funktionen aus Abb. 5.6 auf Bearbeitungsmethoden aufzuteilen. Eine in Hardware nachzubildende Bearbeitungsmethode sollte nur Operationen zusammenfassen, die im selben Zeitschritt ausgeführt werden. In der Initialisierungsphase wird in jedem Zeitschritt die Koeffizientenadresse um eins erhöht, die Datenadresse um eins verringert und ein Eingabewert in den Datenspeicher geschrieben. Dieses Verhalten lässt sich mit einer Prozedur mit dem zu speichernden Datenwert als Eingabeparameter sowie mit dem Datenspeicher und dem Pipeline-Objekt als les- und veränderbare Signale beschreiben: procedure InitStep(x: tDaten; signal xRAM: inout tMem; signal pp: inout tPipeline) is begin pp.cAdr <= (pp.cAdr + 1) mod M; cAdr 1 2 ... M − 1 pp.xAdr <= pp.xAdr - 1; xAdr M −1 M −2 . . . 1 xRAM(pp.xAdr) <= x; xRAM W(x0 ) W(x1 ) . . . W(xM −2 ) end procedure;
Die Register-Transfer-Operationen im Start- und im Normalzyklus sind einander sehr ähnlich (Abb. 5.7). Die Koeffizientenadresse wird modulo-M hochgezählt, das Koeffizientenregister übernimmt den adressierten Inhalt des Koeffizientenspeichers und das Produktregister das berechnete Produkt. Für die anderen Datenobjekte weicht die auszuführende Operation jeweils für eine Koeffizientenadresse von der Standardoperation ab. Der Datenspeicher wird bei Zählstand null beschrieben statt gelesen. Bei Zählstand zwei wird dem Akkumulator das Produkt statt der Summe zugewiesen und beim höchstwertigen Zählstand wird die Datenadresse nicht weitergezählt. Die in Abb. 5.7 mit einem grauen Dreieck gekennzeichneten Zuweisungen sind im Startzyklus
390
5 Komplexe Beispielentwürfe
cAdr 0 1 xAdr +1 mod M +1 mod M xRAM W(xn ) R(xn−1 ) cDat cM −1 c0 xDat xn−M +1 xn Prod c · xn−M +2 cM −1 · xn−M +1 M −2 PM −3 PM −2 Akku ... ... a) cAdr cROM cDat xAdr xRAM
Koeffizientenadressregister Koeffizientenspeicher Koeffizientenregister Datenadressregister Datenspeicher
2 +1 mod M R(xn−2 ) c1 xn−1
... ... ... c2 xn−2
M −2 +1 mod M R(xn−M +2 ) ... ...
M −1 nicht z¨ahlen R(xn−M +1 ) cM −2 xn−M +2
c0 · xn PM −1 yn = ...
c1 · xn−1
c2 · xn−2 P2
...
c0 · xn
...
xDat Datenregister Prod Produktregister Akku Akkumulationsregister im Startzyklus optional ge¨anderter Datenfluss
x... c... W(...) R(...) M
... Datenwert Koeffizient Schreiben Lesen Filtergrad
procedure NormalStep(x: tDaten; cROM: tMem; signal xRAM: inout tMem; signal pp: inout tPipeline) is begin pp.cAdr <= (pp.cAdr + 1) mod M; if pp.cAdr=0 then xRAM(pp.xAdr) <= x; pp.xDat <= x; else pp.xDat <= xRAM(pp.xAdr); end if; pp.cDat <= cROM(pp.cAdr); pp.Prod <= pp.cDat * pp.xDat; if pp.cAdr=2 then pp.Akku <= pp.Prod; else pp.Akku <= pp.Akku + pp.Prod; end if; if pp.cAdr/=M-1 then pp.xAdr <= (pp.xAdr + 1) mod M; end if; b) end procedure; ⇒Web-Projekt:P5.2/FIR1 pack.vhdl Abb. 5.7. Speicher- und Registerzuweisungen in allen Takten nach der Initialisierung a) tabellarisch b) als Bearbeitungsprozedur
nicht erforderlich. Aber sie stören auch nicht, so dass auf Fallunterscheidungen verzichtet werden kann. Die Unterbindung der Ausgabe im Startzyklus erfolgt in der Ausgabefunktion. Bearbeitungsstand: Für die einzelnen Bestandteile und für die gesamte Blockspeicher-Pipeline-Struktur sind Datentypen definiert und für die Datentypen sind die benötigten Bearbeitungsmethoden programmiert. 5.2.4 Das erste komplette Simulationsmodell Die erste simulierbare Beschreibung ist im Beispielprojekt ein Prozess im Testrahmen. Innerhalb des Testrahmens werden vereinbart: •
eine initialisierte Konstante für den Koeffizientenspeicher:
•
ein Signal für den Datenspeicher
constant cROM: tMem(tAdr) := (0.5, 1.0, -1.0, -0.5);
5.2 FIR-Filter mit Blockspeichern
391
signal xRAM: tMem(tAdr);
•
ein Signal für das Pipeline-Objekt signal pp: tPipeline;
•
ein Dateiobjekt zum Einlesen der Eingabewerte xn
•
und eine Konstante für die Taktperiode
file Eingabedatei: text open read_mode is "Eingabe.txt"; constant Tp: delay_length := 10 ns;
Der Filtergrad M steckt in der Typvereinbarung von »tAdr« und ist vier (vgl. Abschnitt 5.2.3). Der Ablauf der Filteroperationen beginnt mit einer Schleife mit Initialisierungsschritten gefolgt von einer Schleife mit Verarbeitungsschritten. Die grau unterlegten Programmzeilen sind Textverarbeitungsanweisungen für die Einund Ausgabe: –- Vereinbarungen im Testprozess variable x: tDaten; variable InitRdy: boolean; variable s: tString; –- Anweisungsfolge im Testprozess –- Initialisierung und Ausgabe Tabellenkopf pp.cAdr <= 1; pp.xAdr <= tAdr’high; wait for tp; write("Eingabe:" & StatusKopfText & "Ausgabe:"); –- Initialisierungszyklus while pp.cAdr/=0 loop read(Eingabedatei, x); write(str(x) & str(pp, xRAM)); InitStep(x, xRAM, pp); wait for tp; end loop; –- Endlosschleife für Verarbeitungsschritte loop if pp.cAdr=0 then if endfile(Eingabedatei) then wait; end if; read(Eingabedatei, x); assign(s, str(x)); else assign(s, " "); end if; append(s, str(pp, xRAM)); if pp.cAdr=2 then if not InitRdy then InitRdy := true; else –- Ausgabe append(s, str(pp.Akku)); end if; end if; write(s); –- Ausgabe der Ergebniszeichenkette NormalStep(x, cROM, xRAM, pp); wait for tp; end loop; ⇒WEB-Projekt: P5.2/Test_FIR1.vhdl
392
5 Komplexe Beispielentwürfe
In der Initialisierungsschleife wird in jedem Schritt ein Eingabewert aus der Datei gelesen, die Prozedur »InitStep(...)« aufgerufen und ein Takt gewartet. Der Start- und der Normalzyklus sind zusammengefasst. Die Unterscheidung im Ablauf erfolgt mit der prozessinternen Variablen »InitRdy«, die im ersten Zyklus nach der Initialisierung die Ausgabe unterdrückt. Die Hauptarbeit leistet die Prozedur »NormalStep(...)«. Bei dem Koeffizientenzählstand »0« (Übernahme eines neuen Eingabewertes) wird zusätzlich vor dem Verarbeitungsschritt ein Wert aus der Datei gelesen und bei dem Koeffizientenzählstand »2« erfolgt zusätzlich nach der Warteanweisung ab dem zweiten Zyklus eine Ausgabe. Die Textausgaben werden mit den für Textobjekte vom Typ »tString« im Package »Tuc.Ausgabe« definierten Prozeduren • • •
assign(Textobjekt, Text): alten Text löschen und neuen Text zuweisen, append(Textobjekt, Text): neuen Text anhängen und write(Textobjekt): Text auf dem Bildschirm ausgeben
erzeugt (siehe Abschnitt 3.3). Die Str-Funktionen für die Umwandlung der projektspezifischen Datentypen »tDaten« etc. in Textdarstellungen sind mit im Package »P5.2/FIR1_pack.vhdl« definiert. Sie sind ähnlich wie die bereits behandelten Str-Funktionen aufgebaut und repräsentieren einen erheblichen Anteil des Gesamtprogrammieraufwands. Die Darstellung der Testausgaben hat entscheidenden Einfluss darauf, ob fehlerhafte Zustände und Ausgaben bei der Simulation als solche erkannt werden. Für die Kontrolle von Pipeline-Operationen ist die in Abb. 5.8 dargestellte Tabellenform zu empfehlen. Die durch einen einfachen Strich getrennten Spalten gehören zur selben Pipeline-Phase. Nach einem Doppelstrich beginnt die nächste Pipeline-Phase. Ungültige Werte werden durch »XX« dargestellt. Zum Simulationsbeginn sind nur die Koeffizienten- und die Datenadresse gültig. Die Werte im Datenspeicher sind alle ungültig und werden von der höchsten Adresse absteigend mit den ersten Eingabewerten beschrieben. Der Datenwert für den niederwertigsten Speicherplatz wird – wie in Abb. 5.6 festgeEingabe:|cAdr|xAdr|| 100.0| 1| 3|| 200.0| 2| 2|| 300.0| 3| 1|| 400.0| 0| 0|| | 1| 1|| | 2| 2|| | 3| 3|| 600.0| 0| 3|| | 1| 0|| | 2| 1|| | 3| 2|| -600.0| 0| 2|| | 1| 3|| | 2| 0||
cDat | XX | XX | XX | XX | 0.2000| 0.5000| -0.5000| -0.2000| 0.2000| 0.5000| -0.5000| -0.2000| 0.2000| 0.5000|
xDat || XX || XX || XX || XX || 400.0|| 300.0|| 200.0|| 100.0|| 600.0|| 400.0|| 300.0|| 200.0|| -600.0|| 600.0||
Prod | XX | XX | XX | XX | XX | 80.0| 150.0| -100.0| -20.0| 120.0| 200.0| -150.0| -40.0| -120.0|
Akku || XX || XX || XX || XX || XX || XX || 80.0|| 230.0|| 130.0|| 110.0|| 120.0|| 320.0|| 170.0|| 130.0||
r(0) XX XX XX XX 400.0 400.0 400.0 400.0 400.0 400.0 400.0 400.0 400.0 400.0
r(1) XX XX XX 300.0 300.0 300.0 300.0 300.0 300.0 300.0 300.0 300.0 300.0 300.0
r(2) XX XX 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 -600.0 -600.0
r(3) |Ausgabe: XX | 100.0| 100.0| 100.0| 100.0| 100.0| 100.0| 100.0| 600.0| 600.0| 110.0 600.0| 600.0| 600.0| 600.0| 130.0
Abb. 5.8. Simulationsausgaben des FIR-Filtermodells (r(i) – Abkürzung für xRAM(i))
5.2 FIR-Filter mit Blockspeichern
393
legt – gleichzeitig in das Datenregister »xDat« kopiert. Die Datenweitergabe über eine Pipeline-Phase ist immer mit einer Verzögerung um einen Taktschritt verbunden. Anhand der Testausgaben sind alle spezifizierten Abläufe und Operationen in einer anschaulichen Weise kontrollierbar. Der zusätzliche Programmieraufwand für übersichtliche, gut auswertbare Ausgaben – Richtwert 50% des Gesamtprogrammieraufwands – gilt als gute Investition. Denn der Test, die Fehlersuche und nicht erkannte Entwurfsfehler sind in der Regel erheblich teurer. Bearbeitungsstand: Die Zielfunktion ist in einer simulierbaren Form beschrieben und wurde mit Beispieleingaben getestet. 5.2.5 Ersatz der Zahlentypen durch Bitvektortypen In Hardware werden Zahlen durch Bitvektoren dargestellt. In einem strukturierten Entwurf mit Bearbeitungsmethoden und Unterprogrammen lassen sich die Zahlentypen gegen die entsprechenden Bitvektoren austauschen, ohne dass das gesamte Modell neu geschrieben werden muss. Die Darstellung der Adresswerte »0« bis »3« erfordert einen 2-Bit-Vektor für vorzeichenfreie Zahlen: subtype tAdr is tUnsigned(1 downto 0);
Mit einem Bitvektor als Adresse ändern sich die Operationen für die Adressrechnung und die Speicherzugriffe. Aus den Additionen bzw. Subtraktionen mod-4 werden einfache Additionen bzw. Subtraktionen, weil arithmetische Ergebnisse immer mod-(2n ) (n – Ergebnisbitanzahl) gebildet werden. Die Speicherzugriffe mit einem Bitvektor als Adresse müssen auch die Fälle für ungültige und unzulässige Adressen mit erfassen. Im Beispiel werden deshalb die Speicherzugriffe durch Aufrufe der Lesefunktion function read(Mem: tMem; adr: tAdr) return tDaten;
und der Schreibprozedur procedure write(signal Mem: inout tMem; adr: tAdr; x: tDaten);
aus Abschnitt 3.4.3 ersetzt. Die Daten sollen durch 16-Bit vorzeichenbehaftete Festkommazahlen dargestellt werden: subtype tDaten: tSigned(15 downto 0);
Die Koeffizienten haben einen Betrag kleiner eins. Die gedachte Kommaposition sei hinter dem führenden Bit. Der kleinste mit einer Vorkommastelle und fünfzehn Nachkommastellen darstellbare Koeffizient ist 1,00 . . . 02 und hat den Wert −1. Der größte darstellbare Koeffizient ist 0,11...1 mit dem Wert 1−2−15 . Der Wertebereich für Daten soll wie im ersten Modell mindestens von −1000 bis +1000 reichen. Das erfordert einschließlich Vorzeichenbit elf Stellen vor
394
5 Komplexe Beispielentwürfe
dem Komma. Die restlichen fünf Bit sind Nachkommastellen. Bei einer Multiplikation addieren sich die Anzahlen der Vor- und der Nachkommastellen. Die Produkte aus Koeffizienten- und Datenwerten haben zwölf Vorkomma- und 20 Nachkommastellen. Die akkumulierten Werte sollen, um Wertebereichsüberläufe auszuschließen, dreizehn Vorkommastellen erhalten (siehe hierzu auch Aufgabe 5.1). Die Anzahl der Nachkommastellen muss nicht größer als die der Daten sein. Für den akkumulierten Wert genügt entsprechend ein 18-Bit-Vektor: subtype tAkku is tSigned(17 downto 0);
Damit die Produkte ohne Konvertierung an den Akkumulator zugewiesen werden können, sollen sie denselben Typ mit derselben gedachten Kommaposition haben. Die Multiplikation ist entsprechend so umzudefinieren, dass zu dem normal berechneten Produkt vorzeichenerweitert eine Vorkommastelle hinzugefügt und die fünfzehn niederwertigsten Nachkommastellen abgeschnitten werden4 : function mult(a, b: tDaten) return tAkku is constant prod: tSigned(2 * tDaten’length - 1 downto 0) := a * b; begin return prod(prod’high) & prod(prod’high downto tDaten’high); end function;
Für die Kontrolle, dass die geänderten Datendarstellungen und Operationen das Eingabe-Ausgabe-Verhalten des Gesamtmodells nicht beeinträchtigen, sollen sich die Wertedarstellungen der Adressen, Daten und Koeffizienten nicht von denen im ersten Modell unterscheiden. Das erfordert eine Anpassung der Str-Funktionen zur Konvertierung in die Ausgabetexte. Für die Datendarstellung wird der Wert der Daten durch 25 dividiert und mit sieben Zeichen und einer Nachkommastelle dargestellt: function str_dat(x: tSigned) return string is begin if is_x(x) then return " XX"; else return rechts(str(real(int(x))/(2.0**5), 1),7); end if; ⇒WEB-Projekt: P5.2/FIR2_pack.vhdl end function;
Die ganzzahlige Werterepräsentation der Koeffizienten wird durch 215 geteilt und mit vier Nachkommastellen dargestellt:
4
Überladen des »*«-Operators scheidet hier aus, weil »tDaten« ein Untertyp und kein eigener Typ ist. Der Wert von »tDaten’high« ist fünfzehn und der Wert von »tProd’high« ist 31.
5.2 FIR-Filter mit Blockspeichern
395
function str_coeff(c: tSigned) return string is begin if is_x(c) then return " XX"; else return rechts(str(real(int(c))/(2.0**15), 4),7); end if; ⇒WEB-Projekt: P5.2/FIR2_pack.vhdl end function;
Als Nächstes müssten die geänderten Typvereinbarungen und Unterprogramme so in die beiden Bearbeitungsprozeduren und in den Testrahmen der vergangenen Abschnitte eingearbeitet werden, dass die Simulationsausgaben im fehlerfreien Fall genau wie in Abb. 5.8 aussehen. Letzteres ist nur eine Empfehlung, aber eine nützliche. Wir wollen diese Schritte überspringen. Nach dem Ersatz aller Zahlentypen durch Bitvektortypen bildet die Simulation bereits die Registerstruktur und überwiegend auch die Register-Transfer-Funktionen der zu entwerfenden Schaltung bit-genau nach. Damit sind wir wieder einen Schritt näher am Ziel. 5.2.6 Ein- und Ausgabe über Signale In den bisherigen Simulationsmodellen für den FIR-Filter wurden die benötigten Eingabedaten aus einer Datei gelesen und die Ausgaben in einer Zeichenkettenvariablen zusammengesetzt und ausgegeben. Eine Schaltung kommuniziert mit ihrer Umgebung über Signale statt über Ein- und Ausgabeanweisungen. In der nächsten Version des Simulationsmodells wird der Testrahmen deshalb in drei über Signale kommunizierende Prozesse geteilt, und zwar in • • •
einen Prozess für die Bereitstellung der Eingabesignale, einen Prozess zur Modellierung des Testobjekts und einen Ausgabeprozess, der möglichst dieselben Textausgaben wie die bisherigen Simulationsmodelle erzeugen soll (Abb. 5.9 a).
Test- RD daten- x quelle I T a)
Testobjekt x FIR RD WR I pp xRAM
I Testx ausgabeRD prozess T WR pp.cAdr 00 01 10 11 00 01 10 11 00 01 10 pp RD xRAM x w0 w1 w2 w3 w4 w5 T
pp Pipeline-Zustand
b)
···
Abb. 5.9. Testrahmen für den FIR-Filter mit Ein- und Ausgabesignalen a) Prozessstruktur b) Signalverläufe
Das Testobjekt – die Funktionsbeschreibung des FIR-Filters – erhält außer dem Dateneingabesignal x und dem Datenausgabesignal y einen Takteingang
396
5 Komplexe Beispielentwürfe
T , einen Initialisierungseingang I, ein Bestätigungssignal für Leseoperationen RD und ein Bestätigungssignal für Schreiboperationen W R. Das Signal für die Lesebestätigung wird während der Lesetakte aktiviert und signalisiert der Datenquelle, dass sie ab dem nächsten Takt den Folgewert ausgeben soll. Das Schreibbestätigungssignal W R wird in den Ausgabetakten aktiviert und signalisiert dem Testausgabeprozess, dass im Akkumulatorregister ein gültiger Ausgabewert steht, der an die Ausgabezeile anzuhängen ist. Der Testeingabeprozess soll durch eine nebenläufige Prozedur beschrieben werden: procedure Testdatenquelle(Dateiname: string; signal RD: std_logic; signal T, I: out std_logic; signal x: out tDaten; tp: delay_length := 10 ns) is variable pstr: tPString; variable ZNr: natural; variable r: real; RD Test- x file f: textio.text open read_mode is Dateiname; daten- I quelle T begin I <= ’1’; T <= ’0’; wait for tp; I <= ’0’ after tp/10; loop T <= ’1’, ’0’ after tp/2; if RD=’1’ then if textio.endfile(f) then wait; end if; read(f, pstr); get(pstr, r, -1000.0, 1000.0); if pstr.Status/=ok then write("Zeile " & str(znr) & ":" & str(pstr.err_msg)); wait; else x <= to_tSigned(integer(r * (2.0**5)), x’length) after tp/3; ZNr := ZNr + 1; end if; end if; wait for tp; end loop; ⇒WEB-Projekt: P5.2/FIR2_pack.vhdl end procedure;
Die Eingabedatei enthält die Filtereingabedaten. Das Initialisierungssignal I, das Taktsignal T und der Eingabevektor x werden innerhalb der Prozedur generiert. Nebenläufig aufgerufen generiert die Prozedur zu Beginn einen Initialisierungsimpuls und erzeugt danach mit der Taktperiode TP Taktimpulse. Nach der ersten aktiven Taktflanke wird der erste gültige Eingabewert an das Signal x zugewiesen. Danach wird nach jeder aktiven Taktflanke, bei der das Lesebestätigungssignal aktiv ist – d.h. bei den nachfolgenden drei und dann in jedem vierten simulierten Takt –, der nächste Eingabewert aus der Datei gelesen und verzögert an x zugewiesen (Abb. 5.9 b). Wenn das Dateiende erreicht ist, terminiert die Prozedur mit einer Warteanweisung ohne Weckbedingung. Durch das Ausbleiben der Eingabeänderungen terminieren auch die anderen Prozesse des Testrahmens und die Simulation stoppt. Der Testausgabeprozess soll etwa dieselben Ausgaben wie in Abb. 5.8 erzeugen. Dazu müssen der Prozedur, die nebenläufig aufgerufen den Protokollprozess nachbildet, das Taktsignal, das Schreib- und das Lesebestätigungssi-
5.2 FIR-Filter mit Blockspeichern
397
gnal, das Eingabesignal, der Pipeline-Zustand und der Zustand des Datenspeichers übergeben werden. Nach Aufruf schreibt die Prozedur die Kopfzeile der Ausgabetabelle und in einer Endlosschleife nach jeder aktiven Taktflanke die Textzeilen mit dem Bearbeitungszustand und optional dem Eingabe- oder Ausgabewert. Zur Kontrolle der Zeitverläufe wird jeder Ausgabezeile zusätzlich die aktuelle Simulationszeit vorangestellt (siehe später Abb. 5.11): procedure Testausgabe(signal T, RD, WR: std_logic; Testx signal pp: tPipeline; signal x: tDat; RD ausgabeprozess signal xRAM: tMem) is WR pp variable s: tString; xRAM begin (1) T write(" Zeit |Eingabe|Z| ... r(3)|Ausgabe"); loop –- Endlosschleife wait until rising_edge(T); –- warte auf aktive Taktflanke assign(s, rechts(str(now, 1),8)); –- Anhängen eines Zeilentextes wie in Abb.5.8, –- der den Zustand der Schaltung beschreibt write(s); –- Textzeile ausgeben end loop; ⇒WEB-Projekt: P5.2/FIR2_pack.vhdl end procedure;
((1) – Ausgabe der Kopfzeile). Bearbeitungsstand: Ein komplettes Verhaltensmodell der Soll-Funktion mit Anschlusssignalen und einer internen Bitvektordarstellung. Der dazu entwickelte Testrahmen kann auch für alle weiteren schnittstellengleichen Versionen des Simulationsmodells genutzt werden. 5.2.7 Umformung in eine Automatenbeschreibung In dem bis hierher entwickelten Ablaufmodell wird in den Schleifenkörpern nach jeder Register-Transfer-Operation eine Taktperiode lang gewartet (Abb. 5.10 a). Der funktionsgleiche synthesefähige Abtastprozess darf nur eine Warteanweisung enthalten, und zwar am Ende der Anweisungsfolge im Prozess (Abb. 5.10 b, vgl. Abschnitt 1.4.2). Nach der Warteanweisung, d.h. zu Beginn der Anweisungsfolge des Prozesses, verzweigt der Kontrollfluss mit Hilfe von Fallunterscheidungen. Jeder Wartezustand in der Ablaufbeschreibung benötigt hierfür einen eigenen Kontrollflusszustand. Der Kontrollflusszustand für drei nacheinander abzuarbeitende Schleifen besteht aus dem Schleifenzähler und einem Aufzählungstyp zur Unterscheidung, welche der drei Schleifen gerade abgearbeitet wird: type tZustand is XX, Init, Startzyklus, Normalzyklus;
Der vierte Zustandswert »XX« bedeutet »ungültig« und dient als Initialwert für den Simulationsstart. Der Schleifenzähler ist der Zähler für die Koeffizientenadresse, die im Register »pp.cAdr«, gespeichert wird, so dass hierfür keine
398
5 Komplexe Beispielentwürfe a) Ablaufbeschreibung Init: for ... loop ... wait for tP; end loop; Startzyklus: for ... loop ... wait for tP; end loop; loop Normalzyklus: for ... loop ... wait for tP; end loop; end loop;
b) synthesef¨ ahige Beschreibung Endlosschleife des Prozesses if I=’1’ then Anfangswertzuweisungen; elsif aktive Taktflanke then Berechnungsschritte mit Fallunterscheidungen nach Schleife und Schleifenz¨ ahlerwert end if ; wait on T, I; Warteliste
Abb. 5.10. Kontrollflussumformung in einen synthesefähigen Abtastprozess
zusätzliche Hardware erforderlich ist. Der Datentyp für den Pipeline-Zustand erweitert sich zu –- erweiterter Datentyp für den Pipeline-Zustand type tPipeline is record cAdr, xAdr: tAdr; cDat, xDat: tDaten; Prod, Akku: tAkku;(1) Zustand: tZustand; ⇒WEB-Projekt: P5.2/FIR2_pack.vhdl end record;
((1) – breiterer Bitvektortyp als »tDaten«, um Wertebereichsüberläufe zu vermeiden, vgl. Abschnitt 5.2.5). Die Übergangsfunktion sei durch eine Prozedur beschrieben, die bei jeder aktiven Taktflanke aufgerufen wird: –- Übergangsfunktion des gesamten FIR-Filters procedure PipeStep(x: tDat; cROM: tMem; signal xRAM: inout tMem; signal pp: inout tPipeline) is begin cAdr 1 2 ... M − 1 case pp.Zustand is xAdr M −1 M −2 . . . 1 when XX => null; xRAM W(x0 ) W(x1 ) . . . W(xM −2 ) when Init => pp.cAdr <= pp.cAdr + "1"; pp.xAdr <= pp.xAdr - "1"; write(xRAM, pp.xAdr, x); if pp.cAdr=to_tUnsigned(M-1, tAdr’length) then pp.Zustand <= Startzyklus; end if; when others => –- Operationen im Start- und Nomalzyklus
... end case; end procedure;
⇒WEB-Projekt: P5.2/FIR2_pack.vhdl
5.2 FIR-Filter mit Blockspeichern
399
Der Eingabevektor x und der Koeffizientenspeicher »cROM« sind Eingabewerte, der Datenspeicher »xRAM« und das Pipeline-Objekt les- und veränderbare Signale. In den Initialisierungsschritten wird die Koeffizientenadresse, die als Zustandszähler dient, hochgezählt. Die Datenadresse wird abwärts gezählt und der Eingabewert auf den adressierten Datenspeicherplatz geschrieben. Die Speicherzugriffe erfolgen hier mit der Read-Funktion und der Write-Prozedur aus Abschnitt 3.4.3, die im Gegensatz zu einem indizierten Feldzugriff auch den Fall, dass die Zugriffsadresse ungültig oder unzulässig ist, mit berücksichtigen. Für die Synthese sind diese Unterprogramme entsprechend zu vereinfachen oder wieder durch indizierte Zugriffe zu ersetzen. Im letzten Initialisierungsschritt wird der Zustand von »Init« auf »Startzyklus« geändert. In allen anderen Fällen, d.h. im Zustand »Startzyklus« oder »Normalzyklus«, sind die Fallunterscheidungen aus Abb. 5.7 zu übernehmen: • • •
Schritt 0: Datenwert in den Datenspeicher schreiben statt lesen (FU1), Schritt 2: Produkt in den Akkumulator kopieren statt zum Akkumulatorinhalt zu addieren und Ergebnis ausgeben (FU2) und letzter Schritt: Datenadresse nicht weiterzählen (FU3).
Im Startzyklus entfällt die Ergebnisausgabe. Statt dessen wird der Zustand weitergeschaltet: –- Operationen im Start- und Nomalzyklus (1) pp.cAdr <= pp.cAdr + "1"; if pp.cAdr="00" then –- Fallunterscheidung FU1 (2) write(xRAM, pp.xAdr, x); pp.xDat <= x; else (2) pp.xDat <= read(xRAM, pp.xAdr); end if; (2) pp.cDat <= read(cROM, pp.cAdr); pp.Prod <= mult(pp.cDat, pp.xDat); (3) if pp.cAdr="10" then –- Fallunterscheidung FU2 pp.Akku <= pp.Prod; if pp.Zustand=Startzyklus then pp.Zustand <= Normalzyklus; end if; else pp.Akku <= pp.Akku + pp.Prod; end if; if pp.cAdr/="11" then –- Fallunterscheidung FU3 pp.xAdr <= pp.xAdr + "1"; ⇒WEB-Projekt: P5.2/FIR2_pack.vhdl end if;
((1) – für 2-Bit-Vektoren automatisch mod 4; (2) – Lesefunktion und Schreibprozedur aus Abschnitt 3.4.3; (3) – Spezialmultiplikation für Faktoren vom Typ »tDaten« und dem Ergebnistyp »tAkku« aus Abschnitt 5.2.5). Die Schaltungsschnittstelle des FIR-Filters in Abb. 5.9 a besitzt drei Ausgabesignale: das Lesebestätigungssignal, das Schreibbestätigungssignal
400
5 Komplexe Beispielentwürfe
und den Akkumulatorzustand. Die beiden Bit-Signale ergeben sich aus dem Pipeline-Zustand und sollen durch getrennte Funktionen berechnet werden. Das Lesebestätigungssignal ist im Initialisierungszustand immer und in den beiden anderen Zuständen nur, wenn die Koeffizientenadresse null ist, zu aktivieren: function RdGnt(pp: tPipeline) return std_logic is begin if pp.Zustand=Init or pp.cAdr=to_tUnsigned(0, tAdr’length) then return ’1’; else return ’0’; end if; ⇒WEB-Projekt: P5.2/FIR2_pack.vhdl end function;
Das Signal für die Schreibbestätigung ist nur im Normalzyklus, wenn die Koeffizientenadresse den Wert zwei hat, zu aktivieren: function WrGnt(pp: tPipeline) return std_logic is begin if pp.Zustand=Normalzyklus and pp.cAdr=to_tUnsigned(2, tAdr’length) then return ’1’; else return ’0’; end if; ⇒WEB-Projekt: P5.2/FIR2_pack.vhdl end function;
Das komplette Verhaltensmodell soll als Prozess beschrieben werden. Dazu sind im Testrahmen folgende Konstanten und Signale zu vereinbaren: constant cROM: tMem := (to_tKoeff( 0.2), to_tKoeff( 0.5), to_tKoeff(-0.5), to_tKoeff(-0.2)); signal xRAM: tMem(0 to M-1); signal pp: tPipeline; signal T, I, rd, wr: std_logic; ⇒WEB-Projekt: P5.2/Test_FIR2.vhdl signal x: tDat;
Die Funktion »to_tKoeff(...)« ist eine im Package definierte Hilfsfunktion zur Konvertierung einer Gleitkommazahl in die Bitvektordarstellung für Koeffizienten. Die Ein- und Ausgabe wird durch nebenläufige Aufrufe der Prozeduren aus dem Vorabschnitt beschrieben: Testdatenquelle("Eingabe.txt", rd, T, I, x); Testausgabe(T, rd, wr, pp, x, xRAM);
Das Verhaltensmodell der kompletten Übergangsfunktion des FIR-Filters ist ein Abtastprozess mit asynchroner Initialisierung. Zur Initialisierung werden den beiden Adressregistern und dem Zustandsregister ihre Anfangswerte zugewiesen und bei jeder aktiven Taktflanke wird die Prozedur, die die Übergangsfunktion beschreibt, aufgerufen:
5.2 FIR-Filter mit Blockspeichern
401
process(T, I) begin if I=’1’ then pp.cAdr <= to_tUnsigned(1, tAdr’length); pp.xAdr <= to_tUnsigned(M-1, tAdr’length); pp.Zustand <= Init; elsif rising_edge(T) then PipeStep(x, cROM, xRAM, pp); end if; ⇒WEB-Projekt: P5.2/Test_FIR2.vhdl end process;
Die beiden Ausgabesignale werden nebenläufig aus dem Pipeline-Zustand mit Hilfe der vordefinierten Funktionen gebildet: rd <= RdGnt(pp);
wr <= WrGnt(pp);
Abbildung 5.11 zeigt die Simulationsausgaben. Die noch fehlenden Schritte bis zu einer synthesefähigen Schaltung beinhalten • • •
Beseitigung aller Abfragen und Zuweisungen mit dem Pseudo-Wert »ungültig«, den entsprechenden Ersatz bzw. die Vereinfachung der Read-Funktion und der Write-Prozedur und eine Kapselung der Testobjektbeschreibung in eine eigenständige Entwurfseinheit mit Schnittstellenbeschreibung5 .
Zeit |Eingabe|Z|cAdr|xAdr|| cDat | 10.0 ns| XX|I| 1| 3|| XX| 20.0 ns| 100.0|I| 1| 3|| XX| 30.0 ns| 200.0|I| 2| 2|| 0.5000| 40.0 ns| 300.0|I| 3| 1||-0.5000| 50.0 ns| 400.0|S| 0| 0||-0.2000| 60.0 ns| |S| 1| 1|| 0.2000| 70.0 ns| |S| 2| 2|| 0.5000| 80.0 ns| |S| 3| 3||-0.5000| 90.0 ns| 600.0|N| 0| 3||-0.2000| 100.0 ns| |N| 1| 0|| 0.2000| 110.0 ns| |N| 2| 1|| 0.5000| 120.0 ns| |N| 3| 2||-0.5000|
xDat || Prod | XX|| XX| XX|| XX| XX|| XX| XX|| XX| XX|| XX| 400.0|| XX| 300.0|| 80.0| 200.0|| 150.0| 100.0|| -100.0| 600.0|| -20.0| 400.0|| 120.0| 300.0|| 200.0|
Akku || XX|| XX|| XX|| XX|| XX|| XX|| XX|| 80.0|| 230.0|| 130.0|| 110.0|| 120.0||
r(0) XX XX XX XX XX 400.0 400.0 400.0 400.0 400.0 400.0 400.0
r(1) XX XX XX XX 300.0 300.0 300.0 300.0 300.0 300.0 300.0 300.0
r(2) XX XX XX 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0 200.0
r(3)|Ausgabe XX| XX| 100.0| 100.0| 100.0| 100.0| 100.0| 100.0| 100.0| 600.0| 600.0| 110.0 600.0|
Abb. 5.11. Simulationsausgaben des FIR-Filtermodells
5
Für den Test und die Fehlersuche in den ersten Entwurfsphasen ist es günstiger, die Zielfunktion im Testrahmen in einem Prozess als sequenzielles Programm zu beschreiben. Denn so ist das Einfügen, Ändern und Löschen von Testausgaben einfacher. Die Kapselung in eine separate Entwurfseinheit ist erst später nützlich, wenn die betrachtete Funktionseinheit gründlich getestet ist, zuverlässig funktioniert und in eine übergeordnete Entwurfseinheit als Teilschaltung eingefügt wird.
402
5 Komplexe Beispielentwürfe
5.2.8 Zusammenfassung und Übungsaufgaben Der gesamte Entwurf der FIR-Filter-Schaltung begann mit der Festlegung des nachzubildenden Algorithmus. Dann folgte als erste Strukturentscheidung die Definition einer geeigneten Blockspeicher-Pipeline-Struktur. Für diese wurde ein erstes Simulationsmodell entwickelt, in dem die Werte noch durch Zahlen, die Kommunikation mit der Schaltungsumgebung durch Dateizugriffe und Textausgaben und der Ablauf imperativ modelliert wurden. Im nächsten Schritt wurden die Zahlentypen im Simulationsmodell durch Bitvektortypen ersetzt. Danach wurde die Kommunikation mit der Testumgebung auf Signale umgestellt. Zuletzt wurde die imperative Ablaufbeschreibung durch eine Automatenbeschreibung mit einer Übergangsfunktion und zwei Ausgabefunktionen nachgebildet. Für den skizzierten Entwurfsablauf bis zur korrekt synthetisierten Filterschaltung sind mehrere Wochen Entwurfszeit einzuplanen. Die meiste Zeit davon entfällt auf die Programmierung der Testhilfen, die Ausführung und Auswertung von Tests und auf die Fehlersuche. Aufgabe 5.1 Überprüfen Sie, ob mit den Typdefinitionen in Abschnitt 5.2.5 Wertebereichsüberläufe möglich sind und wenn ja, für welche Koeffizienten- und Eingabewerte. Aufgabe 5.2 Berechnen Sie die Simulationsausgaben des FIR-Filters für die Eingabefolge 0, 0, 0, 250, 500, 1000, -1000, -500, 300 und die Filterkoeffizienten constant cROM: tMem := (to_tKoeff(0.3), to_tKoeff(0.9), to_tKoeff(0.9), to_tKoeff(0.3));
5.3 Point-of-Interest-Berechnung Die Zugriffszeit eines Speichers nimmt mit der Speicherkapazität zu. Algorithmen zur Verarbeitung großer Datenmengen – z.B. zur Verarbeitung von Bilddatenströmen – benötigen einen großen Hintergrundspeicher für die Gesamtinformation und kleine schnelle Blockspeicher und Register mit lokalen Datenkopien für die Versorgung der Pipeline-Rechenwerke. Das folgende Beispiel – ein fiktiver Algorithmus für die Suche interessanter Bildpunkte in einem Bilddatenstrom – ist absichtlich sehr komplex gewählt, um auch einmal einen Einblick in die Konzeptionsphase für die Entwicklung eines Funktionsmodells für eine wirklich große Schaltung mit mehreren Blockspeichern und Pipelines zu geben.
5.3 Point-of-Interest-Berechnung
403
5.3.1 Die Zielfunktion Die interessantesten Bildausschnitte für die Rekonstruktion geometrischer Informationen aus Bildfolgen sind gekrümmte Kantensegmente und Ecken. Für diese Bildobjekte werden bildweise die Positionsänderungen zum vorherigen Bild und daraus weiter deren 2D-Bewegungsbahnen bestimmt. Alle Bewegungsbahnen zusammen ergeben den optischen Fluss, der Rückschlüsse auf die Geometrie und die Bewegung der beobachteten Objekte, auf die Eigenbewegung der Kamera und auf bevorstehende Zusammenstöße der Kamera mit Objekten aus der Szene erlaubt [35]. Die Suche der interessierenden Bildobjekte soll im Beispiel mit einem Satz zweidimensionaler FIR-Filter erfolgen. Für jedes Bild der Bildfolge und für jeden Punkt des Bildes wird zuerst mit jedem Filter eines Filtersatzes ein Übereinstimmungsmaß berechnet. Dann sollen für jeden Bildpunkt aus allen Übereinstimmungsmaßen die beiden größten ausgewählt und aus diesen und den zugehörigen Filternummern je ein Klassifikator gebildet werden, der die Eckenausprägung, -orientierung und -krümmung beschreibt. Im nächsten Verarbeitungsschritt sollen die bildpunktbezogenen Klassifikatoren gemeinsam mit ihren Koordinaten so in eine Liste nach der Stärke ihrer Eckenausprägung einsortiert werden, dass Punkte, die nicht auf Eckpunkten und gekrümmten Kanten von Bildobjekten liegen, aus der Liste herausfallen. Übrig bleibt eine Liste von Datensätzen mit den Koordinaten und Klassifikatoren der interessantesten Bildpunkte, die an die nächste Verarbeitungseinheit geschickt werden. Die nächste Verarbeitungseinheit – hierfür genügt ein leistungsfähiger Rechner – sucht anhand der Klassifikatoren für alle so bestimmten Bildpunkte die korrespondierenden Punkte im vorherigen Bild und bestimmt so deren 2D-Bewegungsbahnen für die weitere Verarbeitung. Die rechenzeitintensiven Teilschritte, die eine Hardware-Lösung verlangen, sind insbesondere die Berechnung der Übereinstimmungsmaße, die Berechnung der Klassifikatoren und das Sortieren. Bearbeitungsstand: Eine Skizze der Zielfunktion und ihre Aufteilung in mehrere in Hardware oder in Software zu realisierende Teilfunktionen. 5.3.2 Grobkonzept der Hardware-Struktur Der Hardware-Entwurf einer sehr komplexen Schaltung ist eine Iteration, bei der nach der Aufstellung der Zielfunktion in der Regel als nächstes eine hypothetische Grobstruktur der Hardware aufgestellt wird, um die Machbarkeit zu kontrollieren. Der skizzierte Algorithmus benötigt voraussichtlich eine mehrstufige Speicherhierarchie. Die Zwischenspeicherung der Bilder verlangt einen relativ großen und entsprechend langsamen Bildspeicher, der z.B. von einer Kamera nachgefüllt wird. Für die Berechnung der Übereinstimmungsmaße muss jeder Bildpunktwert viele Male gelesen werden. Das erfordert einen schnellen und
404
5 Komplexe Beispielentwürfe
Bildquelle Bild n + 1 (Kamera) Bild n N xRAM cROM Filter K SortRAM Sortierer
N
Berechnung der Filterantworten
Klassifizieren der Punkte
xRAM
cROM
Filter
K
Klassif.
Bildhintergrundspeicher
Akkus
entsprechend kleinen Blockspeicher, in den nur noch ein kleiner Bildausschnitt passt. Für den Bildausschnittsspeicher ist eine geeignete Datenzuordnung zum Hintergrundspeicher und für die Schnittstelle zwischen beiden Speichern ein geeigneter Nachladealgorithmus zu entwerfen. Die Berechnung der Filterantworten für mehrere FIR-Filter verlangt voraussichtlich eine ähnliche Hardware-Struktur wie die für die Nachbildung des einzelnen FIR-Filters in Abschnitt 5.2. Abbildung 5.12 unterstellt, dass die Schaltung auch hier wieder aus einem Nur-Lese-Speicher für die Filterkoeffizienten, einem Blockspeicher für die Daten, einer Verarbeitungs-Pipeline und einem Akkumulatorregister für jeden Filter besteht. Die Struktur kann sich im weiteren Entwurfsablauf ändern. Das nächste Rechenwerk berechnet aus den Akkumulatorwerten die Klassifikatoren. In Abb. 5.12 ist unterstellt, dass es sich dabei um eine Schaltung handelt, die die Berechnung in einem oder mehreren Takten ausführt und das Ergebnis in ein Register schreibt. Die Vergleiche, die für die Suche der beiden größten Übereinstimmungsmaße erforderlich sind, werden vermutlich eine sequenzielle Berechnung nahelegen. Das nächste Rechenwerk soll die Klassifikatordatensätze sortieren. Dazu benötigt es außer einem Steuerwerk für den Sortierablauf und internen Datenregistern auch einen Blockspeicher für die sortierte Liste. Auch das ist nur eine Hypothese, die wie alle anderen Hypothesen in dieser Entwurfsphase dazu dient, eine Struktur in den weiteren Entwurfsablauf zu bringen. Sortieren der Punkte SortRAM
zum Rechner
Sortierer
Nachladesteuerung f¨ ur den Bildausschnittsspeicher Bildausschnittsspeicher Konstantenspeicher f¨ ur Filtermasken Pipeline-Rechenwerk f¨ ur die Filter Rechenwerk f¨ ur die Klassifikation der Bildpunkte Sortierspeicher f¨ ur Koordinaten-Klassifikator-Tupel Sortierrechenwerk
Abb. 5.12. Speicherhierarchie und Verarbeitungswerke für die Suche interessanter Bildpunkte
Eine nahe liegende Vorgehensweise für die weiteren Entwurfsschritte ist, mit den Hardware-Komponenten fortzufahren, die die meiste Rechenleistung bzw. den größten Datendurchsatz haben müssen. Das ist für die gewählte Zielfunktion die Berechnung der Übereinstimmungsmaße, die für alle Bildpunkte aller Bilder einer Bildfolge mit allen Filtermasken ausgeführt werden muss. Der nächste Schritt sei die Kontrolle, dass es für die gefundene Lösung einen
5.3 Point-of-Interest-Berechnung
405
ausreichend schnellen Nachladealgorithmus für den Blockspeicher aus dem Hintergrundspeicher gibt. Dann sollen Ablaufskizzen für die Berechnung der Klassifikatoren und die Sortieroperationen folgen. Bearbeitungsstand: Eine hypothetische Hardware-Struktur und ein Fahrplan für die nächsten Entwurfsschritte. 5.3.3 Optimierung der 2D-FIR-Filter Abbildung 5.13 a veranschaulicht den Algorithmus für die Bildung der Übereinstimmungsmaße. Die Bildpunkte bilden ein zweidimensionales Feld. Der Bildpunkt, dessen Umgebung klassifiziert wird, hat die Spalten- und Zeilenkoordinaten i und j. Die Filterkoeffizienten der 2D-FIR-Filter bilden Masken, die nacheinander mittig über alle Bildpunkte gelegt werden. Im Beispiel sind die Filtermasken zweidimensionale Felder der Größe 5 × 5. Die Filterantwort ist die Summe der abgedeckten Bildpunkte multipliziert mit den Koeffizienten der Filtermaske. Spaltennummer i−4 i−2
i
M1
M2
M3
M4
M5
M6
M7
M8
M9
M10
M11
M12
i+2 i+4
j −4 j −2 j
ZeilenNummer
2 0 q
2 0 p ¨ Ubereinstimmungsmaß P2 P2 a) yi,j = p=−2 q=−2 cp,q · xi−p,j−q
b)
-1 (Bildwert subtrahieren) 0 (Bildwert ignorieren) 1 (Bildwert addieren)
Abb. 5.13. a) Algorithmus zur Berechnung der Übereinstimmungsmaße b) Filtersatz
Vor einer Hardware-Nachbildung sind alle Möglichkeiten zur Verkürzung der Rechenzeit, die der Algorithmus bietet, auszuschöpfen. Für FIR-Filter ist eine dieser Möglichkeiten die Optimierung der Filtermasken. Für die gewählte Zielfunktion muss der Filtersatz aus Bildern von Ecken mit unterschiedlichen Orientierungen und Krümmungen bestehen. Der Rechenaufwand wird von der Anzahl der Masken, der Größe der Masken und vom Wertebereich der Filterkoeffizienten bestimmt. Abbildung 5.13 b zeigt den gewählten Filtersatz. Er besteht aus zwölf Masken mit einer Größe von 5 × 5 Bildpunkten und einem Koeffizientenwertebereich cp,q ∈ {−1, 0, 1}. Die Beschränkung der Koeffizientenwerte auf »0«, »1« und »-1« erspart die Multiplikationen. Die mit »1« abgedeckten Bildpunktwerte sind zum Akkumulatorwert zu addieren, die mit
406
5 Komplexe Beispielentwürfe
»−1« abgedeckten Bildpunktwerte sind zu subtrahieren und die mit »0« abgedeckten Werte sind zu ignorieren. Weiterhin sind die Filter so aufgebaut, dass für jeden Filter etwa dieselbe Anzahl von Bildwerten addiert und subtrahiert werden. Eine betragsmäßig größere Filterantwort erlaubt so ohne weitere Nachbearbeitung einen Rückschluss auf eine größere geometrische Ähnlichkeit mit dem Kantenverlauf in der Filtermaske, bei einer positiven Filterantwort mit dem Maskenbild selbst und bei einer negativen Filterantwort mit dem Negativ des Maskenbilds. Der skizzierte Algorithmus ist extrem rechenzeitintensiv. Es sind etwa 10 bis 20 Millionen Bildpunkte je Sekunde zu klassifizieren. Für jeden Bildpunkt und jede Filterantwort sind 25 Indexrechnungen für Daten- und Koeffizientenadressen und 25 bedingte Additions-Subtraktions-Operationen auszuführen. Beim Entwurf der Blockspeicher-Pipeline-Struktur für die Filterberechnung ist der Engpass genau wie in Abschnitt 5.2 die Speicherbandbreite des lokalen Blockspeichers für die Daten. Eine nahe liegende Optimierung ist, einmal gelesene Bildpunktwerte parallel mit allen Maskenwerten zu verknüpfen. Dazu muss der Koeffizientenspeicher bei jedem Zugriff die Koeffizientenwerte cp,q für alle Masken liefern. Des Weiteren sind für jede Filtermaske ein eigenes Additions-Subtraktions-Werk und ein eigener Akkumulator erforderlich (Abb. 5.14). Bearbeitungsstand: Ein in Hardware umsetzbarer Algorithmus zur Berechnung der Filterantworten. Durch eine geschickte Koeffizientenwahl wurden die Multiplikationen eingespart und mit einem geschickten Parallelisierungsschema die Anzahl Blockspeicherzugriffe minimiert. 5.3.4 Entwurf der Filter-Pipeline Nach dem Vorabschnitt besteht ein Filterberechnungsschritt in der Bestimmung der nächsten Blockspeicheradressen für die zu lesenden Koeffizienten und Daten, den Leseoperationen selbst und in Abhängigkeit vom gelesenen Koeffizientenwert in einer bedingten Addition oder Subtraktion des gelesenen Datenwertes zum Wert des jeweiligen Akkumulationsregisters. Die Blockspeicherzugiffe erhalten wie in Abschnitt 5.2.1 eine eigene Pipeline-Phase, die Adressrechnungen davor und die Akkumulationen danach gleichfalls (Abb. 5.14). Die Filter-Pipeline in Abb. 5.5 besitzt zusätzlich eine Phase für die Multiplikation, die hier durch die Beschränkung der Filterkoeffizientenwerte mit eingespart wird. Mit der Skizze der Filter-Pipeline lassen sich die ersten VHDL-Datentypen und Operationen definieren. Die Filterkoeffizienten sind Aufzählungstypen mit drei Werten: type tOperation is(add, sub, keine_Operation);
Der gesamte Koeffizientenvektor ist ein Feld aus Elementen von diesem Typ:
5.3 Point-of-Interest-Berechnung cAdr +1 xAdr A2D AR
cDat cROM a ROM y xRAM xDat x RAM y a w OF
s
s
... y
s y y s y s y 00 a a 10 a + b b 11 a − b
407
AkkuF y
s
M3 M2 M1 Masken
EX (Add/Sub)
cAdr Koeffizientenadresse xRAM Bildausschnittsspeicher xDat Datenwert xAdr Datenadresse cDat Koeffizientenwert A2D 2D-Adressrechnung cROM Koeffizientenspeicher AkkuF Akku-Register 0 bis 11
Pipelinestufen AR Adressrechnung OF Operanden lesen EX Ausf¨ uhrung
Abb. 5.14. Speicherhierarchie und Verarbeitungswerke für die Berechnung der Übereinstimmungsmaße subtype tFilterIdx is natural range 0 to N_Mask-1; type tKoeff is array (tFilterIdx) of tOperation;
Der Akkumulator muss wie die Daten einen Bitvektortyp zur Darstellung vorzeichenbehafteter Zahlen haben und soll, um Wertebereichsüberläufe auszuschließen, vier Bit breiter als ein Datenregister sein6 : constant cDatenbreite: natural := ...; subtype tDaten is tSigned(cDatenbreite - 1 downto 0); subtype tAkku is tSigned(cDatenbreite + 3 downto 0);
Die Funktion zur Berechnung des Folgewertes für einen einzelnen Akkumulator ist function FilterAdd(Akku: tAkku; x: tDaten; s: tOperation) return tAkku is begin case s is when add => return Akku + x; when sub => return Akku - x; when others => return Akku; end case; ⇒WEB-Projekt: P5.3/POI_pack.vhdl end function;
Alle Akkumulatoren zusammen bilden ein Feld: type tAkkFeld is array (tFilterIdx) of tAkku;
Die auf das gesamte Akkumulatorfeld anzuwendende Methode ist eine Iteration über alle Akkumulatoren mit der auf den einzelnen Akkumulator anzuwendenden Funktion: 6
Der sechzehnfache Wertebereich genügt, weil keine der Filtermasken mehr als sechzehn Koeffizienten ungleich null enthält.
408
5 Komplexe Beispielentwürfe procedure AllFilterAdd(signal AkkuF: inout tAkkFeld; x: tDaten; c: tKoeff) is begin for idx in tKoeff’range loop AkkuF(idx) <= FilterAdd(AkkuF(idx), x, c(idx)); end loop; ⇒WEB-Projekt: P5.3/POI_pack.vhdl end procedure;
Bearbeitungsstand: Skizze der Blockspeicher-Pipeline-Struktur zur Bestimmung der Übereinstimmungsmaße und erste Datentypvereinbarungen und Bearbeitungsmethoden für diese Datentypen. 5.3.5 Nachladen des Bildausschnittsspeichers Das wichtigste Optimierungskriterium für die Größe und Organisation des Bildausschnittsspeichers ist ein beherrschbarer Datentransfer zwischen dem Bildhintergrundspeicher und dem Bildausschnittsspeicher. Ziel sei, dass im Mittel für zehn Lesezugriffe auf den Bildausschnittsspeicher nicht mehr als ein Bildwert nachgeladen werden muss. Eine nahe liegende Organisationsform für den Bildausschnittsspeicher ist, ihn fünf Bildzeilen groß zu wählen. In Abb. 5.15 werden am Anfang von jedem Bild die ersten fünf Zeilen in den Bildausschnittsspeicher geladen. Anschließend werden für jede Filterberechnung 25 Bildwerte aus dem Bildausschnittsspeicher gelesen und das Filterfenster eine Spalte nach rechts bewegt. Das nächste Filterfenster nutzt 20 Bildpunkte des vorherigen Fensters. Von den freiwerdenden fünf Bildpunkten werden vier noch für die Filteroperationen für die nächste Zeile gebraucht. Nur der Bildwert der niederwertigsten Zeile ist durch den Bildwert für fünf Zeilen weiter zu ersetzen. Am Zeilenende springt das Maskenfenster an den Anfang der nächsten Zeile und hinterlässt fünf nachzuladende Bildpunktspeicherplätze. Bildanfang f¨ unf Zeilen laden .. . nachzuladender Wert
Nach jeder Filterberechnung einen Wert nachladen .. .
Nach jedem Zeilenwechsel f¨ unf Werte nachladen
.. .
.. .
vorhandener Bildwert
Filtermatrix
Abb. 5.15. Bildausschnittsspeicher mit Platz für fünf komplette Bildzeilen
Beim Nachladen eines Bildpunktwertes müssen alle anderen Bildpunkte im Bildausschnittsspeicher ihren Speicherplatz behalten. Denn Zeit für die Verschiebung der Datenwerte ist nicht verfügbar. Die Werte der fünften Zeile überschreiben die Werte der nullten Zeile, die Werte der sechsten Zeile die der
5.3 Point-of-Interest-Berechnung
409
ersten Zeile etc. Das führt bei der Berechnung der Übereinstimmungsmaße zu einer zirkularen, von der Zeilennummer des zu klassifizierenden Bildpunktes abhängigen Verschiebung der Adressen für die Filterkoeffizienten (Abb. 5.16).
j mod 5 = 2
Zeile
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
j−2 j−1 j j+1 j+2
i−2
i
j mod 5 = 3 20 21 22 23 24 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
i−2
i+2
i
Zeile
j mod 5 = 4
Zeile
j+2 j−2 j−1 j j+1
15 16 17 18 19 20 21 22 23 24 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14
j+1 j+2 j−2 j−1 j
i−2
i+2
i
i+2
Bewegung des Filterfensters u ¨ber der Bildausschnittszeile Nachladen des Bildpunktwertes xi−3,j+3 nach jedem Maskenschritt i, j Bildspalte und -zeile k Filterwert mit Koeffizientenindex
Abb. 5.16. Zirkulare Verschiebung der Koeffizientenadressen im Bildausschnittsspeicher
Ein Bildausschnittsspeicher für fünf Zeilen ist relativ groß und entsprechend langsam. Abbildung 5.17 zeigt eine alternative Lösung mit einem nur fünf Spalten mal 25 Zeilen großen Bildausschnittsspeicher. Am Bildanfang wird dieser mit den ersten 125 Bildpunktwerten der linken oberen Bildecke beschrieben (Zeile 0 bis 24, Spalte 0 bis 4). Danach wird das Filterfenster von Zeile zwei bis 22 nach unten bewegt. Nach den ersten 20 Schritten muss genau wie bei der Bewegung des Filterfensters von links nach rechts in Abb. 5.15 immer nur ein Wert nachgeladen werden. Nach dem 21. Schritt folgt ein Spaltenwechsel. Dafür sind die fünf Werte in Spalte null, Zeile 20 bis 24 nachzuladen. Der Nachladerhythmus bleibt bis zum Zeilenende gleich: zwanzigmal einen und einmal fünf Werte nachladen. Nach der Berechnung der Klassifizierungswerte für die ersten 21 Zeilen muss der Bildausschnittsspeicher komplett neu mit den ersten fünf Werten der Zeilen 20 bis 25 geladen werden. Für den nächsten und alle weiteren Zeilenblöcke verschiebt sich das Zeilenfenster um ur 20 Filterberechnungen nach Spaltenwechsel Zeilenwechsel f¨ 125 Werte laden je einen Wert nachladen 5 Werte nachladen
. ..
.
. ..
. ..
. ..
...
..
nachzuladender Wert vorhandener Bildwert Filtermatrix Bewegung des Filterfensters
Abb. 5.17. Nachladeschema mit einem Bildausschnittsspeicher der Größe 5 × 25 Bildpunkte.
410
5 Komplexe Beispielentwürfe
21 Zeilen nach unten. Die Gesamtanzahl der Nachladeoperationen beträgt für jeden Block von 21 Zeilen 125 nachzuladende Werte am Zeilenanfang und für jede Bildspalte innerhalb des Blocks zwanzigmal einen gefolgt von einmal fünf nachzuladenden Werten. Das ist nicht wesentlich mehr als bei der ersten Lösung, bei der der Bildausschnittsspeicher Platz für fünf komplette Bildzeilen bieten musste. Auch in der modifizierten Lösung behalten alle einmal in den Bildausschnittsspeicher geladenen Bildwerte bis zu ihrem Überschreiben ihre Adresse. Die Adressrechnungen für die nachzuladende Bildpunktadresse, die zugehörige Adresse im Bildausschnittsspeicher etc. sind für den FIR-Filter in Abschnitt 5.2 durch Konstantenzuweisungen und Additionen beschreibbar. Da die Filterfensterbewegung hier spaltenweise erfolgt, muss man sich die zirkulare Verschiebung der Filterkoeffizienten, die in Abb. 5.15 b für eine spaltenweise Bewegung gezeigt ist, um 90° gedreht vorstellen. Bearbeitungsstand: Es wurde gezeigt, dass es mit einem relativ kleinen Blockspeicher möglich ist, die erforderlichen Bilddaten mit einem mittleren Verhältnis aus der Anzahl der Speicherzugriffe auf den schnellen Bildausschnittsspeicher zur Anzahl der Speicherzugriffe auf den langsamen Hintergrundspeicher von mehr als zehn zu eins nachzuladen. Damit ist gezeigt, dass das Konzept mit dem großen langsamen Hintergrundspeicher und dem kleinen schnellen Bildausschnittsspeicher prinzipiell funktioniert. 5.3.6 Suche der betragsmäßig größten Filterantworten Aus den zwölf Filterantworten, die der bisherige Algorithmus für jeden Bildpunkt berechnet, sollen mit dem nächsten Teilalgorithmus je ein Klassifikator und ein Signifikanzmaß berechnet werden. Der Klassifikator soll, wenn es sich um einen Eckpunkt handelt, die Orientierung und Krümmung der Ecke beschreiben und aus den beiden Filternummern mit den betragsmäßig größten Filterantworten und den Vorzeichen dieser Filterantworten gebildet werden. Das Signifikanzmaß, an dem später die auffälligsten Eckpunkte ausgewählt werden, ist eine Funktion der beiden größten Beträge. Die Suche der beiden größten Beträge und der zugehörigen Filternummern benötigt ein Register »Max1« für die betragsmäßig größte und ein Register »Max2« für die betragsmäßig zweitgrößte Filterantwort. Jedes der beiden Register muss einen Datensatz aus der Filternummer sowie aus dem Betrag und dem Vorzeichen der Filterantwort speichern: type tFilterDat is FNr: tFilterIdx; betrag: tAkku; s: std_logic; end record; signal Max1, Max2:
record –- Filternummen –- Betrag des Akkuwertes –- Vorzeichen des Akkuwertes tFilterDat;
Zusätzlich wird eine Konstante für einen leeren Datensatz benötigt:
5.3 Point-of-Interest-Berechnung
411
constant cFDat_leer:tFilterDat := (FNr=>0, Betrag=>"0...0", s=>’0’);
Im ersten Schritt wird das Signal für »Max1« mit dem Datensatz des ersten Filters und das Signal »Max2« mit der Konstanten für den leeren Datensatz beschrieben. In den nachfolgenden elf Schritten wird nacheinander für alle anderen Filterantworten der Betrag gebildet und mit dem von »Max1« verglichen. Ist er größer, übernimmt »Max1« den Datensatz dieses Filters und reicht seinen aktuellen Datensatz an das Signal »Max2« weiter. Wenn der Betrag des betrachteten Filters kleiner als der von »Max1« aber größer als der von »Max2« ist, übernimmt »Max2« den Filterdatensatz. Der skizzierte Algorithmus lässt sich als Prozedur mit dem Akkumulatorfeld als Eingabesignal und »Max1« und »Max2« als les- und veränderbare Signale beschreiben: procedure SortUeMass(AkkuF: tAkkFeld; signal Max1, Max2: inout tFilterDat) is begin Max1 <= (FNr=>0, betrag=>abs(AkkuF(0)), s=>Vorzeichen(AkkuF(0))); Max2 <= cFDat_leer; wait for tP; for idx in 1 to AkkuF’high loop if abs(AkkuF(idx))>Max1.betrag then Max1 <= (FNr=>idx, betrag=>abs(AkkuF(idx)), s=>Vorzeichen(AkkuF(idx))); Max2 <= Max1; elsif abs(AkkuF(idx))>=Max2.Betrag then Max2 <= (FNr=>idx, betrag=>abs(AkkuF(Idx)), s=>Vorzeichen(AkkuF(idx))); end if; wait for tP; end loop; end procedure;
Die Prozedur gibt den Kontrollfluss immer nach zwölf Zeitschritten zurück. Während dieser zwölf Schritte dürfen sich die Akkumulatorinhalte nicht ändern. Entweder die Maximabestimmung arbeitet mit einer Kopie der Akkumulatorwerte oder die Berechnung der Filterantworten und die Suche der beiden betragsmäßig größten Filterantworten erfolgen nacheinander. In Abschnitt 5.3.8 folgt später die Entscheidung für »nacheinander«, weil sich in diesem Abschnitt herausstellen wird, dass nicht die Filterberechnung, sondern das Sortieren am längsten dauert. Blockspeicher und Pipelines werden für die Suche der beiden größten Übereinstimmungsmaße nicht benötigt. Bearbeitungsstand: Algorithmusskizzen bis zur Bestimmung der beiden Filterantworten mit der besten Übereinstimmung für jeden Bildpunkt.
412
5 Komplexe Beispielentwürfe
5.3.7 Klassifikation Nach der Bestimmung der Maxima folgt die Klassifikation. Der Klassifikationsdatensatz soll aus den Punktkoordinaten, Datenobjekten für zwei Vorzeichen und zwei Filternummern sowie einem Maß für die Eckenausprägung bestehen: type tKlassPkt is record FNr1, FNr2: tFilterIdx; s1,s2: std_logic; Betrag: tAkku; –- Mass für die Eckenausprägung i: integer range 2 to Spaltenanzahl-2; j: integer range 2 to Zeilenanzahl-2; end record;
Die Klassifikation ist eine Funktion mit den beiden signifikantesten Filterdatensätzen als Eingabe und einem Klassifikationsdatensatz als Ergebnis. Wenn nach der Sortierung der Betrag des Filters mit der besten Übereinstimmung »Max1.Betrag« mehr als doppelt so groß wie der des Filters mit der zweitbesten Übereinstimmung »Max2.Betrag« ist, soll der Klassifikationsdatensatz nur aus den Werten von »Max1« und sonst aus den Werten von »Max1« und »Max2« zusammengesetzt werden. Auf der Hardware-Ebene verbergen sich dahinter Registerzuweisungen mit umschaltbarem Datenfluss: function Klassifikation(Max1, Max2: tFilterDat) return tKlassPkt is begin if (Max1.Betrag sra 1) > Max2.Betrag then return (FNr1=>Max1.FNr, s1=>Max1.s, FNr2=>Max1.FNr, s2=>Max1.s, Betrag=>Max1.Betrag + (Max1.Betrag sra 1), i=>..., j=>...); else return (FNr1=>Max1.FNr, s1=>Max1.s, FNr2=>Max2.FNr, s2=>Max2.s, Betrag=>Max1.Betrag + Max2.Betrag, i=>..., j=>...); end if; end function;
(sra 1 – vorzeichenbehaftete Halbierung durch eine arithmetische Rechtsverschiebung um eine Stelle). Bearbeitungsstand: Algorithmusskizzen bis zur Bestimmung der Bildpunktklassifikatoren. 5.3.8 Sortieren der klassifizierten Punkte Der nachfolgende Sortieralgorithmus ähnelt einem Bubble-Sort, bei dem nur eine begrenzte Anzahl von Werten im Sortierspeicher gehalten wird. Die Datensätze mit geringer Signifikanz fallen unten heraus. Der Sortierspeicher sei ein Blockspeicher, dessen Elemente klassifizierte Bildpunkte sind:
5.3 Point-of-Interest-Berechnung
413
type tPtrKlassFeld is array (natural range <>) of tKlassPkt;
Zu Beginn der Bearbeitung eines neuen Bildes ist jedes Feldelement mit einer Konstanten für den Wert »leer« zu beschreiben: constant Pkt_leer: tKlassPkt := (FNr1=>0, s1=>’0’, FNr2=>0, s2=>’0’, Betrag => "0...0", i=>0, j=>0);
Bei einem Hardware-Sortierer ist es wichtig, die Anzahl der Blockspeicherzugriffe zu minimieren, da diese sequenziell erfolgen. Der in Abb. 5.18 skizzierte Algorithmus verwendet dazu zwei Register, »RegL« für den aus dem Blockspeicher gelesenen Wert und »RegE« für den in den Blockspeicher einzusortierenden Wert: signal RegL, RegE: tPktKlass;
Bei unserem speziellen Bubble-Sort wird für alle einzusortierenden Werte nacheinander jedes Element im Sortierspeicher gelesen und mit dem einzusortierenden Wert verglichen. Wenn der gelesene Wert kleiner ist, wird er in das Feldelement davor kopiert. Anderenfalls, wenn er größer ist, wird der einzusortierende Wert in das vorherige Feldelement und der gelesene Wert in das Register für den einzusortierenden Wert kopiert. Beim ersten Einsortierschritt für jeden neuen Datensatz hat das Feldelement, das gelesen wird, keinen Vorgänger, so dass der Datensatz für den Bildpunkt mit der geringeren Signifikanz aus der Liste herausfällt. 0 89 2
1 112
1
4
RegL 89 RegE 120
2 123
3
5 6
112 2
120
4
123 120
... ...
4 215 7 8 6
215 123
Listenindex Kantensignifikanz des Bildpunktes
Nummer des Berechnungsschritts Datensatz f¨allt aus der Liste heraus RegL Register mit gelesenem Wert . . . RegE Register mit einzusortierendem Wert i
8
Abb. 5.18. Sortieren der klassifizierten Punkte
Jeder Sortierschritt benötigt eine Schreib- und eine Leseoperation, die nacheinander ausgeführt werden müssen. Das Sortieren dauert folglich mindestens 2 · NPOI Takte (NPOI – Anzahl der zu berechnenden »interessanten« Bildpunkte). Die Filteroperation benötigt für jeden Bildpunkt 25 Schritte. Da das Sortieren offensichtlich mehr Zeit benötigt, ist es in Abschnitt 5.3.6 nicht notwendig, während der Suche der beiden betragsmäßig größten Werte schon mit der nächsten Filteroperation zu beginnen. Wenn die Filterberechnung und die Klassifizierung nacheinander erfolgen, dauert das insgesamt etwa 40 Takte. Diese Zeit genügt, um den zuvor klassifizierten Punkt in eine Liste mit bis zu 20 Bildpunkt-Klassifikator-Tupeln einzusortieren. Die Listenlänge ist
414
5 Komplexe Beispielentwürfe
gleichzeitig die maximale Anzahl der interessanten Bildpunkte, die der Algorithmus bestimmt. Für das gesamte Bild sind 20 interessante Punkte zu wenig. Eine Möglichkeit, ihre Anzahl zu erhöhen, ist, das Gesamtbild in Teilbilder zu unterteilen und die interessanten Bildpunkte für jedes Teilbild extra zu berechnen. Jedem Teilbild wird dazu ein Teilbereich im Sortierspeicher zugeordnet. Bearbeitungsstand: Es wurde gezeigt, dass der gesamte Algorithmus in der skizzierten Weise in Hardware umsetzbar ist. Für einige Datenobjekte wurden bereits die Datentypen und die Unterprogramme für ihre Bearbeitung entwickelt. Das Entwurfsprojekt soll hier enden, obwohl es noch lange nicht abgeschlossen ist. In den nächsten Schritten sind auch für die restlichen Datenobjekte Typen zu vereinbaren, Bearbeitungsmethoden zu programmieren, Testbeispiele zu entwickeln, die algorithmischen Bausteine zu größeren Systemen bis hin zum Gesamtsystem zusammenzusetzen, auch dafür wieder Testrahmen und Testbeispiele zu entwickeln und vieles mehr. Der noch zu leistende Entwicklungsaufwand ist erheblich, denn wir reden hier immerhin über eine zu entwerfende Schaltung der Größe eines kleinen Graphikprozessors. 5.3.9 Zusammenfassung Das Beispiel hat das Herangehen an die Umsetzung komplexer, rechenzeitintensiver Algorithmen in Hardware demonstriert. Gegenüber den etwas kleineren Entwurfsaufgaben wie dem Entwurf eines FIR-Filters in Abschnitt 5.2 und dem Entwurf des CORDIC-Rechenwerks in Abschnitt 3.4.6 kommen zusätzliche vorangestellte Entwurfsschritte hinzu, die den Gesamtalgorithmus auf Hardware und Software verteilen, den Hardware-Teil weiter aufspalten, die Teilalgorithmen zuerst skizzenhaft durch Hardware nachbilden, dabei die Machbarkeit untersuchen, bis dann gezielt mit der Entwicklung des Simulationsmodells für die Zielfunktion begonnen wird. Neu waren weiterhin einige Hardware-typische Optimierungsansätze, insbesondere das Konzept von Hinter- und Vordergrundspeicher. Bei Letzterem geht es darum, den Speicherengpass für Zugriffe auf einen großen und langsamen Hintergrundspeicher dadurch zu umgehen, dass mit einer Datenkopie in einem viel kleineren und entsprechend schnelleren Vordergrundspeicher gearbeitet wird. Abgesehen von den algorithmenspezifischen Optimierungen und Tricks war ansonsten eigentlich nichts grundlegend neu. Die bis hierher behandelten Entwurfstechniken erlauben, auch sehr große Schaltungen zu entwerfen.
5.4 Entwurf eines RISC-Prozessors Ein Prozessor ist ein universeller Automat, der nacheinander Befehle aus einem Speicher liest und abarbeitet. Der Entwurf erfolgt wie bei jeder anderen
5.4 Entwurf eines RISC-Prozessors
415
komplexen digitalen Schaltung. Aus der vorgegebenen Funktion werden zuerst Blockspeicher-Pipeline-Strukturen abgeleitet, die dann zu einem Funktionsmodell zusammengefügt werden, das die Grundlage für den Schaltungsentwurf bildet. Im Vergleich zu den bisherigen Entwürfen ist bei einem Prozessor bereits die Konzeption der Zielfunktion und des Befehlssatzes ein mehrstufiger iterierender Prozess. Entwurf der Zielfunktion Praktisch alle in den letzten 20 bis 30 Jahren neu entwickelten Prozessoren haben eine RISC-Architektur. Das Akronym RISC steht für reduced instruction set computer. Die Besonderheit eines RISC-Prozessors gegenüber anderen Prozessorarchitekturen ist jedoch weniger ein kleiner Befehlssatz – es gibt RISC-Prozessoren mit sehr großen Befehlssätzen –, sondern die Pipeline-Fähigkeit der Befehle. Pipeline-Fähigkeit setzt voraus, dass die Ausführung aller Befehle in dieselbe Folge von Pipeline-Phasen untergliedert ist und dass sich die Pipeline-Phasen weder Zielregister noch Rechenwerke teilen. Eine Pipeline-Phase besteht aus einer oder mehreren parallel ausgeführten Register-Transfer-Operationen und dauert eine Taktperiode. Der Entwurf der Zielfunktion eines RISC-Prozessors beginnt mit Skizzen der VerarbeitungsPipelines für die unterschiedlichen Befehlstypen. Es ist zwischen Verarbeitungsbefehlen, Lade- und Speicherbefehlen und Steuerbefehlen zu unterscheiden. Aufbauend auf den Pipeline-Skizzen wird der Befehlssatz konstruiert. Dabei müssen in der Regel die Pipeline-Operationen etwas nachgebessert werden. Mit dem Befehlssatz und den nachgebesserten Pipeline-Skizzen werden Funktionsmodelle für das Rechenwerk, die Gesamt-Pipeline und die Blockspeicher entwickelt. Jede separat beschriebene Teilfunktion und das daraus zusammengesetzte Verhaltensmodell des Prozessors benötigen einen Testrahmen. 5.4.1 Die Pipeline für Verarbeitungsbefehle Der zu entwerfende RISC-Prozessor soll eine vierstufige Verarbeitungs-Pipeline erhalten, die in folgende Phasen untergliedert ist: • • • •
IF (instruction f etch) Befehlswort lesen, OF (operand f etch) Operanden lesen, Befehlszähler weiterschalten, EX (execute) Befehl ausführen und RW (result write) Ergebnis speichern.
Abbildung 5.19 a zeigt den Datenfluss für Verarbeitungsbefehle mit allen Register-Transfer-Operationen. In der Befehlsholphase (IF) wird ein Befehlswort aus dem Befehlsspeicher gelesen. Quellregister ist der Befehlszähler »PCount«, mit dem der Befehlsspeicher adressiert wird, und Zielregister das
416
5 Komplexe Beispielentwürfe
Befehlsregister »Instr«. In der Operandenholphase (OF) werden zwei Operanden aus den Arbeitsregistern geladen. Quellregister sind die Teile des Befehlsregisters, in denen die Operandenadressen stehen. Zielregister sind die Operandenregister »OReg_A« und »OReg_B«. Zeitgleich werden in dieser Pipeline-Phase der Operationscode in das Register »Opcode« und die Ergebnisadresse in das Register »ARegY1« kopiert. Das Weiterzählen der Befehlsadresse im Register »PCount« soll gleichfalls der Operandenholphase zugeordnet werden, weil in den später einzuführenden Sprungbefehlen in dieser Phase die Sprungadresse zugewiesen wird. In der Ausführungsphase (EX) wird mit den beiden Operanden aus den Registern »OReg_A« und »OReg_B« eine Operation ausgeführt. Die auszuführende Operation legt der Inhalt des Registers »Opcode« fest. Zielregister ist das Ergebnisregister »Ergebnis«. Das berechnete Ergebnis besteht aus dem Wert und Flags. Die Flags sind einzelne Bits mit Zusatzinformationen über das Ergebnis, die für die nachfolgenden Operationen aufbewahrt werden, z.B. der Übertrag einer Addition. Zeitgleich mit der Operationsausführung wird die Ergebnisadresse aus dem Register »ARegY1« in das Register »ARegY2« weitergeschoben. In der Ergebnisschreibphase (RW) wird der Ergebniswert in den Registersatz unter der Adresse, die im Register »ARegY2« steht, gespeichert. BefehlsPCount speicher +1
ROM IF
1 0
a) IF OF EX RW
Registersatz
Instr
Befehlscode holen Operanden holen Operation ausf¨ uhren Ergebnis schreiben
PCount Instr OReg A, OReg B Opcode ARegY1, ARegY2 Ergebnis
0
Befehlsz¨ ahler Befehlsregister Operandenregister Operationscoderegister Ergebnisadressregister Ergebnisregister
x1 RAM y1 a1 w1 x2 y2 a2 w2 x3 y3 a3 w3
ARegY1
ARegY2
Opcode Wert
OReg A OReg B
Ergebnis
ALU
Flags
OF RW
EX Zeitschritt
b)
IF OF Pipeline- EX phasen RW
1 ··· ··· ···
2 1 ··· ···
3 2 1 ···
4 3 2 1
5 4 3 2
Abb. 5.19. Datenfluss der Verarbeitungsbefehle in einem RISC-Prozessor Blockspeicher-Verarbeitungswerk-Struktur b) Pipeline-Ablauf
6 5 4 3
a)
Die Register-Transfer-Operationen aller Pipeline-Phasen werden gleichzeitig abgearbeitet. In jedem Takt wird für einen Befehl das Befehlswort gelesen. Für den vorherigen Befehl werden zwei Operanden gelesen. Der zwei Takte zuvor gestartete Befehl wird ausgeführt und für den drei Takte zuvor gestar-
5.4 Entwurf eines RISC-Prozessors
417
teten Befehl wird das Ergebnis geschrieben (Abb. 5.19 b). Den Engpass bilden die vier parallelen Speicherzugriffe, die einen getrennten Befehlsspeicher und einen 3-Port-Speicher als Registersatz für die zu verarbeitenden Daten erfordern. Von den drei Registerports dienen zwei nur zum Lesen und einer auch zum Schreiben. 5.4.2 Lade- und Speichereinheit Ein Prozessor benötigt zusätzlich zu den Verarbeitungsbefehlen Befehle zum Laden von konstanten und variablen Werten in die Register und zum Speichern der Ergebnisse aus den Registern in einen größeren Datenspeicher. Eine lückenlose Befehlsabarbeitung verlangt, dass auch diese Befehle in die Pipeline-Phasen aus Abb. 5.19 b untergliedert sind. Abbildung 5.20 zeigt eine geeignete Schaltung zum Laden einer Konstanten. Die Register-Transfer-Operationen der IF-, der EX- und der RW-Phase sind identisch mit denen der Verarbeitungsbefehle. Die Operandenholphase (OF) hat folgenden geänderten Datenfluss. Das erste Operandenregister übernimmt den Teilbitvektor aus dem Befehlsregister »Instr«, in dem die zu ladende Konstante steht. In das Operationscoderegister wird der Code für eine Spezialoperation geschrieben, bei der in der nachfolgenden Ausführungsphase der erste Operand unverändert an das Ergebnisregister übergeben wird, ohne die Flags zu verändern. Der Übernahmewert des anderen Operandenregisters spielt keine Rolle (don’t care), und in das Register »ARegY1« wird wie bei Verarbeitungsbefehlen die Zieladresse der Operation, hier die Adresse, in die die Konstante zu laden ist, geschrieben. BefehlsPCount speicher +1
Instr
ROM IF
IF OF EX RW
Registersatz
Befehlscode holen Operanden holen Operation ausf¨ uhren Ergebnis schreiben
1
ARegY1
ARegY2
sopc mov Opcode x1 RAM y1 a1 w1 OReg A tDaten OF RW
Wert
ALU
Ergebnis
EX
Abb. 5.20. Pipeline zum Laden einer Konstanten
Für den Datenaustausch mit einem größeren Datenspeicher benutzt ein RISC-Prozessor üblicherweise alle Pipeline-Phasen bis zur Ausführungsphase für die Adressrechnung. Damit stimmen die Register-Transfer-Operationen bis dahin weitgehend mit denen der Verarbeitungsbefehle überein. Der Datenaustausch findet in der Ergebnisschreibphase (RW) statt. Abbildung 5.21 zeigt
418
5 Komplexe Beispielentwürfe
eine gleichermaßen für Lade- und Speicherbefehle geeignete Schaltungsstruktur. Die Datenspeicheradresse ist das Operationsergebnis im Register »Ergebnis«. Die Registeradresse für den Datenaustausch steht wie bei allen anderen behandelten Befehlen in der Ergebnisschreibphase im Register »ARegY2«. Zusätzlich wird aus der Operandenholphase (OF) über die Register »Ctrl_EX« und »Ctrl_RW« ein Bitvektor übergeben, der für den dritten Port des Registersatzes und für den Datenspeicher die Information für die Bildung der Schreibsignale enthält. Für einen Speicherbefehl wird das adressierte Register gelesen und der gelesene Inhalt auf den adressierten Datenspeicherplatz geschrieben. Für einen Lesebefehl wird der adressierte Datenspeicherplatz gelesen und sein Inhalt in das adressierte Register geschrieben. ARegY1, Ctrl EX ARegY2, Ctrl RW Konstante f¨ ur Laden bzw. Speichern Registersatz Datenspeicher Instr Opcode x1 RAM y1 x RAM y a1 sop add w1 w OReg A IF x2 y2 a2 Ergebnis OpTyp Transferrichtung 0 w2 a ALU OReg B x3 ld DS ⇒ RS y 3 a3 RS ⇒ DS st 0 w3 DS Datenspeicher EX OF RW (speichern) RS Registersatz Ctrl ... Kontrollregister RW (laden)
Abb. 5.21. Lade- und Speicher-Pipeline
5.4.3 Sprung-Pipeline Die Kontrollflussanweisungen einer höheren Programmiersprache – Fallunterscheidungen, Schleifen, Unterprogrammaufrufe und Unterprogrammrücksprünge – werden im übersetzten ausführbaren Programm mit Sprungbefehlen nachgebildet. Die Nachbildung aller Hochsprachen-Kontrollflussanweisungen erfordert mindestens zwei Arten von Sprüngen: • •
einen unbedingten Sprung zu einer berechneten Adresse unter Aufbewahrung der Rückkehradresse und einen bedingten Sprung zu einer berechneten Adresse.
Eine berechnete Adresse ist auf der Maschinenprogrammebene der Inhalt eines Registers, in das zuvor die Adresse geschrieben wurde. Der Speicherplatz für die Rückkehradresse ist gleichfalls ein Register, dessen Inhalt von nachfolgenden Befehlen bearbeitet oder in den Datenspeicher kopiert werden kann.
5.4 Entwurf eines RISC-Prozessors
419
Als Sprungbedingung wird das Ergebnis einer vorherigen Verarbeitungsoperation ausgewertet, ob es größer, gleich oder kleiner null ist oder ob es einen oder keinen Übertrag gegeben hat. Die Schaltung für die Berechnung, ob der Sprung auszuführen ist oder nicht, wertet dazu die Flags aus, die im Register »Ergebnis« mitgespeichert sind. Abbildung 5.22 zeigt die Schaltung für einen Sprung zu einer Registeradresse, bei dem die um eins erhöhte Folgeadresse des Befehlszählers in ein Register gespeichert wird. Der Datenfluss in der Befehlsholphase (IF) ist identisch mit dem der anderen Befehle. In der Operandenholphase (OF) wird nur einer statt zwei Operanden aus dem Registersatz gelesen, der in den Befehlszähler »PCount«, statt in das Operandenregister übergeben wird. Das Operandenregister übernimmt statt dessen den um eins erhöhten Befehlszählerstand. In das Operationscoderegister wird wie bei dem Befehl zum Laden einer Konstanten ein Operationscode geschrieben, der in der Ausführungsphase bewirkt, dass der Operand mit der Rücksprungadresse unverändert in das Ergebnisregister kopiert wird, um dann in der Ergebnis-Schreib-Phase weiter in das adressierte Register übernommen zu werden. Dadurch, dass die Zuweisung an den Befehlszähler erst in der OF-Phase erfolgt, wird nach jedem Sprungbefehl erst der Folgebefehl auf der nächsten Adresse ausgeführt, bevor der Sprung wirksam wird.
+1
BefehlsPCount speicher Instr ROM
Registersatz
1
IF
0
ARegY1
ARegY2
Opcode x1 RAM y1 sopc mov a1 w1 OReg A Ergebx2 y2 nis a2 ALU w2 Flags
RW
OF und Sprung
EX
Abb. 5.22. Sprung-Pipeline mit Speicherung der Rücksprungadresse
Abbildung 5.23 zeigt den Register-Transfer-Fluss für bedingte Sprünge zu einer variablen, in einem Register gespeicherten Adresse. Der Datenfluss in der Befehlsholphase (IF) ist wieder derselbe. In der Operandenholphase (OF) steuert die Schaltung zur Berechnung der Sprungbedingung über einen Multiplexer, ob der Befehlszähler »PCount« den Inhalt des adressierten Registers oder seinen um eins erhöhten Wert übernimmt. Über die Kontrollregister »Ctrl_EX« und »Ctrl_RW« wird an die Ergebnisschreibphase (RW) eine Bitvektorkonstante geschickt, die das Schreibsignal für den Datenspeicher und den Registersatz deaktiviert, damit keine Ergebnisdaten geschrieben werden.
420
5 Komplexe Beispielentwürfe PCount Befehlsspeicher
+1
0 1
ROM IF
non keine Ergebnisspeicherung
Instr
non
MCtrl1 MCtrl2 Registersatz
x1 RAM y1 a1 w1 x2 y2 a2 0 w2
EX Ergebnis tBed
Flags
Schreibdeaktivierrung f¨ ur den Datenspeicher und den Registersatz
Sprungbedingung
OF und Sprung, wenn Bedingung erf¨ ullt
Abb. 5.23. Pipeline für bedingte Sprünge
5.4.4 Entwurf eines Befehlssatzes für einen Minimalprozessor Zur Präzisierung der Pipeline-Abläufe sind einige Grundsatzentscheidungen zu treffen. Dazu zählen die Festlegung der Datenwortbreite, der Befehlswortbreite, der Adressbreiten und der Registeranzahl. Das Ziel in diesem Abschnitt ist der Entwurf eines möglichst kleinen und einfachen Prozessors. Die zu verarbeitenden Daten, Befehlsadressen und Datenspeicheradressen sollen acht Bit breit sein. Die Registeranzahl sei gleichfalls acht. Diese Entscheidung begrenzt die Größe des Befehlsspeichers und des Datenspeichers auf je 256 Speicherplätze. Mit einer 16-Bit-Architektur ließen sich Daten- und Befehlsspeicher mit 216 und mit einer 32-Bit-Architektur Daten- und Befehlsspeicher mit 232 Speicherplätzen adressieren. Zur Bestimmung der minimalen Befehlswortbreite ist der Befehlssatz zu konstruieren. Ein RISC-Prozessor liest in jedem Takt einen Befehl aus dem Befehlsspeicher. Das setzt voraus, dass alle Befehlsworte gleich groß sind. Jeder der betrachteten Befehlstypen benötigt Parameter: • • • • •
Die Verarbeitungsbefehle nach Abb. 5.19 benötigen drei Registeradressen und den Operationscode. Der Befehl zum Laden einer Konstanten nach Abb. 5.20 benötigt eine Registeradresse und eine 8-Bit-Konstante. Die Lade- und Speicherbefehle nach Abb. 5.21 benötigen je drei Registeradressen. Ein unbedingter Sprung mit Aufbewahrung der Rückkehradresse nach Abb. 5.22 benötigt zwei Registeradressen. Ein bedingter Sprung nach Abb. 5.23 benötigt eine Registeradresse und einen Bitvektor für die Codierung der Sprungbedingung.
Die Registeradressen sind alle drei Bit groß. Zusätzlich zu den Parameterbits muss jedes Befehlswort konstante Bits enthalten, in denen es sich von allen anderen Befehlsworten unterscheidet. Jetzt folgt ein Puzzle für die Bitaufteilung, bei dem möglicherweise für einen Teil der skizzierten Register-TransferOperationen die Pipeline-Abläufe geändert werden müssen. Die Befehlswortbreite soll minimal sein. Der Befehl zur Initialisierung eines Registers mit einer Konstanten braucht elf Parameterbits, acht für die
5.4 Entwurf eines RISC-Prozessors
421
Konstante und drei für die Zieladresse. Zusätzlich muss das Befehlswort mindestens ein konstantes Bit enthalten, in dem es sich von allen anderen Befehlen unterscheidet. In Abb. 5.24 ist das eine führende »1«. Die Assembler-Notation für den Initialisierungsbefehl sei im Weiteren: init ra, c8
(ra ∈ {r0, r1, . . . , r7} – eines von acht Registern; c8 – 8-Bit-Konstante). Die Verarbeitungsbefehle benötigen neun Parameterbits für die drei Registeradressen und mindestens zwei konstante Bits – in Abb. 5.24 die führenden Bits »01« – zur Unterscheidung von den anderen Befehlstypen. Bei einem 12-Bit-Befehlswort würde nur ein Bit für die Operationsauswahl übrig bleiben. Damit wären nur zwei Operationen unterscheidbar. Das ist zu wenig. Deshalb soll im Weiteren gelten, dass das Ergebnis immer dieselbe Adresse wie der erste Operand haben soll. Das erspart die drei Bits für die dritte Adresse und erhöht die Bitanzahl für die Operationsauswahl auf vier. Damit lassen sich sechzehn Verarbeitungsoperationen unterscheiden. Das genügt. Die Assembler-Notation für Verarbeitungsbefehle soll lauten: opc ra, rb
(opc ∈{add, . . .}7 – Operationscode; ra ∈ {r0, r1, . . . , r7} – erstes Operandenund Ergebnisregister; rb ∈ {r0, r1, . . . , r7} – zweites Operandenregister). Bitnummer 11 10 Befehlstyp 1 lade Konstante 0 1 Verarbeitungsbefehle 0 0 Speichern 0 0 Laden aus dem Datenspeicher Laden aus dem Konstantenspeicher 0 0 0 0 unbedingter Sprung 0 0 bedingter Sprung parameterfreie Befehle Operationscode
9 8 7 6 5 4 3 2 1 0 ra c8 opc rb ra 1 0 c2 rb ra 1 1 c2 rb ra 0 1 c2 ra rb 0 0 1 1 ra rb 0 0 1 0 bed ra
nop set cy clr cy cpl cy x”000” x”001” x”002” x”003”
Assemblernotation init ra c8 opc ra, rb st @(rb + c2), ra ldd ra, @(rb + c2) ldc ra, @(rb + c2) jmb ra, rb jc bed ra
frei f¨ ur Erweiterungen x”004” bis x”03F”
Abb. 5.24. Bittafel für den Entwurf eines Befehlssatzes
Die weiteren Befehle werden nach absteigendem Bedarf an Parameterbits in die Bittafel in Abb. 5.24 einsortiert. Den nächstkleineren Bitbedarf haben die Lade- und Speicherbefehle mit drei Registeradressen. Zur Unterscheidung vom Initialisierungsbefehl und von den Verarbeitungsbefehlen wird ihnen die Konstante »001« und ein weiteres Bit zur Unterscheidung zwischen Laden und Speichern vorangestellt. Zusammen mit den neun Adressbits ist das ein Bit zu viel. Auch die Lade- und Speicherbefehle müssen mit einer Registeradresse weniger auskommen. Deshalb soll die Register-Transfer-Operation in Abb. 7
noch zu definierende Liste von Operationscodes
422
5 Komplexe Beispielentwürfe
5.21 so abgewandelt werden, dass das Operandenregister »OReg_A« in der Operandenholphase (OF) seinen Wert nicht aus dem Registersatz bezieht, sondern mit einer 2-Bit-Konstanten aus dem Befehlswort geladen wird. Die Operation für die Adressrechnung sei eine Addition ohne Auswertung und Bildung des Übertrags. Die beiden Sprungbefehle kommen jeweils mit sechs Parameterbits aus. Der unbedingte Sprung benötigt zwei Registeradressen und der unbedingte Sprung eine Registeradresse und einen Bitvektor für die Auswahl der Sprungbedingung. Für unseren Minimalprozessor soll ein 3-Bit-Auswahlvektor zur Unterscheidung von acht Sprungbedingungen genügen. In der Bittafel für den Befehlssatz in Abb. 5.24 ist dadurch noch Platz für einen weiteren Befehlstyp mit acht Parameterbits. Dieser wird im Beispielentwurf für einen zusätzlichen Lesebefehl, der einen Wert aus einem adressierbaren Zusatzspeicher für Konstanten liest, verwendet. Die Assembler-Notationen für die Lade-, Speicherund Sprungbefehle seien: st @(rb+c2), ra ldd ra, @(rb+c2) ldc ra, @(rb+c2) jmb ra, rb jc_bed ra
–––––-
Schreiben in den Datenspeicher Laden aus dem Datenspeicher Laden aus dem Konstantenspeicher Sprung mit Aufbewahrung der Rückkehradresse bedingter Sprung
Die Symbole »st« (store), »ldd« (load data), »ldc« (load constant), »jmb« (jump) und »jc« (jump conditional) sind die Schlüsselworte zur Kennzeichnung des Befehlstyps. Das Register ra ist für die Ladebefehle das Zielregister und für den Speicherbefehl das Quellregister. Für die Sprünge steht in ra die Adresse für das Sprungziel. Aus dem Inhalt von Register rb und der Konstanten c2 wird bei den Lade- und Speicherbefehlen durch Addition die Datenspeicheradresse berechnet. Für den unbedingten Sprung ist rb der Speicherplatz für die Rücksprungadresse. Die acht Symbole für die Sprungbedingung, die für _bed einzusetzen sind, werden erst später in Abschnitt 5.4.8 festgelegt. Mit der Befehlssatzdefinition bis hierher sind am Ende der Bittafel in Abb. 5.24 noch 26 = 64 Bitvektorwerte ungenutzt, von denen ein Teil für folgende parameterfreie Befehle reserviert werden soll: nop; set_cy; clr_cy; cpl_cy;
––––-
keine Operation, no operation Übertragsbit setzen, set carry Übertragsbit löschen, clear carry Übertragsbit invertieren, complement carry
Der Befehl »nop« (no operation) verbraucht nur Zeit und wird für Warteschleifen und Ähnliches verwendet. Die Befehle »set_cy«, »clr_cy« und »cpl_cy« setzen, löschen und invertieren das Übertragsbit. Sie werden für arithmetische Berechnungen und für Einzelbitmanipulationen benötigt.
5.4 Entwurf eines RISC-Prozessors
423
5.4.5 Konstanten- und Datentypdefinitionen Mit der Befehlssatzdefinition und der Präzisierung der Pipeline-Abläufe ist der informale Teil des Prozessorentwurfs abgeschlossen und es folgt die schrittweise Entwicklung der VHDL-Beschreibung. Diese beginnt damit, alle bereits getroffenen Festlegungen in einem Package in Form von Konstanten- und Datentypdefinitionen zusammenzufassen. Im vergangenen Abschnitt wurden folgende Grundparameter für den Prozessor festgelegt: constant constant constant constant constant
cInstAdrBreite: cInstrBreite : cRegAdrBreite : cOpcodeBreite : cDatenBreite :
natural := 8; natural := 12; natural := 3; natural := 4; natural := 8;
–––––-
Befehlsadressbreite Befehlswortbreite Registeradressbreite Operationscodebreite Datenwortbreite
Die Datentypen für die Register und Bitvektorkonstanten seien Untertypen von std_logic_vector mit der entsprechenden Bitanzahl: subtype subtype subtype subtype subtype
tInstrAdr tInstr tRegAdr tOpcode tDaten
is std_logic_vector(cInstAdrBreite - 1 is std_logic_vector(cInstrBreite - 1 is std_logic_vector(cRegAdrBreite - 1 is std_logic_vector(cOpcodeBreite - 1 is std_logic_vector(cDatenBreite - 1
downto downto downto downto downto
0); 0); 0); 0); 0);
Für die Blockspeicher sind zwei Datentypen festzulegen, zum einen ein Feldtyp für Befehle als Elemente zur Modellierung des Befehlsspeichers und zum anderen ein Feldtyp mit Daten als Elemente zur Modellierung des Registersatzes, des Datenspeichers und des Konstantenspeichers: type tInstrMem is array (natural range <>) of tInstr; type tDatMem is array (natural range <>) of tDaten;
Die Indexbereiche für die Befehlsbestandteile in dem in Abb. 5.24 definierten Befehlssatz lassen sich in VHDL in Typdefinitionen codieren: is range (2 downto 0) 11 10 9 8 7 6 5 4 3 2 1 0 of std_logic; ra 1 c8 tInstrRB is range (5 downto 3) opc rb ra 0 1 rb ra 0 0 1 0 c2 of std_logic; rb ra 0 0 1 1 c2 tInstrRY is range (2 downto 0) ra rb 0 0 0 1 c2 of std_logic; ra rb 0 0 0 0 1 1 tInstrOp is range (9 downto 6) ra 0 0 0 0 1 0 bed of std_logic; tInstrC8 is range (10 downto 3) of std_logic; tInstrC2 is range ( 7 downto 6) of std_logic; tInstrBed is range ( 5 downto 3) of std_logic;
type tInstrRA type type type type type type
Die Konstanten zur Befehlsunterscheidung sollen gleichermaßen den Wert und den Indexbereich beschreiben:
424
5 Komplexe Beispielentwürfe constant constant constant constant constant constant constant
cInstr_Init: cInstr_Op : cInstr_st : cInstr_ldd : cInstr_ldc : cInstr_jmp : cInstr_jb :
std_logic_vector(11 std_logic_vector(11 std_logic_vector(11 std_logic_vector(11 std_logic_vector(11 std_logic_vector(11 std_logic_vector(11
downto 11) := "1"; downto 10) := "01"; downto 8) := "0010"; downto 8) := "0011"; downto 8) := "0001"; downto 6) := "000011"; downto 6) := "000010";
Die Befehlsworte der parameterfreien Befehle in der Tabelle in Abb. 5.24 unten sind 12-Bit-Konstanten: constant constant constant constant
cInstr_nop : cInstr_set_cy: cInstr_clr_cy: cInstr_cpl_cy:
tInstr := x"000"; tInstr := x"001"; tInstr := x"002"; tInstr := x"003";
Die Befehlscodes von x"004" bis x"03f" sind in Abb. 5.24 ungenutzt und stehen für weitere Befehle, z.B. für die Befehle zur Interrupt-Behandlung, zur Verfügung. Der zu entwerfende Prozessor soll, wenn er einen ungenutzten Befehlscode aus dem Befehlsspeicher liest, einen nop-Befehl ausführen. 5.4.6 Das Rechenwerk Wie bei jedem Prozessor ist das Herz eines RISC-Prozessors sein Rechenwerk (engl. ALU arithmetic logic unit). Das Rechenwerk ist eine kombinatorische Schaltung, die, gesteuert durch ein Operationsauswahlsignal, zwei Operanden teilweise unter Berücksichtigung von Flags miteinander verknüpft. Die Bitbreite des Operationsauswahlsignals ist vier, so dass insgesamt sechzehn Operationen unterschieden werden können. Für jeden Operationscode sei eine Konstante definiert. Die arithmetischen Basisoperationen zur Nachbildung auch aller anderen arithmetischen Operationen sind Addition, Subtraktion, Verdopplung und Halbierung. Bei der Halbierung ist zwischen der Halbierung für vorzeichenfreie und der für Zweierkomplementzahlen zu unterscheiden (vgl. Abschnitt 2.6.7). Unser Prozessor soll weiterhin einen Befehl für die Inkrement- und für die Dekrement-Operation (Addition und Subtraktion einer »1«) und für die Rotation um eine Stelle nach links und eine Stelle nach rechts erhalten: constant constant constant constant constant constant constant constant constant constant
opc_add:tOpcode := "0000"; opc_inc:tOpcode := "0001"; opc_sub:tOpcode := "0010"; opc_dec:tOpcode := "0011"; opc_neg:tOpcode := "0100"; opc_sll:tOpcode := "0101"; opc_srl:tOpcode := "0110"; opc_sra:tOpcode := "0111"; opc_rol:tOpcode := "1000"; opc_ror:tOpcode := "1001";
––––––––––-
Addition R(A):=R(A)+R(B)+cy Inkrement R(A):=R(B)+1 Subtrak. R(A):=R(A)-R(B)-cy Dekrement R(A):=R(B)-1 Negation R(A):=-R(B) Verdopplung R(A):=2*R(B) Halbierung R(A):=(R(B))/2 Halbierung Zweierkompl.(1) Rotation links Rotation rechts
5.4 Entwurf eines RISC-Prozessors
425
((1) – wie opc_srl, nur dass das führende Bit, das in der Zweierkomplementdarstellung das Vorzeichen beschreibt, seinen Wert behält, vgl. Abschnitt 2.6.7). Die Basisoperationen zur Nachbildung aller logischen Operationen sind die bereits definierten Verschiebeoperatoren zur Verdopplung und Halbierung, das bitweise UND, das bitweise ODER, die bitweise Invertierung und das bitweise EXOR: constant constant constant constant
opc_not:tOpcode := "1010"; opc_and:tOpcode := "1011"; opc_or: tOpcode := "1100"; opc_xor:tOpcode := "1101";
––––-
Invert. R(A):=not(R(B)) UND R(A):=R(A) and R(B) ODER R(A):=R(A) or R(B) EXOR R(A):=R(A) xor R(B)
Die beiden verbleibenden freien Codes werden für die Operationen benötigt, die das Rechenwerk bei den Nicht-Verarbeitungsbefehlen ausführen soll. Zum Laden einer Konstanten und zum Speichern der Rücksprungadresse ist der Wert aus dem Operandenregister »OReg_A« in der Ausführungsphase unverändert an das Ergebnisregister weiterzureichen, ohne Flags zu verändern. Dafür wird folgender Operationscode vereinbart: constant sopc_mov:tOpcode := "1110";
Für die Lade- und Speicherbefehle sind in der Ausführungsphase die Werte der Operandenregister zu addieren, ohne die Flags zu verändern. Dafür wird folgender Operationscode vereinbart: constant sopc_add:tOpcode := "1111";
Die Bitanzahl der Operanden und des Ergebnisses ist mit acht festgelegt und hat den Typ »tDaten«. Zur Verkettung arithmetischer Operationen und zur Auswertung als Sprungbedingungen soll unser Prozessor folgende Flags erhalten: • • •
ein Übertrags-Flag »cy« (carry) für die Addition und Subtraktion vorzeichenfreier Zahlen, ein Vorzeichen-Flag »s« (sign) für vorzeichenbehaftete Ergebnisse und das Zero-Flag »z«, das gesetzt wird, wenn das Ergebnis null ist.
Das Vorzeichen- und das Zero-Flag beschreiben zusammen, ob das vorherige Ergebnis größer, größer gleich, gleich etc. null war. Alle drei Flags bilden einen Verbund: type tFlags is record cy: std_logic; –- Uebertrags- (carry-) Flag z: std_logic; –- Zero-Flag s: std_logic; –- Vorzeichen- (sign-) Flag end record;
Das Operationsergebnis, das aus den Flags und dem Ergebnisbyte besteht, ist gleichfalls ein Verbund:
426
5 Komplexe Beispielentwürfe type tErgebnis is record Wert: tDaten; F: tFlags; end record;
⇒WEB-Projekt: P5.4/RISC_pack.vhdl
Das Rechenwerk selbst ist eine kombinatorische Schaltung. Es soll, damit seine Beschreibung sich einfach testen und debuggen lässt, als Package-Funktion beschrieben werden (vgl. Abschnitt 3.4.1). Die Eingabeparameter sind die beiden Operanden, der Ist-Status und der Operationscode. Der Rückgabewert hat den soeben definierten Typ »tErgebnis«. Die Beschreibung besteht im Wesentlichen aus einer Fallunterscheidung nach dem Operationscode und den Beschreibungen der einzelnen Operationen. Dabei ist zu beachten, dass nicht alle Flags von allen Befehlen verändert werden. Unveränderte Flags übernehmen den Eingabewert. Wenn der Wert oder ein Flag des Ergebnisses von einem ungültigen Eingabewert abhängt, wird er gleichfalls ungültig. In der Synthesebeschreibung, die in einer späteren Entwurfsphase zu entwickeln ist, sind die Abfragen auf und die Zuweisungen von »ungültig« zu beseitigen. Die detaillierte Wirkung der einzelnen Operationen ist der Beschreibung zu entnehmen. function ALU(a,b:tByte; F:tStatus; op: tOpcode op tOpCode) return tErgebnis is tDaten variable y: tErgebnis; a tErgebnis variable tmp: std_logic_vector ALU tDaten (tDaten’length downto 0); b variable neg, cy: std_logic; tFlags begin F y.F := F; case Op is –- Operation ohne Statusveränderung when opc_not|opc_and|opc_or|opc_xor|sopc_add|sopc_mov => case op is –- bitweise Logikoperationen when opc_not => y.Wert := not b; when opc_and => y.Wert := a and b; when opc_or => y.Wert := a or b; when opc_xor => y.Wert := a xor b; –- Spezialoperation für Laden, Speichern und Sprünge when sopc_add=> y.Wert := a + b; when sopc_mov=> y.Wert := a; when others => null; end case; –- Operation, die die Statusbits cy, s und z verändern when others => –- Addition und Subtraktion case Op is
5.4 Entwurf eines RISC-Prozessors
427
when opc_add => tmp := (’0’&a) + b + F.cy;8 when opc_inc => tmp := (’0’&b) + ’1’; when opc_sub => tmp := (’0’&a) - b - F.cy; when opc_dec => tmp := (’0’&b) - ’1’; when opc_neg => tmp := (not(’0’&b)) + ’1’; –- Verschiebe- und Rotationsbefehle when opc_sll => tmp := b & ’0’; when opc_srl => tmp := b(0) & ’0’ & b(b’high downto 1); when opc_sra => tmp := b(0) & b(b’high) & b(b’high downto 1); when opc_rol => tmp := b & F.cy; when opc_ror => tmp := b(0) & F.cy & b(b’high downto 1); when others => tmp := (others =>’X’); end case; –- Ergebnis- und Flag-Zuweisungen y.Wert := tmp(tmp’high-1 downto tmp’low); y.F.cy := tmp(tmp’high); y.F.s := tmp(tmp’high-1); if tmp(tmp’high-1 downto tmp’low)=x"00" then y.F.z := ’1’; elsif not is_x(tmp) then y.F.z := ’0’; else y.F.z := ’X’; end if; end case; return y; ⇒WEB-Projekt: P5.4/RISC_pack.vhdl end function;
5.4.7 Test des Rechenwerks Das Funktionsmodell des Rechenwerks ist so umfangreich, dass es einen eigenen Testrahmen benötigt. Für einen gründlichen Test empfiehlt sich ein Pseudo-Zufallstest. Ein Pseudo-Zufallstest wählt die Testbeispiele unvoreingenommen aus [29]. Der hier entwickelte Testrahmen verwendet die »unreine« Funktion »rand(...)« aus dem Package »Tuc.Zufallstest« impure function rand(xVec: std_logic_vector; px: tWeight := 0.0) return std_logic_vector;
für die pseudo-zufällige Auswahl der Eingabewerte für die beiden Operanden und den Operationscode. Die Funktion erzeugt einen Bitvektor vom Typ std_logic_vector mit der Länge des übergebenen Vektors x und beschreibt seine einzelnen Bits mit einer Wahrscheinlichkeit px = 0,003, die als Aufrufparameter übergeben wird, mit »X« und je mit einer Wahrscheinlichkeit von (1 − px ) /2 mit »0« bzw. »1«. Der Wert des Übertragsbits wird mit der Funktion 8
Die hier verwendeten Operatorfunktionen für die Addition und die Subtraktion mit den Operandentypen std_logic_vector und std_logic sind im Package P5.4/RISC_pack.vhdl definiert.
428
5 Komplexe Beispielentwürfe impure function rand(w: tWeight; px: tWeight := 0.0) return std_logic;9
erzeugt, die aufgerufen mit den Parametern w = 0,3 und px = 0,003 mit der relativen Häufigkeit px = 0, 003 den Wert »X«, mit der relativen Häufigkeit ≈ W = 0,3 den Wert »1« und sonst »0« zurückgibt. Der Zustand des PseudoZufallsgenerators hat den Typ type tRandState is array(0 to 1) of positive;
und wird in der globalen Variablen shared variable shared_variable_RandStat: tRandState;
gespeichert, die zum Simulationsbeginn mit zwei positiven ganzen Zahlen initialisiert wird. In dem Testprozess werden in einer Endlosschleife immer zehn Testbeispiele ausgewürfelt, abgearbeitet und die Ergebnisse angezeigt. Nach der manuellen Kontrolle, dass die Ergebnisse richtig sind, ist die Eingabetaste zur Fortsetzung zu betätigen: –- Vereinbarungen im Testprozess variable a, b: tDaten; variable opc: tOpcode; variable Ergebnis: tErgebnis; variable F: tFlags; constant px: tWeight := 0.03; –- Anweisungsfolge im Testprozess shared_variable_RandStat := (1235, 34567); loop write(str(shared_variable_RandStat)); for idx in 1 to 10 loop a := rand(a, px); b := rand(b, px); opc := rand(opc, px); F.cy := rand(0.3, px); Ergebnis := ALU(a, b, F, opc); –- eigentliche Testanweisung write(rechts(str_Opcode(opc),4) & ... (siehe Beispielausgaben) end loop; read; –- Warte auf Eingabetaste end loop; ⇒WEB-Projekt: P5.4/Test_ALU.vhdl
Zu Beginn eines jeden Zehnerblocks wird der Zustand des Pseudo-Zufallsgenerators angezeigt. Diese Ausgabe ist erforderlich, um Tests mit falschen Ausgaben nach der Fehlerbeseitigung mit eben diesem Startwert zu wiederholen [29]. Die nur angedeutete Write-Anweisung für die Testergebnisse stellt den Operationscode symbolisch und alle Zahlenwerte hexadezimal auf dem Bildschirm in der folgenden Tabellenform dar: 9
Der Parameter w beschreibt die Wichtung. Das ist die relative Auftrittshäufigkeit einer »1« (vgl. Abschnitt 3.5.1).
5.4 Entwurf eines RISC-Prozessors
429
Startwert des Pseudo-Zufallsgenerators: 271301075, 1801607646 inc 99, 27 cy:0 => 28:c0 s0 z0 smov 3c, 78 cy:1 => 3c:c1 sU zU XX 94, bc cy:1 => XX:cX sX zX
... Der Programmieraufwand, um die Testergebnisse in einer übersichtlichen, gut kontrollierbaren Form darzustellen, zahlt sich beim Nachrechnen der Testbeispiele und bei der Fehlersuche mehrfach aus. Denn nach der Entwicklung des Testrahmens sind einige Hundert Testbeispiele manuell zu kontrollieren. 5.4.8 Schaltung zur Auswertung der Sprungbedingung Ein bedingter Sprung wertet eine zuvor berechnete Sprungbedingung aus. In einer Hochsprache sind die Verzweigungsbedingungen Größenvergleiche, die sich durch Subtraktionen und logische Operationen so umformen lassen, dass nur noch die Relation des letzten Berechnungsergebnisses zum Wert null auszuwerten ist. Diese und weitere Zusatzinformationen können den Flags entnommen werden. Für die Sprungbedingung wurden in Abb. 5.24 die Bits drei bis fünf des Befehlswortes reserviert. Im Abschnitt 5.4.5 wurde für diesen Teilbitvektor der Datentyp type tInstrBed is range (5 downto 3) of std_logic;
vereinbart. Tabelle 5.1 legt nachträglich die auszuwertenden Sprungbedingungen, deren Symbole in der Assemblernotation und deren Codierung im Befehlswort fest. Tabelle 5.1. Sprungbedingungen des RISC-Prozessors Sprungbedingung
Symbol
Code
Bedeutung
z=1
equ
000
Ergebnis gleich null
z=0
neq
001
Ergebnis ungleich null
cy = 1
cy
010
Übertrag
cy = 0
ncy
011
kein Übertrag
s=1
neg
100
negativ (< 0)
s=0
geq
101
nicht negativ (≥ 0)
s 6= cy
ov
110
Überlauf
s = cy
nov
111
kein Überlauf
Ob ein Sprung auszuführen ist, wird mit einer kombinatorischen Funktion mit den Flags und der Sprungbedingung als Eingabe und einem zweiwertigen Rückgabewert vom Typ boolean bestimmt. Genau wie das Rechenwerk soll diese kombinatorische Funktion als Package-Funktion beschrieben werden:
430
5 Komplexe Beispielentwürfe tBed function Sprung(Bed: tBed; F: tFlags) Bed BOOLEAN return boolean is tFlags Sprung constant ov: boolean := (F.s /= F.cy); F begin case Bed is when "000" => return F.z = ’1’; –- z, Zero-Flag gesetzt when "001" => return F.z = ’0’; –- nz, Zero-Flag zurückgesetzt when "010" => return F.cy = ’1’; –- cy, Uebertragsflag gesetzt when "011" => return F.cy = ’0’; –- ncy, Uebertragsflag null when "100" => return F.s = ’1’; –- neg, negativ when "101" => return F.s = ’0’; –- geq, positiv when "110" => return ov; –- ov, Ueberlauf when "111" => return not ov; –- nov, kein Ueberlauf when others => return false; end case; ⇒WEB-Projekt: P5.4/RISC_pack.vhdl end function;
Das Simulationsmodell benötigt zusätzlich die Information, ob die berechnete Sprungentscheidung gültig ist. Eine Sprungentscheidung ist ungültig, wenn entweder der Bedingungscode oder eines der für die Sprungbedingung auszuwertenden Flags ungültig ist. Auch die Gültigkeitsberechnung soll mit einer Package-Funktion beschrieben werden: function SprungX(Bed: tBed; F: return boolean is begin case Bed is when "000"|"001" => return when "010"|"011" => return when "100"|"101" => return when "110"|"111" => return when others => return true; end case; end function;
tFlags)
Bed F
tBed tFlags SprungX
BOOLEAN
is_x(F.z); is_X(F.cy); is_x(F.s); is_X(F.cy) or is_x(F.s); ⇒WEB-Projekt: P5.4/RISC_pack.vhdl
Jede nicht-triviale Funktion benötigt einen Testrahmen. Für die beiden gerade behandelten Funktionen hat der Testrahmen denselben Aufbau wie für das Rechenwerk (siehe Web-Projekt P5.4/Test_Sprungbedingung.vhdl). 5.4.9 Das Pipeline-Objekt Abbildung 5.25 zeigt die Verarbeitungs-Pipeline aus Abschnitt 5.4.1 erweitert um die Datenpfade, Register, Verarbeitungswerke und Blockspeicher, die für die anderen Befehlstypen zusätzlich benötigt werden. Mit den schwarz unterlegten Nummern sind die Datentypen der Signale, Register und Blockspeicher gekennzeichnet. Die Ein- und Ausgabesignale der Register haben jeweils den Registerdatentyp. Der Daten- und der Konstantenspeicher werden mit dem Typ »tDaten« adressiert.
5.4 Entwurf eines RISC-Prozessors 9 PCount +1
1
InstrMem ROM 10
RegSet Instr
3
x1 RAM a1 y1 w1 y2 a2 11
3
a3
2
0 1 IF normale Verarbeitungsoperationen Erweitungen f¨ ur andere Operationstypen 1 tInstrAdr 2 tInstr
3
3 tRegAdr 4 tOpCode
8
y3
Ctrl EX AReg Y1
Opcode 4 OReg A 5 OReg B 5
Sprungbedingung
9
Ctrl RW AReg Y2
3
5
5
Ergebnis ALU 6 7 EX
431
DatMem w y 5 x 11 5 a KonstMem 5 a y 5 11
RW
OF 5 tDaten 6 tErgebnis
7 tFlags 8 tBed
9 tOpTyp
10 tInstrMem 11 tDatMem
Abb. 5.25. Verarbeitungs-Pipeline des RISC-Prozessors
Die in Abb. 5.25 eingezeichneten Datentypen sind bereits alle bis auf »tOpTyp« definiert. Für Letzteren wird die Definition jetzt entwickelt. In der Ergebnisschreibphase (RW) wird anhand des Wertes im Register »Ctrl_RW«, der vom Typ »tOpTyp« ist, unterschieden zwischen non wr
st ldd ldc
kein Ergebnis schreiben (bedingter Sprung und nop-Befehl), berechnetes Ergebnis in ein Register schreiben (Ergebnisspeicherung für Verarbeitungsbefehle und Speicherung der Rückkehradresse nach einem unbedingten Sprung), Kopieren des Inhalts des adressierten Registers auf den adressierten Datenspeicherplatz (Speicherbefehl), Kopieren des adressierten Datenspeicherinhalts in das adressierte Register (Laden aus dem Datenspeicher) und Kopieren des adressierten Konstantenspeicherinhalts in das adressierte Register (Laden aus dem Konstantenspeicher).
In der Ausführungsphase soll anhand des Wertes im Register »Ctrl_EX«, der auch vom Typ »tOpTyp« ist, unterschieden werden zwischen Übertragsbit setzen (setc), Übertragsbit löschen (clrc), Übertragsbit invertieren (cplc) und den anderen Operationstypen (non, wr, ...), für die in der Ausführungsphase die Operation mit dem Operationscode ausgewählt wird. Insgesamt muss der Datentyp »tOpTyp« acht Operationstypen und den Pseudo-Wert für ungültig darstellen können: type tOpTyp is (XX, non, wr, st, ldd, ldc, setc, clrc, cplc);
Die Pipeline-Register bilden zusammen einen Verbund:
432
5 Komplexe Beispielentwürfe type tPipeline is record PCount : tInstrAdr; Instr : tInstr; OReg_A, OReg_B : tDaten; Opcode : tOpcode; ARegY1, ARegY2 : tRegAdr; Ergebnis : tErgebnis; Ctrl_EX, Ctrl_RW: tOpTyp; end record;
5.4.10 Die Übergangsfunktion für einen Pipeline-Schritt Das Funktionsmodell für einen einzelnen Pipeline-Schritt soll als Prozedur mit dem Pipeline-Objekt, dem Registersatz und dem Datenspeicher als les- und veränderbare Signale und dem Befehlsspeicher und dem Konstantenspeicher als nur lesbare Konstanten beschrieben werden: procedure PipeStep(signal pp:inout tPipeline; InstrMem: tInstrMem; signal RegSet: inout tDatMem(0 to 2**cRegAdrBreite-1); signal DatMem: inout tDatMem; KonstMem: tDatMem) is begin
Das Verhaltensmodell, das alle Befehle zusammenfasst, besteht aus den Signalzuweisungen für die einzelnen Pipeline-Phasen mit den entsprechenden Fallunterscheidungen nach Befehlstyp. Unser Modell soll mit den Registerzuweisungen für Verarbeitungsbefehle aus Abb. 5.19 beginnen und diese in den nachfolgenden Fallunterscheidungen bei Bedarf überschreiben. Ausgenommen seien die Zuweisung an das Ergebnisregister in der Ausführungsphase (EX) und die Schreiboperationen in die Blockspeicher in der Ergebnisschreibphase (RW). Diese Zuweisungen werden später in den Fallunterscheidungen für die EX- bzw. RW-Phase mit berücksichtigt. Dem Kontrollregister »Ctrl_EX« wird dazu für Verarbeitungsbefehle der Wert »wr« für »Ergebnis in den Registersatz schreiben« zugewiesen: –- Befehlszähler weiterschalten pp.PCount <= pp.PCount + 1; –- IF: Befehlswort lesen pp.Instr <= read(InstrMem, pp.PCount)); –- OF: Operanden lesen, Operationscode, Zieladresse und –Kontrollwert für Verarbeitungsbefehle weitergeben pp.OReg_A <= read(RegSet, pp.Instr(tInstrRA’range)); pp.OReg_B <= read(RegSet, pp.Instr(tInstrRB’range)); pp.OpCode <= pp.Instr(tInstrOp’range); pp.ARegY1 <= pp.Instr(tInstrRY’range); pp.Ctrl_EX <= wr;
5.4 Entwurf eines RISC-Prozessors
433
–- EX: Zielregisteradresse und Kontrollwert weitergeben –- pp.Ergebnis<=ALU(...); => Fallunterscheidung EX-Phase pp.ARegY2 <= pp.ARegY1; pp.Ctrl_RW <= pp.Ctrl_EX; –- Ergebnis schreiben => Fallunterscheidung RW-Phase
(tInstr...’range – Indexbereich des Befehlswortes, in dem der entsprechende Befehlsbestandteil steht). Die hier verwendete Read-Funktion und die in der Prozedur »PipeStep(...)« weiter unten verwendete Write-Prozedur wurden in Abschnitt 3.4.3 als allgemeine Bearbeitungsmethoden für Blockspeicher entwickelt. Befehlsabhängige Datenflussänderungen in der OF-Phase Bei Abarbeitung eines Befehls zur Initialisierung eines Registers ra mit einer Konstanten c8 init ra, c8
wird in Abweichung vom Standarddatenfluss dem ersten Operanden-Register »OReg_A« eine Konstante aus dem Befehlswort und dem Operationscoderegister der Spezialoperationscode »für unverändert weiterleiten und Flags nicht verändern« zugewiesen (vgl. Abschnitt 5.4.2): if pp.Instr(cInstr_Init’range)=cInstr_Init then pp.OReg_A <= pp.Instr(tInstrC8’range); pp.Opcode <= sopc_mov;
Für Verarbeitungsbefehle ist nichts zu verändern: elsif pp.Instr(cInstr_Op’range)=cInstr_Op then null;
Die Lade- und Speicherbefehle unterscheiden sich in der OF-Phase darin, dass in das Operandenregister »OReg_A« die 2-Bit-Konstante aus dem Befehlswort, verlängert um führende Nullen, in das Operationscode-Register der Spezialoperationscode für Adressrechnung und in das Kontrollregister »Ctrl_Ex« das Symbol für die Art des Datenaustausches geschrieben wird: elsif pp.Instr(cInstr_st’range)=cInstr_st then pp.OReg_A <= "000000" & pp.Instr(tInstrC2’range); pp.OpCode <= sopc_add; pp.Ctrl_EX <= st; elsif pp.Instr(cInstr_ldd’range)=cInstr_ldd then pp.OReg_A <= "000000" & pp.Instr(tInstrC2’range); pp.OpCode <= sopc_add; pp.Ctrl_EX <= ldd; elsif pp.Instr(cInstr_ldc’range)=cInstr_ldc then pp.OReg_A <= "000000" & pp.Instr(tInstrC2’range); pp.OpCode <= sopc_add; pp.Ctrl_EX <= ldc;
434
5 Komplexe Beispielentwürfe
Der unbedingte Sprung mit der Aufbewahrung der Rückkehradresse überschreibt die Zuweisung an den Befehlszähler mit dem aus dem Registersatz gelesenen Wert, übernimmt in das Operandenregister »OReg_A« den inkrementierten Befehlszählerstand und schreibt in das Operationscoderegister den Spezialoperationscode »für unverändert weiterleiten und Flags nicht verändern« (vgl. Abschnitt 5.4.3): elsif pp.Instr(cInstr_jmp’range)=cInstr_jmp then pp.PCount <= read(RegSet, pp.Instr(tInstrRB’range)); pp.OReg_A <= pp.PCount + ’1’; pp.OpCode <= sopc_mov;
Ein bedingter Sprung überschreibt die Standardzuweisung an den Befehlszähler nur bei erfüllter Bedingung. Wenn die Sprungbedingung ungültig ist, wird auch der Wert des Befehlszählers ungültig. Über die Kontrollregister wird an die RW-Phase der Code »non« für kein Ergebnis schreiben weitergegeben: elsif pp.Instr(cInstr_jb’range)=cInstr_jb then if SprungX(pp.Instr(tInstrBed’range), pp.Ergebnis.F) then pp.PCount <= (others=>’X’); elsif Sprung(pp.Instr(tInstrBed’range), pp.Ergebnis.F) then pp.PCount <= read(RegSet, pp.Instr(tInstrRB’range)); end if; pp.Ctrl_EX <= non;
Die parameterfreien Befehle für »keine Operation« sowie für das Setzen, Löschen und Invertieren des Übertragsbits schicken nur ihre Kontrollworte an die EX- und die RW-Phase weiter. Als Nop-Befehle werden außer dem reservierten Befehlswort x"000" auch alle ungenutzten gültigen Befehlscodes interpretiert: elsif elsif elsif elsif
pp.Instr=cInstr_set_cy pp.Instr=cInstr_clr_cy pp.Instr=cInstr_cpl_cy not is_x(pp.Instr);
then then then then
pp.Ctrl_EX <= setc; pp.Ctrl_EX <= clrc; pp.Ctrl_EX <= cplc; pp.Ctrl_EX <= non;
Bei einem ungültigen Befehlswort werden alle Registerinhalte, die aus diesem gebildet werden, ungültig: else pp.OReg_A <= (others=>’X’); pp.OReg_B <= (others=>’X’); pp.Opcode <= (others=>’X’); pp.ARegY1 <= (others=>’X’); pp.Ctrl_EX <= XX; end if;
Datenfluss in der Ausführungsphase (EX) In der Ausführungsphase (EX) wird die Zuweisung an das Ergebnisregister vom Kontrollwert im Register »Ctrl_EX« gesteuert. Für die Kontrollwerte zur Manipulation des Übertrags-Flags wird nur dieses verändert. Für den
5.4 Entwurf eines RISC-Prozessors
435
Kontrollwert für »kein Ergebnis erzeugen« bleibt der Wert des Ergebnisregisters einschließlich der Flags unverändert. Bei einem ungültigen Kontrollwert wird das Ergebnis ungültig. In allen anderen Fällen übernimmt das Ergebnisregister den Wert der Funktion »ALU(...)«, die diesen aus den Werten der Operanden-, der Flags- und des Operationscode-Registers bildet (vgl. Abschnitt 5.4.6). case pp.Ctrl_EX is when setc => pp.Ergebnis.F.cy <= ’1’; when clrc => pp.Ergebnis.F.cy <= ’0’; when cplc => pp.Ergebnis.F.cy <= not pp.Ergebnis.F.cy; when non => null; when XX => pp.Ergebnis <= (Wert =>(others=>’X’), F => (cy=>’X’, s=>’X’, z=>’X’)); when others => pp.Ergebnis<=ALU(pp.OReg_A,pp.OReg_B,pp.Ergebnis.F,pp.Opcode); end case;
Datenfluss in der Ergebnisschreibphase (RW) In der Ergebnisschreibphase (RW) wird das berechnete Ergebnis normaler Operationen in den Registersatz geschrieben. Für die Lade- und Speicheroperationen dient der Ergebniswert als Adresse für den Datenspeicherplatz, mit dem Daten auszutauschen sind. Ein ungültiger Kontrollwert invalidiert das adressierte Register im Registersatz und den adressierten Datenspeicherplatz. Für alle anderen Kontrollwerte wird weder der Registersatz noch der Datenspeicher beschrieben (Null-Anweisung): case pp.Ctrl_RW is when wr =>write(RegSet, pp.ARegY2, pp.Ergebnis.Wert); when st =>write(DatMem,pp.Ergebnis.Wert, read(RegSet,pp.ARegY2)); when ldd=>write(RegSet,pp.ARegY2, read(DatMem,pp.Ergebnis.Wert)); when ldc=>write(RegSet,pp.ARegY2,read(KonstMem,pp.Ergebnis.Wert)); when XX =>write(RegSet, pp.ARegY2, (others=>’X’)); write(DatMem, pp.Ergebnis.Wert, (others=>’X’)); when others=>null; end case;
Mit der Beschreibung der RW-Phase endet die Prozedur zur Beschreibung der Registerzuweisungen für einen einzelnen Pipeline-Schritt: end procedure;
Die gesamte Prozedurbeschreibung steht im Package P5.4/RISC_pack.vhdl. 5.4.11 Test der Übergangsfunktion Ein komplexes Funktionsmodell benötigt einen Testrahmen, der die Ergebnisse in einer leicht zu kontrollierenden Weise visualisiert. Der hier zu entwickelnde Testrahmen benötigt dafür Hilfsfunktionen zur Erzeugung übersichtlicher
436
5 Komplexe Beispielentwürfe
Textdarstellungen für den Inhalt des Befehlsspeichers, den Inhalt der Datenspeicher, den Inhalt des Registersatzes und den Zustand der Pipeline. Diese Funktionen sind im Web-Projekt im Package »P5.4/RISC_Str_pack.vhdl« beschrieben. Eine übersichtliche Darstellung des Befehlsspeicherinhalts erfordert praktisch einen Disassembler, der die Befehlsworte zurück in ihre Assemblernotation übersetzt und mit vorangestellter Befehlsspeicheradresse in Tabellenform darstellt. Die Funktion zur Erzeugung dieser Textdarstellung ist wie folgt definiert: function str_array(x: tInstrMem) return tStringArray;
Der Rückgabetyp »tStringArray«, definiert im Package »tAusgabe«, ist ein eindimensionaler Feldtyp mit Elementen vom Typ »tString« zur Darstellung und Bearbeitung mehrzeiliger Ausgabetexte. Die Funktion »str_array(...)« wandelt den Befehlsspeicherinhalt in ein mehrspaltiges Assembler-Listing um: 0:init 1:init 2:init 3:init 4:init 5:init 6:init 7:init
r0,23 r1,34 r2,45 r3,56 r4,67 r5,78 r6,89 r7,a0
8:clr_cy 9:add r1,r0 10:sub r2,r3 11:sub r1,r1 12:nop 13:nop 14:inc r2,r1 15:nop
16:nop 17:st @(r2+0),r0 18:st @(r2+1),r1 19:st @(r2+2),r2 20:st @(r2+3),r3 21:jmp r3,r2 22:nop 23:nop
24:nop 25:nop 26:nop 27:nop 28:nop 29:nop 30:nop :
Bei der Übergabe eines Teilspeicherbereichs wird nur ein Programmausschnitt dargestellt. Für die Bildschirm- und Dateiausgabe des Datenspeicherinhalts ist derselbe Funktionsbezeichner überladen: function str_array(x: tDatMem) return tStringArray;
Aufgerufen mit einem Objekt vom Typ »tDatenMem« wird eine mehrspaltige Textdarstellung des Datenspeicherinhalts in der Form von Adresse-WertPaaren erzeugt: 0:23 1:ff 2:00 3:56
4:XX 5:XX 6:XX 7:XX
8:XX 9:XX 10:XX 11:XX
12:XX 13:XX 14:XX 15:XX
16:XX 17:XX 18:XX 19:XX
20:XX 21:XX 22:XX 23:XX
24:XX 25:XX 26:XX 27:XX
28:XX 29:XX 30:XX :
Die Bytewerte sind hexadezimal dargestellt. Für die Kontrolle der korrekten Arbeit der Pipeline ist es sehr hilfreich, wenn der Zustand der Pipeline und der Inhalt des Registersatzes in einer Textzeile dargestellt werden. Denn so lässt sich am einfachsten verfolgen, wie die einzelnen Datenobjekte durch die Pipeline wandern und verändert werden. Dafür wurde eine Funktion function str(pp: tPipeline; r: tDatMem(0 to 7)) return string;
5.4 Entwurf eines RISC-Prozessors
437
und eine Konstante für die Tabellenüberschrift definiert, mit denen zusammen eine Ausgabe in der folgenden Form erzeugt werden kann: |PC|| Instr |00||ungueltig |01||init r0,23 |02||init r1,34 |03||init r2,45 |04||init r3,56 |05||init r4,67 |06||init r5,78 |07||init r6,89 |08||init r7,a0 |09||clr cy |0a||add r1,r0 |0b||sub r2,r3 |0c||sub r1,r1 |0d||nop |0e||nop |0f||inc r2,r1 |10||nop |11||nop |12||st @(r2+0),r0 |13||st @(r2+1),r1 |14||st @(r2+2),r2 |15||st @(r2+3),r3 |16||jmp r3,r2 |00||nop |01||init r0,23 |02||init r1,34 |03||init r2,45
||ORA|ORB|ORY1|OpCode|CtrlEX|| Ergebnis |CtrlRW|ORY2||r0 || XX| XX| XX | XX| xx ||XX:cU sU zU| xx | XX ||XX || XX| XX| XX | XX| xx ||XX:cX sX zX| xx | XX ||XX || 23| XX| r0 | smov| wr ||XX:cX sX zX| xx | XX ||XX || 34| XX| r1 | smov| wr ||23:cX sX zX| wr | r0 ||XX || 45| XX| r2 | smov| wr ||34:cX sX zX| wr | r1 ||23 || 56| XX| r3 | smov| wr ||45:cX sX zX| wr | r2 ||23 || 67| XX| r4 | smov| wr ||56:cX sX zX| wr | r3 ||23 || 78| 23| r5 | smov| wr ||67:cX sX zX| wr | r4 ||23 || 89| 34| r6 | smov| wr ||78:cX sX zX| wr | r5 ||23 || a0| 23| r7 | smov| wr ||89:cX sX zX| wr | r6 ||23 || 45| 23| r2 | add| clrc ||a0:cX sX zX| wr | r7 ||23 || 34| 23| r1 | add| wr ||a0:c0 sX zX| clrc | r2 ||23 || 45| 56| r2 | sub| wr ||57:c0 s0 z0| wr | r1 ||23 || 34| 34| r1 | sub| wr ||ef:c1 s1 z0| wr | r2 ||23 || 23| 23| r0 | add| non ||ff:c1 s1 z0| wr | r1 ||23 || 23| 23| r0 | add| non ||ff:c1 s1 z0| non | r0 ||23 || ef| ff| r2 | inc| wr ||ff:c1 s1 z0| non | r0 ||23 || 23| 23| r0 | add| non ||00:c1 s0 z1| wr | r2 ||23 || 23| 23| r0 | add| non ||00:c1 s0 z1| non | r0 ||23 || 00| 00| r0 | sadd| st ||00:c1 s0 z1| non | r0 ||23 || 01| 00| r1 | sadd| st ||00:c1 s0 z1| st | r0 ||23 || 02| 00| r2 | sadd| st ||01:c1 s0 z1| st | r1 ||23 || 03| 00| r3 | sadd| st ||02:c1 s0 z1| st | r2 ||23 || 17| 00| r3 | smov| wr ||03:c1 s0 z1| st | r3 ||23 || 23| 23| r0 | add| non ||17:c1 s0 z1| wr | r3 ||23 || 23| 56| r0 | smov| wr ||17:c1 s0 z1| non | r0 ||23 || 34| 67| r1 | smov| wr ||23:c1 s0 z1| wr | r0 ||23
r1 XX XX XX XX XX 34 34 34 34 34 34 34 34 57 57 ff ff ff ff ff ff ff ff ff ff ff ff
r2 XX XX XX XX XX XX 45 45 45 45 45 45 45 45 ef ef ef ef 00 00 00 00 00 00 00 00 00
r3 XX XX XX XX XX XX XX 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 56 17 17
r4 XX XX XX XX XX XX XX XX 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67 67
r5 XX XX XX XX XX XX XX XX XX 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78 78
r6 XX XX XX XX XX XX XX XX XX XX 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89 89
r7| XX| XX| XX| XX| XX| XX| XX| XX| XX| XX| XX| a0| a0| a0| a0| a0| a0| a0| a0| a0| a0| a0| a0| a0| a0| a0| a0|
Der eigentliche Testrahmen für die Übergangsfunktion ist relativ einfach aufgebaut. Er definiert eine Konstante TP für die Dauer eines Pipeline-Schritts, je eine Konstante für den Befehlsspeicher und den Konstantenspeicher und je ein Signal für das Pipeline-Objekt, den Registersatz und den Datenspeicher. Aus der Zeitkonstanten wird in den nachfolgenden Entwurfsschritten die Taktperiode. Der Befehlsspeicher wird mit einer Befehlsfolge initialisiert und der Konstantenspeicher mit Beispielkonstanten. Der Pipeline-Zustand, der Inhalt des Registersatzes und der Inhalt des Datenspeichers sind zum Simulationsbeginn bis zu ihrer Initialisierung ungültig: constant tp: delay_length := 10 ns; signal pp: tPipeline; signal RegSet: tDatMem(0 to 7); constant InstrMem: tInstrMem(0 to 30) := ( 0=> ’1’& x"23" & "000", –- init r0,23 ...); signal DatMem: tDatMem(0 to 30); constant KonstMem: tDatMem(0 to 30) := (0=> x"1a", others=>x"00");
Der Prozess des Testrahmens initialisiert den Befehlszähler des PipelineObjekts mit dem Wert null und ruft in einer Schleife die Prozedur mit der Übergangsfunktion des Prozessors immer wieder auf. Vor der Schleife wird einmal der Text mit der Tabellenüberschrift und nach jedem Prozeduraufruf eine Tabellenzeile ausgegeben. Die Warteanweisungen für eine Taktperiode
438
5 Komplexe Beispielentwürfe
beschreiben den Zeitablauf der Verarbeitung und sind erforderlich, damit die Signalzuweisungen wirksam werden. Die abschließende Warteanweisung ohne Weckbedingung beendet die Simulation: write(" "); write(cProcStatTxt); –- Tabellenüberschrift pp.PCount <= x"00"; wait for tp; for idx in 0 to 26 loop PipeStep(pp, InstrMem, RegSet, DatMem, KonstMem); write(str(pp, RegSet)); wait for tp; end loop; wait;
Unser Exkurs zum Thema Prozessorentwurf soll an dieser Stelle enden. Der nächste Schritt wäre der Entwurf von Beispielprogrammen und deren Simulation als Testbeispiele. Da steckt noch einmal mindestens derselbe Arbeitsaufwand darin wie in den bisherigen Entwurfsschritten. Das Zwischenergebnis, ein getestetes Simulationsmodell für einen Prozessor, ist eine Funktionsbeschreibung aus Registern und Register-Transfer-Funktionen. Der weitere Entwurf – die Umsetzung in eine synthesefähige Beschreibung – wurde bereits an mehreren kleineren Beispielen vorgeführt. 5.4.12 Zusammenfassung Ein Prozessor ist eine komplexe digitale Schaltung, deren Funktionsmodell in mehreren Schritten entwickelt wird. Den Engpass im Verarbeitungsfluss bilden wieder die Zugriffe auf den (bzw. die) Blockspeicher. Zuerst werden Skizzen für den Datenfluss der einzelnen Operationen entwickelt. Eine prozessorspezifische Entwurfsaufgabe ist der Entwurf des Befehlssatzes. Das ist ein Puzzle, bei dem der Befehlssatz genau festgelegt und jedem Befehl ein Befehlscode zugeordnet wird. Das gesamte Funktionsmodell ist eine Zusammenfassung und Konkretisierung der Datenflussskizzen der unterstützten Befehle in einer Prozedur. Größere kombinatorische Teilfunktionen – im Beispiel die des Rechenwerks und die zur Berechnung der Sprungentscheidung – wurden in separat testbare Package-Funktionen ausgelagert. Die Register der Pipeline wurden zu einem Datenobjekt zusammengefasst. Die Prozedur beschreibt den Übergang von einem Prozessorzustand zum nächsten und lässt sich wie ein imperatives Programm testen. Die weiteren Entwurfsschritte von der Beschreibung durch Datenobjekte und eine Prozedur für die Register-Transfer-Funktionen bis zu einer synthesefähigen Entwurfseinheit sind nicht prozessorspezifisch und wurden für andere Beispielentwürfe behandelt. Der größte Teil des Entwurfsaufwands entfällt wie immer auf die Entwicklung der Testrahmen, die Tests und die Fehlersuche. Ein moderner synthesebasierter Hardware-Entwurf ist im Grunde ein Programmierprojekt, bei dem Hardware-spezifische Besonderheiten zu beachten sind. Weiterführende und ergänzende Literatur siehe [23, 36].
6 Lösungen zu den Übungsaufgaben
6.1 Modellbildung und Simulation 6.1.1 Entwurf digitaler Schaltungen Lösung zu Aufgabe 1.1 Eine Entwurfseinheit beschreibt eine Schaltung oder einen Testrahmen. Ein Package ist eine Sammlung von Vereinbarungen von Datentypen, Unterprogrammen etc., die es für den Import in Entwurfseinheiten und andere Packages zur Verfügung stellt. Die Beschreibungen der im Package-Kopf vereinbarten Unterprogramme stehen im Package-Körper. Lösung zu Aufgabe 1.2 a) Eine Funktionsbeschreibung zeigt, wie aus den Eingabewerten des Systems die Ausgabewerte gebildet werden. Eine Strukturbeschreibung gibt (zusätzlich) die Zusammensetzung eines Systems aus Teilsystemen wieder. Eine geometrische Beschreibung beschreibt (zusätzlich) die geometrischen Abmessungen und die Anordnung der einzelnen Teilsysteme und der Verbindungen zwischen ihnen. b) Der Testrahmen ist das Rahmenprogramm, das der Simulator ausführt. Es hat weder Anschlusssignale noch Eingabeparameter, d.h., es muss intern die Testeingaben mit normalen Programmiermitteln bereitstellen und die Testergebnisse auswerten. c) Informale Modelle sind in der Regel anschaulicher, dafür aber weder vollständig, noch eindeutig noch simulierbar. Formale Modelle beschreiben einen Sachverhalt eindeutig, sind meist weniger anschaulich, dafür aber simulierbar. In einem Entwurfsprozess wird die Zielfunktion in der Regel zuerst informal spezifiziert und dann schrittweise in eine formale Beschreibung überführt.
G. Kemnitz, Technische Informatik, eXamen.press, DOI 10.1007/978-3-642-17447-6_6, © Springer-Verlag Berlin Heidelberg 2011
440
6 Lösungen zu den Übungsaufgaben
Lösung zu Aufgabe 1.3 entity hallo3x is end entity; architecture v of hallo3x is begin process begin report "Hallo"; wait for 2 ms;
report "Hallo"; wait for 2 ms; report "Hallo"; wait for 2 ms; report "Hallo"; wait for 2 ms; report "Hallo"; wait; end process; end architecture;
Lösung zu Aufgabe 1.4 Zulässige Bezeichner sind »bereit« und »tTyp«. Unzulässig sind »To« (Schlüsselwort), »3dimensional« (beginnt mit einer Ziffer) und »_escape« (beginnt mit einem Unterstrich). 6.1.2 Funktion, Struktur und Simulation Lösung zu Aufgabe 1.5 ϕ(b0 ) ϕ(b1 ) ϕ(b2 ) ϕ(b3 ) b = b3 b 2 b 1 b 0
0001
0110 0111
1001 1100
0011
ung¨ ultig
0101 t
Lösung zu Aufgabe 1.6 a) Aus Gleichung 1.1 folgt N = 256. b) Aus Gleichung 1.2 folgt n ≥ log2 (25), d.h. mindestens fünf Bit. Lösung zu Aufgabe 1.7 Anweisung
A1
A2
A3
A4
A5
Signal a Variable b
0 1
1 1
1 2
1 3
2 3
Das Signal hat immer erst nach der Wartezeit den um »1« erhöhten Wert. In Zeile A4 wird die in Zeile A1 zugewiesene Signaländerung überschrieben, weil diese noch schwebend ist. Die Variable hat nach jeder Zuweisung den erhöhten Wert (siehe Web-Projekt P1.2/VarSigWirkung.vhdl).
6.1 Modellbildung und Simulation
441
Lösung zu Aufgabe 1.8 a) Berechnungsbaum
b) Signalflussplan
( ∧ )
( ∧ )
( ∧ )
( ∧ )
( ∧ )
x1 x2 x3
x1 x2 x3
x1 x2 x3
& & &
Für die gleichberechtigten UND-Verknüpfungen ist für den Berechnungsbaum eine Abarbeitung von links nach rechts unterstellt. Im Signalflussplan sind die gleichberechtigten UND-Verknüpfungen und die anschließenden Negationen jeweils zu NAND-Gattern mit drei Eingängen zusammengefasst. c) Siehe Web-Projekt P1.2/LsgSchaltungAusdruck.vhdl: –- Vereinbarungen der Entwurfseinheit signal x: std_logic_vector(3 downto 1) ; signal y: std_logic; –- nebenläufige Anweisungen der Entwurfseinheit y <= not(x(1) and x(2) and not x(3)) and not(not x(1) and not x(2) and not x(3)) after 2 ns;
Lösung zu Aufgabe 1.9 –- a) Strukturbeschreibung entity S2B is port(x1,x2,x3,x4: in std_logic; y: out std_logic); end entity; architecture stk of S2B is signal z: std_logic; begin G1: entity work.Gxx(fkt) port map (a=>x1, b=>x2, c=>x3, d=>z); G2: entity work.Gxx(fkt) port map (a=>z, b=>x3, c=>x4, d=>y); end architecture; ⇒WEB-Projekt: P1.2/LsgGxx.vhdl
–- b) Testrahmen entity Test_S2B is end entity; architecture t1 of Test_S2B is signal x: std_logic_vector( 4 downto 1); signal y: std_logic; begin TObj: entity work.S2B(stk) port map (x1=>x(1), x2=>x(2), x3=>x(3), x4=>x(4), y=>y); process begin wait for 5 ns; x<="0000"; wait for 5 ns; x<="0001"; wait for 5 ns; x<="0011"; wait for 5 ns; x<="0111"; wait; end process; end architecture;
442
6 Lösungen zu den Übungsaufgaben
6.1.3 Laufzeittoleranz Lösung zu Aufgabe 1.10 a) Die vier Gatter und die drei Eingabesignale lassen sich am kürzesten mit nebenläufigen Signalzuweisungen beschreiben (Abb. 6.1 a). Für den Eingabewert x0 = x2 = 0 und x1 =1 stellen sich spätestens nach 5 ns die Zwischenwerte z0 = z1 = 0 und z2 = 1 und spätestens nach 6,5 ns der Ausgabewert y = 1 ein. Diese sind laut Aufgabenstellung als Anfangswerte zu verwenden. b) Siehe Abb. 6.1 b. c) Das ist die fallende Flanke am Eingang x1 . Unter der Bedingung x0 = x2 = 1 pflanzt sie sich als steigende Flanke auf z1 und als stärker verzögerte fallende Flanke auf z2 fort und verursacht am Ausgang y eine fallende Flanke gefolgt von einer steigenden Flanke. –- Vereinbarungen der Entwurfseinheit signal x0, x2, z0, z1: std_logic := ’0’; signal x1, z2, y: std_logic := ’1’; –- nebenläufige Anweisungen der Entwurfseinheit G1: z0 <= not x1 after 1 ns; G2: z1 <= x0 and z0 after 2 ns; G3: z2 <= z0 xnor x2 after 4 ns; G4: y <= z1 nand z2 after 1.5 ns; Ex0: x0 <= ’1’ after 1 ns; Ex1: x1 <= ’0’ after 2 ns; a) Ex2: x2 <= ’1’ after 10 ns;
x0 x1 x2
1 0 1 0 1 0
z0 z1 z2 y
1 0 1 0 1 0 1 0
0 2 4 6 8 10
tsim
b) ⇒Web-Projekt: P1.3/LsgAfgHazard.vhdl
Abb. 6.1. Lösung zu Aufgabe 1.10
Lösung zu Aufgabe 1.11 Die beiden ersten Wert-Zeit-Tupel der ersten Signalzuweisung sind bis zum Zeitpunkt der zweiten Signalzuweisung abgearbeitet und aufgezeichnet. Die anderen Wert-Zeit-Tupel sind noch schwebend. Die zweite Signalzuweisung weist in allen drei Fällen eine »1« für den Simulationszeitpunkt tsim = 17 ns zu. a) Mit dem Standardverzögerungsmodell werden alle schwebenden Wertzuweisungen gelöscht. Da aber bereits zum Zeitpunkt tsim = 13 ns eine »1« und bis tsim = 17 ns kein anderer Wert zugewiesen wird, übernimmt die Änderung nach »1« den Änderungszeitpunkt tsim = 13 ns (Abb. 6.2 a). b) Mit dem Transportverzögerungsmodell bleiben alle schwebenden Zuweisungen für tsim = 5 . . . 17 ns erhalten und werden ausgeführt. Die schwebenden Zuweisungen ab tsim ≥ 17 ns werden gelöscht (Abb. 6.2 b).
6.1 Modellbildung und Simulation
443
c) Bei einer Rückweiszeit von 8 ns werden die schwebenden Zuweisungen für tsim ≥ 17 ns − 8 ns d.h. für tsim ≥ 9 ns gelöscht. Da die Werte der zu löschenden Zuweisungen für die Zeitpunkte 13 ns und 15 ns auch schon »1« sind, übernimmt die neu eingestellte Zuweisung den frühesten Wert 13 ns (Abb. 6.2 c). 1. Zuweisung: nach 10 ns:
0 ns 3 ns 0 X
7 ns 1
8 ns X
10 ns 1
abgearbeitet
2. Zuweisung:
Signalverlauf:
a) b) c)
11 ns 0
13 ns 1
15 ns 1
18 ns X
20 ns 0
schwebend l¨ oschen, aber 13 ns u ¨bernehmen abarbeiten abarbeiten l¨ oschen, aber 13 ns u ¨bern.
l¨ oschen l¨ oschen l¨ oschen
2
16
neue Zuweisung 17 ns 1
a) b) c) 0
4
6
8
10
12
14
tsim in ns
Abb. 6.2. Lösung zu Aufgabe 1.11 (siehe Web-Projekt P1.3/AfgVerzMod.vhdl)
Lösung zu Aufgabe 1.12 Nach dem Simulationsbeginn dauert es zwei, drei, sieben und acht Nanosekunden, bis die Signale z0 , z1 , z2 und y initialisiert sind. Die beiden Eingabeänderungen bewirken jeweils nach 2 ns eine Änderung von z0 nach 1 ns und 3 ns Änderungen von z1 , nach 5 ns und 7 ns Änderungen von z2 und nach insgesamt 2 ns, 4 ns, 6 ns und 8 ns Ausgabeänderungen. Die Schaltung vervierfacht insgesamt die Anzahl der Änderungen am Eingang. x z0 z1 z2 y
1 0 1 0 1 0 1 0 1 0
U U U U
0
2
4
6
8 10 12 14 16 18 20 22 24
tsim in ns
Lösung zu Aufgabe 1.13 a) Abbildung 6.3 links zeigt den extrahierten Signalflussplan und die aus der VHDL-Beschreibung abgelesenen Halte- und Verzögerungszeiten der Gatter.
444
6 Lösungen zu den Übungsaufgaben
b) Die Schaltung hat fünf mögliche Signalpfade, für die in Abb. 6.3 rechts die Halte- und Verzögerungszeiten aufsummiert sind. Die minimale Haltezeit ist 8 ns und die maximale Verzögerungszeit ist 25 ns.
x0 x1
G1 z & 0 G2 z ≥1 1
G3 z ≥1 2
G6 ≥1
y
G4 z G5 & 3 ≥1
x2 z4 x3 x4 Halte- und Verz¨ogerungszeiten der Gatter G1 G2 G3
th.i
td.i
4 ns 5 ns 3 ns
8 ns 6 ns 6 ns
G4 G5 G6
Summe der Halte- und Verz¨ogerungszeiten f¨ ur alle Pfade der Gesamtschaltung P P Pfad th.i td.i
th.i
td.i
4 ns 3 ns 5 ns
7 ns 5 ns 7 ns
G1-G3-G6 G2-G3-G6 G2-G4-G5-G6 G4-G5-G6 G5-G6
12 ns 13 ns 17 ns 12 ns 8 ns
21 19 25 19 12
ns ns ns ns ns
minimale Haltezeit: 8 ns maximale Verz¨ogerungszeit: 25 ns
Abb. 6.3. Schaltung und Signallaufzeiten zu Aufgabe 1.13
6.1.4 Register Lösung zu Aufgabe 1.14 a) Das Zeitfenster, in dem x0 und x1 vor der aktiven Taktflanke gültig sein müssen, ergibt sich aus der Summe der Halte- und Verzögerungszeiten beider Gatter und der Vorhaltezeit des Registers: t1 = th1 + th2 t2 = td1 + td2 + ts Für das Zeitfenster, in dem x2 gültig sein muss, sind nur die Halte- und Verzögerungszeit von G2 und die Registervorhaltezeit zu berücksichtigen: t3 = th2 t4 = td2 + ts Die Zeit t5 ist die Vorhaltezeit ts , t6 die Verzögerungszeit und t7 die Haltezeit des Registers. b) Die Signale sind mit dem Typ std_logic zu vereinbaren. Ein Abtastprozess hat nur den Takt in der Weckliste. Für eine korrekte Übernahme müssen die Eingabesignale x0 bis x2 innerhalb ihres Abtastfensters stabil sein. Die Zeitbedingungen sind aus Aufgabenteil a zu übernehmen. Wenn alle Vorhaltebedingungen erfüllt sind, wird dem Ausgang nach der Haltezeit der Wert »ungültig« und nach der Verzögerungszeit der Wert des logischen Ausdrucks (x0 ⊕ x1 ) x2 zugewiesen, sonst nur nach der Haltezeit der Wert »ungültig«.
6.1 Modellbildung und Simulation
445
signal x0, x1, x2, y, T: std_logic; ... process(T) begin if rising_edge(T) then if x0’delayed(th1+th2)’last_event>td1+td2+ts-th1-th2 and x1’delayed(th1+th2)’last_event>td1+td2+ts-th1-th2 and x2’delayed(th2)’last_event>td2+ts-th2 then y <= ’X’ after thr, (x0 xor x1) and x2 after tdr; else y <= ’X’ after thr; end if; end if; ⇒WEB-Projekt: P1.4/AfgKombReg.vhdl end process;
Lösung zu Aufgabe 1.15 a) Nach Gleichung 1.7 ist die Mindestdauer der Taktperiode die Summe aus der Verzögerungszeit des Eingaberegisters, der maximalen Verzögerungszeit der Verarbeitungsfunktion und der Vorhaltezeit des Ergebnisregisters. Der Signalpfad mit der längsten Verzögerung ist der durch alle drei Verarbeitungsbausteine: tdf ≥ td1 + td2 + td3 = 2,5 ns + n · 1 ns TP ≥ tdr + tdf + ts = 4 ns + n · 1 ns Die maximale Taktfrequenz ist der Kehrwert der minimalen Taktperiode: fT ≤
1 4 ns + n · 1 ns
b) Für n = 16 beträgt die maximale Taktfrequenz fT ≤
1 = 50 MHz 4 ns + 16 · 1 ns
Lösung zu Aufgabe 1.16 Die Bedingungen für die Taktperiode und den Taktversatz lauten nach den Gleichungen 1.10 und 1.11 t∆T12 ≥ tdr + tdf + ts − TP = 1 ns + 11 ns + 2 ns − t∆T12 ≤ thr + thf − tn = 0 ns + 5 ns − 0 ns
1 100 MHz
Der Taktversatz muss im Bereich zwischen 4 ns und 5 ns liegen.
= 4 ns = 5 ns
446
6 Lösungen zu den Übungsaufgaben
6.1.5 Asynchrone Eingabe Lösung zu Aufgabe 1.17 a) Die Taktperiode muss zwischen 20 ms und 200 ms liegen. Der zulässige Wertebereich für den Abtasttakt ist 5 Hz bis 50 Hz. b) Die Tasten verbinden die Eingabesignale mit »0«. Die Pull-Up-Widerstände sorgen bei einer nicht gedrückten Taste für eine »1« (Abb. 6.4 a). Beide Eingabesignale werden einmal zur Ausrichtung und ein zweites Mal, um eine Änderung des Abtastwertes zu erkennen, abgetastet. Die Ausgabesignale sollen aktiv sein, wenn der eigene aktuelle Abtastwert »0«, der eigene vorherige Abtastwert »1« und der aktuelle Abtastwert der anderen Taste »1« ist: y0 = x ¯0’ ∧ x0” ∧ x1’ y1 = x ¯1’ ∧ x1” ∧ x0’ c) Siehe Abb. 6.4 b. signal x, x_del, x_del2, y: std_logic_vector(1 downto 0); signal T: std_logic; ... UV x0’ x0” process(T) & y0 x0 begin if rising_edge(T) then x1 x1’ & y1 x_del <= x; x1” a) T x_del2 <= x_del; end if; ⇒Web-Projekt: P1.5/Lsg2Tasten.vhdl end process; y(0) <= not x_del(0) and x_del2(0) and x_del(1); b) y(1) <= not x_del(1) and x_del2(1) and x_del(0); Abb. 6.4. a) Schaltung zu Aufgabe 1.17 b b) VHDL-Beschreibung zu Aufgabe 1.17 c
Lösung zu Aufgabe 1.18 a) Die über Schalter einstellbare Konstante ist ein unabgetastetes Eingabesignal. Solange die Schalterstellungen nicht verändert werden, funktioniert die Schaltung zuverlässig, aber selbst bei der Betätigung eines einzelnen Schalters kann der Schaltmoment einen so ungünstigen Zeitversatz zum Abtasttakt haben, dass das Register einen ungültigen Summenwert übernimmt. b) Um das Abtasten eines ungültigen Summenwertes auszuschließen, ist zusätzlich das Schaltersignal vor der Addition abzutasten.
6.1 Modellbildung und Simulation
447
Lösung zu Aufgabe 1.19 a) Die Periode des Abtasttaktes muss so gewählt werden, dass das Gültigkeitssignal g mindestens einmal, wenn es null ist, mindestens einmal, wenn es eins ist, und maximal einmal während der Signalwechsel, wenn es ungültig ist, abgetastet wird: (TP < tg0 ) ∧ (TP < tg1 ) ∧ (TP > tgx ) Für die Taktfrequenz des Abtasttaktes als der Kehrwert der Taktperiode muss entsprechend gelten 1 1 1 fT > ∧ fT > ∧ fT < tg0 tg1 tgx b) Der abgetastete Datenwert ist gültig, wenn der Wert des einmal abgetasteten Gültigkeitssignals »1« und der Wert des zweimal abgetasteten Gültigkeitssignals »0« ist: G = g’ ∧ g¯” 6.1.6 Sequenzielle Schaltungen
Lösung zu Aufgabe 1.20 Abbildung 6.5 a zeigt die Übergangstabelle zum Aufgabenteil a, Abb. 6.5 b den Zustandsgraphen zu Aufgabenteil b und Abb. 6.5 c die Zeitverläufe des Zustands und der Ausgabe zu Aufgabenteil c.
s1 s0 00 00 01 01 10 10 11 a) 11
x 0 1 0 1 0 1 0 1
+ s+ 1 s0 00 01 11 10 01 00 10 11
y 0 1 1 0 1 0 0 1
0/0
00 1/1 01
b)
s1 s0
1/0 0/1
1/0
0/1 x/y
10 0/0 11 1/1
T I x
1 0 1 0 1 0
00 01 11 11 11 10 00 00 00
s1 s0 y c)
1 0
Abb. 6.5. Lösung zu Aufgabe 1.20
448
6 Lösungen zu den Übungsaufgaben
Lösung zu Aufgabe 1.21 Der Automat benötigt für alle darzustellenden Differenzen N1 − N0 ∈ {−3, −2, . . . 3} einen Zustand. Bei einer »0« als Eingabe geht er in einen Zustand nach links und bei einer »1« am Eingang einen Zustand nach rechts. Wenn es keinen linken bzw. rechten Zustand mehr gibt, bleibt der Automat in seinem Zustand und aktiviert das Fehlersignal. Der Zustandsgraph ist folgender: 0/1
N3
0/0
N2
1/0 N1 − N0
-3
0/0
N1
1/0 -2
N1 Anzahl der Einsen N0 Anzahl der Nullen
0/0
M
1/0 -1 b/a
1/0
E1
0/0 0
1/0
E2
0/0 1
1/0
E3
1/1
0/0 2
3
Ausgabe (0 – ok; 1 – Fehler) Eingabewert
Lösung zu Aufgabe 1.22 a) In Abb. 6.6 a werden die beiden asynchronen low-aktiven Tastensignale abgetastet. Der eingebettete Automat ist ein Moore-Automat mit einer Übergangsfunktion fs , die das abgetastete Morse-Signal und den Zustand auswertet, und einer Ausgabefunktion fy , die die drei Ausgabesignale aus dem Ist-Zustand bildet. Das abgetastete Initialisierungssignal führt invertiert auf den Initialisierungseingang des Registers. b) Ein Punkt ist vier bis sechs und ein Strich zwölf bis achtzehn 50ms-Schritte lang. Für die Klassifizierung der Pulsbreite genügt ein 5-Bit-Zähler, der vor jedem Impuls zurückgesetzt wird und dann die Anzahl der Takte zählt, die das Morsesignal »1« ist. Für den Operationsablauf genügen drei Zustände: »Bereit«, »Zählen« und »Ausgabe«. Im Bereitschaftszustand ist der Zähler zurückgesetzt und die Schaltung wartet auf einen Impuls. Im Zählzustand wird nur gezählt, wenn der Zähler noch nicht seinen Maximalwert erreicht hat. Das verhindert, dass der Zähler bei zu langen Impulsen überläuft und diese dadurch fälschlicherweise als Punkte oder Striche klassifiziert. Nach der fallenden Flanke des Impulses verbleibt die Schaltung für einen Takt im Ausgabezustand, aktiviert den der Pulsbreite zugeordneten Ausgang, löscht den Zähler und geht in den Bereitschaftszustand über (Abb. 6.6 b). c) Der erste Prozess in Abb. 6.6 c beschreibt die Abtastung des Initialisierungs- und des Eingabesignals, der zweite Prozess die Übergangsfunktion und der dritte die Ausgabefunktion. Die Werte der Ausgabesignale werden zuerst in Variablen berechnet und dann den Signalen zugewiesen. Die Bereichsabfrage des Zählstands ist mit einer Auswahlanweisung beschrieben. Für die Zählwerte vier bis sechs aktiviert diese das Ausgabesignal p,
6.1 Modellbildung und Simulation
c)
449
signal nI, I_del, nx, x_del: std_logic; signal z: std_logic_vector(1 downto 0); signal ct: tUnsigned(4 downto 0); ... Morse- Reset–- Eingabeabtastung taste taste U process(T) V begin p x ¯ x fs fy s if rising_edge(T) then err I ¯ I I_del <= not nI; I’ T a) x_del <= not nx; end if; Operationsablaufgraph end process; Z¨ ahlen –- Uebergangsfunktion wenn ct 6= 11111 dann ct <= ct + 1 (1) process(I_del, T) x’= 0 x’= 1 begin Bereit Ausgabe if I_del=’1’ then z <= "00"; ct <= 00000 ct <= 00000 (1) (2) elsif rising_edge(T) then b) case z is when "00" => –- Zustand Bereit ct <= "00000"; if x_del=’1’ then z <= "01"; end if; when "01" => –- Zustand Zählen if ct /= "11111" then ct <= ct+"1"; end if; if x_del=’0’ then z <= "10"; end if; when others => –- Zustand Ausgabe z <= "00"; end case; end if; den Zust¨ anden zugeordnete Ausgaben end process; (1) p <= 0; s <= 0; err <= 0 –- kombinatorische Aufgabefunktion process(z, ct) (2) wenn ct ∈ {00100, ..., 00110} p <= 1; s <= 0; err <= 0; variable vp, vs, verr: std_logic; sonst wenn ct ∈ {01100, ..., 10010} begin p <= 0; s <= 1; err <= 0; vp := ’0’; vs := ’0’; verr := ’0’; sonst if z="10" then p <= 0; s <= 0; err <= 1; case ct is when "00100"|"00101"|"00110" => vp := ’1’; when "01100"|"01101"|"01110"|"01111"|"10000"|"10001"|"10010" => vs := ’1’; when others => verr := ’1’; end case; end if; p <= vp; s <= vs; err <= verr; end process; ⇒Web-Projekt: P1.6/Morse.vhdl
Abb. 6.6. Lösung zu Aufgabe 1.22
450
6 Lösungen zu den Übungsaufgaben
für die Zählwerte zwölf bis achtzehn das Ausgabesignal s und sonst das Fehlersignal.
6.2 Synthese und Logikoptimierung 6.2.1 Register-Transfer-Synthese Lösung zu Aufgabe 2.1 a) Die vorgegebenen Prozesse beschreiben insgesamt fünf 8-Bit-Register. Die Register mit den Zustandssignalen L, M und N übernehmen die Eingabedaten mit der steigenden Flanke des Taktes c0 bzw. c1 . Für die beiden mit dem letzten Prozess beschriebenen Register erfolgt die Übernahme mit der fallenden Flanke von c1 . Registereingang
K
L
M
L
P
Registerzustand
L
M
N
P
Q
Bitbreite Taktsignal
8
8
8
8
8
c0 ↑
c1 ↑
c1 ↑
c1 ↓
c1 ↓
b) Signalflussplan: L
K c0
8
8
c1
M 8
N 8
P 8
Q 8
Lösung zu Aufgabe 2.2 signal a, b, y: tUnsigned (3 downto 0); signal s: std_logic_vector (1 downto 0); ... process(s, a, b) variable v0, v1: tUnsigned (3 downto 0); begin if s(0)=’0’ then v0 := a and b;
v1 := a; else v0 := a or b; v1 := not a; end if; if s(1)=’0’ then y <= v0; else y <= v1 + b; end if; end process; ⇒WEB-Projekt: P2.1/AfgSyntALU.vhdl
Die Signale a, b und y sind laut Abb. 2.27 4-Bit-Vektoren vom Typ »tUnsigned«. Die Ausgabewerte der beiden linken Multiplexer sollen als Zwischenergebnisse in Variablen vom selben Typ berechnet werden. Die Variable v0 übernimmt entweder die bitweise UND- oder die bitweise ODER-Verknüpfung der beiden Eingabevektoren und die Variable v1 entweder den direkten oder den negierten Eingabewert. Der zweite Multiplexer wählt zwischen der bitweisen Logikverknüpfung und der Summe mit dem zweiten Operanden aus.
6.2 Synthese und Logikoptimierung
451
Lösung zu Aufgabe 2.3 Die Zähloperationen benötigen das Package »Tuc.Numeric_Synth«. Die Addition und die Subtraktion der Konstanten eins werden mit dem »+«- und den »−«-Operator, die Multiplexer durch zwei Fallunterscheidungen und das Register durch eine Signalzuweisung im Abtastprozess beschrieben. Im Fall E = 1 wird nichts an y und sonst für R = 0 der Wert y + 1 und für R = 1 der Wert y − 1 zugewiesen. Die vorzeichenbehaftete Konstante mit dem Wert eins ist, wie man bei der Simulation bemerken wird, nicht der 1-Bit-Vektor »1«, sondern der 2-Bit-Vektor »01«. Da am Register der negierte Takt T¯ angeschlossen ist, muss die Übernahme mit der fallenden Taktflanke erfolgen. signal y: tSigned (7 downto 0); signal R, E, I, T: std_logic; ... process(T, I) begin if I=’1’ then y <= "00000000"; elsif falling_edge(T) then if E=’1’ then
if R=’1’ then y <= y - "01"; else y <= y + "01"; end if; end if; end if; end process; ⇒WEB-Projekt: P2.1/AfgSynthVRZ.vhdl
Lösung zu Aufgabe 2.4 Die beiden Register »Reg1« und »Reg2« sollten nicht zu einem Prozess zusammengefasst werden, weil sie nicht gemeinsam initialisiert werden. Das Gatter »G« lässt sich mit als Teil des Abtastprozesses von »Reg2« beschreiben. signal T, I, x, x_del, y: std_logic;
... Reg1: process(T) begin if rising_edge(T) then x_del <= x; end if; end process;
Reg2: process(T, I) begin if I=’1’ then y <= ’0’; elsif rising_edge(T) then y <= y xnor x_del; end if; end process; ⇒WEB-Projekt: P2.1/AfgSeriParit.vhdl
Lösung zu Aufgabe 2.5 Die Übergangsfunktion ist im Beispiel gleich der Ausgabefunktion. Die einfachste Beschreibung ist ein kombinatorischer Prozess für die Berechnung des Folgezustands, seine nebenläufige Zuweisung an das Ausgabesignal und seine Abtastung in einem Abtastprozess. Für die Nachbildung einer Tabellenfunktion empfiehlt sich eine Auswahlanweisung mit je einem Auswahlfall für jeden Ausgabewert:
452
6 Lösungen zu den Übungsaufgaben signal T, I: std_logic; signal x, s_next, s, y: std_logic_vector(1 downto 0); ... process(s, x) variable sx: std_logic_vector(3 downto 0); begin sx := s & x; –- Berechnung des Folgezustands case sx is –- und des Ausgabewertes when "00"&"00" | "01"&"01" | "10"&"10" => s_next <= "01"; when "01"&"00" | "10"&"01" | "11"&"10" => s_next <= "10"; when "10"&"00" | "11"&"01" | "00"&"10" => s_next <= "11"; when others => s_next <= "00"; end case; end process; y <= s_next;
–- nebenläufige Ausgabezuweisung
process(I, T) –- Abtastprozess variable sx: std_logic_vector(3 downto 0); begin if I=’1’ then s <= "00"; elsif rising_edge(T) then s <= s_next; end if; ⇒WEB-Projekt: P2.1/AfgSynthAutomat.vhdl end process;
Lösung zu Aufgabe 2.6 Die beiden 4-Bit-Signale »acc« und »tmp« beschreiben Register, die beiden Operationen »+« und »-« einen Addierer und einen Subtrahierer und die Auswahlanweisung einen Multiplexer. Die Null-Anweisung im Sonst-Zweig ist durch eine Übernahme des Ist-Wertes nachzubilden. + x
tmp 4
4
op T
a a−b b
4 4 4 4
11 01 10 00
acc 4
4
y
2
6.2.2 Schaltungsvereinfachung auf Basis der Schaltalgebra Lösung zu Aufgabe 2.7 Die Schaltung hat folgende logische Funktion: y = x2 x ¯ 1 x0 ∨ x2 x1
6.2 Synthese und Logikoptimierung
453
Der Ausdruck auf der rechten Zuweisungsseite wird doppelt negiert und anschließend mit Hilfe der ersten De Morgan’schen Regel aus Tabelle 2.1 in die NAND-NAND-Form umgestellt: y = x2 x ¯1 x0 ∨ x2 x1 = x2 x ¯ 1 x0 ∧ x2 x1 Die resultierende Schaltung ist: x0 x1
& &
y
&
x2
Lösung zu Aufgabe 2.8 In beiden KV-Diagrammen lassen sich die Einsen jeweils mit vier Rechtecken abdecken: x2
x2
d
1 x0
a
b
1a 1
1
1
0
1 c
0
0
0
0
1
0
1b 0
d
a)
1
0
x1
a: b: c: d:
x ¯3 x ¯1 x2 x ¯1 x ¯3 x2 x0 x3 x2 x ¯0
x3
x0
b)
c
1
a
1 0
b
1
1
1
0
-
-
-
-
0
0
1
1d
x1
a: b: c: d:
x ¯1 x ¯0 x3 x2 x ¯3 x ¯2 x ¯1 x3 x1
x3
Die minimierten Ausdrücke lauten: a) x ¯3 x ¯ 1 ∨ x2 x ¯1 ∨ x ¯3 x2 x0 ∨ x3 x2 x ¯0 b) x ¯1 x ¯ 0 ∨ x3 x2 ∨ x ¯3 x ¯2 x ¯ 1 ∨ x3 x1 Lösung zu Aufgabe 2.9 a) Jedes Rechteck wird durch eine Konjunktion beschrieben, die genau dann »1« ist, wenn die Eingabe für eines der Felder innerhalb des Rechtecks ausgewählt ist. Wenn ein Feld ausgewählt wird, das mit mehreren Rechtecken abgedeckt ist, werden mehrere der ODER-verknüpften Konjunktionen gleichzeitig »1«. Das ändert nichts an der logischen Funktion. b) Der Ausdruck hat vier freie Variablen. Die Konjunktion x ¯0 hat drei Don’tCare-Stellen und beschreibt im KV-Diagramm einen 8er-Block. Die beiden Konjunktionen x2 x1 und x3 x2 haben zwei Don’t-Care-Stellen und beschreiben 4er-Blöcke. Die drei Blöcke überlagern sich, wie in Abb. 6.7 gezeigt, jeweils paarweise in zwei Feldern.
454
6 Lösungen zu den Übungsaufgaben x2 a
0
x0
vorgegebener Ausdruck: x ¯0 ∨ x2 x1 ∨ x3 x2
a
1 0 1
b
1
1
1
0
1c 0
1
1
0
1
1
1
x1 a
a
Konjunktionen: a: x ¯0 b: x2 x1 c: x3 x2
x3
¨ Uberlagerung a und b ¨ Uberlagerung a und c ¨ Uberlagerung b und c
Abb. 6.7. KV-Diagramm zu Aufgabe 2.9
Lösung zu Aufgabe 2.10 f := x(2) and g := x(0); h := not x(3) i := x(1) and j := not x(3) ... y(3) <= not(a y(4) <= not(b y(5) <= not(a y(6) <= not(j
x(1) and x(0); and not x(2) and x(1); x(0); and not x(2) and not x(1); or or or or
b or f); g); h or i); f);
⇒WEB-Projekt: P2.2/Seg7Dec.vhdl
Lösung zu Aufgabe 2.11 a) In Abb. 6.8 a sind jeweils die Einsen abgedeckt. Die minimierten logischen Ausdrücke lauten s+ ¯1 x ¯ ∨ s0 x ¯ ∨ s¯2 s¯0 x 0 = s2 s
s+ ¯1 s¯0 x ¯ ∨ s2 s¯1 ∨ s¯1 s0 x ∨ s1 s0 x ¯ ∨ s1 s¯0 x 1 = s s+ ¯ ∨ s1 s¯0 x ¯ 2 = s2 s1 ∨ s2 x y = s¯1 x ¯ b) Die ungenutzten (redundanten) Zustände sind »101« und »110«. Die Zustandsübergänge zeigt Abb. 6.8 b. c) Siehe Abb. 6.8 c und Web-Projekt P2.2/LsgAutomat.vhdl. Lösung zu Aufgabe 2.12 Zuerst werden in Abb. 6.9 die gegebenen Minterme in die Quine’sche Tabelle nullter Ordnung nach der Anzahl der enthaltenen Einsen einsortiert. Die Mintermnummer ist jeweils der Dezimalwert des Bitvektors, der den Minterm beschreibt. Dann werden alle Paare von Mintermen gesucht, die sich nur in
6.2 Synthese und Logikoptimierung s2
s+ 2
0
s0
1
0
0
0
-
-
0
0
1
1
0
1
-
-
0
s+ 1 s0 s1
s2 1
1
1
0
0
-
-
1
1
1
0
0
0
-
-
1
x
a)
s2
s+ 0
0
s0 s1
1
0
1
1
-
-
0
1
1
0
0
0
-
-
1
x
y s0 s1
455
s2 1
1
0
0
1
-
-
0
0
0
0
0
0
-
-
0
s1
x
x x x ¯ s2 s¯2 s1 s¯1 s0 s¯0
011 0/0 1/0
000
1/0 0/0
1/0
x/y
0/1
010
100
b)
x
0/1 001
1/0
s2 s1 s0
1/0
111
0/1 0/1 0/0
1/0
redundante Zust¨ ande
1/0
101 0/0 110 1/0
c)
& & &
≥1
x R
& & & & &
≥1
x R
& & &
≥1
x R
&
y
R
T
Abb. 6.8. a) KV-Diagramme b) Zustandsgraph mit redundanten Zuständen c) Schaltung zu Aufgabe 2.11
einer Bitinvertierung unterscheiden. Diese werden abgehakt und in zusammengefasster Form in die Quine’sche Tabelle erster Ordnung eingetragen. Danach werden in der Quine’schen Tabelle erster Ordnung die zusammenfassbaren Konjunktionspaare gesucht, abgehakt und in die Quine’sche Tabelle zweiter Ordnung eingetragen. Mehr Zusammenfassungsmöglichkeiten gibt es nicht. Die nicht abgehakten Konjunktionen – eine in der ersten, vier in der zweiten und zwei in der dritten Tabelle – werden in die Tabelle der Primterme übernommen. Der einzige Primterm, dessen Minterme von anderen Primtermen mit abgedeckt werden, ist »P5«. Der minimierte Ausdruck ist ¯ 4 x3 x1 ∨ x5 x ¯ x ¯ x ¯ x ¯ ∨x x ¯ x ¯ x ¯ x ¯ x5 x ¯ 4 x3 x ¯ 2 ∨ x5 x | {z } | {z } | 4 {z3 1 }0 | 5 3 {z2 1 }0 P1
P2
P3
P4
∨ x5 x3 x2 x1 x ¯0 ∨ x ¯ x x3 x ¯2 x ¯1 x ¯0 | {z } | 5 4 {z } P6
Probe siehe Web-Projekt P2.2/LsgQuine.vhdl.
P7
456
6 Lösungen zu den Übungsaufgaben Quine’sche Tabelle nullter Ordnung
Quine’sche Tabelle erster Ordnung
A1 MNr. Konj. √ 32 100000 1 √ 36 10 0 1 00 √ 2 48 11 0 0 0 0 24 0 11 0 0 0 P7 √ 40 1 0 1 0 0 0 √ 42 101 0 1 0 √ 3 41 1 0 1 00 1 √ 46 101110 √ 4 43 101 0 11 √ 5 62 11111 0 √ 47 10 1 1 1 1
A1 MNr. Konj. 1 32,36 100 00 32,48 1 0000 32,40 10 000 2 40,42 1 0 1 0 0 40,41 1 0 1 0 0 3 42,46 101 10 42,43 101 0 1 41,43 1 0 1 0 1 4 46,62 1 111 0 46,47 1 0 111 43,47 101 11
MNr. Minterm-Nummer(n) A1 Anzahl der Einsen
Quine’sche Tabelle zweiter Ordnung A1 MNr. Konj. 2 40,41,42,43 1 0 1 0 3 42,43,46,47 101 1
P3 P4 P5 √ √ √ √ √
P1 P2 P3 P4 P5 P6 P7
P6 √ √
P1 P2
Tabelle der Primterme 24 32 36 40 41 42 43 46 47 48 62
Konj. Konjunktion √ abgedeckt
Pi Primterm Primterm enthalten
Abb. 6.9. Tabellen zu Aufgabe 2.12
6.2.3 Binäre Entscheidungsdiagramme Lösung zu Aufgabe 2.13 a) Eine EXOR-Verknüpfung ist »1«, wenn die Anzahl der Einsen im Eingabevektor ungerade ist (Abb. 6.10 a). b) Das unreduzierte binäre Entscheidungsdiagramm ist ein Baum, in dem in der obersten Ebene die Variable a, in der mittleren Ebene die Variable b und in der untersten Ebene die Variable c ausgewertet wird. Die Werte der Blätter ergeben sich aus der Wertetabelle (Abb. 6.10 b). c) Wie in Abb. 6.10 b gekennzeichnet, gibt es in der untersten Entscheidungsebene 2 × 2 identische Teilbäume zum Zusammenfassen. Das reduzierte binäre Entscheidungsdiagramm hat zwei Knoten weniger (Abb. 6.10 c). d) Die Entscheidungsknoten, die die Variable c auswerten, liefern einmal den direkten und einmal den invertierten Wert von c und können durch eine Identität und einen Inverter nachgebildet werden. Aus den übrigen drei Entscheidungsknoten werden Multiplexer (Abb. 6.10 d).
a 0 0 0 0 1 1 1 1 a)
b 0 0 1 1 0 0 1 1
c 0 1 0 1 0 1 0 1
y 0 1 1 0 1 0 0 1
a
0 b
0 0 0 b)
c
1
1 0
1
1
b
0 c
1
0
1
0
0
1 = =
c
1
1
0
0
0
c
0 1
0
1 c)
a
y 1
b 11
b 0
c 1 1 c 0
Abb. 6.10. Lösung zu Aufgabe 2.13
1
0
0
1
a
1
0
1 b
0 d)
c
6.2 Synthese und Logikoptimierung
457
Lösung zu Aufgabe 2.14 a) Das Entscheidungsdiagramm liefert im Beispiel für x0 = 1 und x2 = 1 oder für x0 = 0, x1 = 1 und x2 = 1 den Wert »1« und sonst »0« (Abb. 6.11 a). b) Der Entscheidungsknoten, der x2 auswertet, liefert genau den Wert von x2 . Das ist eine Identität. Der Entscheidungsknoten mit x1 liefert für x1 = 0 den Wert »0« und sonst den Wert von x2 . Das entspricht einer UND-Verknüpfung. Der oberste Entscheidungsknoten hat für beide Entscheidungsalternativen einen variablen Eingabewert und ist entsprechend durch einen Multiplexer nachzubilden (Abb. 6.11 b). x2 x1 x0 1 1 1 1 0 1 1 1 0 a) sonst
y 1 1 1 0
x1
b)
x2 x0
&
0 1
y
Abb. 6.11. Lösung zu Aufgabe 2.14
6.2.4 Zahlendarstellung
Lösung zu Aufgabe 2.15 Aufgabenteil a: 345 : 2 = 172 172 : 2 = 86 86 : 2 = 43 43 : 2 = 21 21 : 2 = 10 10 : 2 = 5
Rest: Rest: Rest: Rest: Rest: Rest:
b0 b1 b2 b3 b4 b5
=1 =0 =0 =1 =1 =0
5 : 2 = 2 Rest: b6 = 1 2 : 2 = 1 Rest: b7 = 0 1 : 2 = 0 Rest: b8 = 1 bin¨ ar: 1 0101 1 0 0 1 hexadezimal: 1 5 9 oktal: 5 3 1
Aufgabenteil b: 1023 : 2 = 511 511 : 2 = 255 255 : 2 = 127 127 : 2 = 63 63 : 2 = 31 31 : 2 = 15 15 : 2 = 7
Rest: Rest: Rest: Rest: Rest: Rest: Rest:
b0 b1 b2 b3 b4 b5 b6
=1 =1 =1 =1 =1 =1 =1
7 : 2 = 3 Rest: b7 = 1 3 : 2 = 1 Rest: b8 = 1 1 : 2 = 0 Rest: b9 = 1 bin¨ ar: 11 1111 1111 hexadezimal: 3 F F oktal: 1 7 7 7
458
6 Lösungen zu den Übungsaufgaben
Lösung zu Aufgabe 2.16 a) Stellenkomplement plus »1«: D5D016 + 1 = D5D116 Probe: D5D116 + 2A2F16 = 1∗ 000016 b) Stellenkomplement plus »1«: 1001 01102 + 1 = 1001 01112 Probe: 1001 01112 + 0110 10012 = 1∗ 0000 00002 (∗ – nicht darstellbare führende Ziffer) Lösung zu Aufgabe 2.17 a) Ganzzahlige Hexadezimalkonstante 3f116 mit dem Wert 1009. b) Keine zulässige Zahlenkonstante. In einer Binärkonstanten sind die Ziffern »2« und »3« zwischen den Nummernzeichen nicht erlaubt. c) Keine zulässige Zahlenkonstante. Hexadezimalziffern sind nur bei einer expliziten Kennzeichnung als Hexadezimalzahl erlaubt. d) Ganzzahlige Dezimalkonstante mit dem Wert 2 · 102 = 200.
Lösung zu Aufgabe 2.18 Die drei Bitvektoren sind in der nachfolgenden Tabelle, die denselben Aufbau hat wie die Tabelle in Abb. 2.68, eingetragen und in ihre Bestandteile – Vorzeichenbit, Charakteristik und Mantisse – zerlegt. Für den Bitvektorwert in Aufgabenteil a ist das Vorzeichen positiv und die Charakteristik viel größer als c0 . Zur Mantisse ist die führende »1« vor dem Komma ergänzt. Der Wert ist positiv und sehr groß. Der Bitvektorwert in Aufgabenteil b stellt den Pseudo-Wert für »ungültig« (nan – not a number) dar. In Aufgabenteil c ist das Vorzeichen negativ und die Charakteristik viel kleiner als c0 , so dass der Wert negativ und betragsmäßig sehr klein ist. Bitvektor 31
24 23
16 15
8
Bitnummer 7 0
s c M a) 0 1 01 0 111 1 010001 1 00101000 00010000 0 A
F
F
F
4
F
4
6
5
0
2
0
M = 1,46502016 + AF16 b) 1 1111111 111111 1 1 11111111 11111111 0 M 6= 0 ⇒ Wert f¨ ur ung¨ ultig FF16 c) 1 0 10 0111 1 0 1 00110 00110001 11111110 0 4F16
4
C
6
3
M = 1,4C63FC16
F
C
Wert
+1,46502016 · 2AF16 −7f 16 ≈ +3,588 · 1014 nan/ung¨ ultig −1,4C63FC16 · 24F16 −7f 16 ≈ −4,613 · 10−15
6.2 Synthese und Logikoptimierung
459
6.2.5 Addierer Lösung zu Aufgabe 2.19 a) Die Einsen können mit drei Zweiergruppen abgedeckt werden (Abb. 6.12 a). Die resultierende Schaltung für die Übertragsbildung stimmt mit der aus Abb. 2.70 c überein. b) In Abb. 6.12 b wird zuerst ein unreduziertes geordnetes binäres Entscheidungsdiagramm aus der Wertetabelle konstruiert. Die gleichen Teilbäume in der untersten Ebene werden zusammengefasst. In der abgeleiteten Schaltung sind die drei Knoten mit a oder b als Entscheidungsgröße durch Multiplexer und die beiden untersten Knoten mit c als Entscheidungsgröße durch eine Verbindung bzw. einen Inverter nachgebildet.
c
a
0
0
1
0
1
1
c
0
1
b
0 0
c = ab ∨ ac ∨ bc a b
a)
c
b 0
1
1
c
b)
1 0 0
1 0 1
c
&
0 b
0
a ≥1
1
1
1
& &
a
0
0
b c
0 1
c
1
1
0
0
0
0 1
c
0 1 1
0
a
b 11
1 b 0
c 1 1 c 0
0
1
s
Abb. 6.12. Lösung zu Aufgabe 2.19
6.2.6 Weitere Rechenwerke Lösung zu Aufgabe 2.20 a) Aus der negativen Bewertung der Eingabebits a3 und b3 folgt, dass folgende Teilprodukte negativ zu bewerten sind: a3 b0 , a3 b1 , a3 b2 , a0 b3 , a1 b3 und a2 b3 (Abb. 6.13). Bei den Volladdierern mit einem negativ bewerteten Eingabebit ist jeweils der Summenausgang und bei den Volladdierern mit zwei negativ bewerteten Eingabebits der Übertragsausgang negativ zu bewerten. Bei dem Halbaddierer 1.3 mit einem negativ bewerteten Eingang gäbe es auch die Möglichkeit, den Summenausgang negativ zu bewerten,
460
6 Lösungen zu den Übungsaufgaben
und bei dem Halbaddierer HA3.2 können auch beide Ausgänge negativ bewertet werden. Aber dann würde die Schaltung nicht das gewünschte Ergebnis – p0 bis p6 positiv und p7 negativ bewertet – liefern. b) In Abb. 6.13 oben sind die Signalwerte für das Testbeispiel in Form von Quadraten mit einem Kreis für den Wert null und Quadraten mit einem Strich für den Wert eins eingetragen. Die Tabellen darunter zeigen die Werte an den Addiereranschlüssen, für positiv bewertete Anschlüsse {0, 1} und für negativ bewertete Anschlüsse {−0, −1}. Die Summe der Werte an den Eingängen muss immer gleich dem Wert, der durch die beiden Ausgabesignale s und c dargestellt wird, sein (Probe siehe Web-Projekt P2.1/Test_SignedMult.vhdl, erstes Beispiel). b0
b1
a0
&
a1
&
a2
&
a3
&
b2
&
HA 1.0
s c
&
VA 1.1
s c
&
VA 1.2
s c
&
HA 1.3
s c
b3 p0 p1
&
s c s c
&
HA 3.2
s c
p3
&
VA 2.2
s c
&
VA 3.3
s c
p4
&
VA 2.3 VA 2.4
s c
VA 3.4
s c
p5
& &
VA 3.5
s c
p6 p7
& Beispielwert 1 Beispielwert 0 negativ bewertete Signalbits negativ bewertete Addiereranschl¨ usse x2 x1 x0 c 0 1 0 HA1.0 VA1.1 0 0 0 0 VA1.2 0 -0 0 0 HA1.3 -0 0 -0
s 1 0 -0 0
p2
HA 2.1
x2 x1 x0 c HA2.1 1 0 0 VA2.2 1 -0 0 1 VA2.3 0 0 1 0 VA2.4 -0 -0 0 -0
⇒Web-Projekt: P2.6/SignedMult.vhdl
s 1 -1 1 0
x2 x1 x0 HA3.2 -1 -1 VA3.3 -1 1 -1 VA3.4 -0 0 -1 VA3.5 0 -0 -1
c -1 -1 -1 -1
s 0 1 1 1
Abb. 6.13. Lösung zu Aufgabe 2.20
Lösung zu Aufgabe 2.21 a) Eine vorzeichenbehaftete 4-Bit-Zahl im Zweierkomplement hat einen Wertebereich von −23 = −8 bis 23 − 1 = 7. Die −8 lässt sich jedoch nicht negieren und ist deshalb im gewählten Algorithmus unzulässig.
6.2 Synthese und Logikoptimierung
461
b) Nach der Betragsbildung der Eingabesignale a und b werden nur die drei niederwertigsten Bits zum Multiplizierer geführt. Das Produkt hat sechs Bit. Das negierte Produkt wird vor dem Multiplexer um zwei führende Einsen und das nicht negierte Produkt um zwei führende Nullen auf acht Bit verlängert. Bei gleichen Vorzeichen der Faktoren oder wenn das Ergebnis null ist, wird der um Nullen verlängerte nicht negierte Wert des Produkts und sonst der um Einsen verlängerte negierte Wert an den Ausgang weitergeleitet. a b
4 4
b3
a3
abs abs
3 3
6
*
00 11 neg
= o”00” ==
1
8
0 ≥1
y abs Bildung des Betrags neg Negation des Zahlenwertes
c) VHDL-Beschreibung des Multiplizierers: signal a,b:tSigned(3 downto 0); signal y:tSigned(7 downto 0); ... process(a, b) variable va,vb:tUnsigned(2 downto 0); variable vy: tUnsigned(5 downto 0); begin if a(3)=’1’ then va := tUnsigned(not a(2 downto 0)) + "1"; else va := tUnsigned(a(2 downto 0)); end if; if b(3)=’1’ then vb := tUnsigned(not b(2 downto 0)) + "1"; else vb := tUnsigned(b(2 downto 0)); end if; vy := va * vb; if (a(3) = b(3)) or vy="000000" then y <= "00" & tSigned(vy); else y <= "11" & tSigned((not vy) + "1"); end if; ⇒WEB-Projekt: P2.6/SignedMult.vhdl end process;
Lösung zu Aufgabe 2.22 Es ist folgender Operationsablauf in VHDL zu beschreiben:
462
6 Lösungen zu den Übungsaufgaben busy = 0 ct = 11
sonst
Start = 1 ct <= 00 Subtr <= Divisor & 000 Rest <= Dividend
busy = 1 Kernoperation sonst ct <= ct + 1
Kernoperation f¨ ur busy = 1 1 Subtri slr
a b
(a − b)
y c
Resti
0 q0
q1
T
signal Dividend, Divisor, Rest, q: tUnsigned(3 downto 0); signal Subtr: tUnsigned(6 downto 0); signal T, Start, busy: std_logic; signal ct: tUnsigned(1 downto 0); ... process(T) variable d: tUnsigned(6 downto 0); begin if rising_edge(T) then if busy=’1’ then d := Rest - Subtr; Subtr <= ’0’ & Subtr(6 downto 1); q <= q(2 downto 0) & (not d(6)); ct <= ct + "1"; if d(6)=’0’ then Rest <= d(3 downto 0); end if; if ct="11" then busy <= ’0’; end if ; elsif Start=’1’ then Rest <= Dividend; Subtr <= Divisor & "000"; busy <= ’1’; ct <= "00"; end if ; end if ; ⇒WEB-Projekt: P2.6/SerDiv.vhdl end process;
Lösung zu Aufgabe 2.23 y1: 11110110 y2: 11101101 y3: 00000101
6.3 VHDL im Detail
463
6.3 VHDL im Detail 6.3.1 Imperative Beschreibungsmittel Lösung zu Aufgabe 3.1 A1: Nicht zulässig. Addition einer Variablen vom Typ real mit einer ganzzahligen Konstante. A2: Zulässig, Wert false. A3: Nicht zulässig. Vergleich einer Variablen vom Typ real mit einer ganzzahligen Konstanten. A4: Zulässig. Zuerst wird der Wert von r mit dem ganzzahligen Wert von i potenziert und dann mit 0,5 multipliziert. Das an r zugewiesene Ergebnis ist 32.0 und hat den Typ real. Lösung zu Aufgabe 3.2 Für den Anweisungsteil genügt eine Fallunterscheidung, in der für jeden Eingabewert der Bitvektorwert für das darzustellende Zeichen zurückgegeben wird: function seg7(x: std_logic_vector(2 downto 0)) return std_logic_vector is begin case x is when "000" => return "0111111"; when "001" => return "1110011"; when "010" => return "0111110"; when "011" => return "1110001"; when others => return "0000000"; end case; ⇒WEB-Projekt: P3.1/Lsg_pack.vhdl end function;
Lösung zu Aufgabe 3.3 Die Funktion benötigt eine lokale Zählvariable mit dem Rückgabetyp natural und dem Anfangswert »0«, die in einer Schleife über den gesamten Indexbereich des Bitvektors, wenn das Bit »1« ist, um eins hochgezählt wird: function count1(x: std_logic_vector) return natural is variable i: natural := 0; begin for idx in x’range loop if x(idx)=’1’ then i := i + 1; end if; end loop; return i; ⇒WEB-Projekt: P3.1/Lsg_pack.vhdl end function;
464
6 Lösungen zu den Übungsaufgaben
Lösung zu Aufgabe 3.4 a) Die zu entwerfende Prozedur soll nach ihrem Aufruf unverzögert dem Signal I den Wert »1«, dem Signal T den Wert »0« und dem Signal x den Wert »00« zuweisen, 2 ns warten, dem Signal I den Wert »0« zuweisen und 1 ns warten. Dann ist in einer Schleife fünfmal derselbe Ablauf zu wiederholen. Dem Signal T ist eine »1« zuzuweisen. Anschließend ist 1 ns zu warten. Dann sind dem Signal x zwei zufällige Bitwerte und dem Signal T eine »0« zuzuweisen und wieder 1 ns zu warten. Zum Auslosen der Bitwerte wird mit der Funktion »rand« eine Pseudo-Zufallszahl zwischen null und eins erzeugt, mit 0,5 verglichen, bei kleineren Werten eine »0« und sonst eine »1« zugewiesen. Nach fünf Schleifendurchläufen sind alle geforderten Signaländerungen abgearbeitet, und die Prozedur gibt den Kontrollfluss ab. procedure TMG(signal T, I: out std_logic; signal x: out tUnsigned(1 downto 0)) is begin I <= ’1’; T <= ’0’; x <= "00"; wait for 2 ns; I <= ’0’; wait for 1 ns; for idx in 1 to 5 loop T <= ’1’; wait for 1 ns; if rand<0.5 then x(0) <= ’0’; else x(0) <= ’1’; end if; if rand<0.5 then x(1) <= ’0’; else x(1) <= ’1’; end if; T <= ’0’; wait for 1 ns; end loop; ⇒WEB-Projekt: P3.1/Lsg_pack.vhdl end procedure;
b) Zur Nachbildung einer Schaltung durch eine nebenläufige Prozedur müssen alle Anschlusssignale in der Parameterliste stehen, die Eingabesignale, damit die Prozedur bei jeder Eingabeänderung aufgerufen wird, und die Ausgabesignale, damit ihnen Signaländerungen zugewiesen werden können. Neu ist, dass die Schaltung ein Register enthält, das seinen Zustand speichern soll. Um das Speicherverhalten nachzubilden, wird das Ausgabesignal in der Prozedurschnittstelle als les- und veränderbar vereinbart. Für den Prozedurkörper selbst wird im einfachsten Fall die Anweisungsfolge des Abtastprozesses aus Abb. 3.13 c übernommen. Wenn nach der Eingabeänderung, die den Prozeduraufruf verursacht hat, I = 1 ist, wird y initialisiert und sonst, wenn der Prozeduraufruf von einer aktiven Taktflanke verursacht wurde, wird zu dem Signalwert von y der Eingabewert addiert und die Summe an y zugewiesen: procedure Akku(signal T, I: std_logic; signal x: tUnsigned(1 downto 0); signal y: inout tUnsigned(3 downto 0)) is begin if I=’1’ then y <= "0000";
6.3 VHDL im Detail elsif rising_edge(T) then y <= x + y; end if; end procedure;
465
⇒WEB-Projekt: P3.1/Lsg_pack.vhdl
6.3.2 Anwendungsspezifische Datentypen Lösung zu Aufgabe 3.5 A1: Der Term in der Klammer hat wieder den Typ time. Dazu wird ein Wert vom Typ time addiert. Das Ergebnis ist auch vom Typ time. Die Anweisung ist zulässig. A2: Die Konstante »5« darf mit jedem ganzzahligen Typ, d.h. auch mit »int8«, multipliziert werden. Das Produkt hat den Typ »int8« und darf nicht mit einer Gleitkommakonstanten addiert werden. Die Anweisung ist unzulässig. A3: Die Maximalwerte der beiden Datentypen sind beides Zahlen, aber von unterschiedlichem Typ. Sie dürfen nicht miteinander verglichen werden. Die Anweisung ist unzulässig. Lösung zu Aufgabe 3.6 Typvereinbarungen: type tZeichenwert is range 1 to 9; type tZeichen is (’1’, ’2’, ’3’, ’4’, ’5’, ’6’, ’7’, ’8’, ’9’);
Konvertierungsfunktionen: function to_Zeichenwert(x: tZeichen) return tZeichenwert is begin return tZeichen’pos(x) + 1; end function; function to_Zeichen(x: tZeichenwert) return tZeichen is begin return tZeichen’val(x - 1); ⇒WEB-Projekt: P3.2/Afg_ZB_Enum.vhdl end function;
Lösung zu Aufgabe 3.7 Die Wertebereichsgrenzen sind ganzzahlige Vielfache der kleinsten Maßeinheit »mV«. Ein Volt ist gleich 1000 mV: type tSpannung is range -10000 to 10000 units mV; V = 1000 mV; end units;
466
6 Lösungen zu den Übungsaufgaben
Lösung zu Aufgabe 3.8 type tMem is array (natural range <>) of tUnsigned(31 downto 0); variable Daten: tUnsigned(31 downto 0) := (others =>’0’); variable Adresse: tUnsigned(7 downto 0); variable Mem: tMem(0 to 2**Adresse’high-1);
Lösung zu Aufgabe 3.9 a) Definition des Verbunds: type tZustand3SG is record s: std_logic; y: tUnsigned( 3 downto 0); end record;
b) Initialisierungsprozedur: procedure Init(signal x: out tZustand3SG) is begin x<=(s =>’0’, y=>"0000"); end procedure;
c) Zählprozedur: procedure Count(signal x: inout tZustand3SG) is begin
if x.s=’0’ then x.y <= x.y + "1"; elsif x.s=’1’ then x.y <= x.y - "1"; end if; end procedure;
d) Konvertierungsfunktion in eine Zeichenkette: function str(x: tZustand3SG) return string is begin return "(s=" & str(x.s) & ", y=" & str(x.y) & ")"; end function; ⇒WEB-Projekt: P3.2/Afg_tZustand3SG.vhdl
6.3.3 Ein- und Ausgabe Lösung zu Aufgabe 3.10 Eine Get-Prozedur für einen eigenen Aufzählungstyp übernimmt am einfachsten die Programmstruktur der Get-Prozedur für boolean: procedure get(pstr: inout tPString; w: out tZustand) is constant ptr: positive := pstr.pta; variable pos: natural; begin if pstr.Status=ok then get_sym(pstr, pos, "Idle Start Run Ready "); if pstr.Status=ok then w := tZustand’val(pos); return; end if; pstr.pta := ptr; pstr.err_pos := ptr; assign(pstr.err_msg, "Idle, Start, Run oder Ready erwartet"); end if; ⇒WEB-Projekt: P3.3/AfgEA_pack.vhdl end procedure;
6.3 VHDL im Detail
467
Schnittstellenparameter sind das les- und veränderbare Datenobjekt »pstr« für das abzuräumende Band und der Ausgabeparameter »w« mit dem Ergebniswert. Der Kernalgorithmus steckt im Prozeduraufruf von »get_sym(...)«, der testet, ob eine der mit Leerzeichen abgeschlossenen Teilzeichenketten des dritten Übergabeparameters am Bandanfang steht und wenn ja, diese abräumt und die Tabellenposition zurückgibt (Idle 7→ 0, Start 7→ 1, ...). Aus der Tabellenposition wird mit dem ’val-Attribut von »tZustand« der Rückgabewert gebildet. Falls sich keines der vier Zeichenketten auf dem Band befindet, ist der Statuswert von »pstr« nach dem Prozeduraufruf von »get_sym(...)« ungleich »ok«. Der Lesezeiger wird auf den Aufrufwert zurückgesetzt, der Fehlerzeiger wird auf das erste unzulässige Zeichen gesetzt und dem Datenobjekt »pstr.err_msg« wird ein Text mit der erwarteten Eingabe zugewiesen. Die Str-Funktion für den eigenen Aufzählungstyp hat hier dieselbe Programmstruktur wie die Str-Funktion für boolean: function str(x: tZustand; fmt:character := ’b’) return string is begin if fmt=’t’ then return "tZustand"; else return tZustand’image(x); end if; ⇒WEB-Projekt: P3.3/AfgEA_pack.vhdl end function;
Übergabeparameter sind das Datenobjekt, dessen Wert als Text dargestellt werden soll, und das Formatzeichen »fmt«. Für »fmt=’t’ wird wie bei der Vorbildfunktion die Typbezeichnung, hier »tZustand«, zurückgegeben. Sonst wird der Wert des Image-Attributs, d.h. die Textdarstellung des Wertes, zurückgegeben. Lösung zu Aufgabe 3.11 Die Hexadezimalziffern seien als Tabelle vereinbart: constant HexTab:string := "0123456789ABCDEF";
Bei einem ungültigen Ausgabewert wird »XX« zurückgegeben. Sonst werden die Tabellenwerte zum Zahlenwert des ersten und des nullten Halbbytes aneinandergekettet und zurückgegeben. Vor dem indizierten Zugriff auf die Zeichenkette ist »1« zu addieren, weil der Indexbereich der Zeichenkette »HexTab« mit »1« beginnt: function str_hex(x: tByte) return string is begin if is_x(x) then return "XX"; else return HexTab(int(x(7 downto 4))+1) & HexTab(int(x(3 downto 0))+1); end if; end function;
⇒WEB-Projekt: P3.3/AfgEA_pack.vhdl
468
6 Lösungen zu den Übungsaufgaben
Lösung zu Aufgabe 3.12 Eine nebenläufige Prozedur wird zum Simulationsbeginn und bei jeder Änderung des Eingabesignals geweckt. Für die Aufgabenstellung muss sie zu jedem Weckzeitpunkt eine Zeichenkette mit der aktuellen Simulationszeit verkettet mit einer Zeichenkettenkonstanten und einer Zeichenkettendarstellung des Wertes von x in das Dateiobjekt »f« schreiben: procedure Protokoll(signal x: std_logic_vector; file f: text) is begin write(f, str(now) & " : " & str(x)); ⇒WEB-Projekt: P3.3/AfgEA_pack.vhdl end procedure;
Lösung zu Aufgabe 3.13 a) Innerhalb der Funktion ist ein Dateiobjekt für die zum Lesen geöffnete Datei, eine Variable für das Leseband, eine Variable für die zu lesenden Zahlenwerte, eine Variable als Zeilenzähler und ein Feld für den Rückgabewert zu vereinbaren. Dann wird in einer Wiederholschleife »für alle Feldelemente« in jedem Schleifendurchlauf eine Dateizeile in die Lesebandvariable »pstr« übernommen und versucht, einen Zahlenwert im Bereich von null bis 255 vom Band abzuräumen. Falls das nicht möglich ist, wird eine Fehlermeldung mit Angabe des Dateinamens, der Zeile und Spalte, in der der Lesefehler aufgetreten ist, und der Angabe, was das Programm an dieser Stelle in der Datei erwartet hat, ausgegeben und die Simulation beendet. Sonst wird der gelesene Wert nach »tUnsigned«, Länge acht und weiter nach std_logic_vector konvertiert und dem Feldelement zugewiesen. Nach der Schleife wird das Feld, dessen Elemente mit Werten aus der Datei beschrieben sind, an die aufrufende Einheit übergeben: function Init(Dateiname:string; N:natural) return tByteFeld is file f: text open read_mode is Dateiname; variable pstr: tPString; variable Wert: integer; variable Zeile: positive; variable Feld: tByteFeld(0 to N-1); begin for idx in Feld’range loop read(f, pstr); read(pstr, Wert, 0, 255); assert pstr.Status=ok report "Lesefehler Datei " & Dateiname & "(" & str(Zeile) & ": " & str(pstr.err_pos) & " : " & str(pstr.err_msg) severity error; Feld(idx) := std_logic_vector(to_tUnsigned(Wert, 8)); Zeile := Zeile + 1; end loop;
6.3 VHDL im Detail return Feld; end function;
469
⇒WEB-Projekt: P3.3/AfgEA_pack.vhdl
b) Zur Initialisierung einer Konstanten wird nach der Typzuordnung der Rückgabewert der Initialisierungsfunktion mit dem Dateinamen und dem Wert 1024 als Aufrufparameter zugewiesen: constant ROM: tByteFeld := Init("Daten.txt", 1024) ⇒WEB-Projekt: P3.3/Test_Afg3_13_pack.vhdl
6.3.4 Beschreibungsschablonen für digitale Schaltungen Lösung zu Aufgabe 3.14 a) In einer Schleife über alle Eingabewerte wird die Zählvariable für alle Eingabebits, die »1« sind, um eins erhöht: function Anzahl1a(x: std_logic_vector; N: natural) return tUnsigned is variable sum: tUnsigned(N-1 downto 0) := (others=>’0’); begin for idx in x’range loop if x(idx)=’1’ then sum := sum + "1"; end if; end loop; return sum; end function;
b) Die Umstellung des Berechnungsflusses für die Additionen von einer Ketten- in eine Baumstruktur nutzt im einfachsten Fall den Algorithmus aus Abb. 3.22. Die Summanden und Zwischensummen dürfen laut der Fußnote in der Aufgabenstellung alle die Bitbreite N haben und können somit wie im Vorbildalgorithmus zu einem Vektor zusammengefasst werden. Der Typ dieses Vektors ist von den Aufrufparametern abhängig und muss deshalb innerhalb der Funktion vereinbart werden. Die erste Schleife im Anweisungsteil überträgt die Bitwerte des Eingabevektors in die niederwertigsten Bits der ersten Vektorelemente der Variablen z und schreibt in die höherwertigen Bits Nullen. Auf diese Weise initialisiert sie die Vektorelemente für die Eingabewerte wahlweise mit dem Wert null oder eins. Dann werden wie in Abb. 3.22 immer die Werte benachbarter Feldelemente zusammengefasst – hier im Beispiel addiert – und das Ergebnis in das nächste freie Feldelement eingetragen: function Anzahl1b(x: std_logic_vector; N: natural) return tUnsigned is type tVektortyp is array (natural range <>) of tUnsigned(N-1 downto 0); constant xl: natural := x’length; variable z: tVektortyp(2 * xl - 2 downto 0);
470
6 Lösungen zu den Übungsaufgaben variable idx2: natural; begin for idx in x’range loop if x(idx)=’1’ then z(idx2) := (0=>’1’, others=>’0’); else z(idx2) := (others=>’0’); end if; idx2 := idx2 + 1; end loop; for idx in 0 to xl-2 loop z(idx+xl) := z(2*idx) + z(2*idx + 1); end loop; return z(z’high); ⇒WEB-Projekt: P3.4/AfgCount1_pack.vhdl end function;
c) Datenflussgraphen für N = 7: Kettenstruktur zu Aufgabenteil a x0 x1 x2 x3 x4 x5 x6
Baumstruktur zur Aufgabenteil b x0 x1 x2 x3 x4 x5 x6
2 2 3 3 3
3
y
z0 z1 z2 z3 z4 z5 z6
z7 z8 z9
2
2
z10
2
z11
2
3
ein Bit zwei Bit
xi zi y
2
z12
3
y
3
drei Bit Eingabewerte lokale Variable R¨ uckgabewert
Die erforderlichen Bitbreiten der Zwischenergebnisse resultieren aus der Regel, dass 2N – das ist die Anzahl der darstellbaren Werte – größer oder gleich der Anzahl der aufsummierten Bits sein muss. Lösung zu Aufgabe 3.15 Die Suchmethode wird durch eine Schleife simuliert, in der mit der kleinsten Adresse beginnend die Inhalte aller Speicherplätze nacheinander mit dem gesuchten Wert verglichen werden. Bei der ersten Übereinstimmung erfolgt ein Rücksprung mit der aktuellen Adresse und hit = 1. Wenn das Schleifenende erreicht wird, erfolgt der Rücksprung mit hit = 0 und dem Adresswert null: function Suche(Mem: tMem; x: tDaten) return tSuchergebnis is begin for idx in Mem’low to Mem’high loop if Mem(idx)=x then return (hit=>’1’, adr=>to_tUnsigned(idx, tAdr’length)); end if; end loop; return (hit=>’0’, adr=>(others=>’0’); end function;
6.3 VHDL im Detail
471
Lösung zu Aufgabe 3.16 N φist
4
w:1,128422 d:0,071578 cosN (1,2) w:0,428086 d:-0,065729 sinN (1,2) w:0,903738 d:0,028301
8
12
16
Soll-Wert
w:1,198645 d:0,001355 w:0,363621 d:-0,001263 w:0,932039 d:0,000492
w:1,200110 d:-0,000110 w:0,362256 d:0,000102 w:0,932079 d:-0,000040
w:1,200018 d:-0,000018 w:0,362341 d:0,000017 w:0,932046 d:-0,000007
1,200000 0,362358 0,932039
(w – berechneter Wert; d – Abweichung) 6.3.5 Methoden und Funktionsbausteine für den Test Lösung zu Aufgabe 3.17 Eine erfolgreiche Probe bestätigt, dass das Ergebnis richtig ist, ein erfolgreicher Plausibilitätstest erlaubt nur den Rückschluss, dass es entsprechend der kontrollierten Plausibilitätskriterien zulässig ist. Richtige Ergebnisse sind immer zulässig, aber ein zulässiges Ergebnis kann auch falsch sein. Lösung zu Aufgabe 3.18 Die Plausibilitätsbedingung für die Amplitude A, bei deren Verletzung eine Assert-Anweisung die Simulation beenden soll, ist A ≤ 2by −1 − 1 (by – Bitanzahl des Ausgabesignals y). Bei der Berechnung des Arguments der Sinusfunktion gibt es eine Falle. Der Quotient aus der aktuellen Simulationszeit und der Periodenlänge tsim /tPsin wird auf den nächsten ganzzahligen Wert gerundet. Würde man ihn einfach in dieser Form in den Ausdruck einsetzen, würde immer der Sinus eines Vielfachen von 2π plus der Phasenverschiebung berechnet werden. Das ist eine nur von der Phasenverschiebung abhängige Konstante. Damit das Verhältnis aus Simulationszeit und Periodendauer mit Nachkommastellen berechnet wird, müssen beide Zeitangaben vor der Division durch übereinstimmende wesentlich kleinere Zeiteinheiten dividiert und die Quotienten nach real konvertiert werden. Die Prozedur hat den Takt als Eingabesignal und erzeugt ein Ausgabesignal. Als nebenläufige Prozedur wird sie zum Simulationsbeginn und bei jeder Taktänderung aufgerufen. Die Neuberechnung und Wertzuweisung an das Ausgabesignal erfolgt nur bei jeder steigenden Taktflanke.
472
6 Lösungen zu den Übungsaufgaben procedure GenSin(signal T: std_logic; signal y: out tSigned; A: natural; tpsim: delay_length; phi: real) is variable r: real; begin assert A < 2**(y’length - 1) report "A ist zu gross" severity error; if rising_edge(T) then r := 2.0 * math_pi * real(now/fs)/real(tPsim/fs) + phi; y <= to_tSigned(integer(real(A) * sin(r)), y’length); end if; ⇒WEB-Projekt: P3.5/GenSin_pack.vhdl end procedure;
6.4 Vom Transistor zur Schaltung 6.4.1 Entwurf und Modellierung von CMOS-Gattern Lösung zu Aufgabe 4.1 Für den Übergabeparameter der Auflösungsfunktion ist in Anlehnung an Abschnitt 4.1.3 zuerst ein Vektortyp mit »tZ01« als Elementtyp zu definieren: type tZ01_vector is array (natural range <>) of tZ01;
In der nachfolgenden Lösung wird eine mit dem Wert der ersten Quelle initialisierte Variable »tmp« angelegt. In einer Schleife für alle anderen Quellenwerte wird ihr Wert, wenn er »Z« und der andere Quellenwert »0« oder »1« ist, durch den anderen Quellenwert ersetzt. Wenn beide Werte ungleich »Z« sind, beendet eine Assert-Anweisung die Simulation mit »severity failure«. Anderenfalls ist der Rückgabewert der Wert in »tmp« nach Abarbeitung der Schleife: function AufloesZ01(x: tZ01_vector) return tZ01 is variable tmp: tZ01 := x(x’low); begin for idx in x’low+1 to x’high loop if x(idx)/=’Z’ then assert tmp=’Z’ report "Mehrere aktive Quellen" severity failure; tmp := x(idx); end if; end loop; return tmp; end function;
6.4 Vom Transistor zur Schaltung
473
Lösung zu Aufgabe 4.2 Das NMOS-Netzwerk muss die invertierte Zielfunktion haben: fn = y¯ = (x1 ∧ x3 ) ∨ ((x1 ∨ x3 ) ∧ (x2 ∨ x4 )) Für das PMOS-Netzwerk ist die Zielfunktion mit den De Morgan’schen Regeln in eine UND-ODER-Verknüpfung invertierter Eingabevariablen umzustellen. Dabei werden aus den UND-Operatoren ODER-Operatoren und umgekehrt: fp = (¯ x1 ∨ x ¯3 ) ∧ ((¯ x1 ∧ x ¯3 ) ∨ (¯ x2 ∧ x ¯4 )) UV
x1
x3
x1
x2
x3
x4
x1
x1
x3
x3
x2
x4
y
Lösung zu Aufgabe 4.3 a) Am Gatterausgang treffen entweder die Signalwerte »0«, »L« und »Z« oder »1«, »H« und »Z« zusammen. Der resultierende Wert ist immer »0« oder »1«. Ob die NMOS-Transistoren mit einer »1« am Drain und die PMOS-Transistoren mit einer »0« am Drain den logischen Wert zum Source als starken oder als schwachen Signalwert oder gar nicht weiterleiten, hat letztendlich keinen Einfluss auf die Funktion. x2 x1 z 0 0 1 1
0 1 0 1
1 0 1 0
yT3 yT4 yT5 yT6 0 Z H Z
L Z 1 Z
Z Z H 0
L 1 Z Z
y 0 1 1 0
b) Es handelt sich um ein EXOR-Gatter, das im Vergleich zu dem EXORGatter aus Abb. 4.10 b nur aus sechs statt aus zwölf Transistoren besteht.
Lösung zu Aufgabe 4.4 Die Gleichungen für das NMOS- und das PMOS-Netzwerk sind jeweils eine UND-Verknüpfung aus zwei Eingabegrößen, die durch eine Reihenschaltung aus zwei Transistoren nachzubilden sind:
474
6 Lösungen zu den Übungsaufgaben
¯ yn = x ∧ E ¯ yp = x ¯∧E Für die Ansteuerung des NMOS-Netzwerks wird zusätzlich ein Inverter aus zwei Transistoren benötigt, der das Freigabesignal E invertiert. UV x
y
E
Lösung zu Aufgabe 4.5 a) Schaltung siehe Abb. 6.14 a. b) Ausgabesignalverlauf siehe Abb. 6.14 b. c) Die Auswertung muss mit der fallenden Taktflanke erfolgen, bei der das Gatter die logische Funktion y = x1 ∨ x2 ∨ x3 x4 nachbildet. Bei der steigenden Taktflanke ist das Ausgabesignal des Gatters immer »1«. UV T x3 x1
x2
a)
T y
0011 1000 0010
x y
x4 T
1 0
b)
1 0
mit x = x4 x3 x2 x1
Abb. 6.14. Schaltung und Ausgabesignalverlauf zur Lösung von Aufgabe 6.5
6.4.2 Zeitverhalten Lösung zu Aufgabe 4.6 Der Ausschaltvorgang erfolgt über vier in Reihe geschaltete NMOS-Transistoren mit der relativen Breite zwei. Die relative Breite für den Ersatztransistor des eingeschalteten NMOS-Netzwerks ist nach Gleichung 4.31 bN = 0,5. Die Ausschaltzeit beträgt nach Gleichung 4.20
6.4 Vom Transistor zur Schaltung
taus ≈ 2 ·
τA + τLtg +
NL X
475
! τL.i
i=1
Das Einschalten erfolgt im ungünstigsten Fall über einen einzelnen PMOSTransistor und dauert nach Gleichung 4.19 ! NL X 2 tein ≈ · τA + τLtg + τL.i bP i=1 Damit beide Zeiten übereinstimmen, ist für die PMOS-Transistoren unabhängig von der leitungs- und lastabhängigen Verzögerung eine relative Breite von bP ≈ 1 zu wählen. Lösung zu Aufgabe 4.7 Im oberen Inverter haben das erste und das zweite Gatter je eine und das dritte Gatter zwei Lasten. Die relativen Transistorbreiten sind aus der Voraufgabe zu übernehmen und betragen bN = 0,5 und bP = 1. Der Zusammenhang zwischen den beiden gesuchten Parametern τA und τL und den Ein- und Ausschaltzeiten der drei Inverter im Ring betragen:
taus tein
G1
G2
G3
2 · (τA + τL ) 2 · (τA + τL )
2 · (τA + τL ) 2 · (τA + τL )
2 · (τA + 2 · τL ) 2 · (τA + 2 · τL )
Die Schwingungsdauer ist nach Gleichung 4.21 die Summe aller Ein- und Ausschaltzeiten: TP1 ≈ 12 · τA + 16 · τL (6.1) Im zweiten Ringinverter haben alle Gatter die vierfache Last. Das Aufladen der Lastkapazitäten erfolgt jeweils über vier parallel geschaltete Transistoren, so dass nach Gleichung 4.28 die relative Transistorbreite des einschaltenden PMOS-Netzwerks viermal so groß und die Einschaltzeit nur ein Viertel so groß wie bei den Gattern aus dem ersten Ringinverter ist: G5 taus tein
G6
G7
2 · (τA + 4 · τL ) 2 · (τA + 4 · τL ) 2 · (τA + 8 · τL ) 1 1 1 2 · (τA + 4 · τL ) 2 · (τA + 4 · τL ) 2 · (τA + 8 · τL )
Die Schwingungsdauer setzt sich wie folgt aus der Grundverzögerung und der lastabhängigen Verzögerung zusammen: TP2 ≈ 7,5 · τA + 40 · τL
(6.2)
Die Lösung des Gleichungssystems aus den Gleichungen 6.1 und 6.2 ergibt für die Grundverzögerung τA = 80 ps und für die lastabhängige Verzögerung τL = 40 ps.
476
6 Lösungen zu den Übungsaufgaben
6.4.3 Speicherzellen, Latches und Register Lösung zu Aufgabe 4.8 a) Die Gatter G1 und G4 sind Inverter und die Gatter G2 und G3 deaktivierbare Treiber. b) Gatter G1 erzeugt den invertierten Takt. Gatter G2 invertiert bei T = 0 das Eingabesignal x und speichert sonst seinen Ausgabewert in der Lastkapazität von z1 . Gatter G3 invertiert bei T = 1 den in z1 gespeicherten Signalwert und speichert ihn sonst in der Lastkapazität von z2 . Gatter G4 invertiert das Ausgabesignal von Gatter G3. T x T¯ z1 z2 y
1 0 1 0 1 0 1 0 1 0 1 0
c) Die gesamte Transistorschaltung ist eine taktflankengesteuerte dynamische Speicherzelle, die nach dem Master-Slave-Prinzip arbeitet und den invertierten gespeicherten Wert ausgibt. Lösung zu Aufgabe 4.9 Nach Gleichung 4.36 muss für die beiden Teilerwerte gelten: Q · 50 MHz P Der P-Teiler kann entfallen (P = 1). Für den Q-Teiler ist dann Q = 6 zu wählen. 300 MHz =
6.5 Komplexe Beispielentwürfe 6.5.1 FIR-Filter mit Blockspeichern Lösung zu Aufgabe 5.1 Es gibt nur eine Möglichkeit für einen Wertebereichsüberlauf. Diese liegt vor, wenn alle Operanden den kleinsten darstellbaren Wert haben: ci = −1 und xi = −1024. Die Summe von vier dieser Produkte beträgt 4 · (−1) · (−1024) = 4096 = 0100 0000 00002 und verlangt einschließlich der führenden Null für das Vorzeichen zwölf statt elf Vorkommastellen. Im Simulationsmodell ist dieser Fall dadurch ausgeschlossen, dass die Prozedur »Testdatenquelle(...)«, die die Testeingabewerte aus der Datei einliest, nur Datenwerte im Bereich von ±1000 akzeptiert.
6.5 Komplexe Beispielentwürfe
477
Lösung zu Aufgabe 5.2 Die Aufgabe lässt sich praktisch nur mit dem Simulator lösen. Dazu sind vor der Simulation in der Datei »P5.2/Test_FIR2.vhdl« die Koeffizientenwerte und in der Datei »P5.2/Eingabe.txt« die Datenwerte zu ändern. Simulationsausgaben siehe Abb. 6.15. Zeit |Eingabe|Z|cAdr|xAdr|| 10.0 ns| XX|I| 1| 3|| 20.0 ns| 0.0|I| 1| 3|| 30.0 ns| 0.0|I| 2| 2|| 40.0 ns| 250.0|I| 3| 1|| 50.0 ns| 500.0|S| 0| 0|| 60.0 ns| |S| 1| 1|| 70.0 ns| |S| 2| 2|| 80.0 ns| |S| 3| 3|| 90.0 ns| 1000.0|N| 0| 3|| 100.0 ns| |N| 1| 0|| 110.0 ns| |N| 2| 1|| 120.0 ns| |N| 3| 2|| 130.0 ns|-1000.0|N| 0| 2|| 140.0 ns| |N| 1| 3|| 150.0 ns| |N| 2| 0|| 160.0 ns| |N| 3| 1|| 170.0 ns| -500.0|N| 0| 1|| 180.0 ns| |N| 1| 2|| 190.0 ns| |N| 2| 3|| 200.0 ns| |N| 3| 0||
cDat | xDat || Prod | Akku || XX| XX|| XX| XX|| XX| XX|| XX| XX|| XX| XX|| XX| XX|| XX| XX|| XX| XX|| XX| XX|| XX| XX|| 0.3000| 500.0|| XX| XX|| 0.9000| 250.0|| 150.0| XX|| 0.9000| 0.0|| 225.0| 150.0|| 0.3000| 0.0|| 0.0| 374.9|| 0.3000| 1000.0|| 0.0| 374.9|| 0.9000| 500.0|| 300.0| 374.9|| 0.9000| 250.0|| 450.0| 300.0|| 0.3000| 0.0|| 225.0| 749.9|| 0.3000|-1000.0|| 0.0| 974.9|| 0.9000| 1000.0|| -300.0| 974.9|| 0.9000| 500.0|| 900.0| -300.0|| 0.3000| 250.0|| 450.0| 600.0|| 0.3000| -500.0|| 75.0| 1049.9|| 0.9000|-1000.0|| -150.0| 1124.9|| 0.9000| 1000.0|| -900.0| -150.0||
r(0) r(1) r(2) r(3)|Ausgabe XX XX XX XX| XX XX XX XX| XX XX XX 0.0| XX XX 0.0 0.0| XX 250.0 0.0 0.0| 500.0 250.0 0.0 0.0| 500.0 250.0 0.0 0.0| 500.0 250.0 0.0 0.0| 500.0 250.0 0.0 0.0| 500.0 250.0 0.0 1000.0| 500.0 250.0 0.0 1000.0| 374.9 500.0 250.0 0.0 1000.0| 500.0 250.0 0.0 1000.0| 500.0 250.0-1000.0 1000.0| 500.0 250.0-1000.0 1000.0| 974.9 500.0 250.0-1000.0 1000.0| 500.0 250.0-1000.0 1000.0| 500.0 -500.0-1000.0 1000.0| 500.0 -500.0-1000.0 1000.0| 1124.9 500.0 -500.0-1000.0 1000.0|
Abb. 6.15. Simulationsausgaben zu Aufgabe 5.2
A Ergänzungen zu VHDL
A.1 Syntax, Schlüsselworte, Bezeichner, Konstanten A.1.1 Syntaxregeln für die Beschreibung von Sprachkonstrukten Schlüsselworte optionales Element, darf fehlen oder einmal enthalten sein optionales Element, darf beliebig oft enthalten sein Alternative, eines der aufgezählten Elemente muss enthalten sein kursiv Methasymbol (Nichtterminalsymbol); Symbol, das nach weiteren Regeln zu ersetzen ist IEEE standardisierter Bezeichner 1,"ab", ... Wertangabe (Zahl, Zeichenkette, ...) normal Operator oder selbst vereinbarter Bezeichner fett [...] {...} ...|...
• •
VHDL unterscheidet nicht zwischen Groß- und Kleinbuchstaben. Die Symbole »Name«, »NAME« und »name« sind identisch. Kommentare beginnen mit »--« und reichen stets bis zum Zeilenende.
A.1.2 Schlüsselworte •
Schlüsselworte haben eine besondere Bedeutung und dürfen nicht als Bezeichner verwendet werden. Im Standard bis einschließlich Revision VHDL-2008 sind folgende Worte als Schlüsselworte reserviert. Die im Buch verwendeten Schlüsselworte sind fett darstellt. abs, access, after, alias, all, and, architecture, array, assert, assume, assume_guarantee, attribute, begin, block, body, buffer, bus, case, component, configuration, constant, convert, cover, default, disconnect, downto, else, elsif, end, entity, exit, fairness, file, for, force, function, generate, generic, group, guarded,
G. Kemnitz, Technische Informatik, eXamen.press, DOI 10.1007/978-3-642-17447-6, © Springer-Verlag Berlin Heidelberg 2011
480
A Ergänzungen zu VHDL
if, impure, in, inertial, inout, is, label, library, linkage, literal, loop, map, mod, nand, new, next, nor, not, null, of, on, open, or, others, out, package, parameter, port, postponed, procedure, process, property, protected, pure, range, record, register, reject, release, rem, report, restrict, restrict_guarantee, return, rol, ror, select, sequence, severity, shared, signal, sla, sll, sra, strong, subtype, then, to, transport, type, unaffected, units, until, use, variable, vmode, vprop, vunit, wait, when, while, with, xnor, xor
A.1.3 Syntaxregeln für Bezeichner Bezeichner sind weitgehend frei wählbare Namen für Entwurfseinheiten, Entwurfsbeschreibungen, Packages, Datentypen, Unterprogramme, Konstanten, Variablen, Signale, Dateien, ..., deren Bedeutung in einer Deklaration festgelegt und die anschließend über diesen Namen referenziert werden. Bezeichner dürfen • • • •
nur aus den Buchstaben »A« bis »Z«, »a« bis »z«, den Ziffern »0« bis »9« und dem Unterstrich »_« bestehen, nur mit einem Buchstaben beginnen, nicht mit einem Unterstrich enden und keine zwei Unterstriche hintereinander enthalten.
Es wird nicht zwischen Groß- und Kleinschreibung unterschieden. Gleiche Bezeichner verdecken sich gegenseitig. Wenn für ein Unterprogramm und eine Variable derselbe Bezeichner vereinbart wird, ist entweder nur der Bezeichner der Variablen sichtbar, so dass es bei seiner Verwendung als Unterprogrammbezeichner einen Analysefehler gibt oder umgekehrt. Ausnahmen, in denen gleiche Bezeichner mehrfach verwendet werden dürfen, sind überladene Funktions- und Prozedurnamen (vgl. Abschnitt 3.1.4 und 3.1.7) und symbolische Werte unterschiedlicher Aufzählungstypen (vgl. Abschnitt 3.2.2). Mysteriöse Fehlermeldungen haben oft eine Mehrfachdefinition gleicher Bezeichner als Ursache, z.B. die Verwendung des Bezeichners für die Maßeinheit »fs« (Femtosekunden) als Bezeichner für eine Funktion. Bei gleichen Wertebezeichnern für unterschiedliche Aufzählungstypen z.B. ’0’ für character, bit und std_logic hat die Analyse gelegentlich Probleme, Ausdrücken den richtigen Typ zuzuordnen. Abhilfe schafft eine explizite Typqualifikation. Dazu wird der Typ, gefolgt von einem Hochkomma dem geklammerten Ausdruck vorangestellt, z.B. ... std_logic’(’0’) ...
Weitere Regeln hier im Buch sind, dass
A.1 Syntax, Schlüsselworte, Bezeichner, Konstanten
• •
481
Bezeichner aus standardisierten Packages zur Unterscheidung von selbst definierten Bezeichnern mit dem Schriftfont capitalized dargestellt werden und Bezeichnern selbst definierter Datentypen mit einem vorangestellten »t« beginnen (z.B. »tZustand«).
A.1.4 Zeichen- und Zeichenkettenkonstanten VHDL unterstützt den Zeichensatz »Latin-1« mit dem ASCII-Zeichensatz als Teilmenge (Abb. A.1). Die druckbaren Zeichen sind in einfache Hochkommas einzurahmen, z.B. ’a’. Die kleinen deutschen Umlaute sind enthalten, das »ß« und die Umlaute »Ä«, »Ö« und »Ü« sind auch in Kommentaren verboten. type character nul, soh, stx, dle, dc1, dc2, ’ ’, ’!’, ’"’, ’0’, ’1’, ’2’, ’@’, ’A’, ’B’, ’P’, ’Q’, ’R’, ’‘’, ’a’, ’b’,
is ( etx, dc3 ’#’, ’3’, ’C’, ’S’, ’c’,
-- 128 Zeichen eot, enq, ack, bel, dc4 nak, syn, etb, ’$’, ’%’, ’&’, ’’’, ’4’, ’5’, ’6’, ’7’, ’D’, ’E’, ’F’, ’G’, ’T’, ’U’, ’V’, ’W’, ’d’, ’e’, ’f’, ’g’,
des ASCII-Zeichensatzes bs, ht, lf, vt, lf, cr, can, em, sub, esc, fsp, gsp, ’(’, ’)’, ’*’, ’+’, ’,’, ’-’, ’8’, ’9’, ’:’, ’;’, ’<’, ’=’, ’H’, ’I’, ’J’, ’K’, ’L’, ’M’, ’X’, ’Y’, ’Z’, ’[’, ’\’, ’]’, ’h’, ’i’, ’j’, ’k’, ’l’, ’m’,
so, rsp, ’.’, ’>’, ’N’, ’∧’, ’n’,
si, usp, ’/’, ’?’, ’O’, ’ ’, ’o’,
’p’, ’q’, ’r’, ’s’, ’t’, ’u’, ’v’, ’w’, ’x’, ’y’, ’z’, ’{’, ’| ’, ’}’, ’∼’, del, . . . );
-- 128 weitere Zeichen (ISP 8859 Latin-1)
Abb. A.1. Zeichensatz
Zeichenkettenkonstanten sind beliebige Zeichenfolgen, eingerahmt in doppelte Hochkommas, z.B. "Text". Auch Konstanten für eindimensionale Vektortypen, deren Elemente als Aufzählungen druckbarer Zeichen definiert sind, können durch Zeichenketten dargestellt werden, z.B. type tZM is (’a’, ’x’, ’f’); type tZM_Vektor is array (range 1 to 5) of tZM; constant c: tZM := "axaff";
A.1.5 Darstellung von Zahlenwerten VHDL kann Zahlenwerte durch Zahlentypen oder Bitvektortypen darstellen. Die Darstellung von Zahlenwerten beginnt zur Unterscheidung von Schlüsselworten und Bezeichnern immer mit einer Ziffer oder einem Vorzeichen. •
Ganzzahlige Dezimalzahlen werden als Ziffernfolge dargestellt. Der Ziffernfolge kann ein Vorzeichen vorangestellt sein und sie darf einzelne Unterstriche zur Trennung von Zifferngruppen enthalten. Beispiele:
482
A Ergänzungen zu VHDL 1_234, -345
•
Binär-, Oktal- und Hexadezimalzahlen werden als in Nummernzeichen eingerahmte Ziffernfolge mit vorangestellter Basis dargestellt. Beispiele:
•
Reellwertige Zahlenangaben enthalten zur Unterscheidung von ganzzahligen Konstanten immer einen Dezimalpunkt. In der Exponentialschreibweise folgt hinter der Ziffernfolge mit »Punkt« ein »E«, gefolgt von der Angabe der Zehnerpotenz. Beispiele:
10102 7→ 2#1010#,
7258 7→ 8#725#, 3F16 7→ 16#3f#
1 7→ 1.0, 2,76 · 104 7→ 2.76E+4
Bitvektorkonstanten können als Zeichenfolgen aus »0«, »1« [,»U«, »X« etc.] dargestellt werden. Darüber hinaus gibt es folgende Spezialschreibweisen: •
Für Binärzahlen kann der Zeichenkette ein »b« vorangestellt werden. Dann ist innerhalb der Ziffernfolge zusätzlich der Unterstrich zur Trennung von Zifferngruppen erlaubt. Beispiel:
•
Zur Oktaldarstellung wird das Zeichen »o« vorangestellt. Die nachfolgende Zeichenfolge darf Oktalziffern und den Unterstrich zur Trennung von Zifferngruppen enthalten. Beispiel:
•
Zur Hexadezimaldarstellung wird der Ziffernfolge das Zeichen »x« vorangestellt. Beispiel:
b"0100_1100"
1258 7→ o"125"
(identisch mit b"001_010_101")
7E16 → x"7E" (identisch mit b"0111_1110").
Bis zur Standardrevision VHDL-2008 entspricht eine Oktalziffer genau drei und eine Hexadezimalziffer genau vier Binärziffern. Ab VHDL-2008 werden überflüssige führende Nullen des zugehörigen Binärwertes ignoriert. Die Konstante x"7E" darf dann z.B. auch an einen Bitvektor der Länge sieben zugewiesen werden. A.1.6 Vordefinierte Attribute •
Signalattribute (vgl. Abschnitt 1.4.2) Attribut
Ergebnistyp
Ergebnis
s’event
boolean
true, wenn der Prozess durch eine Änderung von s geweckt wurde
s’stable(t)
boolean
true, wenn seit einer Zeit t keine Signaländerung stattgefunden hat
s’last_event
delay_length
Zeit seit der letzten Änderung von s
s’last_value
tTyp
Wert vor der letzten Änderung von s
s’delayed(t)
tTyp
das um t verzögerte Signal zu s
...
...
s – Signal mit einem beliebigen Typ tTyp; t – Ausdruck vom Typ delay_length.
A.1 Syntax, Schlüsselworte, Bezeichner, Konstanten
•
483
Attribute für elementare Datentypen (vgl. Abschnitt 3.2.4)
Attribut
Typ von T
Ergebnis- Ergebnis typ wie T
T ’left, T ’right, T ’low, T ’high
beliebig
T ’ascending
beliebig
T ’image(x)
beliebig
string
Textdarstellung des Wertes von x
T ’value(s)
diskret
Basistyp von T
Wert der Zeichenkette in der Darstellung von T
T ’pos(x)
diskret universal Position von Wert x integer(1)
T ’val(i)
diskret
T ’pred(x), T ’succ(x), diskret T ’leftof(x), T ’rightof(x)
linke, rechte, untere bzw. obere Grenze des Wertebereichs
boolean true für aufsteigende, false für absteigende Wertebereiche
Werte von Position i Basistyp von T
Vorgänger, Nachfolger, linker bzw. rechter Wert von x
x – Datenobjekt vom Typ T ; s – Datenobjekt vom Typ string; i – ganzzahliger Typ; (1) Spezialtyp, der ohne Konvertierung mit jedem ganzzahligen Typ verknüpft und jedem ganzzahligen Typ zugewiesen werden kann; beliebig – auf alle elementaren Typen anwendbar; diskret – nur auf ganzzahlige Typen, Aufzählungstypen und physikalische Typen anwendbar.
•
Feldattribute (vgl. Abschnitt 3.2.5) Attribut
Ergebnis
F ’left[(n)], F ’right[(n)], F ’high[(n)], F ’low[(n)],
linker, rechter, größter bzw. kleinster Indexwert
F ’range[(n)], F ’reverse_range[(n)]
Indexbereich, umgekehrt geordneter Indexbereich
F ’length[(n)]
Anzahl der Elemente
F ’ascending[(n)]
true für einen aufsteigenden und false für einen absteigenden Indexbereich
F – Feldobjekt oder Feldtyp; n – Nummer des Indexbereichs.
484
•
A Ergänzungen zu VHDL
Namensattribute (vgl. Abschnitt 3.3.2) Attribut
Ergebnis
bez ’simple_name
Umwandlung des Bezeichners in eine Textdarstellung
bez ’path_name
wie simple_name plus zusätzliche Pfadangabe durch die Entwurfshierarchie von der obersten Entwurfseinheit bis zum bezeichneten Objekt
bez ’instance_name
wie path_name, jedoch mit der zusätzlichen Angabe der Beschreibungsnamen der Teilschaltungen
bez – Bezeichner eines vereinbarten Objekts (Datenobjekt, Typ etc.).
A.2 Die Bibliothek »Tuc« Die Bibliothek »Tuc« gehört zu den Web-Projekten des Buches und enthält die folgenden Packages (siehe »Tuc/Hilfe_Tuc.txt«) [27]: • • • • • •
Tuc/Ausgabe.vhdl Tuc/StopSim_pack.vhdl Tuc/Eingabe.vhdl Tuc/Numeric_Sim.vhdl Tuc/Numeric_Synth.vhdl Tuc/Zufallstest.vhdl
A.2.1 Textausgabe (Tuc.Ausgabe) Datenobjekte für die Erzeugung von Textausgaben •
Verbund zur Darstellung und Bearbeitung von Texten variabler Länge: constant zeilenlaenge: positive := 100; type tString is record str: string(1 to zeilenlaenge); ptr: positive; end record;
•
Feld von Textzeilen vom Typ »tString« zur Darstellung und Bearbeitung mehrzeiliger Ausgabetexte: type tStringArray is array (natural range <>) of tString;
A.2 Die Bibliothek »Tuc«
485
Bearbeitungsmethoden für Textobjekte vom Typ »tString« procedure assign(x: inout tString; s: string); procedure clear(x: inout tString); procedure append(x: inout tString; s: string);
–- (1) –- (2) –- (3)
(1) Inhalt löschen und neuen Text zuweisen. (2) Inhalt löschen. (3) Neuen Text an den bisherigen Inhalt anhängen. Objekte vom Typ tStringArray werden zeilenweise mit diesen Methoden bearbeitet. Ausgabeprozeduren Die Ausgabe erfolgt mit den überladenen Funktionen »write(...)«: procedure procedure procedure procedure procedure procedure
write(file f: text, s: string); write(s: string); write(file f: text, ts: tString); write(ts: tString); write(file f: text, s: tStringArray); write(s: tStringArray);
––––––-
(1) (1) (2) (2) (3) (3)
(1) Ausgabe des Inhalts einer Zeichenkette. (2) Ausgabe des Inhalts eines Textobjekts. (3) Ausgabe eines mehrzeiligen Textes. Die Aufrufversionen ohne Angabe des Dateiobjekts geben die Texte auf dem Bildschirm aus. Die hier der Vollständigkeit halber mit aufgezählte Prozedur procedure write(file f: text, s: string);
ist im Package std.textio vereinbart und muss genau wie der Typ text von dort importiert werden. Dateiobjekte sind wie folgt zu vereinbaren: use std.textio.all;
... file f: text open write_mode is "Dateiname.txt";
Konvertierung von Datenobjekten in Ausgabetexte function str(x: tTyp, [fmt:character]) return string; function str(x: real, fmt:positive) return string;
(tT yp ∈{character, bit, std_ulogic1 , bit_vector, std_logic_vector, integer, real, time}). Das optionale Formatierungszeichen »fmt« legt die Wertedarstellung fest (siehe Tabelle A.1). Bei einem unzulässigen Formatierungszeichen wird die Standardformatierung verwendet. 1
Basistyp von std_logic
486
A Ergänzungen zu VHDL Tabelle A.1. Formatierungszeichen für die Str-Funktionen
fmt ’t’
Textdarstellung Bezeichner des Datentyps
’b’, ’o’, Binäre, oktale oder hexadezimale Darstellung. Negative Werte werden ’h’ im Zweierkomplement dargestellt. ’d’, ’s’ Dezimaldarstellung. Negative Werte werden durch Vorzeichen und Betrag dargestellt. Mit ’d’ werden Bitvektoren als vorzeichenfreie und mit ’s’ als vorzeichenbehaftete Zahlen interpretiert. ’1’, ’2’, Anzahl der Nachkommastellen für real. Es gibt auch eine ’3’, ’4’ Aufrufvariante mit der Anzahl der Nachkommastellen als positive ganze Zahl. ’e’
Exponentialdarstellung für real
Erzeugung spezieller Textdarstellungen •
Darstellung des auszugebenden Wertes als Symbol aus einer Tabelle: function str(x: tTyp, SymbolTab:string) return string;
(tT yp ∈{integer, bit_vector, std_logic_vector}). Die Symboltabelle ist eine Zeichenkette, in der die Symbole in aufsteigender Reihenfolge, gefolgt von einem Leerzeichen stehen, z.B. »"load store add sub mult div "«2 . Bitvektoren werden als vorzeichenfreie Zahlen interpretiert. Für den Wert null wird das erste, für den Wert eins das zweite Symbol etc. ausgegeben. •
Darstellung als Hexadezimalzahl ohne Zahlenbasis: function str_hex(x: std_logic_vector) return string;
Diese Funktion dient zur verkürzten Wertedarstellung ohne Angabe der Zahlenbasis und wird für den Test des RISC-Prozessors in Abschnitt 5.4 genutzt. •
Textausrichtung: function rechts(x: string; n: positive) return string; function links(x: string; n: positive) return string;
Die beiden Funktionen verlängern die übergebene Zeichenkette auf die Länge n, bei einer Rechtsausrichtung um führende und bei einer Linksausrichtung um nachfolgende Leerzeichen. A.2.2 Kontrollierter Simulationsabbruch (Tuc.StopSim_pack) Das Package dient dazu, über eine Eingabe oder einen Eingabeprozess eine Simulation mit mehreren Prozessen zu beenden. Das Package enthält dazu ein globales Flag 2
Auch hinter dem letzten Symbol muss ein Leerzeichen folgen.
A.2 Die Bibliothek »Tuc«
487
shared variable StopSimulation: boolean;
das über den Aufruf der Prozedur procedure StopSim(msg: string := "");
gesetzt und über die Prozedur procedure CheckStopSim(msg: string := "")
abgefragt werden kann. Die Prozedur »CheckStopSim(...)« legt sich, wenn das Flag gesetzt ist, dauerhaft schlafen und beendet auf diese Weise den aufrufenden Prozess. Der im Aufrufparameter »msg« übergebene Text dient als Debug-Ausgabe. Zusätzlich enthält das Package eine Prozedur zur Nachbildung eines Taktgenerators mit einstellbarer Periodendauer procedure TaktGen(signal T: out std_logic; tP: delay_length);
die im Testrahmen nebenläufig aufzurufen ist und die sich, wenn das Flag »StopSimulation« gesetzt wird, automatisch dauerhaft schlafen legt. A.2.3 Texteingabe (Package Tuc.Eingabe) Der Grunddatentyp für die Texteingabe ist type tPString is record str: string(1 to zeilenlaenge); –- Zeichenkette pta, pte: positive; –- Anfangs- und Endzeiger Status: tLesestatus; –- Status der letzen Operation err_pos: positive; –- Fehlerposition err_msg: tString; –- Fehlermeldung end record;
Er modelliert ein abräumbares Zeichenband (vgl. Abschnitt 3.3.3). Der Status ist ein Aufzählungstyp: type tLesestatus is (X, ok, trunc, err);
(X – ungültig; ok – kein Fehler bei der letzte Operation; truc – abgeschnitten, gelesene Dateizeile war länger als die Zeichenkettenvariable des Zeichenbandobjekts; err – Fehler beim Abräumen). Bei einem Abräumfehler wird in »err_pos« die Textposition, bei der er aufgetreten ist, und in »err_msg« ein Ausgabetext für die Fehlermeldung, in dem steht, welche Zeichen an dieser Textposition erwartet wurden, eingetragen. Für ein Zeichenbandobjekt sind folgende Bearbeitungsmethoden und Funktionen definiert: procedure procedure procedure procedure function
read(file f: text; pstr: out tPString); append(pstr: inout tPString; s: string); clear(pstr: inout tPString); assign(pstr: inout tPString; s: string); str(pstr: tPString) return string;
–––––-
(1) (2) (3) (4) (5)
488
(1) (2) (3) (4) (5)
A Ergänzungen zu VHDL
Zeichenbandobjekt mit einer Textzeile aus einer Datei laden. Inhalt löschen und neuen Text zuweisen. Inhalt löschen. Text anhängen. Konvertierung in eine Textdarstellung.
Die Grundfunktionen für das Abräumen von Zeichen und Zeichenketten von einem Zeichenbandobjekt sind: procedure get_char(pstr: inout tPString; pos: out natural; CharTab: string); –- (1) procedure get_sym(pstr: inout tPString; pos: out natural; SymbolTab: string; TZ: character := ’ ’); –- (2) procedure skip(pstr: inout tPString; CharTab: string := " "); –- (3)
(1) Abräumen eines Zeichens aus der Zeichentabelle »CharTab« und Rückgabe der Zeichenposition. (2) Abräumen eines Symbols aus mehreren Zeichen aus der Symboltabelle »SymTab« und Rückgabe der Symbolposition. Mit »TZ« wird das Trennzeichen, das in der Symboltabelle hinter jedem Symbol stehen muss, festgelegt. (3) Abräumen aller Trennzeichen. Die Zeichenkette »CharTab« legt die Menge der Trennzeichen fest, die beim Aufruf vom Zeichenbandobjekt abgeräumt werden. Standardmäßig ist das nur das Leerzeichen. Auf diesen beiden Methoden setzen die überladenen Get-Prozeduren zum Abräumen von Zeichenketten mit den einzulesenden Werten auf: procedure get(pstr: inout tPString; w: out integer; wmin: integer := integer’low + 1; wmax: integer := integer’high); procedure get(pstr: inout tPString; w: out real; wmin: real := real’low/10.0; wmax: real := real’high/10.0); procedure get(pstr: inout tPString; w: out time; wmin: time := 0 fs; wmax: time := 1 sec); procedure get(pstr: inout tPString; w: out tTyp);
–- (1) –- (2) –- (3) –- (4)
(1-3) Einlesen von Zahlen- und Zeitwerten mit einstellbarem Wertebereich. (4) Einlesen der Werte von Datenobjekten vom Typ bit, boolean, std_ulogic, bit_vector und std_logic_vector. Die nachfolgenden Prozeduren sind für die Dialogeingabe in Testrahmen vorgesehen. Sie geben in einer Schleife solange den Prompt-Text mit einer Eingabeaufforderung aus, bis entweder ein zulässiger Datenwert oder ein Symbol zur Simulationsbeendigung eingegeben wird. Die Symbole für die Simulationsbeendigung sind »exit«, »Exit«, »stop«, »Stop«, »halt« oder »Halt«: procedure read(promt: string; w: out integer; wmin: integer := integer’low + 1; wmax: integer := integer’high); –- (1) procedure read(promt: string; w: out real; wmin:
A.2 Die Bibliothek »Tuc« real := real’low/10.0; wmax: real := real’high/10.0); procedure read(promt: string; w: out tTyp);
489
–- (2) –- (3)
(1,2) Einlesen von Zahlenwerten mit einstellbarer Wertebereichsbeschränkung. (3) Einlesen der Werte für Datenobjekte vom Typ boolean, std_ulogic (Basistyp von std_logic), bit, bit_vector und std_logic_vector. Die folgende Prozedur hält die Simulation bis zum Betätigen der Eingabetaste an: procedure read;
Die Prozedur procedure CheckExit(pstr: inout tPString);
dient zum kontrollierten Simulationsabbruch bei einer Dialogeingabe. Wenn sie am Anfang des Lesebands eines der Symbole »exit«, »Exit«, »stop«, »Stop«, »halt« oder »Halt« findet, ruft sie die Funktion »StopSim« aus dem Package »Tuc.StopSim_pack« auf, die das Flag für die Simulationsbeendigung setzt und den Prozess, der »StopSim« aufruft, schlafen legt. A.2.4 Arithmetische Operationen (Tuc.Numeric_Sim und Tuc.Numeric_Synth) Beide Packages definieren die Datentypen type tUnsigned is array (natural range <>) of std_logic; type tSigned is array (natural range <>) of std_logic;
und überladen für diese die arithmetischen und logischen Operatoren. Für die Simulation werden die arithmetischen und logischen Operatoren so überladen, dass für ungültige Eingabewerte der Ergebniswert »ungültig« zurückgegeben wird. Sonst wird über die standardisierten Konvertierungsfunktionen dieselbe Operatorfunktion wie für den Typ unsigned bzw. signed aus dem standardisierten Package ieee.numeric_bit aufgerufen und das zurückkonvertierte Ergebnis zurückgegeben. In der Package-Version für die Synthese fehlen die Sonderbehandlungen für ungültige Operanden und Ergebnisse, weil diese in einer Synthesebeschreibung nicht zulässig sind. Beim Übergang von einer Simulationsbeschreibung mit ungültigen Werten zu einer Synthesebeschreibung ist das Package auszutauschen. •
Konvertierungsfunktionen nach und von integer: function function function function
int(x: tUnsigned) return integer; int(x: tSigned) return integer; to_tUnsigned(x: integer; n: natural) return tUnsigned; to_tSigned(x: integer; n: natural) return tSigned;
(n – Bitanzahl des Rückgabewertes)
490
•
A Ergänzungen zu VHDL
arithmetische Operatoren: function function function function function function function function function function function function function function
"-"(x: tSigned) return tSigned; "abs"(x: tSigned) return tSigned; "+" (a, b: tUnsigned) return tUnsigned; "+" (a, b: tSigned) return tSigned; "-" (a, b: tUnsigned) return tUnsigned; "-" (a, b: tSigned) return tSigned; "*" (a, b: tUnsigned) return tUnsigned; "*" (a, b: tSigned) return tSigned; "/" (a, b: tUnsigned) return tUnsigned; "/" (a, b: tSigned) return tSigned; "rem" (a, b: tUnsigned) return tUnsigned; "rem" (a, b: tSigned) return tSigned; "mod" (a, b: tUnsigned) return tUnsigned; "mod" (a, b: tSigned) return tSigned;
––––––––––––––-
(1) (1) (2) (2) (2) (2) (3) (3) (4) (4) (5) (5) (5) (5)
(1) Negation und Betragsbildung. Indexbereich des Ergebnisses: x’length - 1 downto 0
(2) Addition und Subtraktion. Indexbereich des Ergebnisses: max(a’length, b’length) - 1 downto 0
(3) Multiplikation. Indexbereich des Ergebnisses: a’length + b’length - 1 downto 0
(4) Division. Indexbereich des Ergebnisses: a’length - 1 downto 0
Bei einer Division durch Null wird im Simulations-Package das Ergebnis auf ungültig gesetzt und eine Warnung ausgegeben. (5) Divisionsrest und Modulo-Operation. Indexbereich des Ergebnisses: b’length - 1 downto 0
Bei einer Division durch Null wird im Simulations-Package das Ergebnis auf ungültig gesetzt und eine Warnung ausgegeben. •
logische Operatoren: function function function function function function function function function function function function function function
"not" "not" "and" "and" "nand" "nand" "or" "or" "nor" "nor" "xor" "xor" "xnor" "xnor"
(a: (a: (a: (a: (a: (a: (a: (a: (a: (a: (a: (a: (a: (a:
tUnsigned) return tUnsigned; tSigned) return tSigned; tUnsigned) return tUnsigned; tSigned) return tSigned; tUnsigned) return tUnsigned; tSigned) return tSigned; tUnsigned) return tUnsigned; tSigned) return tSigned; tUnsigned) return tUnsigned; tSigned) return tSigned; tUnsigned) return tUnsigned; tSigned) return tSigned; tUnsigned) return tUnsigned; tSigned) return tSigned;
A.2 Die Bibliothek »Tuc«
•
Rotations- und Verschiebeoperatoren: function function function function function function function function function function function function
•
491
"sll" "sll" "srl" "srl" "rol" "rol" "ror" "ror" "sla" "sla" "sra" "sra"
(a: (a: (a: (a: (a: (a: (a: (a: (a: (a: (a: (a:
tUnsigned; n: natural) return tUnsigned; tSigned; n: natural) return tSigned; tUnsigned; n: natural) return tUnsigned; tSigned; n: natural) return tSigned; tUnsigned; n: natural) return tUnsigned; tSigned; n: natural) return tSigned; tUnsigned; n: natural) return tUnsigned; tSigned; n: natural) return tSigned; tUnsigned; n: natural) return tUnsigned; tSigned; n: natural) return tSigned; tUnsigned; n: natural) return tUnsigned; tSigned; n: natural) return tSigned;
Vergleichsoperatoren: function function function function function function function function function function function function
"=" "=" "/=" "/=" ">" ">" "<" "<" ">=" ">=" "<=" "<="
(a, (a, (a, (a, (a, (a, (a, (a, (a, (a, (a, (a,
b: b: b: b: b: b: b: b: b: b: b: b:
tUnsigned) return boolean; tSigned) return boolean; tUnsigned) return boolean; tSigned) return boolean; tUnsigned) return boolean; tSigned) return boolean; tUnsigned) return boolean; tSigned) return boolean; tUnsigned) return boolean; tSigned) return boolean; tUnsigned) return boolean; tSigned) return boolean;
Die nachfolgenden Funktionen und Prozeduren dienen ausschließlich zur Simulation, im Simulations-Package mit und im Synthese-Package ohne Berücksichtigung des Pseudo-Wertes »ungültig«: •
Test auf Gültigkeit: function is_x (a: tUnsigned) return boolean; function is_x (a: tSigned) return boolean;
•
Umwandlung in eine Textdarstellung: function str(x: tUnsigned; fmt:character := ’b’) return string; function str(x: tSigned; fmt:character := ’b’) return string;
•
Abräumen von einem Zeichenbandobjekt: procedure get(pstr: inout tPString; w: out tUnsigned); procedure get(pstr: inout tPString; w: out tSigned);
•
Aufforderung zu einer Tastatureingabe: procedure read(promt: string; w: out tUnsigned); procedure read(promt: string; w: out tSigned);
492
A Ergänzungen zu VHDL
A.2.5 Pseudo-Zufallstest (Tuc.Zufallstest) Das Package stellt Funktionen zur Erzeugung gewichteter pseudo-zufälliger Bit- und Bitvektorwerte bereit, die den Zustand in einer globalen PackageVariablen speichern. Die Wichtung ist die Auftrittshäufigkeit des Bitwertes »1«. Die Datentypen zur Vorgabe der Wichtung sind subtype tWeight is real range 0.0 to 1.0; type tWeightVec is array (natural range <>) of tWeight;
Der Parameter »px« erlaubt es zusätzlich, eine Wahrscheinlichkeit px vorzugeben, mit der die Bitwerte mit dem Pseudo-Wert »ungültig« belegt werden. Folgende Funktionen werden exportiert: impure function rand return real; –- (1) impure function rand(w: tWeight; px: tWeight := 0.0) return std_logic; –- (2) impure function rand(x: std_logic; px: tWeight := 0.0) return std_logic; –- (3) impure function rand(wVec: tWeightVec; px: tWeight := 0.0) return std_logic_vector; –- (4) impure function rand(xVec: std_logic_vector; px: tWeight := 0.0) return std_logic_vector; –- (5)
(1) Rückgabe eines Pseudo-Zufallswertes im Bereich von 0.0 bis 1.0. (2) Rückgabe eines Bitwertes, der mit einer Wahrscheinlichkeit px ungültig, mit einer Wahrscheinlichkeit w · (1 − px ) »1« und sonst »0« ist. (3) Rückgabe eines Bitwertes, der mit einer Wahrscheinlichkeit px ungültig, mit einer Wahrscheinlichkeit 0,5 · (1 − px ) »1« und sonst »0« ist. Der Übergabeparameter »x« dient nur zur Unterscheidung des Funktionsaufrufs von denen der anderen überladenen Funktionen »rand«. (4) Rückgabe eines Bitvektors, in dem jedes Bit i mit einer Wahrscheinlichkeit px ungültig, mit einer Wahrscheinlichkeit wi · (1 − px ) »1« und sonst »0« ist. (5) Rückgabe eines Bitvektors, in dem jedes Bit i mit einer Wahrscheinlichkeit px ungültig, mit einer Wahrscheinlichkeit 0,5 · (1 − px ) »1« und sonst »0« ist. Der Parameter »xVec« legt nur die Vektorbreite des Rückgabewertes fest.
Sachverzeichnis
abs (Schlüsselwort), 158, 185, 205 Absorptionsgesetz, 125–127 Abtastflanke, 56, 66 Abtastperiode, 67–68 Abtastprozess Automat, 86 Pipeline, 382 Register, 67, 97 Simulation, 57–62 Synthese, 97, 106–111, 397 Abweisschleife, 196, 261, 391 Addierer Carry-Save-, 171 hierarchisch, 168 Ripple-, 161 schneller Übertragsdurchlauf, 167 seriell, 163 after (Schlüsselwort), 29–31, 33–36 aktiv Signalwert, 18, 358 Taktflanke, 56–60, 97 Akzeptorautomat, 84, 240 all (Schlüsselwort), 12, 23, 40, 190, 485 ALU, 424–429 Ampelsteuerung, 296 and (Schlüsselwort), 26, 34, 100, 205 Anfangswert Datenobjekt, 23–24 Parameter, 199 Register, 97 Zustandsregister, 78 Anschlusssignal, 10, 37, 116, 210
architecure (Schlüsselwort), 12, 23, 34, 38–40 array (Schlüsselwort), 221–230 ascending (Attribut), 222 Assembler, 421 assert (Schlüsselwort), 58–59, 245, 261, 284 Assoziativgesetz, 125–128, 250 Assoziativspeicher, 369–370 asynchrone Eingabe, 66–72, 84, 89, 274 Initialisierung, 68–70, 350 parallele Schnittstelle, 70–72 SRAM-Schnittstelle, 360 Attribute Bezeichner, 237 elementare Typen, 220–221 Felder, 222–223 Signale, 57–58, 202 Zusammenfassung, 482–484 Auffrischen (DRAM), 373 Auflösungsfunktion, 307 Aufzählungstyp, 218–219, 237, 241, 397 Ausdruck, 25–29, 100, 126–140 Ausgabefunktion, 74–76, 78–81 Auswahlanweisung, 79–81, 101, 114, 194–195, 426 Automat autonomer, 77, 82, 204, 352, 363 endlicher, 74–77 Handentwurf, 135–138 VHDL-Beschreibung, 79–82 Baumstruktur, 128, 169, 171, 250–252
494
Sachverzeichnis
BDD, 143–149 Befehlssatz, 420–422 begin (Schlüsselwort) Entwurfsbeschreibung, 14 Funktion, 199 Prozedur, 206 Prozess, 23 Berechnungsfluss, 26–28, 99–106 Bereichsvereinbarung, 214–218, 221–222 Betrag, 174, 175, 185, 194 Bibliothek Begriffsdefinition, 10 ieee, 22, 189 Tuc, 22, 189, 484–492 Vereinbarung, 12 bit (Datentyp), 17, 218 bit_vector (Datentyp), 18, 223 Bitvektortypen, 18–19, 21–22, 226–230 Block-Shifter, 184 Blockspeicher, 357–376, 384 body (Schlüsselwort), 14, 199 boolean (Datentyp), 17, 57–59, 99, 194–198, 218 Bottom-Up-Entwurf, 7, 301 buffer (Schlüsselwort), 37 Bus, 312 case (Schlüsselwort), 79–81, 194–195 character (Datentyp), 24, 481 component (Schlüsselwort), 41 constant (Schlüsselwort), 23, 225, 238 Constraints, 116–118 CORDIC, 278–286 D-Flipflop, 346–347 Datei öffnen, 235 zur Speicherinitialisierung, 259–261 zur Testeingabe, 294–296, 390 Datentyp Aufzählungstypen, 218 Begriffsdefinition, 17 eng verwandt, 217, 223–224, 227–229 physikalische Typen, 219 vordefinierte Typen, 17–19 Zahlentypen, 214–218 zusammengesetzte Typen, 221–233 DDR-SDRAM, 373 De Morgan’sche Regel, 126–128, 310
deaktivierbarer Treiber, 312, 344, 378 Dekrement, 87, 174, 185, 424 delayed (Attribut), 57, 60 delay_length (Untertyp), 19, 30 Delta-Delay-Modell, 31, 95 Disassembler, 436 Distributivgesetz, 125, 131 Dividierer, 177–181 don’t care, 131–134, 139–140, 417 downto (Schlüsselwort), 214, 221 DRAM, 344, 371–375 Dreiecksignalgenerator, 87 Eliminationsgesetz, 125–127, 131 else, elsif (Schlüsselworte), 194 end (Schlüsselwort) architecture, 38–40 case, 194 entity, 37–40 function, 199 if, 194 loop, 197 package, 199 procedure, 206 Endzustand, 84–86 entity (Schlüsselwort), 12, 23, 34, 37–40 Entprellen, 67–68 Entscheidungsdiagramm, binäres, 143–149 Entwicklungssatz (ROBDD), 146 Entwurfs -ablauf, 6–7, 265–286, 385–438 -automatisierung, 8–9 -beschreibung, 10, 12, 38 -einheit, 10, 37 -raum, 4–6 -strategie, 6 event (Attribut), 57–58, 203, 298 exit (Schlüsselwort), 198, 202, 210 exponentielles Wachstum, 51, 86, 140, 145 Fallunterscheidung, 58, 99, 100, 194, 399, 426 FCMOS, 309–312 Fehlerschwere, 58 Fehlerzustand, 86, 240
Sachverzeichnis Fehlfunktion, 51, 67, 70, 80, 83, 290, 354 Feld, 221–226 Festkommazahlen, 155 Festwertspeicher, 261, 375 FIFO, 262–265, 367–369 file (Schlüsselwort), 235–236 FIR-Filter, 384–385, 405–406 Flag, 416, 425 Folgezustand, 78, 79 for (Schlüsselwort) Warteanweisung, 31 Wiederholschleife, 197–198, 202 formal, 4–8, 10, 21, 167 Freigabesignal Latch, 111–115 Register, 108, 350 Treiber, 312 function (Schlüsselwort), 198–206 Funktion kombinatorische, 17, 27, 199–201 logische, 125 VHDL, 198–206 Funktionsbeschreibung, 5 Funktionsblockebene, 21 Gültigkeit, siehe Signalgültigkeit Gatter, 20, 309–319 generic (Schlüsselwort), 37–40, 59 geometrische Beschreibung, 6, 7, 317–320, 359, 375 Constraints, 116 gepufferte CMOS-Gatter, 341 GHDL (Simulator), 13, 30, 329 Gleitkommazahlen, 156–157 Glitch, 44–46 Glitch-frei, 56, 113 globale Variable, 204, 205, 428 halbformal, 4–6, 387 Hallo-Welt (Programm), 13–15 Haltezeit, 48–52, 55–56, 59–64, 329, 339–341 Hierarchie, 1–2, 40, 96, 237, 248 high (Attribut), 19, 152, 197, 220, 222 high-aktiv, 18 if (Schlüsselwort), 58, 194 image (Attribut), 220, 237, 238
495
imperative Beschreibungsmittel, 189–212 Modelle, 22–33 Import aus Packages, 12, 22, 190 impure (Schlüsselwort), 204–205, 427 in (Schlüsselwort) Parameterübergaberichtung, 206 Signalflussrichtung, 37 Wiederholschleife, 197–198, 202 inaktiv, 18 Indexbereich, 24, 221 inertial (Schlüsselwort), 47–48 informal, 4–5, 8, 21, 423 Initialisierung asynchron, 68–70 Automat, 78, 87, 271–275 Feldkonstante, 225, 259–261 FIFO, 262–263 Latch, 347 Register, 97, 98, 350 Speicherzellen, 346–347 Initialisierungsablauf, 391–392 Initialisierungsbefehl, 420 Initialisierungsfehler, 50, 69 Inkrement, 87, 174, 185, 424 inout (Schlüsselwort) Parameterübergaberichtung, 205–207, 232, 241 Signalflussrichtung, 37 Instanziierung (Teilschaltungen) component, 41 entity, 38–40 integer (Datentyp), 19, 158, 218 Inverter, 20, 309, 318 is (Schlüsselwort) Auswahlanweisung, 194 Datentypvereinbarung, 214–233 Entwurfseinheit, 12 Funktionsbeschreibung, 198 Packagevereinbarung, 14 Prozedurbeschreibung, 206 Prozessbeschreibung, 34 Klammernsetzung, 28–29, 191 kombinatorische Funktion, 17, 27, 199–201 Schaltung, 16, 33–52, 99–106 kombinatorischer Prozess, 80, 99 Kommutativgesetz, 125
496
Sachverzeichnis
Komplexgatter, 311, 319, 337 Konfigurationsparameter, 37–40 Konjunktionsmenge, 129 Konkatenation, 191 Bitvektoren, 80, 104 Zeichenketten, 25, 237 Konstanten -elimination, 27, 162, 174, 182 -tabelle, 282, 308, 313 -vereinbarung, 23 Kosinus, 278–283 KV-Diagramm, 131–137 Lade- und Speichereinheit, 417–418 last_event (Attribut), 57, 59–62, 112, 292 Lastkapazität, 303–306, 314, 323, 330–332, 344 last_value (Attribut), 57 Latch, 111–115, 346–347 Laufzeitanalyse, 51–52, 62, 319 Laufzeittoleranz, 44–51 leftof (Attribut), 220 length (Attribut), 220, 222, 245, 284 library (Schlüsselwort), 12, 23 Logikoptimierung, 27–28, 125–149 logische Operatoren, 26, 99, 191 lokal statisch, 37 loop (Schlüsselwort), 195–198 low (Attribut), 24, 152, 197, 202, 220 low-aktiv, 18, 360 map (Schlüsselwort), 38–41 Master-Slave-Flipflop, 69, 349 math_real (Package), 205, 282 Mealy-Automat, 76 Mehrportspeicher, 365–369, 417 Mehrversionsvergleich, 288–293 Mensch-Maschine-Interaktion, 7–8 Minterm, 129, 138–140 mod (Schlüsselwort), 191, 205, 217 Modell, Begriffsdefinition, 4 Moore-Automat, 77, 81, 84, 363 MOS-Transistor, 301–339 Multiplexer, 20, 99–105, 148–149, 315–317 Multiplizierer, 176–177 Nachhaltezeit, 56, 59–60, 118, 348–349
nand (Schlüsselwort), 26, 33, 191, 209 natural (Untertyp), 19, 158, 218 nebenläufige Prozedur, 209–211, 269, 396 Signalzuweisung, 34–36, 49, 71, 86 Negation, 28, 125, 130, 174 doppelte, 125–127 Neuinitialisierung, 83 nor (Schlüsselwort), 26, 100 Normalform, 131, 145 not (Schlüsselwort), 26, 29, 50, 205 now (Funktion), 31–33, 197, 204 null (Schlüsselwort), 91, 121, 435, 452 objektorientiert, 4, 248–250, 265, 388 Objekttyp, 202–203, 207–209, 236 on (Schlüsselwort), 31–33, 35 open (Schlüsselwort) Datei, 235 Zuordnung, 39, 203, 211 Operationsablaufgraph, 86–88 Begriffsdefinition, 74 Beispiele, 88, 165, 181, 275, 374 Operationscode (Prozessor), 420–422 Operatoren, 26, 191–193, 205–206 Optimierung, 5–9, 27, 128–140, 185–186, 280 optionale Aufrufparameter, 203, 211 or (Schlüsselwort), 26, 34, 162, 205, 206 Oszillator (Takterzeugung), 351–352 others (Schlüsselwort) Auswahlanweisung, 102, 194 Zuordnung, 211, 225, 228 out (Schlüsselwort) Parameterübergaberichtung, 206, 207 Signalflussrichtung, 37, 38 Package, 14, 198–199, 244 Begriffsdefinition, 10 Bibliothek ieee math_real, 205, 282 std_logic_1164, 22, 189 std_logic_arith, 229 std_logic_signed, 229 std_logic_unsigned, 229 Bibliothek Tuc Ausgabe, 237–239, 484–485 Eingabe, 240–242, 487–489 Numeric_..., 226–229, 489–491
Sachverzeichnis StopSim, 486–487 Zufallstest, 492 package (Schlüsselwort), 14, 198–199 parallele Schnittstelle, 70 Parallelschaltung, 305, 310, 334 Parameterliste, 199, 203, 206 parametrisierte Schaltung, 2, 37, 96 Phasenregelkreis, 354–355 Physikalischer Typ, 219–220 Pipeline Ausbancieren, 383 Beispielentwürfe, 405–407, 430–435 Prinzip, 381–383 Platzierung, 3, 116, 317 Plausibilitätstest, 288–289, 296 PLL, siehe Phasenregelkreis port (Schlüsselwort), 37–38 pos (Attribut), 218, 220, 238 positive (Untertyp), 19, 158, 218 postponed (Schlüsselwort), 270 pred (Attribut), 220 Prelldauer, 67–68 Primterm, 138–140 procedure (Schlüsselwort), 206–212 process (Schlüsselwort), 13, 34–36 programmierbare Ausgabeinvertierung, 379 Logikschaltkreise, 116, 119, 376–380 Tabellenfunktion, 377–378 UND-Matrix, 378 Prototyptest, 119 Prozedur, 206–212 Prozess, 13 Begriffsdefinition, 10 kombinatorischer, 80, 99 mit Weckliste, 34–36, 98, 99 Postponed, 270 Test-, 23, 190 Prozessorentwurf, 414–438 Pseudo-Zufallstest, 204, 289–293, 427 Quadratur-Encoder, 89–91 Quarzoszillator, 351–352 Quine’sche Tabellen, 139–140 Quine-McCluskey, 138–140 Rückkehr-Anweisung, 199, 206 Rückweiszeit, 47
497
range (Attribut), 197, 220, 222, 232, 238, 250, 253, 282 range (Schlüsselwort), 214, 219, 221, 222 Rechenwerke, 103–105, 161–186, 424–429 record (Schlüsselwort), 230–232 Register Extraktion, 98–99, 107–111 Schaltung, 349–350 Simulationsmodell, 55–91 Synthesebeschreibung, 97–99 Register-Transfer-Funktion, 61–64, 68, 78–91, 96–121, 247–248, 348 Registersatz, 417 Reihenschaltung, 305, 310, 334–335 reject (Schlüsselwort), 47–48 relative Transistorbreite, 330, 334–335 report (Schlüsselwort), 13, 58–59 return (Schlüsselwort), 198–199 rightof (Attribut), 220 Ringinverter, 120, 328–329, 332–333 RISC-Prozessor, 415 ROBDD, 144–149 rol (Schlüsselwort), 183, 426 ROM, 375 ror (Schlüsselwort), 183, 426 Rotation, 182–184, 279, 424 RS-Flipflop, 120, 345–346 Schaltalgebra, 3, 27, 125–140 Schaltkreis, 301–380 Schaltnetz, siehe kombinatorische Schaltung Schaltungsgenerator, 3, 7, 101 Schaltverhalten, 303 Schaltwerk, siehe sequenzielle Schaltung Schieberegister, 82, 109, 165, 181, 383 Schleifenabbruch, 198, 202, 232, 271 Schnittstelle asynchron parallel, 70–72 Entwurfseinheit, 37–38 seriell, 265–278 Speicher, 258–259, 360–365, 373–375 Schreib-Lese-Speicher, 253–259, 357–375 schwebende Zuweisung, 30, 46–49 sequenzielle Schaltung, 74–91, 106–111 Begriffsdefinition, 74
498
Sachverzeichnis
serielle Schnittstelle, 265–278 severity (Schlüsselwort), 58–59 shared (Schlüsselwort), 204, 205, 428 Sicherheitsbereich, 17 signal (Schlüsselwort), 23 Signal, Begriffsdefinition, 17 Signalflussplan, 20–22 Signalflussrichtung, 37 Signalgültigkeit, 18, 48–51, 59–62, 66–72, 244–247, 335–339, 430 Signalquellen, mehrere, 307 Signalvereinbarung, 23 Signalzuweisung, 29–36 signed (Datentyp), 224 Simulation ereignisgesteuert, 33–36, 335–341 Transistorebene, 335–339 von Zeittoleranzen, 48–51, 339–341 zeitdiskret, 323–329 Sinus, 278–283 sla (Schlüsselwort), 183 sll (Schlüsselwort), 183, 412, 426 Software-orientierte Ablaufbeschreibung, 74, 91, 163 Soll-Ist-Vergleich, 288, 294–296 Speicher dynamischer (DRAM), 344, 371–375 statischer (SRAM), 358–369 Speichercontroller, 362–365 Speicherengpass, 381–384 Speicherhierarchie, 403 Spezifikationstest, 296–299 Sprung-Pipeline, 418–419 Sprungbedingung, 429–430 sra (Schlüsselwort), 183, 282, 286, 412 SRAM, 358–369 srl (Schlüsselwort), 178, 183, 426 stable (Attribut), 57 Startzustand, 74–76, 78, 84, 276 std_logic (Datentyp), 17, 26, 218, 307–309, 313–315 std_logic_arith (Package), 229 std_logic_signed (Package), 229 std_logic_unsigned (Package), 229 std_logic_vector (Datentyp), 18, 22, 102, 226 Stellenwertsystem, 151–152 string (Datentyp), 24, 481 Strukturbeschreibung, 5, 37–41
Subtrahierer, 173 subtype (Schlüsselwort), 216–217, 219, 222 succ (Attribut), 220 Syntax Begriffsdefinition, 9 -regeln, 116, 241, 479 -test, 13 Synthese, 2–4, 95–121 Synthese-Report, 119, 187 Systemabsturz, 67, 82–83 Tabellenfunktion, 76–77, 105, 196, 200, 377–378 Takt -flankenübernahme, 347–349 -generator, 291, 351 Modellierung, 56 -netz, 352 -teiler, 352–353 -versatz, 62–64, 349–350, 352 -versorgung, 351–355 Tasteneingabe, 67, 84–86 Technologieabbildung, 3, 127, 138, 378 Technologiemaß, 319 Teilschaltungsinstanziierung, 38–41 testbench, siehe Testrahmen Testeingabe aus einer Datei, 294–296 Testrahmen Begriffsdefinition, 10 Beispiele, 22–23, 40, 189–191, 427–429, 435–438 Schablonen, 288–299 text (Dateityp), 485 Textausgabe, 24–25, 236–239 then (Schlüsselwort), 58, 194 time (Datentyp), 19, 219 to (Schlüsselwort), 214, 221 Top-Down-Entwurf, 6 Transfergatter, 315–317, 378 Transistor, 305–306 transport (Schlüsselwort), 47, 112, 209 Treiber, deaktivierbar, 312, 344, 378 tSigned (Datentyp), 158, 227 tUnsigned (Datentyp), 158, 227 type (Schlüsselwort), 214, 218, 219, 221, 230 Typkonvertierung, 217, 227, 237 Typqualifikation, 226, 480
Sachverzeichnis Übergangsbedingung, 76, 87 Übergangsfunktion, 74, 76, 78, 79 überladen, 203–205, 211–212 Umformungsregeln, 125–127 Umladestrom, 304, 323, 330 Umlaute, 24 uniform (Funktion), 205 unreine Funktion, 204–205 unsigned (Datentyp), 224 Untertyp, 216–217, 219, 222, 282, 307 until (Schlüsselwort), 31, 163, 180 unzulässige Zustände, 82–83 use (Schlüsselwort), 12, 40, 190, 205 val (Attribut), 218, 220, 238 value (Attribut), 220 variable (Schlüsselwort), 23 Verbund, 230–232, 240, 244–245, 262 Verdrahtung, 3, 116, 317 Vereinbarung Bibliotheksbezeichner, 12, 22 Dateiobjekt, 235–236 Datentyp, 17–19, 24, 214–232 Konstante, 23 Package, 199 Prozedur, 206 Signal, 23 Untertyp, 216–217, 219, 282, 307 Variable, 23 Vergleicher, 102–106, 182, 292 Verschiebeoperator, 182–184, 282, 425 Verschmelzung, 28, 135, 144–149 Verzögerungsmodell, 29–30, 46–48 Verzögerungsparameter, 329–333 Verzögerungszeit, 29, 48–52, 55–56, 59–64, 329, 339–341 Vorhaltezeit, 56, 59–64 vorzeichenbehaftete Zahlen, 153–157 Wachstum, exponentielles, 51, 86, 140, 145
499
wait (Schlüsselwort), 31–33, 163 Warteanweisung, 13, 31–35, 163–166, 207–212, 275–281 Watchdog, 83 Weckbedingung, 22, 31–32 Weckliste, 31, 34, 58 Wertetabelle, 126, 130, 131, 143–146, 195, 199, 306 when (Schlüsselwort), 79, 80, 194–195 while (Schlüsselwort), 196–197, 208 Wiederholschleife, 197–198, 202 xnor (Schlüsselwort), 26, 182, 205 xor (Schlüsselwort), 26, 71, 162, 205 Zahlendarstellung, 150–159, 214–218, 226–230 Zahlenschloss, 84–86 Zahlentypen, 214–218 Zähler Johnson-, 82–83, 354 Operationsablauf-, 87–91, 165 Register-Transfer-Funktion, 174 vorwärts/rückwärts, 87, 91, 108–109, 136–138 Zeitüberwachung, 83 zeitdiskrete Simulation, 323–329 Zeitschnittlinien (Pipeline), 383 Zuordnung Anschlusssignale, 38–41 Aufrufparameter, 203, 211 Feldelemente, 224–226, 437 Verbund, 230 Zustandscodierung, 74, 76, 86, 136 Zustandsgraph, 74–83 Zustandsregister, 75, 78, 79, 84, 89, 135 Zuverlässigkeit, 51, 67 Zuweisungsoperator, 25 Zweierkomplement, 153–155 Zykluslänge, 82, 87
Literaturverzeichnis
[1] Constraints Guide. http://www.xilinx.com/support/documentation/ dt_ise11-1_userguides.htm. [2] Digilent Products. http://www.digilentinc.com/. [3] GHDL home page. http://ghdl.free.fr/. [4] Welcome to GTKWave. http://gtkwave.sourceforge.net/. [5] Xilinx Design Tools. http://www.xilinx.com/tools/designtools.htm. [6] XST User Guide. http://www.xilinx.com/support/documentation/ dt_ise11-1_userguides.htm. [7] IEEE Standard for Binary Floating-Point Arithmetic (ANSI/IEEE Std 754-1985). New York: The Institute of Electrical and Elektronics Engineers., 1985. [8] IS61LV25616AL: 256K x 16 High Speed Asynchronous CMOS Static Ram with 3.3V Supply. http://www.issi.com, 2006. [9] 1076-2008 IEEE Standard VHDL Language Reference Manual. New York: The Institute of Electrical and Elektronics Engineers., 2008. [10] S. B. Akers. Binary decision diagrams. IEEE Transactions on Computers, C-27:509–516, Juni 1959. [11] P. J. Ashenden. The Designer’s Guide to VHDL. Morgan Kaufmann Publishers, Inc., 2008. [12] J. Bergeron. Writing Testbenches using SystemVerilog. Springer, 2006. [13] J. Borgmeyer. Grundlagen der Digitaltechnik. München. Carl Hanser Verlag, 1997. [14] L. Borucki. Digitaltechnik. Teubner, 2000. [15] R. E. Bryant. Graph-Based Algorithms for Boolean Function Manipulation. IEEE Transactions on Computers, C-35:677–691, August 1986. [16] P. P. Chu. RTL Hardware Design Using VHDL: Coding for Efficiency, Portability, and Scalability. John Wiley & Sons, 2006. [17] P. P. Chu. FPGA Prototyping by VHDL Examples: Xilinx Spartan-3 Version. John Wiley & Sons, 2008. [18] K. Dembowski. Das AW Handbuch der Hardwareprogrammierung: 2 Bände. Addison-Wesley, 2006.
502
Literaturverzeichnis
[19] R. Drechsler and B. Becker. Graphenbasierte Funktionsdarstellung. Boolesche und Pseudo-Boolesche Funktionen. Teubner Verlag, 1998. [20] R. Ernst and I. Könenkamp. Digitale Schaltungstechnik für Elektroniker und Informatiker. Spectrum Akademischer Verlag, 1995. [21] J. Goerth. Bauelemente und Grundschaltungen. Teubner, 1999. [22] J. Hartmann and G. Kemnitz. How to do weighted random testing for BIST? In IEEE International Conference on Computer Design, pages 568–571, 1993. [23] J. L. Hennessy and D. A. Patterson. Computer Architecture. A Quantitative Approach. Morgan Kaufmann, 2006. [24] D. W. Hoffmann. Grundlagen der Technischen Informatik. Hanser Verlag, 2007. [25] B. Hoppe. Mikroelektronik 1. Vogel-Fachbuch, 1997. [26] G. Jorke. Rechnergestützter Entwurf digitaler Schaltungen: Schaltungssynthese mit VHDL. Hanser, 2004. [27] G. Kemnitz. Web-Projekte zur Vorlesung Entwurf digitaler Schaltungen. http://techwww.in.tu-clausthal.de/site/Lehre/VHDL-Web-Projekte/. [28] G. Kemnitz. Untersuchungen zu Spezialhardware für ausgewählte Algorithmen der Bildverarbeitung. In Tech. Rep. FI/95/11, Fakultät Informatik,TU Dresden, 1995. [29] G. Kemnitz. Test und Verlässlichkeit von Rechnern. Springer, 2007. [30] G. Kemnitz. Technische Informatik Band 1: Elektronik. Springer, 2009. [31] F. Kesel and R. Bartholomä. Entwurf von digitalen Schaltungen und Systemen mit HDLs und FPGAs: Einführung mit VHDL und SystemC. Oldenbourg, 2009. [32] H. Klar. Integrierte digitale Schaltungen MOS / BICMOS. Springer, 1996. [33] M. V. Künzli and M. Meili. Vom Gatter zu VHDL: Eine Einführung in die Digitaltechnik. vdf Hochschulverlag, 2007. [34] C. Y. Lee. Representation of Switching Circuits by Binary-Decision Programs. Bell Systems Technical Journal, 38:985–999, 1959. [35] H. A. Mallot. Sehen und die Verarbeitung visueller Information. Vieweg, 2000. [36] M. M. Mano and M. D. Ciletti. Digital Design. Pearson, 2006. [37] P. Molitor and J. Ritter. VHDL: Eine Einführung. Pearson, 2004. [38] P. Molitor and C. Scholl. Datenstrukturen und effiziente Algorithmen für die Logiksynthese kombinatorischer Schaltungen. Teubner, 1999. [39] P. Pirsch. Architekturen der digitalen Signalverarbeitung. Teubner, 1996. [40] P. Rechenberg and G. Pomberger. Informatik-Handbuch. Carl Hanser Verlag, 2002. [41] J. Reichardt and B. Schwarz. VHDL-Synthese: Entwurf digitaler Schaltungen und Systeme . Oldenbourg, 2009. [42] W. Schiffmann and R. Schmitz. Technische Informatik (Teil 1). SpringerVerlag, 1996. [43] C. Siemers and A. Sikora. Taschenbuch Digitaltechnik. Hanser, 2007.
Literaturverzeichnis
503
[44] D. Sommerfeld. Minimierung von Schaltfunktionen mit dem Quine-McCluskey-Verfahren. http://www2.in.tu-clausthal.de/dsommerfeld/qmc.pdf. [45] K. ten Hagen. Abstrakte Modellierung digitaler Schaltungen. Springer Verlag, 1995. [46] U. Tietze and C. Schenk. Halbleiter-Schaltungstechnik. Springer-Verlag, 2002. [47] K. Urbanski and R. Woitowitz. Digitaltechnik. Springer, 2006. [48] G. Vastianos. Boolean Functions’ Minimisation Software Based on the Quine-McCluskey Method. Athen: Software Notes, 1998. [49] J. Waikukauski, E. Lindbloom, E. B. Eichelberger, and O. Forlenza. A Method for Generating Weighted Random Test Patterns . IBM J. Res. Develop, pages 149–161, 1989. [50] I. Wegener. Effiziente Algorithmen für grundlegende Funktionen. Teubner, 1996. [51] I. Wegener. Komplexitätstheorie. Grenzen der Effizienz von Algorithmen. Springer, 2003. [52] R. Weissel and F. Schubert. Digitale Schaltungstechnik. Springer, 1995.