Entwurf digitaler Schaltungen mit Hochsprachen (VHDL)

Entwurf digitaler Schaltungen mit Hochsprachen (VHDL)
Entwurf digitaler Schaltungen
mit
Hochsprachen (VHDL)
Einführung in die Hardwarebeschreibungssprache VHDL (Text/Graphik)
sowie Schaltungssynthese bw. -optimierung
F. Stockmayer
Inhaltsverzeichnis
Inhaltsverzeichnis
1
Entwurfsspezifikation durch Hochsprachen
5
1.1
Einführung
5
1.1.1
1.1.2
1.2
1.2.1
1.2.2
1.2.3
1.2.4
1.2.5
1.2.6
1.2.7
1.2.8
1.3
1.3.1
1.3.2
1.3.3
1.3.4
1.4
1.4.1
1.4.2
1.4.3
1.4.4
1.5
1.5.1
1.5.2
1.5.3
1.6
1.6.1
1.6.2
1.6.3
1.6.4
Ursprung von VHDL ................................................................................5
VHDL - Entwurfszyklus ...........................................................................6
Aufbau eines VHDL-Entwurfs
10
Signale (Signals) ...................................................................................10
Schnittstellen (Ports) .............................................................................13
Entities ..................................................................................................14
Die Arbeitsbibliothek WORK .................................................................14
Architectures .........................................................................................15
Komponenten (Components) ................................................................17
Konfigurationen (Configurations) ..........................................................18
Beispiel: Zweistellige Umwandlung ’Hex nach 7Segment’....................20
Konkurrierende Anweisungen (Concurrent Statements)
21
Einfache Signalzuweisung (Concurrent Signal Assignment) ................21
Bedingte Signalzuweisung (Conditional Signal Assignment) ................22
Selektierte Signalzuweisung (Selected Signal Assignment) .................23
Beispiel: Kodierer mit Priorisierung der Eingänge.................................24
Das Simulationsmodell in VHDL
26
Treiber (Drivers) ....................................................................................26
Delta-Delay-Mechanismus ....................................................................26
Modellierung von Verzögerungen .........................................................29
Beispiel: Vergleich von Transport und Inertial Delay ............................32
Prozesse (Processes)
34
Eigenschaften .......................................................................................34
Signale und Variablen ...........................................................................36
Sequentielle Anweisungen (Sequential Statements) ............................39
Sequentielle (Taktsynchrone) Logik
52
Kombinatorik .........................................................................................52
Register und D-Flip-Flop .......................................................................53
Getaktete Prozesse...............................................................................54
Beispiel: Verarbeitung asynchroner Bussignale....................................61
ii
CAE: VHDL
1.7
Typen
1.7.1
1.7.2
1.7.3
1.7.4
1.7.5
1.7.6
1.7.7
1.7.8
1.8
Standard Typen.....................................................................................63
Enumerator ...........................................................................................67
Physikalische Typen .............................................................................68
Record...................................................................................................68
Array......................................................................................................69
Access...................................................................................................70
File ........................................................................................................71
Beispiel: Modellierung eines Stacks mit Access-Typen ........................72
Operatoren
1.8.1
1.8.2
1.8.3
1.8.4
1.8.5
1.9
Unterprogramme (Subprograms)
81
Funktionen (Functions) .........................................................................81
Prozeduren (Procedures)......................................................................82
Beispiel: Wired-Or mit Resolution-Function ..........................................84
Test-Umgebung (Test-Bench)
1.10.1
1.10.2
1.10.3
1.10.4
1.11
75
Standard Operatoren ............................................................................76
Boolesche Operatoren ..........................................................................76
Vergleichsoperatoren ............................................................................77
Überladen von Operatoren....................................................................78
Beispiel: Addition von Enumeratoren mit überladenem '+' - Operator ..79
1.9.1
1.9.2
1.9.3
1.10
63
87
Das Stimuli-Modell ................................................................................87
Response-Modell ..................................................................................89
Package TextIO ....................................................................................89
Beispiel: Test-Bench .............................................................................91
Packages und Libraries
95
1.11.1 Aufbau von Packages ...........................................................................95
1.11.2 Bibliotheken (Libraries) .........................................................................95
1.11.3 Beispiel: Überladener Operator in einer Package .................................96
1.12
Advanced VHDL
98
1.12.1 Generics................................................................................................98
1.12.2 Attribute.................................................................................................99
1.13
iii
Literatur
103
Inhaltsverzeichnis
2
Graphische Verhaltensspezifikation
104
2.1
Einführung
104
2.1.1
2.1.2
2.1.3
2.2
2.2.1
2.2.2
2.2.3
2.2.4
Übersichtliche Darstellung ..................................................................104
Gliederung des Entwurfs.....................................................................105
Der Entwurfszyklus mit graphischer Spezifikation ..............................105
Prinzipielle graphische Darstellungsmöglichkeiten
106
Blockdiagramme .................................................................................106
Wahrheitstabellen ...............................................................................107
Flußdiagramme ...................................................................................109
Zustandsdiagramme ...........................................................................111
2.3
Literatur
120
3
Synthese
121
3.1
Einführung
121
3.2
Kodierbeispiele für synthesefähigen VHDL-Kode
122
3.3
Partitionierung
125
3.4
Modifizieren von Hierarchien
128
3.5
Optimierung
129
3.5.1
3.5.2
3.5.3
3.5.4
Auswirkung der Optimierungsvorgaben ..............................................130
Optimierungsstrategien .......................................................................132
Optimierung zweistufiger Logik ...........................................................135
Optimierung sequentieller Logik..........................................................138
3.6
Retiming
142
3.7
Umsetzung auf die Zieltechnologie (Technology Mapping)
144
3.7.1
Mapping ..............................................................................................145
3.8
Synthesefähige Konstrukte, Attribute, Typen und Operatoren
147
3.9
Literatur
151
4
Anhang B: VHDL - Syntax
152
5
Anhang C: Packages
162
iv
Einführung
1 Entwurfsspezifikation durch Hochsprachen
1.1 Einführung
VHDL hält als Beschreibungssprache immer mehr Einzug in die Entwicklung digitaler Hardware und
bietet reichhaltige Unterstützung zur Bewältigung der stetig steigenden Anforderungen. Die folgenden
Abschnitte bilden zusammen mit vielen Beispielen eine Grundlage für die praktische Umsetzung und
zeigen Lösungsansätze für häufig gestellte Fragen. Die einzelnen Abschnitte bauen aufeinander auf und
fügen so die vielen Teilaspekte zu einem Gesamtbild zusammen. Dabei ist das Ziel nicht eine vollständige
syntaktische Beschreibung der Sprache, sondern vielmehr das Herausstellen wesentlicher Elemente, die
bei der praktischen Umsetzung in einen synthesefähigen Entwurf wichtig erscheinen. Schwerpunkte bilden
die Themen:
• Aufbau eines VHDL-Entwurfes
• Konkurrierende und sequentielle Anweisungen: Anwendung in synchronen Entwürfen
• Aufbau einer Simulationsumgebung
1.1.1 Ursprung von VHDL
VHDL ist eine Beschreibungssprache für digitale Schaltungen. Sie ist aus einer Initiative innerhalb des
VHSIC - Programms (Very High Speed Integrated Circuits) des amerikanischen
Verteidigungsministeriums hervorgegangen. Die treibende Kraft hierbei war der problematische Zustand,
daß Zulieferfirmen unterschiedliche Beschreibungssprachen zur Entwicklung ihrer Systeme verwendeten.
Dies machte nicht nur einen Austausch der Entwürfe untereinander, bzw. eine Zusammenarbeit in großen
Projekten nahezu unmöglich, auch gab es keine Garantie, daß die einzelnen Sprachen für die erwartete
Lebensdauer der damit beschriebenen Systeme Bestand hatten.
Die Lösung war eine standardisierte Sprache mit garantierter Zukunft. Erste Entwürfe dieser Sprache
gingen Anfang der 80er-Jahre aus dem erwähnten VHSIC-Programm hervor, was ihr den Namen VHDL
(VHSIC Hardware Description Language) gab. Anschließend hat das Institute of Electrical and Electronics
Engineers (IEEE) die Sprache in einen Standard überführt und im Jahr 1987 mit der Bezeichnung IEEE1076 ratifiziert (VHDL’87). Damit war der Weg frei für die Entwicklung der nächsten Generation
integrierter Schaltungen.
Im Vergleich zur klassischen Entwurfsmethode auf Gatterebene, eignet sich die neue Methode zum
Entwurf großer und komplexer Schaltungen. Vor allem die vielseitigen Möglichkeiten bereits während der
Simulation konzeptionelle und logische Fehler frühzeitig zu erkennen, erhöht die Qualität des Entwurfes
und reduziert letztlich die Entwicklungsdauer.
Aufgrund der zum größten Teil technologieunabhängigen Beschreibung mit VHDL, lassen sich die
Entwürfe mit Hilfe von Synthesewerkzeugen auf die gewünschte Zieltechnologie abbilden. Diese
Eigenschaft ist die Voraussetzung dafür, um mit der rasanten Entwicklung der Halbleitertechnologie
Schritt halten zu können. Nicht nur das Portieren bestehender Schaltungen auf neue Herstellungsprozesse,
auch die Wiederverwendung bereits getesteter und bewährter Schaltungsteile stellen einen wichtigen
Aspekt dar.
Die Standardisierung von VHDL ist auch noch nicht abgeschlossen. Gerade die zunehmende
Verbreitung dieser Sprache macht eine regelmäßige Erweiterung und Ergänzung notwendig. Im Jahre 1993
fand die letzte Erweiterung statt (VHDL’93). Dieser Stand ist im IEEE-Standard “VHDL Language
Reference Manual” festgehalten /LRM93/ und ist auch Grundlage für diese Einführung in die
Beschreibungssprache VHDL.
5
CAE: VHDL
1.1.2 VHDL - Entwurfszyklus
VHDL unterstützt alle Phasen im Verlauf einer Schaltungsentwicklung und ermöglicht so den in Bild 4.5
vorgestellten Entwurfszyklus, der sich folgendermaßen aufteilt:
• Systemmodellierung (Spezifikationsphase)
• Beschreibung auf Register-Transfer-Ebene (Entwurfsphase)
• Netzliste (Implementationsphase)
Die universelle Verwendbarkeit stellt aber auch hohe Anforderungen an die Sprache selbst und das läßt
den Einstieg vielleicht wie eine unüberwindbare Hürde erscheinen. VHDL ist jedoch eine vielschichtige
Sprache, die für jede der drei genannten Phasen besondere Eigenschaften bereithält. Für die praktische
Anwendung ist daher in jeder Phase jeweils nur eine Untermenge des gesamten Sprachumfanges relevant,
was den Überblick erleichtert und den Lernprozess vereinfacht.
Spezifikationsphase
So beinhaltet das Systemmodell hauptsächlich die Algorithmen und nimmt noch keine Rücksicht auf die
Synthetisierbarkeit. Mittels einer formalen Spezifikation soll ein erstes Modell dazu dienen, das
Systemverhalten und damit die Funktionalität zu untersuchen und zusammen mit dem Auftraggeber zu
überprüfen, ob die Aufgabenstellung verstanden wurde und alle Anforderungen berücksichtigt sind. Im
Vordergrund stehen mehr die Algorithmen, die das grundsätzliche Schaltungsverhalten beschreiben.
Entwurfsphase
Im nächsten Schritt erfolgt dann die Umsetzung der formalen Spezifikation in eine synthesefähige
Beschreibung. Um die Randbedingungen bezüglich Schaltungsgröße, verfügbare Ressourcen (Anzahl
Gatteräquivalente) und Taktfrequenz zu erfüllen, hat der Entwickler dabei die schwierige Aufgabe,
gedanklich ein Brücke zu schlagen zwischen den Konstrukten der Programmiersprache (IF, ELSIF, CASE,
usw.) und deren optimaler Umsetzung auf Register-Transfer-Ebene. Damit ist die Art und Weise gemeint,
wie die Signale miteinander verknüpft (transformiert) und in einer taktsynchronen Schaltung von einem
Flip-Flop (Register) zum nächsten weitergereicht werden. Bild 1-1 zeigt den prinzipiellen Aufbau
taktsynchroner Schaltungen.
Dabei interessieren normalerweise weniger die schaltungstechnischen Details der Kombinatorik, die
zwischen diesen beiden Registern liegt, sondern mehr das Zusammenspiel mit der gesamten umgebenden
Schaltung
D Q
Kombinatorik
Register
D Q
Takt
Bild 1-1: Schaltungsbeschreibung auf Register-Transfer-Ebene
Die Beschreibung kann in Textform erfolgen oder als Grafik in Form von Blockdiagrammen,
Flußdiagrammen, Zustandsdiagrammen und Tabellen mit anschließender automatischer VHDLCodegenerierung. Bild 1-2 gibt dazu ein repräsentatives Beispiel. Kapitel 5 befaßt sich mit graphischen
Beschreibungsformen.
6
Einführung
Bild 1-2: Beispiel zur Beschreibung einer Schaltung als Grafik oder Text
Eine der großen Vorteile der Sprache VHDL ist die Möglichkeit mit ihrer Hilfe auch eine
Testumgebung, eine sogenannte Test-Bench, zu realisieren. Mit Hilfe der Test-Bench werden die Eingänge
der zu testenden Schaltung mit möglichst realen Signalverläufen versehen und die Simulation zeigt dann,
ob an den Ausgängen die erwarteten Ergebnisse erscheinen. Diese Überprüfung der Simulationsergebnisse
kann ebenfalls von der Test-Bench durchgeführt werden.
Bild 1-3 zeigt ein Simulationsergebnis, worin das Schaltungsmodell alle Zustände und
Zustandsübergänge einmal durchläuft. Die Simulation und die Überprüfung der Ergebnisse nimmt oft die
meiste Zeit in einem Entwicklungsablauf in Anspruch. Dies ist wichtig, um sicherzustellen, daß der
Entwurf funktional fehlerfrei ist und damit bereit für den nächsten Entwurfschritt, die Synthese.
Bild 1-3: Ergebnis einer VHDL-Simulation
7
CAE: VHDL
Mitunter kann es vorkommen, daß nach der Synthese ein kritischer Pfad auftaucht, der nur durch eine
Änderung in der VHDL-Beschreibung entschärft werden kann. Mitunter kann das auch eine Änderung der
Spezifikation bedeuten. Es entstehen somit auf dem Weg zur fertigen Netzliste mehrere Iterationsschleifen
(vgl. Bild 1-5).
Implementationsphase
ASIC oder FPGA? Diese Frage stellt sich im Entwicklungsablauf erst in dieser Phase. Entscheidend ist
letztlich die Einbindung einer entsprechenden Bibliothek während des Synthesevorgangs. Hat das
Synthesewerkzeug alle notwendigen Daten, so ist die erzeugte Schaltung ein genaues Abbild der textlichen
Beschreibung, obwohl es kaum mehr möglich ist, die eigene Schaltung in dem Gewirr von Gattern und
Verbindungen wiederzuerkennen. Vorgaben hinsichtlich Fläche und Geschwindigkeit setzt das Werkzeug
bestmöglich um. Eine Simulation auf Gatterebene, mit dem Vergleich der Simulationsergebnisse aus der
Entwurfsphase, verschafft zusätzliche Sicherheit.
Bild 1-4: Die synthetisierte Schaltung
Die ersten beiden Phasen (Spezifikation und Entwurf) können für kleine und überschaubare Entwürfe
zusammengefaßt werden, d.h. die Systemmodellierung erfolgt bereits synthesefähig auf Register-TransferEbene. Die Synthese, Bestandteil der dritten Phase, läuft weitgehend automatisch ab, womit der Entwurf
bzw. die Beschreibung mit VHDL auf Register-Transfer-Ebene in den Vordergrund tritt und auch den
Schwerpunkt in diesem Kapitel bildet.
8
Einführung
Beschreibung mit VHDL:
Systemebene
Fehlerkorrektur
evt. ungeeigneter Algorithmus:
Änderung der Spezifikation
Spezifikationsphase
Idee
Simulation
Ergebnis
Vergleich:
File A = File B ?
Fehlerkorrektur
Simulation
Optimierung
kritischer Pfad
Optimierung kritischer Pfad
Implementationsphase
Entwurfsphase
Beschreibung mit VHDL:
Register Transfer Ebene
Ergebnis
Prozess
0.18m
Synthese/Optimierung
statistische
Gatterlaufzeiten
File A
File B
Vergleich:
File B = File C ?
Bibliothek
Zieltechnologie
Simulation
Ergebnis
File C
exakte
Gatterlaufzeiten
Layout
ASIC/FPGA
Bild 1-5: Entwurfszyklus (Design-Flow) einer Schaltung mit VHDL
9
CAE: VHDL
1.2 Aufbau eines VHDL-Entwurfs
Dieser Abschnitt behandelt grundlegende Aspekte zum Aufbau eines VHDL-Entwurfs:
•
•
•
•
•
Signale und mehrwertige Logik
Schnittstellensignale und Ports
Entity: Das äußere Erscheinungsbild eines Moduls
Architecture: Die Funktionsbeschreibung eines Moduls
Configuration: Die Bauanleitung zu einem Gesamtsystem
1.2.1 Signale (Signals)
Signale haben in VHDL eine zentrale Bedeutung. Ähnlich wie Variable in anderen Programmiersprachen
sind Signale in VHDL die Datenträger. Nach einer Signalzuweisung nimmt das Signal einen bestimmten
logischen Wert an. Natürlich kann das Signal dann auf seinen augenblicklichen Wert abgefragt, oder mit
anderen Signalen und geeigneten Operatoren verknüpft werden.
Vergleichbar mit einem Stück Draht verbindet ein Signal Ausgang und Eingang zweier Teilschaltungen
(z.B. Gatter, Flip-Flop etc.). Doch nicht jedes Signal ist nach der Synthese auch tatsächlich als
Verbindungsleitung vorhanden. Während der Synthese können Signale als Folge einer Optimierung
verschwinden oder sogar neue hinzukommen.
Einzelsignale (Basic signals)
Bevor Signale in einer VHDL-Beschreibung verwendet werden können, müssen sie zuerst deklariert
werden:
-- Kommentare beginnen mit: --- Signaldeklaration:
SIGNAL a,b,c: BIT;
Eine solche Signaldeklaration (signal declaration) besteht aus drei Teilen. Der erste Teil stellt das
sogenannte Schlüsselwort (reserved word) dar, hier SIGNAL. Der zweite Teil gibt den einzelnen Signalen
jeweils einen Namen, hier: a, b und c. Der dritte Teil bestimmt den Typ dieser Signale, hier BIT. Der
Signaltyp wiederum legt fest, welche Werte das Signal annehmen kann. Für ein Signal des Typs BIT sind
das die Werte '0' und '1'. Die logischen Pegel 0 und 1 werden in diesem Fall also durch jeweils ein
Character zwischen zwei einfachen Hochkommata dargestellt.
Wie oben erwähnt, können Einzelsignalen Werte zugewiesen werden, sie können aber auch verknüpft
und abgefragt werden:
a <= '1';
-- Signalzuweisung
a <= b AND c;
-- Signalverknüpfung
IF(a = '0') THEN ... -- Signalabfrage
Bussignale (Bus signals)
Neben Einzelsignalen tauchen häufig Busse mit unterschiedlichen Busbreiten auf:
SIGNAL bus_a: BIT_VECTOR(7 DOWNTO 0); -- 8 Bit-Bus
SIGNAL bus_b: BIT_VECTOR(0 TO 7);
Beide Deklarationen vereinbaren einen 8-Bit breiten Bus, unterscheiden sich jedoch in der Zählweise bzw.
Indizierung der Bussignale und dadurch in der Lage des höchstwertigen Bits (MSB, Most-Significant-Bit).
Für bus_a liegt das MSB auf dem Einzelsignal mit Index 7, bus_a(7), und das niederwertigste Bit bus_a(0)
10
Aufbau eines VHDL-Entwurfs
hat den Index 0. Für bus_b sind die Verhältnisse genau umgekehrt:
bus_a <= "10000000“ -- MSB: bus_a(7)
-- Index: 76543210
bus_b <= “10000000“ -- MSB: bus_b(0)
-- Index: 01234567
Die Darstellung eines Buswertes ist unabhängig von der Indizierung immer dieselbe und entspricht der
eingeführten Leseweise, wobei das MSB an erster Stelle steht. Ein String kennzeichnet diese Folge
einzelner Character, die in doppelten Hochkommata eingeschlossen sind.
Um größere Verwirrung im Entwicklungsteam auszuschließen, und damit auch eine potentielle
Fehlerquelle, empfiehlt sich eine einheitliche Deklaration der Bussignale innerhalb eines Entwurfs und vor
allem an den Schnittstellen zu anderen Komponenten. Entweder alle Busse mit aufsteigender Indizierung
TO deklarieren, oder entsprechend alle mit fallender Zählweise DOWNTO mit dem Vorteil, daß der Index
des Einzelsignals mit dem Exponenten seiner binären Wertigkeit übereinstimmt.
Mehrwertige Logik
Bei der Formulierung einer Prozessorschnittstelle mit bidirektionalem Datenbus stellt man fest, daß
eine zweiwertige Logik mit seinen Zuständen '0' und '1' nicht ausreicht, um das Verhalten möglichst
realitätsnah zu beschreiben. In einem System mit mehreren Einheiten, die über einen gemeinsamen
Datenbus miteinander kommunizieren, muß sichergestellt sein, daß nur eine Einheit den Datenbus aktiv
treibt. Alle anderen Einheiten müssen ihre Ausgangstreiber in einen hochohmigen Zustand versetzen. Ist
dies nicht erfüllt, kommt es zu einem unerwünschten Buskonflikt. Im schlimmsten Fall wird ein Signal
gleichzeitig mit unterschiedlichen Pegeln getrieben (0 und 1), was einerseits zu einer erhöhten
Stromaufnahme führt und andererseits die Treiber in eine Streßsituation bringt mit stark zunehmender
Ausfallwahrscheinlichkeit.
Als Erweiterung des Standards, aber nicht als Teil der Sprache selbst, ist in der IEEE-Bibliothek eine
sogenannte Package mit der Definition eines neunwertigen Logiksystems enthalten (Std_logic_1164, siehe
Anhang C). Die Einbindung dieser Package in den Entwurf und Verwendung der darin enthaltenen
Typdeklarationen std_ulogic bzw. std_logic anstelle des Typs BIT ermöglicht die Darstellung
zusätzlicher Zustände, wie z.B. 'Z' für hochohmig oder 'X' für einen Buskonflikt.
In der Typdeklaration std_ulogic sind zunächst einmal nur die neun Zustände aufgeführt. Im Abschnitt
4.7.2 wird dargestellt, daß es sich dabei um einen Aufzählungstyp (Enumerator) handelt.
TYPE std_ulogic IS(
'U'
-- Nicht initialisiert (Uninitialized)
'X'
-- Konflikt (Forcing Unknown)
'0'
-- Logisch 0 (Forcing 0)
'1'
-- Logisch 1 (Forcing 1)
'Z'
-- Hochohmig (High Impedance)
'W'
-- Weicher Konflikt (Weak Unknown)
'L'
-- Weiche 0 (Weak 0)
'H'
-- Weiche 1 (Weak 1)
'-'
-- Spielt keine Rolle (Don't Care)
);
'U' steht für nicht initialisiert (uninitialized), d.h. dem Signal wurde bisher noch kein Wert zugewiesen.
Die etwas merkwürdig erscheinenden Zustände 'L' und 'H' repräsentieren die weichen Zustände, die durch
Pull-Down bzw. Pull-Up Widerstände entstehen. Sie können durch eine harte '0' oder '1' überschrieben
werden. Will man bei einer Wertzuweisung zum Ausdruck bringen, daß der logische Zustand keine Rolle
spielt, erzeugt man mit dem Zeichen für ’Don’t Care’ (’-’) in der Synthese zusätzliche Freiheitsgrade.
Welcher Unterschied besteht nun zwischen den Typen std_ulogic und std_logic, wobei das u in
std_ulogic für „unresolved“ steht? Von std_ulogic abgeleitet, stellt std_logic eine sog.
Auflösungsfunktion (resolution-function) zur Verfügung. Diese Funktion wird automatisch immer dann
aufgerufen, wenn einem Signal gleichzeitig mehrere Werte zugewiesen werden (s.a. Bild 1-6). Als
Schiedsrichter entscheidet die Auflösungsfunktion welcher Wert sich letztlich auf dem mehrfach
11
CAE: VHDL
getriebenen Signal einstellt. Damit lassen sich nun auch Signale mit Tri-State Eigenschaften einfach
formulieren.
VCC
'1'
'1'
'1'
'X'
'0'
a)
'L'
'Z'
b)
'W'
'H'
c)
Bild 1-6: Beispiele zur Auflösung von Buskonflikten
Mittlerweile hat sich std_logic als Standard etabliert. Alle CAE-Werkzeuge unterstützen die IEEEBibliothek. Kompatible Schnittstellen sind nun aber gerade eine wichtige Voraussetzung zur
Wiederverwendung von VHDL-Komponenten oder deren Zukauf zur Erhöhung der Synergie.
-- Einbinden der IEEE-Bibliothek
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
...
-- Signaltypen aus der IEEE-Bibliothek
SIGNAL d,e,f: STD_LOGIC;
SIGNAL bus_c: STD_LOGIC_VECTOR(15 DOWNTO 0);
Signalinitialisierung
Ein Signal des Typs std_logic, das nicht explizit initialisiert ist, oder dem zu Beginn der Simulation noch
kein Wert zugewiesen wurde, nimmt zunächst den Zustand an, der in der Typdeklaration bei der
Aufzählung aller Werte an erster Stelle steht (’U’). Typen, die anstelle einer Aufzählung einen
Wertebereich vorgeben (z.B. Integer) nehmen eine Grundinitialisierung mit dem Wert an der linken
Bereichsgrenze vor.
Um gleich zu Beginn der Simulation eine selbst bestimmte Anfangssituation herzustellen, erlaubt die
Signaldeklaration eine explizite Initialisierung des Signals. Diese Möglichkeit ist allerdings mit Vorsicht
anzuwenden, da Signalinitialisierungen in der Synthese nicht berücksichtigt werden. Bei der
Inbetriebnahme kann die realisierte Schaltung dann aufgrund eines zufälligen Anfangszustands in einen
unvorhergesehenen und vor allem nicht simulierten Zustand geraten. Nur die ausdrückliche Formulierung
einer Reset-Funktion vermeidet derartige Überraschungen. Es ist auch sinnvoll den Entwurf so
durchzuführen, daß sich die Schaltung aus jedem irregulären Zustand selbständig in einen regulären
Betriebszustand bringt. Gerade bei Zustandsautomaten ist diese Maßnahme oft notwendig und erfordert
meist nur einen geringen zusätzlichen Schaltungsaufwand.
-- Beispiel zur Signalinitialisierung bei der Deklaration
SIGNAL data: STD_LOGIC := '1';
SIGNAL data_bus:STD_LOGIC_VECTOR(7 DOWNTO 0):= "10100101";
12
Aufbau eines VHDL-Entwurfs
1.2.2 Schnittstellen (Ports)
Die Schnittstellen einer VHDL-Komponente sind Signale mit zusätzlichen Eigenschaften. Neben dem Typ
(z.B. std_logic) zeichnet sich ein solcher Port durch die Angabe der zugehörigen Signalflußrichtung, dem
sog. Mode, aus: IN, OUT, INOUT, BUFFER, LINKAGE.
SIGNAL i1 : IN STD_LOGIC;
SIGNAL o1: OUT STD_LOGIC;
Weil innerhalb einer Portdeklaration nur Signale als Objektklasse erlaubt sind (keine Konstanten oder
Variablen) ist die Angabe des Schlüsselwortes SIGNAL optional und wird daher meistens unterschlagen.
i1 : IN STD_LOGIC;
o1: OUT STD_LOGIC;
Die Bedeutung der Signalmodi im einzelnen:
• IN
• OUT
• INOUT
Dieser Port ist ein Eingang und innerhalb der Komponente nur lesbar
Ausgangsport, innerhalb der Komponente nur beschreibbar
Bidirektionaler Port: Innerhalb der Komponente sowohl schreib- als auch lesbar.
In der Synthese nur für Tristate Signale
• BUFFER Verhält sich wie ein Ausgang, in Komponenten zudem aber auch lesbar
• LINKAGE Stellt eine Verbindung zwischen Komponenten her, ohne die Signalflußrichtung
zu kennen.
IN
OUT
INOUT
BUFFER
Komponente A
OUT
IN
INOUT
IN
Komponente B
Bild 1-7: Beispiele für Schnittstellen (Ports)
13
CAE: VHDL
1.2.3 Entities
Die Entity gestattet es einer Funktionseinheit einen Namen zu geben und die Schnittstellen, mit denen sie
mit der Umgebung in Verbindung treten soll, zu spezifizieren. Die Funktion der Komponente ist aber noch
unbekannt (vgl. Bild 1-8:Bild 1-8). Erst die Ergänzung des vorerst leeren Gehäuses mit einem genauen
Plan über die innere Struktur und das Verhalten durch die sogenannte Architecture schafft ein
simulierbares Modell und verwandelt die Black-Box in eine Komponente mit wohldefinierten
Eigenschaften.
a
hex_to_7seg
f
nibble_in
4
7
anzeige
b
g
e
c
d
Bild 1-8: Schnittstellen eines Hexadezimal zu 7 Segment Dekoders
Nachfolgendes Beispiel zeigt die vollständige Deklaration einer Entity zu dem in Bild 1-8Bild 1-8:
dargestellten Symbol. Wichtig ist bei der Verwendung des Typs std_logic bzw. std_logic_vector die
Einbindung der IEEE-Bibliothek mit dem Verweis auf das Package Std_logic_1164.
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY hex_to_7seg IS
PORT (
nibble_in: IN STD_LOGIC_VECTOR(3 DOWNTO 0);
anzeige:
OUT STD_LOGIC_VECTOR(6 DOWNTO 0)
);
END hex_to_7seg;
Nach dem Schlüsselwort ENTITY folgt der Komponentenname. In diesem Fall hat die Komponente zwei
Anschlüsse, einen 4-Bit Eingangsbus und einen 7-Bit Ausgangsbus, die beide in der Portliste PORT( ... )
aufgelistet werden.
Übersetzungseinheiten (Design units)
Eine Entity-Deklaration ist bereits eine eigenständige Übersetzungseinheit. In ein File geschrieben,
könnte man das Beispiel bereits übersetzen und auf Syntaxfehler untersuchen. Solche
Übersetzungseinheiten, dazu gehören auch die Architecture, Package und Configuration, dürfen nicht auf
mehrere Files zerstückelt werden, können aber problemlos Bestandteile eines gemeinsamen Files sein. Der
Compiler behandelt dann jede Einheit so, als wäre sie in separate Files geschrieben. Gehören mehrere Files
zu einem Projekt, ist die Übersetzungsreihenfolge vorgegeben durch den Entwurfsaufbau und die
Abhängigkeiten untereinander. Eine typische Reihenfolge beginnt, falls vorhanden, mit den Packages,
gefolgt von der Entity, den Architectures und den Abschluß bildet optional die Configuration.
1.2.4 Die Arbeitsbibliothek WORK
WORK ist in VHDL ein Schlüsselwort und bezeichnet die aktuelle Arbeitsbibliothek. Unabhängig vom
symbolischen und physikalischen Namen der Bibliothek ist WORK der Ort, an dem der Compiler seine
analysierten Module ablegt und sie standardmäßig später aus derselben Simulationsumgebung an dieser
Stelle auch wieder sucht. Um aus einer anderen Simulationsumgebung auf diese Bibliothek zuzugreifen
benutzt man deren symbolischen Namen (z.B. SIM_LIB):
LIBRARY SIM_LIB;
USE SIM_LIB.ALL;
14
Aufbau eines VHDL-Entwurfs
1.2.5 Architectures
Durch die Trennung von äußerer und innerer Betrachtungsweise, damit sind Schnittstellen und Funktion
einer Komponente gemeint, unterstützt VHDL einen hierarchischen Top-Down und Bottom-Up Entwurf,
der in einem ersten Schritt das Gesamtsystem in überschaubare Module (Komponenten) mit möglichst
einfachen Schnittstellen partitioniert. Am Anfang eines Entwurfs steht daher mehr die Frage nach der
Struktur der Schaltung im Vordergrund, bevor man sich dann im einzelnen Gedanken über das
Modulverhalten macht und Ansätze zur Implementierung der Schaltungsfunktion sucht. Die Architecture
unterstützt beides: Sie kann eine Struktur (Structure) vorgeben in Form von miteinander verbundenen
Komponenten oder das Verhalten (Behave) mit Hilfe geeigneter Anweisungen beschreiben. EntityDeklaration und Architecture bilden zusammen eine simulierbare Abstraktion eines Stücks Hardware, die
sogenannte Design-Entity.
Hardwarestruktur (Structure)
Eine Hardwarestruktur ist festgelegt durch ihre Komponenten, sowie den Schnittstellen und den Signalen.
Die Architecture in Bild 1-9 hat in diesem Fall dann eher die Form einer Netzliste.
FSM
RAM
Komponenten mit
Schnittstellen
Signale
CONTROL
Bild 1-9: Die Komponenten geben die Struktur eines Entwurfes vor
Schaltungsverhalten (Behave)
Das Verhalten einer Schaltung wird in Bild 1-10 durch die Datentransformation zwischen Ein- und
Ausgang bestimmt, verknüpft mit dem logischen Timing, dem Impulsdiagramm und dem physikalischen
Timing, das vor allem Signal- und Gatterlaufzeiten berücksichtigt.
data_in
data_out
data_out =
f(data_in,takt,t)
takt
Bild 1-10: Verhalten einer Komponente, vorgegeben durch Datentransformation und Timing
15
Aufbau eines VHDL-Entwurfs
-- Beispielhafte Architecture
ARCHITECTURE verhalten OF hex_to_7seg IS
-- Deklarationsteil für Komponenten, Signale, Typen etc.
BEGIN
-- Anweisungssteil
-abcdefg
anzeige <= "1111110" WHEN nibble_in = "0000" ELSE
"0110000" WHEN nibble_in = "0001" ELSE
"1101101" WHEN nibble_in = "0010" ELSE
"1111001" WHEN nibble_in = "0011" ELSE
"0110011" WHEN nibble_in = "0100" ELSE
"1011011" WHEN nibble_in = "0101" ELSE
"1011111" WHEN nibble_in = "0110" ELSE
"1110000" WHEN nibble_in = "0111" ELSE
"1111111" WHEN nibble_in = "1000" ELSE
"1111011" WHEN nibble_in = "1001" ELSE
"1110111" WHEN nibble_in = "1010" ELSE
"0011111" WHEN nibble_in = "1011" ELSE
"1001110" WHEN nibble_in = "1100" ELSE
"0111101" WHEN nibble_in = "1101" ELSE
"1001111" WHEN nibble_in = "1110" ELSE
"1000111" WHEN nibble_in = "1111" ELSE
"0000000";
END verhalten;
Die erste Zeile gibt der Architecture einen Namen und stellt gleichzeitig eine Verbindung zur EntityDeklaration her. Dadurch ist es möglich mehrere Implementierungsvarianten nebeneinander vorzuhalten,
die später im Simulator je nach Bedarf ausgetauscht werden können. So könnte beispielsweise eine erste
Variante das Verhalten auf Systemebene beschreiben, noch ohne Rücksicht auf die Synthesefähigkeit zu
nehmen. Eine synthesefähige Variante entsteht anschließend bei der Umsetzung auf Register-TransferEbene. Üblicherweise enthält diese Beschreibungsart keine Angaben zum Zeitverhalten der Schaltung, das
Simulationsergebnis für den Dekoder zeigt Bild 1-11. Eine rein strukturelle Architecture ist dann das
Ergebnis nach der Synthese. In dieser dritten Variante sind alle Gatterelemente in der Architecture
aufgelistet und zu einer Netzliste verbunden. Es liegen also drei alternative Beschreibungen für ein und
dieselben Komponenten vor, mit drei Simulationen auf jeweils anderem Abstraktionsniveau, wobei die
Ergebnisse aber funktional keine Unterschiede aufweisen dürfen.
Bild 1-11: Simulation des Dekoders in VHDL ohne Zeitinformation (Timing)
Die Architecture ist zweigeteilt in einen Deklarationsteil, das ist der Bereich vor dem Schlüsselwort
BEGIN, und einen Anweisungsteil zwischen BEGIN und END. Im Deklarationsteil werden alle Signale,
Konstanten, Typen und Komponenten vereinbart, die innerhalb der Architecture Verwendung finden. Der
Gültigkeitsbereich ist jeweils auf diese Architecture beschränkt. Schnittstellensignale, die ja bereits in der
Entity-Deklaration aufgeführt sind, dürfen hier nicht nochmals deklariert werden. Der Anweisungsteil
nach BEGIN enthält die eigentliche Schaltungsbeschreibung, entweder durch Einsetzen und Verdrahten
bereits bestehender Komponenten oder mittels nebenläufigen Anweisungen, wobei jede einzelne
16
Aufbau eines VHDL-Entwurfs
Anweisung einen kleinen Teil der Gesamtschaltung repräsentiert.
anzeige(6:0)
(0)
(1)
(0)
(1)
(2)
(2)
(3)
(4)
(3)
nibble(3:0)
(5)
(6)
Bild 1-12: Aus der VHDL-Beschreibung synthetisierte Schaltung des Dekoders
1.2.6 Komponenten (Components)
Ein hierarchischer Schaltungsaufbau entsteht durch Zusammenfügen einzelner Design-Entities (EntityArchitecture Paare). Die Design-Entities der unteren Hierarchieebene werden dann zu Komponenten einer
strukturellen Beschreibung in der darüberliegenden Ebene. Bemerkenswert ist hierbei, daß eine
Komponente die Aufgabe eines Platzhalters für eine Design-Entity übernimmt. Erst die Configuration legt
fest, mit welcher passenden Design-Entity der Platz ausgefüllt wird. Ein vollständiges Beispiel zur
Anwendung von Komponenten, deren Deklaration und Instanzierung, ist in Abschnitt 1.2.8 enthalten.
Komponenten Deklaration (Component declaration)
Will man eine Komponente in einer aktuellen Beschreibung verwenden, muß sie vorher bekannt gemacht,
also deklariert, werden. Normalerweise entspricht die Schnittstellenbeschreibung in der KomponentenDeklaration, insbesondere die Signalnamen verbunden mit dem jeweiligen Typ und deren Reihenfolge,
exakt der entsprechenden Entity-Deklaration. Diese Angaben erlauben dem Compiler zu prüfen, ob die
Komponente richtig angeschlossen ist. Andererseits enthält die Deklaration keine Information darüber, wo
die entsprechende Design-Entity zu finden ist. Möglicherweise ist sie zum Übersetzungszeitpunkt der
aktuellen Hierarchieebene noch gar nicht vorhanden.
Eine Komponenten-Deklaration ist entweder innerhalb der Architecture möglich, oder global in einer
Package.
Komponenten-Instanzierung (Component instantiation)
Vergleicht man die Komponenten-Deklaration mit der Beschriftung einer Schublade zur Kennzeichnung
des Inhalts, so entspricht das Herausnehmen, Plazieren und Verdrahten einer Komponente dem Vorgang
einer Instanzierung (siehe Bild Bild 1-13:1-11). Die Instanz ist aber nur eine Instanz der KomponentenDeklaration und nicht der Design-Entity; Die Verbindung zur Design-Entity stellt erst die Configuration
her. Weil eine Komponente durchaus mehrfach instanziert werden kann, bekommt jede Instanz einen
individuellen Namen.
Die Verdrahtung einer Komponente kann auf zweierlei Arten erfolgen. Der schnellste Weg zur
Verdrahtung besteht darin, die anzuschließenden Signale entsprechend der Deklarations-Reihenfolge in die
PORT MAP einzutragen. Der Compiler prüft aufgrund der Deklarationsangaben nur, ob Signale gleichen
Typs miteinander verbunden werden. Bei Nichteinhalten der Reihenfolge kann es allerdings passieren, daß
Signale vertauscht werden, z.B. Takt- und Resetsignal, mit entsprechenden Folgen, die möglicherweise
erst sehr spät entdeckt werden.
17
Aufbau eines VHDL-Entwurfs
Die sichere Methode besteht darin, jedem Anschluß der Komponente, eindeutig das anzuschließende
Signal zuzuordnen. In der PORT MAP erscheint zuerst die Schnittstellenbezeichnung aus der Deklaration
gefolgt von dem Zeichen '=>', das gleich einem Zeiger auf das anzuschließende Signal zeigt.
-- Komponenten-Instanzierung
U1: hex_to_7seg
PORT MAP(
nibble_in => byte(7 DOWNTO 4),
display => display_a);
Instanzierung
hex_to_7seg
U1
Deklaration
Bild 1-13: Unterschied zwischen Deklaration und Instanzierung einer Komponente
1.2.7 Konfigurationen (Configurations)
Eine Configuration-Anweisung ist aus mancherlei Hinsicht sinnvoll und notwendig. Erstens wählt sie aus
mehreren vorhandenen Implementierungsvarianten (Architectures) genau eine für die Simulation aus und
schlägt damit eine Brücke zwischen Entity-Deklaration und Architecture. Zweitens trifft sie eine
Zuordnung von Komponenten-Instanz und Design-Entity. Nicht immer stimmen dabei beide
Deklarationen exakt überein. Ein unterschiedlicher Name der Komponente und Entity, auch
unterschiedliche Bezeichnungen der Schnittstellen, deren Reihenfolge, Typen und Anzahl können in der
Configuration berücksichtigt werden. Sie schafft Ordnung und reproduzierbare Ergebnisse und setzt,
gleich einer Bauanleitung für ein komplexes Gebilde, aus vielen Einzelteilen eines hierarchischen
Entwurfs eine simulierbare Schaltung zusammen.
Unter der Voraussetzung, daß die Namen von Komponente und Entity, sowie Namen, Typen und Modi
der Schnittstellen identisch sind, ist eine Configuration nicht zwingend vorgeschrieben. Der Simulator
nimmt dann gegebenenfalls die zuletzt übersetzte Architecture. Nach diesen Regeln verfährt auch die
Synthese, sie ignoriert Configurations.
Konfigurations-Deklaration (Configuration-Declaration)
Die Configuration-Declaration bildet eine separate Übersetzungseinheit und benennt explizit für jede
Komponente ein Entity-Architecture Paar. Ohne Eingriff in einzelne Module ist das Gesamtmodell von
einer Stelle aus konfigurierbar. Eine Veränderung der Bauanleitung des Systems hat unter der
Voraussetzung, daß alle Modellvarianten verfügbar sind, nur eine Neuübersetzung der Configuration zur
Folge. Das Gerüst einer typischen Configuration sieht folgendermaßen aus:
18
Aufbau eines VHDL-Entwurfs
-- Beispiel einer Configuration Declaration
CONFIGURATION conf_name OF entity_name_top IS
FOR architecture_name_top
FOR instanz_name_komp1: komponent_name1
USE ENTITY WORK.entity_name_komp1
(architecture_name_komp1);
END FOR;
FOR instanz_name_komp2: komponent_name2
USE ENTITY WORK.entity_name_komp2
(architecture_name_komp2);
END FOR;
END FOR;
END conf_name;
Die Configuration bekommt einen Namen (conf_name) und für die betrachtete Hierarchieebene eine
Zuordnung zur obersten Entity (entity_name_top). Danach erfolgt die Auswahl der entsprechenden
Architecture für diese Entity (FOR architecture_name_top).
Ist diese Architecture hierarchisch aufgebaut und sind darin Komponenten enthalten, so wird die
Configuration
zweidimensional.
Jede
Komponente
wird
zunächst
identifiziert
über
Instanzname:Komponentname, um dann eine Aussage zu treffen, aus welcher Bibliothek eine DesignEntity mit der Komponente verknüpft werden soll, da es nicht immer die Standard Arbeitsbibliothek
WORK sein muß. Existiert für tiefere Hierarchieebenen bereits eine Configuration, kann man sie anstelle
der Anweisung USE ENTITY... auch mit
USE CONFIGURATION WORK.conf_name einbauen.
Konfigurations-Spezifikation (Configuration specification)
Mit einigen Einschränkungen verbunden ist die Configuration-Specification eine zweite Möglichkeit der
Konfiguration. Sie ist nun keine separate Übersetzungseinheit mehr, sondern direkt im Deklarationsteil der
Architecture enthalten, was einen gewissen Verlust an Flexibilität mit sich bringt.
-- Im Deklarationsteil der Architecture:
FOR U1, U2: hex_to_7seg
USE ENTITY WORK.hex_to_7seg(verhalten);
oder zur gleichzeitigen Anpassung unterschiedlicher Namensgebung in den Deklarationen:
FOR ALL: hex_to_7seg
-- Bei unterschiedlichen Portbezeichnungen:
-- nibble_in in der Entity, nib in der Komponente
USE ENTITY WORK.hex_to_7seg(verhalten)
PORT MAP (nibble_in => nib, anzeige => disp);
19
Aufbau eines VHDL-Entwurfs
1.2.8 Beispiel: Zweistellige Umwandlung ’Hex nach 7Segment’
Dieses Beispiel zeigt anhand einer strukturellen Beschreibung die Deklaration und Instanzierung von
Komponenten, sowie den Aufbau der dazu passenden Konfiguration.
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY dualhex IS
PORT (
byte_in : IN STD_LOGIC_VECTOR(7 DOWNTO 0);
anzeige_a : OUT STD_LOGIC_VECTOR(6 DOWNTO 0);
anzeige_b : OUT STD_LOGIC_VECTOR(6 DOWNTO 0));
END dualhex;
ARCHITECTURE struktur OF dualhex IS
-- Komponenten Deklaration
COMPONENT hex_to_7seg
PORT (
nibble_in : IN STD_LOGIC_VECTOR(3 DOWNTO 0);
anzeige : OUT STD_LOGIC_VECTOR(6 DOWNTO 0));
END COMPONENT;
BEGIN
-- Komponenten Instanzierung mit Verdrahtung
U1: hex_to_7seg
PORT MAP(
nibble_in => byte(7 DOWNTO 4),
anzeige
=> anzeige_a);
U2: hex_to_7seg
PORT MAP(
nibble_in => byte(3 DOWNTO 0),
anzeige
=> anzeige_b);
END struktur;
-- Optional
CONFIGURATION dualhex_conf OF dualhex IS
FOR struktur
FOR ALL: hex_to_7seg USE ENTITY WORK.hex_to_7seg(verhalten);
END FOR;
END FOR;
END dualhex_conf;
20
Konkurrierende Anweisungen (Concurrent Statements)
1.3 Konkurrierende Anweisungen (Concurrent Statements)
VHDL als Hardwarebeschreibungssprache unterscheidet sich von anderen Programmiersprachen
hauptsächlich dadurch, daß parallele und sequentielle Anweisungen möglich sind:
Alle Anweisungen innerhalb der Architecture beschreiben Aktivitäten, die concurrent (konkurrierend,
parallel oder nebenläufig) stattfinden. Dazu gehört neben der einfachen Signalzuweisung auch die
bedingte (conditional) und ausgewählte (selected) Zuweisung, sowie die Komponenten-Instanzierung und
der Prozeß als Ganzes. Die Reihenfolge der Concurrent-Anweisungen im VHDL-Text spielt dabei keine
Rolle. Dies spiegelt im wesentlichen das Verhalten digitaler Hardware wieder, in der Signalveränderungen
parallel vorkommen.
Innerhalb eines Prozesses allerdings erfolgt die Ausführung der Anweisungen sequentiell. Das entspricht
eher dem bisherigen Verständnis einer Programmiersprache. Die speziellen Eigenschaften von Prozessen
sind Bestandteil des Abschnittes 4.5.
1.3.1 Einfache Signalzuweisung (Concurrent Signal Assignment)
Signale haben nicht nur die Aufgabe Komponenten miteinander zu verbinden, sie können auch
miteinander verknüpft werden und so einen Teil kombinatorischer Logik entstehen lassen. Eine einfache
Signalzuweisung sieht z.B. so aus:
a <= '1';
z <= a XOR b;
-- zu lesen: “Signal z bekommt den Wert aus der XOR-Verknüpfung von a und b zugewiesen”
Der Signaltyp auf der rechten Seite muß dem Typ auf der linken Seite entsprechen.
a
XOR
b
z
Kombinatorische Verknüpfung
Bild 1-14: Aus einer Signalzuweisung wird eine kombinatorische Verknüpfung
Beispiel Volladdierer:
ENTITY addierer IS
PORT (
op_a: IN STD_LOGIC;
op_b: IN STD_LOGIC;
carry_in: IN STD_LOGIC;
summe: OUT STD_LOGIC;
carry_out: OUT STD_LOGIC);
END addierer;
ARCHITECTURE verhalten OF addierer IS
BEGIN
summe <= op_a XOR op_b XOR carry_in;
carry_out <= (op_a AND op_b) OR (op_a AND carry_in) OR
(op_b AND carry_in);
END verhalten;
21
CAE: VHDL
op_a
op_a XOR
op_b XOR
carry_in
op_a AND op_b
OR
op_a AND carry_in
OR
op_b AND carry_in
op_b
summe
carry_out
carry_in
Bild 1-15: Kombinatorik des Volladdierers
Zu der VHDL-Beschreibung des Volladdierers könnte man sicherlich sofort eine äquivalente Schaltung
aus XOR-, AND- und OR-Gattern zeichnen. Doch stellt diese Schaltung nicht unbedingt das Ergebnis
nach einer automatischen Synthese dar. Eine Synthese muß noch eine Reihe von Randbedingungen
berücksichtigen und daraus einen Kompromiß ableiten. Das Ergebnis wird von der Zieltechnologie und
den Vorgaben zur Fläche und Geschwindigkeit, sowie der Peripherie abhängen. Dabei werden
Randbedingungen berücksichtigt wie z.B.: Reicht die Treiberleistung am Eingang aus, um mehrere Lasten
zu treiben, wie stark werden die Ausgänge belastet und sind XOR-Bauelemente verfügbar? Die Antworten
auf diese Fragen entscheiden wie die Schaltung nach der Synthese tatsächlich aussieht. Für eine
prinzipielle Darstellung reicht es daher völlig aus, die kombinatorischen Verknüpfungen in einer Wolke
darzustellen. Davon abgesehen beschreibt in VHDL ein entsprechender Verknüpfungsoperator die
Funktion wesentlich anschaulicher, wie noch später ausgeführt wird.
1.3.2 Bedingte Signalzuweisung (Conditional Signal Assignment)
Die einfachste Form der bedingten Signalzuweisung beschreibt einen Multiplexer (Bild 1-16):
z <= input_a WHEN sel = '0' ELSE input_b;
input_a
T
z
F
input_b
'0'
TRUE: T
FALSE: F
sel
=?
Bild 1-16: Multiplexer mit einer bedingten Signalzuweisung
Abhängig von der Bedingung sel='0' mit dem Ergebnis TRUE oder FALSE, wird eine der beiden
Quellen input_a oder input_b ausgewählt und mit dem Ausgang z, dem Ziel, verbunden. Auch hier gilt die
Regel, daß Quelle und Ziel vom gleichen Typ sind (z.B. STD_LOGIC). Die Bedingung besteht meist aus
den Relationen = , > , < , >= , <= . In der Zuweisung können auch Verknüpfungen untergebracht
werden:
summe <= op_a XOR op_b WHEN carry_in = '0' ELSE NOT (op_a
XOR op_b);
Die bedingte Signalzuweisung kann beliebig erweitert werden, um komplexe hierarchische
Multiplexstrukturen zu erzeugen (Bild 1-17).
22
Konkurrierende Anweisungen (Concurrent Statements)
Kombinatorik
logischer Vergleich
Bild 1-17: Hierarchische Multiplexstruktur
Es kann nicht vorausgesetzt werden, daß Abhängigkeiten der Bedingungen untereinander von der
Synthese immer erkannt werden. Bei vorhandenen Abhängigkeiten empfiehlt es sich die ausgewählte
(selected) Signalzuweisung einzusetzen, um redundante Logik zu verhindern.
Die letzte bedingungslose ELSE-Anweisung sollte alle bis dahin nicht behandelten Fälle
berücksichtigen. Es könnten sonst Situationen entstehen, bei der die Synthese Rückkopplungen oder
asynchrone Speicherelemente einbaut, da dem Ausgang kein neuer Wert zugewiesen wird und der
bisherige Wert beibehalten werden muß.
1.3.3 Selektierte Signalzuweisung (Selected Signal Assignment)
Diese Form der Signalzuweisung läßt nur die Angabe einer einzigen Bedingung zu. Das Ergebnis des Tests
bestimmt mit welcher Quelle das Ziel verbunden wird.
SIGNAL auswahl: NATURAL RANGE 0 TO 3;
...
WITH auswahl SELECT
z <= a WHEN 0,
b WHEN 1,
c WHEN 2,
d WHEN 3;
a
b
c
d
z
2
a u sw a h l
Bild 1-18: Multiplexer mit ausgewählter Signalzuweisung
Die einzelnen Tests (WHEN 0, WHEN 1, usw.) beschreiben im Gegensatz zur bedingten Zuweisung
keine Hierarchie, sondern haben alle gleiche Priorität und schließen sich gegenseitig aus. Alle Werte aus
dem Wertebereich der Bedingung (im Beispiel 0 ... 3) müssen aufgezählt werden. Der letzte Test (WHEN
OTHERS) kann alle restlichen Möglichkeiten zusammenfassen:
WITH auswahl SELECT
z <= input_a WHEN '0',
input_b WHEN OTHERS ;
23
CAE: VHDL
1.3.4 Beispiel: Kodierer mit Priorisierung der Eingänge
Dieses Beispiel zeigt zwei unterschiedliche Implementierungsvarianten zu der Funktionsbeschreibung
in Bild 1-19. Beide Lösungen verhalten sich in der Simulation und Synthese gleich (Bild 1-20 und Bild 121).
S TA R T
a = '1 ' ?
y < = "01";
b = '1 '?
y < = "10";
c = '1'?
y < = "11 ";
y < = "00";
STO P
Bild 1-19: Flußdiagramm des Kodierers
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY kodierer IS
PORT(
a,b,c: IN STD_LOGIC;
y : OUT STD_LOGIC_VECTOR(1 DOWNTO 0));
END kodierer;
ARCHITECTURE kodierer_arch OF kodierer IS
-- Konstanten erhöhen die Lesbarkeit
CONSTANT eins: STD_LOGIC_VECTOR(1 DOWNTO 0) := "01";
CONSTANT zwei: STD_LOGIC_VECTOR(1 DOWNTO 0) := "10";
CONSTANT drei: STD_LOGIC_VECTOR(1 DOWNTO 0) := "11";
-- NULL ist ein reserviertes Schlüsselwort in VHDL
CONSTANT zero: STD_LOGIC_VECTOR(1 DOWNTO 0) := "00";
BEGIN
y <=
eins WHEN a = '1' ELSE
zwei WHEN b = '1' ELSE
drei WHEN c = '1' ELSE
zero;
END kodierer_arch;
24
Konkurrierende Anweisungen (Concurrent Statements)
ARCHITECTURE umstaendlich OF kodierer IS
TYPE AUSWAHL IS (0, 1, 2, 3);
SIGNAL conditional: AUSWAHL;
SIGNAL selected: STD_LOGIC_VECTOR(1 DOWNTO 0);
BEGIN
-- concurrent signal assignment
y <= selected;
-- concurrent conditional
conditional <= 0 WHEN a =
1 WHEN b =
2 WHEN c =
3;
signal assignment
'1' ELSE
'1' ELSE
'1' ELSE
-- concurrent selected signal assignment
WITH conditional SELECT
selected <= "01" WHEN 1;
"10" WHEN 2;
"11" WHEN 3;
"00" WHEN OTHERS;
END umstaendlich;
Bild 1-20: Simulation der Architecture kodierer_arch
A
Y
B
C
Bild 1-21: Synthetisiertes Beispiel: gleiches Ergebnis für beide Architectures
25
CAE: VHDL
1.4 Das Simulationsmodell in VHDL
Nach der Betrachtung nebenläufiger Signalanweisungen im Abschnitt 4.3, können nun die fundamentalen
Mechanismen der Sprache etwas näher erläutert werden. War VHDL ursprünglich eine reine
Simulationssprache, so erleichtert der Einblick in die Arbeitsweise eines VHDL-Simulators einerseits das
Verständnis von VHDL-Modellen und vereinfacht andererseits die Auswahl geeigneter Konstrukte zur
Hardwarebeschreibung. Zum Thema Simulationsmodell gehören die Schwerpunkte:
• Treiberkonzept
• Delta Delay Mechanismus
• Modellierung von Verzögerungen
1.4.1 Treiber (Drivers)
Hinter jeder nebenläufigen Signalanweisung, dazu kommt später auch der Prozeß, verbirgt sich ein
Treiber, der für den Signalverlauf verantwortlich ist. Im Simulator existiert dieser Treiber in Form einer
Liste, welche den Zeitpunkt enthält, bei dem der zugewiesene Wert auf dem Signal aktiv werden soll. Ein
solcher Eintrag in der Liste wird als Transaction bezeichnet. Die Treiberliste ist Grundlage zur
Bestimmung des aktuellen Signalwertes während der Simulation (Bild 1-22). Ändert sich der Signalwert
aufgrund eines entsprechenden Listeneintrages, spricht man von einem Ereignis (Event) auf dem Signal.
Entspricht der aktuelle Eintrag jedoch dem zu diesem Zeitpunkt bereits gültigen Signalwert, bleibt die
Signalzuweisung ohne Auswirkung.
-- Beispiel zum Aufbau einer Treiberliste
y <= '0' AFTER 0 ns, '1' AFTER 20 ns, '0' AFTER 50 ns;
Simulationsstart
Zeit
Wert
0 ns
'0'
20 ns
'1'
50 ns
'0'
'1'
'0'
t/ns
0
10
20
30
40
50
Bild 1-22: Bild Treiberliste mit Signalverlauf
1.4.2 Delta-Delay-Mechanismus
Um die Frage zu beantworten, wie der Simulator trotz mehreren nebenläufigen Signalanweisungen zu
einem eindeutigen Ergebnis kommt, ist es hilfreich, einen Simulationszyklus genauer zu betrachten. Es
wird zwischen einem Simulationszyklus mit Zeitfortschritt, z.B. 1 ns, und einem Zyklus ohne
Zeitfortschritt, auch als Delta bezeichnet (∆t = 0 ns), unterschieden.
VHDL-Simulatoren arbeiten ereignisgesteuert. Der Simulator erhöht die Simulationszeit und
durchsucht alle Treiberlisten, ob zu der neuen Simulationszeit ein Ereignis, d.h. ein Zustandswechsel auf
einem Signal, stattfinden soll. Signale, deren Treiberliste einen Eintrag für die momentane Simulationszeit
haben, werden nun mit dem zugehörigen Wert aus der Treiberliste aktualisiert. Auch die graphische
Ausgabe des Simulators zeigt nun die neuen Signalwerte an. Alle anderen Signale ohne Transaction
behalten ihren bisherigen Wert bei.
Ein Zustandswechsel auf einem Signal aktiviert nun den Simulator. Der Simulator prüft jede
nebenläufige Anweisung, ob sie auf dieses Signal mit der Zustandsänderung empfindlich (sensitiv) ist und
bearbeitet diese Anweisungen gegebenenfalls. Nach der Bearbeitung aller nebenläufigen Anweisungen
26
Das Simulationsmodell in VHDL
ergeben sich möglicherweise auf den zugewiesenen Signalen neue Werte und damit neue Ereigniseinträge
in den Treiberlisten. Falls die Signalzuweisungen verzögerungsfrei sind, müssen alle nebenläufigen
Anweisungen noch einmal bearbeitet werden, die auf ein neu geändertes Signal sensitiv sind. Dabei darf
deshalb noch kein Zeitfortschritt erfolgen, es erfolgen also Zyklen ohne Zeitfortschritt, die sog. Deltas ( ∆).
Ein stabiles Simulationsergebnis ist erst dann erreicht, wenn für die aktuelle Simulationszeit keine
neuen Einträge in die Treiberlisten aufgenommen werden müssen und somit alle Signale für mindestens
ein Delta unverändert bleiben. Anschließend kann die Simulationszeit wieder erhöht werden, wobei der
Zeitfortschritt kein konstantes Inkrement sein muß, sondern sich dynamisch nach dem nächsten Ereignis
richtet.
Der hier dargestellte Ablauf bildet die parallelen Vorgänge innerhalb einer VHDL-Beschreibung auf
sequentielle Bearbeitungsschritte eines Simulators ab. Auch spielt es für das Simulationsergebnis keine
Rolle, in welcher Reihenfolge die nebenläufigen Anweisungen im VHDL-Text angeordnet sind. Nur die
Ereignisse auf den Signalen geben die Bearbeitungsreihenfolge vor. Dieses Verhalten macht einen VHDLSimulator besonders effizient, da Rechenzeit erst dann anfällt, wenn im Modell eine Zustandsveränderung
stattfindet.
Beispiel: Verzögerungsfreies RS-Flip-Flop
x <= NOT (y AND lset); -- (1)
y <= NOT (x AND reset); -- (2)
Tabelle 1.1: Simulationszyklus ohne Signalverzögerung
Simulationszeit
lset
20 ns
x
0
y
reset
Auswertung
der
Zuweisung
1
1
(1)
1
1
(2)
1
(1)
20 ns + 1∆
0
20 ns + 2∆
0
1
20 ns + 3∆
0
1
0
1
-
1
0
1
(1)
1
-
30 ns
30 ns + 1∆
1
1
0
40 ns
1
1
0
40 ns + 1∆
1
1
40 ns + 2∆
1
40 ns + 3∆
1
0
(2)
0
(1)
1
0
(1)
1
0
-
27
CAE: VHDL
Beispiel: RS-Flip-Flop mit Verzögerung
x_del <= NOT (y_del AND lset) AFTER 2 ns; -- (1)
y_del <= NOT (x_del AND reset) AFTER 2 ns; -- (2)
Tabelle 1.2: Simulationszyklus mit Signalverzögerung
Simulationszeit
lset
20 ns
x_del
0
y_del
reset
Auswertung
der
Zuweisung
1
1
(1)
1
1
(2)
1
(1)
22 ns
0
24 ns
0
1
24 ns + 1∆
0
1
0
1
-
1
0
1
(1)
1
-
30 ns
30 ns + 1∆
1
1
0
40 ns
1
1
0
42 ns
1
1
44 ns
1
44 ns + 1∆
1
0
(2)
0
(1)
1
0
(1)
1
0
-
Bild 1-23: Simulationsergebnis des RS-FF für verzögerungsfreies (X,Y) und verzögerungsbehaftetes
(X_DEL, Y_DEL) Modell.
28
Das Simulationsmodell in VHDL
Bild 1-24: Oszillierendes RS-Flip-Flop
Eine interessante Situation ist in Bild 1-24 entstanden: Obwohl beide Eingänge lset und reset statisch den
logischen Pegel '0' angenommen haben, wechseln die Ausgänge x_del und y_del periodisch ihren Zustand:
Die Schaltung schwingt. Dabei entspricht die Periodendauer genau der Addition der beiden Verzögerungen
in den Signalzuweisungen für x_del und y_del. Reduziert man die Verzögerung, werden die Signalwechsel
immer häufiger. Im Grenzfall, bei einem Delay von 0 ns, verschwinden die Oszillationen in unendlich
viele Deltas. Der Simulator findet dann aber kein stabiles Ergebnis mehr und bleibt an dieser Stelle
hängen.
Tabelle 1.3: Simulationsablauf des oszillierenden RS-Flip-Flop
Simulationszeit
lset
20 ns
x_del
0
y_del
reset
0
Auswertung
der
Zuweisung
(1),(2)
20 ns + 1∆
0
0
(1),(2)
20 ns + 2∆
0
0
(1),(2)
20 ns + 3∆
0
0
(1),(2)
usw.
0
0
(1),(2)
...
...
1.4.3 Modellierung von Verzögerungen
Für eine synthesefähige VHDL-Beschreibung (RTL) ist es unüblich mit verzögerungsbehafteten
Signalzuweisungen zu arbeiten. Verzögerungen, die als Laufzeiten durch einzelne Gatter der
kombinatorischen Logik entstehen, oder als Reaktionszeit ‘Clock-to-Output‘ an Registern in Erscheinung
treten, sind hauptsächlich von der erst später hinzukommenden Zieltechnologie abhängig.
Verzögerungsangaben können jedoch das zeitliche Verhalten der Schaltung nach der Synthese beschreiben
oder innerhalb einer Simulationsumgebung ein sinnvolles Hilfsmittel zur Erstellung komplexer
Eingangsmuster darstellen:
clk <= NOT clk AFTER 100ns;
29
CAE: VHDL
Man unterscheidet zwei Arten von Verzögerungen:
• Transport-Delay zur Modellierung von Verzögerungen auf Leitungen
• Inertial-Delay (träge Verzögerung), bei gleichzeitiger Unterdrückung sehr kurzer Impulse
Transport-Verzögerung (Transport Delay)
Die Wirkung einer Transport-Verzögerung ist recht einfach: der neue Wert wird zum entsprechenden
Zeitpunkt in die Treiberliste eingetragen. Enthält die Liste nach dem gerade neu hinzugekommenen
Eintrag weitere Einträge, so werden diese jetzt aus der Liste entfernt (Bild 1-25).
Beschreibt y(t) den nicht verzögerten Signalverlauf, dann ist y(t - T) der um T verzögerte Verlauf.
Beispiel: Multiplexer mit Transport - Delay
z <=
TRANSPORT input_a AFTER 15ns WHEN sel = '0' ELSE
input_b AFTER 10ns;
D elay =
15 ns
in p ut_ a
'0 '
z
'1 '
D elay =
10 ns
in p ut_ b
sel
sel
input_a
input_b
nachfolgender Eintrag
entfernt !
Treiberliste für
z
0
1 0
1
z
0
10
18 20
Bild 1-25: Beispiel für Transport Delay
30
30
33
40
50
60
t/ns
Das Simulationsmodell in VHDL
Trägheits-Verzögerung (Inertial Delay)
Obwohl die Inertial-Verzögerung der Grundeinstellung entspricht, ist diese Variante nicht ganz so leicht
verständlich.
Ein Impuls, der kürzer ist als die „Setup“-Zeit, wird unterdrückt.
y <= input_a AFTER 10 ns;
-- Als Setup-Zeit wird die Verzögerung von 10 ns angenommen
y <= REJECT 10 ns INERTIAL input_a AFTER 200 ns;
-- Angabe der Mindestimpulsbreite 10 ns
y <= REJECT 0 INERTIAL input_a AFTER 200 ns;
-- Äquivalent zu TRANSPORT input_a
Der wichtigste Unterschied zur Transport-Verzögerung liegt darin, daß nun auch die Vorgeschichte des
Signalverlaufs eine Rolle spielt. Verzögerte Verläufe entstehen nach den Regeln:
•
•
•
•
Markiere in der Treiberliste den neuen Eintrag
Markiere den Eintrag unmittelbar vor dem neuen Eintrag, welcher denselben Wert besitzt.
Markiere den Eintrag, der für den aktuellen Signalwert verantwortlich ist
Lösche alle nicht markierten Einträge
Beispiel: Multiplexer mit Inertial Delay
z <=
input_a AFTER 15 ns WHEN sel = '0' ELSE
input_b AFTER 10 ns;
sel
'0'
input_a
'0'
input_b
Treiberliste für
z
0
1
0
1
0
gelöscht
siehe 2.
z
0
10
20
30
40
50
60
t/ns
Bild 1-26: Beispiel für Inertial-Delay
Erläuterung zum Ablauf in Bild 1-26:
1. Betrachtungszeitpunkt t=10 ns: Eintrag von '1' bei t=25 ns
2. Betrachtungszeitpunkt t=15 ns: Eintrag von '0' bei 30ns
Einträge bei 0 ns und 30 ns markiert und Eintrag bei 25 ns gelöscht
3. Betrachtungszeitpunkt t=20 ns: Eintrag von '1' bei t=35ns, Eintrag bei 30 ns gelöscht
4. Betrachtungszeitpunkt t=40 ns: Eintrag bei t=35 ns ist bereits wirksam und keine
Einträge zwischen t=40 ns und t=55 ns, daher keine Löschmöglichkeit
31
CAE: VHDL
1.4.4 Beispiel: Vergleich von Transport und Inertial Delay
D elay =
15 ns
'0 '
'0 '
'1 '
'1 '
D elay =
15 ns
D elay =
10 ns
'0 '
'1 '
D elay =
10 ns
'0 '
y
'1 '
z
sel
Bild 1-27: Zweistufige, verzögerungsbehaftete Multiplexstruktur
Um den Unterschied zwischen Transport- und Inertial-Delay zu erkennen soll folgende Frage betrachtet
werden: Welche Pulsbreite muß das Signal sel in Bild 1-27 mindestens aufweisen, damit am Ausgang y
ein Signalwechsel sichtbar wird? Die Antwort darauf ist in den Simulationsergebnissen in Bild 1-28 und 129 zu sehen.
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY vergleich IS
PORT (
sel: IN STD_LOGIC;
y_inertial: OUT STD_LOGIC;
y_transport: OUT STD_LOGIC);
END vergleich;
ARCHITECTURE vergleich_arch OF vergleich IS
SIGNAL z_inertial: STD_LOGIC;
SIGNAL z_transport: STD_LOGIC;
BEGIN
z_inertial <=
'0' AFTER 15ns WHEN sel = '0' ELSE '1' AFTER 10ns;
y_inertial <=
'0' AFTER 15ns WHEN z_inertial = '0' ELSE '1' AFTER 10ns;
z_transport <= TRANSPORT '0' AFTER 15ns WHEN sel='0'ELSE '1' AFTER 10ns;
y_transport <= TRANSPORT '0' AFTER 15ns WHEN z_transport = '0' ELSE '1' AFTER 10ns;
END vergleich_arch;
32
Das Simulationsmodell in VHDL
Bild 1-28: Simulation mit Inertial-Delay: Pulsbreite von sel mindestens 20 ns
Bild 1-29: Simulation mit Transport-Delay: Pulsbreite von sel mindestens 12 ns
33
Prozesse (Processes)
1.5 Prozesse (Processes)
Wie aus den vorangegangenen Abschnitten bekannt, sind innerhalb einer Architecture nur nebenläufige
Anweisungen erlaubt. Das Prinzip der Nebenläufigkeit ist zwar eine wichtige Voraussetzung bei der
Beschreibung digitaler Schaltungen, doch allein mit den bisher kennengelernten Konstrukten, wie
einfache, bedingte oder selektierte Signalzuweisung, ist ein komplexer Ablauf nur schwer beschreibbar.
Jede moderne Programmiersprache stellt Konstrukte zur Verfügung, um strukturiert programmieren zu
können. In VHDL muß es also auch Raum für sequentielle Konstrukte, wie beispielsweise IF-ELSIF,
CASE oder Schleifen geben. Auch die Frage, wie eine größere Kombinatorik oder taktsynchrone
Schaltung formuliert werden kann, blieb bislang unbeantwortet. In VHDL liefert der Prozeß die Antworten
auf diese Fragen. Als weiterer Bestandteil der Architecture verhält sich der Prozeß nach außen
nebenläufig, intern bietet er jedoch Raum für sequentielle Anweisungen und kombiniert somit ideal die
Anforderungen an eine Hardwarebeschreibungssprache und den Wunsch nach strukturierter
Programmierung. Diese Eigenschaften machen den Prozeß zu der am häufigsten benutzten Anweisung,
womit er sicherlich eine zentrale Rolle in VHDL übernimmt. Später wird deutlich, daß auch die o.g.
Signalzuweisungen vereinfachte Formen eines Prozesses darstellen.
1.5.1 Eigenschaften
Ein Prozeß beinhaltet eine Folge von sequentiellen Anweisungen, wobei deren Reihenfolge genau die
Bearbeitungsschritte festlegen. Ein Vergleich mit anderen Programmiersprachen, die Anweisungen
sequentiell abarbeiten, trifft das Verhalten in VHDL allerdings nur teilweise. Der größte Unterschied
zwischen einem Prozeß und einem klassischen Programm liegt darin, daß der Prozeß normalerweise
während der Simulation repetierend bearbeitet wird und somit nie zu einem Ende findet. Dies gilt vor
allem für Prozesse, welche synthetisierbaren VHDL-Code enthalten.
Aufbau von Prozessen
Prozesse dürfen an beliebiger Stelle im Anweisungsbereich einer Architecture plaziert sein. Die
prinzipielle Struktur eines Prozesses besteht aus den Bereichen Sensitivity-List, Deklarationsbereich und
Anweisungsbereich.
PROCESS (sensitivity list)
-- Deklarationen
BEGIN
-- sequentielle Anweisungen
-- IF, CASE, LOOP
END PROCESS;
Sensitivity List
Der Prozeß benennt in dieser Liste alle Signale, auf die er empfindlich reagieren soll. Ein Prozeß ist
entweder im Zustand „Ausführend“ (executed) oder „Schwebend“ (suspended). Jedes Ereignis auf einem
der Signale in der Liste macht den Prozeß ausführend und veranlaßt die Bearbeitung der darin enthaltenen
Anweisungen. Nach der letzten Anweisung versetzt sich der Prozeß selbst wieder in den schwebenden
Zustand und wartet auf ein neues Ereignis.
Deklarationsbereich (Process Declarative Part)
Dieser Bereich erlaubt neben der Vereinbarung von Typen, Funktionen und Prozeduren hauptsächlich die
Deklaration von Signalen und Variablen (siehe Abschnitt 4.21.5.2). Alle hier gemachten Angaben haben
lokalen Charakter, d.h. sie sind nur innerhalb des Prozesses bekannt und verwendbar.
34
Prozesse (Processes)
Anweisungsbereich (Process Statement Part)
Der Anweisungsbereich enthält schließlich die sequentiellen Anweisungen, die aufgrund eines Ereignisses
auf einem der Signale in der Sensitivity-List ausgeführt werden. Zu den sequentiellen Anweisungen
gehören die Konstrukte:
• IF, CASE, LOOP, WAIT
• Signal und Variablenzuweisung
Die Schlüsselworte BEGIN und END grenzen den Anweisungsbereich im Prozeß ein und markieren damit
auch einen physikalisch zusammengehörenden Teil innerhalb der Gesamtbetrachtung: Beispielsweise
einen Zähler, Addierer oder Zustandsautomaten.
Beispiel: Addierer
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY addierer IS
PORT (
in_a, in_b: IN NATURAL RANGE 15 DOWNTO 0;
summe :
OUT NATURAL RANGE 15 DOWNTO 0);
END addierer;
ARCHITECTURE verhalten OF addierer IS
BEGIN
P1: PROCESS(in_a, in_b)
BEGIN
summe <= in_a + in_b;
END PROCESS P1;
END verhalten;
in_a
+
summe
in_b
Bild 1-30: Addierer
Für das Beispiel in Bild 1-30 zeigt der VHDL-Code einen Prozeß, der nur kombinatorische Logik enthält,
also noch keine Speicherelemente bzw. Register. Für kombinatorische Prozesse gilt die Regel, daß alle
Signale, die innerhalb des Prozesses gelesen werden, in der Sensitivity-List aufgeführt sein müssen.
Obiger Prozeß, der die Addition beschreibt, kann nur dann das Ergebnis korrekt auf dem Signal summe
darstellen, wenn er auf jede Veränderung der Eingangssignale in_a und in_b empfindlich ist. Eine
unvollständige Sensitivity-List führt daher entweder zu nicht synthetisierbaren Modellen oder zu
unerwarteten Ergebnissen. Im schlimmsten Fall unterscheiden sich die Simulationen vor und nach der
Synthese. Manche Synthesewerkzeuge ignorieren die Sensitivity-List, da sie ohnehin davon ausgehen, daß
der Prozeß bei der Veränderung eines auszuwertenden Signals auch neu zu bearbeiten ist. Der VHDLSimulator hält sich jedoch streng an die Vorgabe, falls das Signal in_b nicht in der Liste enthalten ist, das
Ergebnis nur bei einer Veränderung von in_a neu zu berechnen. Ändert sich aber nur das Signal in_b,
behält die Summe ihren bisherigen, nun aber falschen Wert bei. Ein Synthesewerkzeug mit derselben
Interpretation versteht unter „bisherigen Wert beibehalten“ die Anweisung, ein Speicherelement (Latch)
einzubauen. In der Regel ist dies ebenso ungewollt wie unerwünscht (s. Abschnitt 4.5.3).
Ergänzend sei noch die Verwendung des Typs NATURAL als Untertyp von INTEGER erwähnt, um
den Wertebereich auf die natürlichen Zahlen zu begrenzen. Eine weitere Reduzierung auf den Bereich 0
bis 15 erfolgt mit der Anweisung RANGE 15 DOWNTO 0.
35
Prozesse (Processes)
1.5.2 Signale und Variablen
Signale (Signals)
Signalzuweisungen innerhalb eines Prozesses verhalten sich völlig anders als die bisher betrachteten
nebenläufigen Zuweisungen. Ohne genaue Kenntnis der Abläufe, enthalten Prozesse einige potentielle
Fehlerquellen. Zunächst gilt in jedem Fall:
Ein Prozeß als Ganzes ist eine nebenläufige Anweisung, seine Bearbeitung beansprucht genau ein
Simulationsdelta.
Signale innerhalb eines Prozesses verändern ihren Wert während der Bearbeitung des Prozesses nicht.
Signalzuweisungen, sofern sie keine Verzögerung enthalten, zeigen ihre Auswirkung erst am Ende des
Prozesses, also erst ein Simulationsdelta später. In einem Prozeß gelesene Signale liefern Werte ursächlich
aus vorangegangenen Simulationszyklen. Damit ist nun auch die Nebenläufigkeit im Verhältnis zu anderen
Prozessen hergestellt. In einem Prozeß sind mehrere Zuweisungen hintereinander auf dasselbe Signal
zulässig, wirksam bleibt aber dann nur die zuletzt aufgeführte Signalzuweisung. Für Signale mit
Verzögerung wird der zugewiesene Wert zum entsprechenden Zeitpunkt in die Treiberliste eingetragen,
entsprechend den Regeln für Inertial- oder Transport Delay (siehe Abschnitt 4.4.3).
Das Beispiel aus dem vorigem Abschnitt soll dahingehend modifiziert werden, daß beim Überschreiten
des Wertes 10 im Ergebnis die Summe auf 0 zurückgesetzt wird (Bild 1-31). Die folgende Implementation
scheint dieses Verhalten auch zu beschreiben. Doch bei genauerer Analyse ist festzustellen, daß eine
Fehlfunktion auftreten kann, wenn beide Eingangssignale zu einem beliebigen Zeitpunkt (z.B. t=20ns)
einen Wert größer als 5 annehmen. Den Ablauf im Detail zeigt die Tabelle 1.4 .
ENTITY addierer IS
PORT ( in_a, in_b: IN NATURAL RANGE 15 DOWNTO 0;
summe : BUFFER NATURAL RANGE 15 DOWNTO 0);
END addierer;
ARCHITECTURE mutig OF addierer IS
BEGIN
V1: PROCESS(in_a, in_b, summe)
BEGIN
summe <= in_a + in_b;
-- 1. Zeile
IF(summe > 10) THEN
-- 2. Zeile
summe <= 0;
-- 3. Zeile
END IF;
END PROCESS V1;
in_a
END mutig;
+
in_b
F
0
summe
T
Tru e
False
summe > 10 ?
Bild 1-31: Addierer mit Vergleicher
Die Bearbeitung des Prozesses erfolgt sequentiell. Die erste Zeile bildet die Summe und weist das
Ergebnis dem Signal summe zu. Die zweite Zeile überprüft, ob der zulässige Bereich überschritten wurde
und setzt gegebenenfalls das Signal summe in der dritten Zeile wieder auf 0. Weil die Bearbeitung der
Signalzuweisungen innerhalb eines Prozesses sequentiell erfolgt, kann eine nachfolgende Zuweisung eine
vorherige überschreiben.
36
Prozesse (Processes)
Die Bearbeitung der Zuweisungen erfolgt zwar sequentiell, doch die Zuweisungen der neuen Werte an
die Signale erfolgt erst am Ende des Prozeßablaufes oder bei einer Unterbrechung mittels der WAITAnweisung. Eine Zustandsänderung auf den Signalen ist somit frühestens im nächsten Simulationsdelta zu
beobachten. Daher findet in dem Beispiel der Vergleich noch mit dem im vorhergehenden
Simulationsdelta zugewiesenen Wert statt, sodaß je nach Vorgeschichte das Signal summe dennoch einen
Wert größer als 10 annehmen kann.
Da der Prozeß aber auf das Signal summe sensitiv ist und sich dessen Wert gerade geändert hat, wird
ein weiterer Bearbeitungszyklus ausgelöst.
Im nachfolgenden Simulationsdelta liefert der Vergleich schließlich eine wahre Aussage und die
Anweisung in der 3. Zeile wird ausgeführt. Führt man diese Überlegungen fort, stellt man allerdings fest,
daß auch dieses Ergebnis nicht stabil sein kann. Der Wert des Signals summe alterniert zwischen 0 und 12
und mit jedem Signalwechsel kommt ein weiteres Simulationsdelta hinzu (s. a. Abschnitt 4.4.2)
.
Tabelle 1.4: Simulationsverlauf zum Prozeß V1
Simulationszeit
in_a
in_b
summe
2
3
5
20 ns
6
6
5
20 ns + 1∆
6
6
12
20 ns + 2∆
6
6
0
20 ns + 3∆
6
6
12
...
...
...
...
Variablen (Variables)
Da Signale ihren Wert während der Bearbeitung eines Prozesses nicht verändern, sind sie nicht geeignet
Zwischenergebnisse zu speichern. Dies ist der Grund für die Einführung von Variablen als wichtige
Ergänzung der sequentiellen Beschreibungsmöglichkeit in VHDL. Die Variablen erhalten ihren Wert
unmittelbar nach der Zuweisung. Dieser Wert steht dann innerhalb des aktuellen Simulationsdeltas sofort
zur Weiterverarbeitung im Prozeß zur Verfügung. Eine Variablenzuweisung ist per Definition mit keiner
Verzögerung behaftet, nicht einmal mit einem Delta. Somit ist es auch nicht gestattet einer
Variablenzuweisung eine Zeitverzögerung (AFTER ...) mitzugeben. Um diesen Unterschied auch optisch
herauszustellen besitzt die Variablenzuweisung ein anderes Symbol.
Bei der Deklaration von Variablen gibt es bezüglich des Typs keine Einschränkungen. Lediglich der
Gültigkeitsbereich der Variablen ist auf den Prozeß beschränkt, in dem die Variable deklariert wurde.
VARIABLE a,b: STD_LOGIC;
VARIABLE c: STD_LOGIC_VECTOR(15 DOWNTO 0);
Genauso wie Signale, haben Variablen einen Initialisierungswert. Dieser wird entweder explizit in der
Deklaration angegeben, oder es ist standardmäßig der linke Wert aus der Typdeklaration; - für
STD_LOGIC ist das ein 'U' - . Die Initialisierung ist aber ein einmaliger Vorgang zu Beginn der Simulation
(t=0ns) und nicht, wie man vermuten könnte, ein Vorgang , der bei jeder erneuten Bearbeitung des
Prozesses durchgeführt wird. Sorgfalt sei dennoch im Umgang mit Signalinitialisierungen empfohlen, da
es für die Hardware dafür keine entsprechende Interpretation gibt und die Synthese folglich
Initialisierungswerte von Signalen ignoriert. Im Vergleich zur vorigen Fassung, zeigt folgendes Beispiel
ein korrektes Verhalten entsprechend der Vorgabe und eine typische Anwendung einer Variablen.
37
Prozesse (Processes)
ARCHITECTURE korrekt OF addierer IS
BEGIN
V2: PROCESS(in_a, in_b)
VARIABLE temp: NATURAL RANGE 15 DOWNTO 0;
BEGIN
temp := in_a + in_b;
in_a
IF(temp > 10) THEN
summe <= 0;
ELSE
summe <= temp;
END IF;
in_b
END PROCESS V2;
END korrekt;
+
F
0
summe
T
Tru e
False
temp > 10 ?
Bild 1-32: Verwendung einer Variablen
Tabelle 1.5: Simulationsverlauf zum Prozess V2
Simulationszeit
in_a
in_b
temp
summe
2
3
5
5
20 ns
6
6
12
5
20 ns + 1∆
6
6
12
0
20 ns + 2∆
6
6
12
0
Globale Variablen (Shared Variables)
Normalerweise sind Signale zuständig für eine Kommunikation zwischen Prozessen. VHDL’93 definiert
mit dem Zusatz SHARED in der Variablendeklaration eine globale Variable, die von mehreren Prozessen
gemeinsam verwendet wird und damit auch zur Kommunikation zwischen Prozessen dient. Diese
Eigenschaft wird allerdings noch nicht von allen Synthesewerkzeugen unterstützt. In diesem
Zusammenhang ist allerdings nicht definiert, was geschieht, wenn zwei Prozesse im selben
Simulationsdelta auf eine Variable gleichzeitig schreibend oder gleichzeitig schreibend und lesend
zugreifen. Die einzig sinnvolle Anwendung wäre ein Datenaustausch in einer Richtung, wobei der Prozess
A die Variable beschreibt und Prozeß B greift darauf nur lesend zu. Deklarationen von globalenVariablen
können im Deklarationsteil der Architecture, neben Signaldeklarationen, erfolgen.
SIGNAL a,b,c: STD_LOGIC;
SHARED VARIABLE d: STD_LOGIC;
38
Prozesse (Processes)
1.5.3 Sequentielle Anweisungen (Sequential Statements)
Sequentielle Anweisungen sind in Prozessen, Funktionen und Prozeduren enthalten. Dieser Abschnitt
stellt die einzelnen Konstrukte vor und zeigt an Beispielen deren Anwendung bei der Beschreibung
kombinatorischer Logik und führt typische Fehlerquellen auf.
IF - THEN - ELSE
Die IF-Anweisung bildet das Gerüst für eine Multiplexstruktur und ist vergleichbar mit der bedingten
nebenläufigen Signalzuweisung (vgl. Abschnitt 4.3.2). Kontrolliert von mindestens einer Bedingung,
entscheidet die IF-Anweisung welcher Zweig exklusiv zu bearbeiten ist.
IF(bedingung1 = TRUE) THEN
-- Zuweisung1
tue etwas ganz besonderes;
ELSIF(bedingung2 = TRUE) THEN
-- Zuweisung2
tue etwas besonderes;
-- beliebig viele ELSIF
ELSE
-- Zuweisung3
tue, was fast immer zu tun ist;
END IF;
außergewöhnlicher Fall
(höchste Priorität)
Normalfall
(niedrigste Priorität)
Die Reihenfolge der alternativen Verzweigungen legt deren Priorität fest. Nur die Anweisungen der ersten
Bedingung, die eine wahre Aussage (TRUE) liefert, werden ausgeführt, alle anderen Zweige bleiben
unberücksichtigt. Trifft keine der angegebenen Bedingungen zu, kommt der ELSE-Zweig zur Ausführung,
den es deshalb nur einmal, dann aber als letzte Verzweigung, geben darf.
Verschachtelte IF-Konstrukte sind zwar erlaubt, doch sollte man bei mehr als zwei ineinander
verzahnten IF-Anweisungen prüfen, ob vielleicht eine andere Struktur mehr Übersicht und Lesbarkeit
bietet. Eine tiefe Verschachtelung erzeugt eine Fülle von Alternativen mit gegenseitigen Ausschlüssen und
es bleibt immer eine gewisse Restunsicherheit, ob die Prioritäten richtig gewählt wurden, oder nicht doch
ein Fall übersehen wurde. Mehrere Zuweisungen innerhalb eines Zweiges sind in komplexeren Teilen eher
die Regel, als eine Ausnahme. Im folgenden Beispiel legt die Synthese für jeden Datenpfad eine separate
Multiplexstruktur an (Bild 1-33).
add_sub: PROCESS(auf, ab, c1, c2)
BEGIN
IF(auf = '1') THEN -- Bedingung1
x <= c1 + 1;
y <= c2 + 1;
ELSIF(ab = '1') THEN -- Bedingung2
x <= c1 - 1;
c1 c2
y <= c2 - 1;
ELSE
x <= c1;
y <= c2,
END IF;
END PROCESS add_sub;
+1 +1
-1 -1
F
T
F
y
T
x
Tru e
False
auf = 1 ?
Tru e
False
ab = 1 ?
Bild 1-33: Multiplexstruktur
39
Prozesse (Processes)
In Prozessen, die kombinatorische Logik erzeugen sollen, muß darauf geachtet werden, daß in allen
vorkommenden Bedingungsfällen je Signal immer eine Zuweisung angegeben wird oder der
abschließende ELSE-Zweig vorhanden ist. Ähnlich dem früher betrachteten Fall einer unvollständigen
Sensitivity-List, führt auch eine fehlende Signalzuweisung dazu, daß das Synthesewerkzeug für dieses
Signal ein unerwünschtes Speicherelement (Latch) einfügt. Das Synthesewerkzeug geht in solchen Fällen
davon aus, daß der Signalwert beibehalten werden muß. Man könnte diese Situation im vorigen Beispiel
leicht herbeiführen, indem man eine der Signalzuweisungen streicht.
Im folgenden soll noch, anhand eines Beispieles, auf ein typisches Problem im Zusammenhang mit der
IF-Anweisung hingewiesen werden:
Beispiel: 4-zu-1 Multiplexer
Die Aufgabe des 4-zu-1 Multiplexers ist es , abhängig vom Kontrollsignal sel, den entsprechenden
Eingang (i1 ... i4) mit dem Ausgang y zu verbinden. Die Lösung mit einer nebenläufigen Anweisung kann
dann folgendermaßen aussehen:
WITH sel
y <= i1
i2
i3
i4
SELECT
WHEN "00"
WHEN "01"
WHEN "10"
WHEN OTHERS;
-- OTHERS steht als Schlüsselwort für alle restlichen Möglichkeiten
Es soll nun die Frage untersucht werden, ob sich die nachfolgende Realisierung mit einer sequentiellen IFAnweisung genauso verhält. Alle Signale, die in der nebenläufigen Anweisung ausgewertet, bzw. ’auf der
rechten Seite’ gelesen werden (implizite Sensitivity-List), stehen im Prozeß M1 explizit in seiner
Sensitivity-List (der Prozess ist ja eine nebenläufige Anweisung). Jede Veränderung eines der Signale führt
zu einer erneuten Bearbeitung der Anweisungen und die Simulation (Bild 1-34) wird daher keine
Unterschiede in der Funktion zeigen.
Bild 1-34: Simulationsergebnis des 4-zu-1 Multiplexers der select- und if- Version
40
Prozesse (Processes)
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY mux IS
PORT (
i1, i2, i3, i4 : IN STD_LOGIC;
sel: IN STD_LOGIC_VECTOR(1 DOWNTO 0);
y : OUT STD_LOGIC);
END mux;
ARCHITECTURE mux_arch OF mux IS
BEGIN
M1: PROCESS(i1,i2,i3,i4,sel)
BEGIN
IF(sel = "00") THEN y <= i1;
ELSIF(sel = "01") THEN y <= i2;
ELSIF(sel = "10") THEN y <= i3;
ELSE y <= i4;
END IF;
END PROCESS M1;
END mux_arch;
Interessant ist nun die Frage, ob es Unterschiede in der synthetisierten Schaltung gibt. Die Priorisierung
der einzelnen Bedingungen innerhalb der IF-Anweisung ist in diesem Beispiel eigentlich gar nicht
notwendig, weil die angegebenen Bedingungen sich bereits selbst gegenseitig ausschließen. Umgesetzt in
eine boolesche Gleichung ergibt die IF-Anweisung daher eigentlich eine redundante Logik.
y = i1*(sel="00")+
i2*(sel/="00")*(sel="01")+
i3*(sel/="00")*(sel/="01")*(sel ="10")+
i4*(sel/="00")*(sel/="01")*(sel/="10")*(sel="11")
Das Synthesewerkzeug kennt aus den deklarierten Bibliotheken alle Komponenten der Zieltechnologie
und findet mit ihrer Hilfe die optimale Realisierung. In diesem Fall, in dem alle Bedingungen in der IFAnweisung von einem Signal abhängen, kann die Synthese darin enthaltene Ausschlüsse erkennen und
erzeugt eine redundanzfreie Logik. Dort, wo mehrere Signale in den Bedingungen miteinander verknüpft
werden, ist es für die Synthese nicht möglich, gegenseitige Ausschlüsse zu erkennen und das Ergebnis fällt
nicht optimal aus. Um die genannten Probleme zu umgehen ist es sinnvoll, anstelle der IF- eine CASEAnweisung oder eine Kombination davon zu verwenden.
Im folgenden Beispiel soll nun eine typische Anwendung für die IF-Anweisung dargestellt werden.
Beispiel: Adreßdekoder
Ein Adreßdekoder verteilt den verfügbaren Adreßraum auf die angeschlossenen Peripheriegeräte. Der
Prozeß decodeA dekodiert stets den vollen Adreßbereich, während decodeB in einer Vorselektion lediglich
maximal 16 Seiten mit jeweils 4K Größe zur Verfügung stellt. Um ein Gerät zu adressieren, muß dann das
entsprechende Chip-Select-Signal aktiv sein. Die zweite Variante ist somit unabhängig von der absoluten
Lage des Adreßbereiches.
41
Prozesse (Processes)
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY adr_decode IS
PORT (
adress: IN STD_LOGIC_VECTOR(15 DOWNTO 0);
cs1, cs2, cs3, cs4: OUT STD_LOGIC);
END adr_decode;
ARCHITECTURE adr_decode_arch OF adr_decode IS
BEGIN
decodeA: PROCESS(adress)
BEGIN
cs1 <= '0';
cs2 <= '0';
cs3 <= '0';
cs4 <= '0';
IF(adress >= X"0000" AND adress < X"2000") THEN
cs1 <= '1';
ELSIF(adress >= X"2000" AND adress < X"3000") THEN
cs2 <= '1';
ELSIF(adress >= X"3000" AND adress < X"7000" ) THEN
cs3 <= '1';
ELSIF(adress >= X"7000") THEN
cs4 <= '1';
END IF;
END PROCESS decodeA;
END adr_decode_arch;
decodeB: PROCESS(adress)
VARIABLE adr : STD_LOGIC_VECTOR(3 DOWNTO 0);
BEGIN
adr := adress(15 DOWNTO 12);
cs1 <= '0';
cs2 <= '0';
cs3 <= '0';
cs4 <= '0';
IF(adr = "0000" ) THEN
cs1 <= '1';
cs1
ELSIF(adr = "0001") THEN
cs2 <= '1';
ELSIF(adr = "0010") THEN
cs3 <= '1';
adress
ELSIF(adr = "0011") THEN
cs4 <= '1';
END IF;
END PROCESS decodeB;
Gerät 1
a := adress(11 DOWNTO 0);
.....
IF(cs1 = '1' AND a = ....) THEN
....
Bild 1-35: Bild 4.35: Adressdekoder
42
Prozesse (Processes)
CASE - WHEN
Ähnlich den IF-Anweisungen erlauben CASE-Anweisungen eine Verzweigung in Abhängigkeit eines
bestimmten Signal- oder Variablenwertes oder des Ergebnisses eines logischen Ausdrucks. Als
Auswahlbedingung ist nur ein diskreter Typ (Integer, Enumerator) zugelassen. Dies eröffnet allerdings
sehr vielfältige Anwendungen, sind doch gerade die meisten Signaltypen als Enumerator deklariert.
Die CASE-Anweisung ist vergleichbar mit einer Tabelle, wobei der Wert der Variablen ausdruck die
Zeile liefert, in der die auszuführende Vorschrift steht:
CASE ausdruck IS
WHEN wert1 =>
tue etwas;
WHEN wert2 =>
tue etwas anderes;
WHEN OTHERS =>
tue das Gewöhnliche;
END CASE;
Ein anschauliches Beispiel ist die Kodierung der Ausgabe eines Zustandsautomaten:
TYPE states IS (STATE1, STATE2, STATE3, STATE4);
TYPE farben IS (ROT, GRUEN, BLAU, GELB);
SIGNAL actual_state : states;
SIGNAL ausgabe: farben;
...
PROCESS(actual_state)
BEGIN
CASE actual_state IS
WHEN STATE1 => ausgabe <= GRUEN;
WHEN STATE2 => ausgabe <= GELB;
WHEN STATE3 => ausgabe <= ROT;
WHEN STATE4 => ausgabe <= BLAU;
END CASE;
END PROCESS;
Jede Verzweigung kann mehrere Anweisungen enthalten, auch weitere CASE- oder IF-Strukturen. Die
Auswahlliste (WHEN ...) muß alle Werte enthalten, die die Variable ausdruck annehmen kann. Will
man nicht alle Fälle aufzählen, bei denen evt. auch dasselbe passieren soll, können diese in der letzten
Auswahl (WHEN OTHERS ...) zusammengefaßt werden. Ebenso ist eine Gruppierung oder die Angabe
eines Bereiches zulässig:
-- STATE1 oder STATE2:
WHEN STATE1 | STATE2 =>
-- Alle Zustände, von STATE1 bis STATE4 mit Attribut
'LEFT und 'RIGHT:
WHEN states'LEFT TO states'RIGHT =>
Nach der Synthese findet man in der Hardware auch hier wieder Multiplexer. Die
Verzweigungsalternativen sind stets nur von einem Input abhängig und schließen sich daher gegenseitig
aus. Aus diesem Grund ist in manchen Fällen die CASE-Anweisung im Vergleich zur IF-Anweisung die
bessere Wahl, weil eine mehrfach verzweigte IF-Struktur von der Unabhängigkeit ihrer Bedingungen
ausgeht.
43
Prozesse (Processes)
LOOP
In VHDL unterscheidet man drei Varianten der Schleife:
1) Die einfache Schleife (LOOP) ist eine Endlosschleife. Ein Abbruch ist nur mit EXIT möglich.
LOOP
tue etwas immer wieder;
EXIT WHEN (Bedingung=TRUE)
END LOOP
2) Die WHILE-Schleife läuft, solange die Schleifenbedingung zutrifft
WHILE (Bedingung=TRUE) LOOP
solange Bedingung TRUE;
END LOOP;
3) Die FOR-Schleife wiederholt den Schleifenrumpf n-mal, dabei ist n im Schleifenkopf angegeben
FOR i IN 0 TO 15 LOOP
16 Mal wiederholen;
END LOOP;
Die Schleife erlaubt die mehrfache Wiederholung eines VHDL-Programmteiles, beispielsweise um jedes
Element eines Arrays in gleicher Weise zu bearbeiten. Für die Synthese ist es wichtig zu wissen, wie oft
die Schleife zu bearbeiten ist, denn danach richtet sich der Umfang der Schaltung. Bei der FOR-Schleife
ist es eindeutig, dort legt der Schleifenparameter fest, wie oft der Schleifenrumpf wiederholt wird.
Beispiel: Parity Generator mit FOR-Schleife
Dieses Beispiel benutzt eine FOR-Schleife, um alle Einzelsignale eines Vektors mit XOR zu verknüpfen.
Das Ergebnis ergänzt das Datenwort zu einer geraden Prüfsumme, welche mit dem Verkettungsoperator
'&' dem Datenwort vorangestellt wird.
ENTITY parity_gen IS
PORT ( data_in: IN STD_LOGIC_VECTOR(15 DOWNTO 0);
data_out: OUT STD_LOGIC_VECTOR(16 DOWNTO 0));
END parity_gen;
ARCHITECTURE parity_arch OF parity_gen IS
BEGIN
gen: PROCESS(data_in)
VARIABLE parity: STD_LOGIC;
BEGIN
par := '0';
FOR i IN 15 DOWNTO 0 LOOP
parity := parity XOR data_in(i);
END LOOP
data_out <= parity & data_in;
END PROCESS gen;
END parity_arch;
44
Prozesse (Processes)
Weitere erwähnenswerte Besonderheiten:
•
•
•
•
•
•
•
Der Schleifenparameter 'i' ist nur innerhalb der Schleife sichtbar
Der Schleifenparameter verhält sich wie eine Konstante
15 DOWNTO 0: Erster Schleifendurchlauf mit i=15
0 TO 15: Erster Schleifendurchlauf mit i=0
Schleifenparameter kann auch Enumerator sein: FOR i IN states'RANGE LOOP
'i' nimmt dann der Reihe nach die Werte aus der Typdeklaration von states an (s.o.)
Anstelle der Variablen parity ist kein Signal möglich, denn das Resultat wäre dann die letzte Zuweisung
an das Signal mit parity aus vorigem Simulationsdelta: parity <= parity XOR data_in(0)
Verwendung des EXIT-Kommandos
Beispiel: Zählen führender '1'en mit der WHILE-Schleife
Eine WHILE-Schleife stellt eine große Herausforderung an ein Synthesewerkzeug dar. Kann der Compiler
die maximale Anzahl der Iterationen nicht erkennen, oder entscheidet darüber dynamisch eine zur Laufzeit
eintretende Bedingung, so ist die Schleife nicht synthetisierbar.
Die WHILE-Schleife im folgenden Beispiel soll die Anzahl der führenden '1'en im Datenwort
bestimmen. Mit der ersten vorkommenden '0' bricht die Schleife ab und für den Fall, daß der Vektor
komplett mit '1'en gefüllt ist, erfolgt ein harter Abbruch mit dem EXIT-Kommando, weil der Index im
Vektor data in keinem Fall den Wert 15 überschreiten darf (Runtime Error). Das Zählergebnis cnt dient
gleichzeitig als Index, um die einzelnen Bits im Vektor anzusprechen.
Der Beispielkode ist dennoch nicht synthetisierbar, weil der Index cnt in der Schleife verändert wird.
Diese Aufgabe ist besser mit einer FOR-Schleife (s. Prozeß zaehlenB) zu realisieren.
ENTITY anzahl_fuehrende_einsen IS
PORT (
data: IN STD_LOGIC_VECTOR(0 TO 15);
anzahl: OUT NATURAL RANGE 0 TO 16);
END anzahl_fuehrende_einsen;
ARCHITECTURE anzahl_arch OF anzahl_fuehrende_einsen IS
BEGIN
zaehlenA: PROCESS(data)
VARIABLE cnt: NATURAL RANGE 0 TO 16;
BEGIN
cnt := 0;
WHILE data(cnt) = '1' LOOP
cnt := cnt + 1;
EXIT WHEN cnt = 16;
END LOOP;
anzahl <= cnt;
END PROCESS zaehlenA;
END anzahl_arch;
zaehlenB: PROCESS(data)
VARIABLE cnt: NATURAL RANGE 0 TO 16;
BEGIN
cnt := 0;
FOR i IN 0 TO 15 LOOP
EXIT WHEN data(i) = '0';
cnt := cnt + 1;
END LOOP;
anzahl <= cnt;
END PROCESS zaehlenB;
45
Prozesse (Processes)
Realisierung ohne Schleife:
...
cnt := 0;
IF(data0) = '1') THEN cnt := 1;
IF(data(1) = '1') THEN cnt := 2;
IF(data(2) = '1') THEN cnt := 3;
...
END IF;
END IF;
END IF;
0
15
data 1 1 1 1 1 0 1 1 0 1 0 1 1 1 0 1
&
&
&
&
K o d ie re r
5
anzahl
Bild 1-36: Interpretation der Schleife im Prozeß zaehlenB
Bild 1-37: Simulationsergebnis: Zählen führender Einsen im Datenwort.
46
Prozesse (Processes)
Verwendung des NEXT-Kommandos
Beispiel: Zählen aller '1'en im Datenwort
ENTITY anzahl_aller_einsen IS
PORT (
data: IN STD_LOGIC_VECTOR(0 TO 15);
anzahl: OUT NATURAL RANGE 0 TO 16);
END anzahl_aller_einsen;
ARCHITECTURE anzahl_arch OF anzahl_aller_einsen IS
BEGIN
zaehlen: PROCESS(data)
VARIABLE cnt: NATURAL RANGE 0 TO (data'HIGH+1);
BEGIN
cnt := 0;
FOR i IN data'RANGE LOOP
NEXT WHEN data(i) = '0';
cnt := cnt + 1;
END LOOP
anzahl <= cnt;
END PROCESS zaehlen;
END anzahl_arch;
WAIT
Bisher hat ausschließlich die Sensitivity-List eines Prozesses darüber entschieden, wann der Prozeß
bearbeitet wird. Eine alternative Form des Prozesses verwendet an Stelle der Sensitivity-List eine WAITAnweisung, um den Prozeß in den schwebenden Zustand zu versetzen. Für die Synthese gelten bei der
Verwendung der WAIT-Anweisung starke Einschränkungen. So darf nur ein einziges WAIT entweder am
Beginn oder am Ende des Prozesses vorkommen. Für ein reines Simulationsmodell eröffnet diese
Anweisung jedoch vielfältige Möglichkeiten, den Ablauf eines Prozesses zu steuern.
PROCESS
BEGIN
y <= a AND b;
-- Vollständiger Ersatz der Sensitivity List:
-- Warte auf ein Ereignis auf dem Signal a oder b
WAIT ON a, b;
END;
Prozesse mit einer Sensitivity-List werden am Anfang der Simulation, in der sogenannten ElaborationsPhase zur Initialisierung einmal durchlaufen. Die WAIT-Anweisung am Ende des Prozesses ist daher ein
gleichwertiger Ersatz, weil alle davorstehenden Anweisungen mindestens einmal beim Start der
Simulation bearbeitet werden. Die Initialisierung von Prozessen in VHDL hat kein entsprechendes
Äquivalent in der Hardware. Aus diesem Grund ist diese Initialisierung in manchen Simulatoren auch
abschaltbar. Damit wird verhindert, daß die Simulation scheinbar mit einem definierten Start beginnt, die
synthetisierte Schaltung aber möglicherweise in einem undefinierten Zustand hängenbleibt. Daher findet
man häufig die WAIT-Anweisung am Anfang des Prozesses.
47
Prozesse (Processes)
Weitere Formen der WAIT-Anweisung sind:
• WAIT FOR zeitangabe;
PROCESS
BEGIN
reset <= '0';
WAIT FOR 100ns;
reset <= '1';
WAIT; -- für immer
END PROCESS;
• WAIT UNTIL (boolescher Ausdruck);
PROCESS
BEGIN
WAIT UNTIL (clk'EVENT AND clk = '1');
q <= d;
END PROCESS;
ASSERT
Die ASSERT-Anweisung ist ein geeignetes Hilfsmittel, um ein fehlerhaftes Verhalten des VHDL-Modells
aufzuspüren. Die Anweisung kennt aufgrund eines logischen Ausdrucks die korrekte Situation und meldet
während der Simulation jegliche Abweichung von der Vorgabe. Die ASSERT-Anweisung ist je nach
Plazierung im VHDL-Code nebenläufig oder sequentiell (außerhalb bzw. innerhalb von Prozessen),
unterscheidet sich dabei aber nicht in ihrer Form, wohl aber in der Sensitivity-List. Die nebenläufige
Anweisung reagiert empfindlich auf alle Signale innerhalb des logischen Ausdrucks, während die
sequentielle Anweisung an die Sensitivity-List des zugehörenden Prozesses gebunden ist.
ASSERT logischer Ausdruck
REPORT "Ausgabe einer Nachricht"
SEVERITY Fehlerstatus: NOTE, WARNING, ERROR, FAILURE;
Beispiel:
ASSERT (COUNT < 255)
REPORT "Invalid Counter value"
SEVERITY FAILURE;
Falls der logische Ausdruck das Ergebnis FALSE liefert, wird die Nachricht ausgegeben. Ohne REPORTAnweisung erfolgt lediglich die Ausgabe „assertion violation“ mit Angabe des Moduls, in dem der Fehler
aufgetreten ist. Der Fehlerstatus, bzw. die Gewichtung entscheidet, ob die Simulation fortgeführt wird:
NOTE:
Dieser Fehlerstatus kennzeichnet harmlose Ausgaben, z.B. allgemeine Hinweise zum
Stand der Simulation, oder um während der Simulation auf eine längere Rechenoperation
hinzuweisen.
WARNING:
Eine Warnung markiert Zustände, die zwar außergewöhnlich sind, aber nicht zwingend
eine Fehlfunktion bewirken. Die Simulation kann weiterlaufen.
ERROR:
Ersatzwert, wenn kein Fehlerstatus angegeben. Oft ist hierbei eine Bedingung verletzt,
dies führt mit großer Wahrscheinlichkeit zu einer Fehlfunktion. Die Simulation wird
typischerweise abgebrochen.
FAILURE:
Es ist ein schwerwiegender Fehler aufgetreten, z.B. Division durch Null.
Die Simulation wird generell abgebrochen.
48
Prozesse (Processes)
NULL
Manchmal ist es nützlich eine Anweisung zu haben, die einfach nichts tut. Die NULL-Anweisung könnte
beispielsweise einen Zweig in einer IF- oder CASE-Anweisung besetzen. VHDL fordert die explizite
Angabe einer Null-Anweisung, nicht wie in der Programmiersprache C, in der bereits ein zusätzliches
Semikolon eine leere Anweisung darstellt.
Zum Abschluß dieses Kapitels soll noch ein Beispiel für eine typische Anwendung dargestellt werden,
das einige Komponenten aus diesem Kapitel verwendet.
49
Prozesse (Processes)
Beispiel: Synchroner Zähler
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY zaehler IS
PORT(
clk: IN STD_LOGIC;
zaehler_aktiv: IN STD_LOGIC;
auf_ab: IN STD_LOGIC;
laden: IN STD_LOGIC;
lade_wert: IN NATURAL RANGE 15 DOWNTO 0;
zaehler_wert: OUT NATURAL RANGE 15 DOWNTO 0;
uebertrag: OUT STD_LOGIC);
END zaehler;
ARCHITECTURE zaehler_arch OF zaehler IS
SIGNAL temporaer: NATURAL RANGE 15 DOWNTO 0;
BEGIN
zaehler_wert <= temporaer;
zaehl: PROCESS
BEGIN
WAIT UNTIL clk'EVENT AND clk = '1';
uebertrag <= '0'; -- wird später evt. überschrieben
ASSERT (laden AND zaehler_aktiv) = '0'
REPORT "Zaehler gleichzeitig Laden und Zaehlen ?!"
SEVERITY WARNING;
IF(laden = '1') THEN
temporaer <= lade_wert;
ELSIF(zaehler_aktiv = '1') THEN
CASE auf_ab IS
WHEN '0' =>
IF(temporaer = 15) THEN
temporaer <= 0;
uebertrag <= '1';
ELSE
temporaer <= temporaer + 1;
END IF;
WHEN '1' =>
IF(temporaer = 0) THEN
temporaer <= 15;
uebertrag <= '1';
ELSE
temporaer <= temporaer - 1;
END IF;
WHEN OTHERS => NULL;
END CASE;
END IF;
END PROCESS zaehl;
END zaehler_arch;
50
Prozesse (Processes)
Bild 1-38: Ein taktsynchroner Zähler in der Simulation
51
CAE: VHDL
1.6 Sequentielle (Taktsynchrone) Logik
Digitale Systeme wie Prozessoren, Zustandsautomaten oder Einrichtungen zur Nachrichtenübertragung
sind auf einen Takt angewiesen. Der Takt gibt die Arbeitsgeschwindigkeit vor und ist dafür verantwortlich,
daß miteinander zu verknüpfende Teilergebnisse zum richtigen Zeitpunkt vorliegen. Ohne Takt verharrt
die Schaltung in ihrem augenblicklichen Zustand und in CMOS-Technologien macht sich dieser Stillstand
sogar durch eine kaum noch meßbare Stromaufnahme bemerkbar.
Enthält die Schaltung mehrere Verarbeitungsstufen, oder ist ein Teilergebnis abhängig vom aktuellen
Zustand der Schaltung, dann sorgt der Takt für einen regulären und vorgegebenen Ablauf. So wechselt die
gesamte Schaltung ihren Zustand nur mit einem ganz bestimmten Ereignis des Taktes, beispielsweise mit
jeder positiven Taktflanke, wodurch die Speicherelemente (auch Register oder Flip-Flops) ihren Wert
gemeinsam, synchron zum Takt, verändern. Das Prinzip ist mit einer Eimerkette vergleichbar, die nur dann
reibungslos funktioniert, wenn alle Beteiligten gleichzeitig den Eimer auf der einen Seite in Empfang
nehmen und auf die andere Seite transportieren.
Den Entwurf einer taktsynchronen Schaltung bezeichnet man auch als Beschreibung auf RegisterTransfer Ebene, wobei Transferfunktionen den Datenfluß zwischen Registern beschreiben. In der
Hardware bilden meist Flip-Flops die Register und kombinatorische Verknüpfungen implementieren die
Transferfunktionen (s. Bild 1-39).
Die Aufteilung eines Entwurfs in einzelne Register und Transferfunktionen ist ein wichtiger Bestandteil
im Entwicklungsablauf digitaler Schaltungen. Auch die Synthesewerkzeuge erwarten diese Struktur in der
VHDL-Beschreibung. Dieser Abschnitt zeigt, wie man in VHDL synchrone Schaltwerke beschreibt und
worauf bezüglich einer nachfolgenden Synthese besonders zu achten ist.
Register
D
D
a
Q
Q
b
Transferfunktion:
y = f(a,b,z)
Kombinatorik
(Schaltnetz)
y
z
D
Q
Takt
Bild 1-39: Prinzip einer sequentiellen und taktsynchronen Logik (Schaltwerk)
1.6.1 Kombinatorik
In VHDL gibt es viele Möglichkeiten kombinatorische Logik zu beschreiben, einige davon wurden in
vorangegangenen Abschnitten bereits vorgestellt (vgl. Abschnitt 4.3 und 4.5). Dazu gehören:
• Nebenläufige Signalzuweisungen und Anwendung der logischen Operatoren, wie AND, NAND, OR,
NOR, XOR, XNOR und NOT. Beispiel: y <= (a AND b) AND NOT z
• Bedingte Signalzuweisungen mit WHEN-ELSE
• Ausgewählte Zuweisungen WITH-SELECT
• Arithmetische Operationen (sequentiell oder nebenläufig). Beispiel: y <= a + b;
• Boolesche Ausdrücke in sequentiellen Anweisungen (innerhalb eines Prozesses)
52
Sequentielle (Taktsynchrone) Logik
Beispiel:
PROCESS(a,b,z) -- Sensitiv auf alle gelesenen Signale
BEGIN
-- der logische Vergleich in der IF-Anweisung
-- ist ebenfalls Bestandteil der Kombinatorik
IF((a AND b) = '1') THEN
y <= NOT z;
ELSE
y <= '0';
...
Alle genannten Konstrukte haben die gemeinsame Eigenschaft, daß sie in der Hardware durch
Kombination der booleschen Grundfunktionen AND, OR und NOT realisiert werden können.
1.6.2 Register und D-Flip-Flop
Ein Register hat die Aufgabe, Zwischenergebnisse zu speichern und den Signalwert für eine Taktperiode
beizubehalten. Das flankengetriggerte D-Flip-Flop erfüllt diese Aufgabe, indem es entweder mit der
positiven oder negativen Taktflanke den Wert am Eingang D an den Ausgang Q überträgt. Dieser Vorgang
wird auch als Signalabtastung bezeichnet. An realen Flip-Flops sind jedoch in der Regel zwei wichtige
Randbedingungen einzuhalten, damit der Ausgang den richtigen Wert annimmt:
Das Signal am Eingang der Flip-Flops muß eine gewisse Zeit vor der aktiven Taktflanke stabil anliegen
(Setup-Zeit, tS) und darf sich auch erst nach der aktiven Taktflanke und nach einer Haltezeit (Hold-Zeit,
tH) wieder ändern.
Der abgetastete Wert erscheint nicht sofort am Ausgang, sondern aufgrund von internen Laufzeiten
etwas verzögert (Clock-to-Output-Zeit, tCO) (s.a. Bild 1-40)
Takt
Vor und nach der
Taktflanke:
Stabiles Signal am Eingang D
tS
tH
D
tCO
Q
Bild 1-40: Impulsdiagramm eines realen Flip-Flops
Worauf es bei der Bescheibung eines Flip-Flops ankommt, soll das nachfolgende Beispiel zeigen:
ENTITY dff IS
PORT(
d: IN STD_LOGIC;
q: OUT STD_LOGIC;
takt: IN STD_LOGIC);
END dff;
53
CAE: VHDL
ARCHITECTURE dff_arch OF dff IS
BEGIN
PROCESS(takt)
BEGIN
IF(takt'EVENT AND takt = '1') THEN
q <= d;
END IF;
END PROCESS;
END dff_arch;
dff
D
Q
Bild 1-41: Ein Ein-Bit-Register (D-Flip-Flop)
Die Signalzuweisung von D nach Q soll z. B. mit jeder positiven Taktflanke erfolgen. Der Prozeß muß also
zunächst auf das Signal takt sensitiv sein. Damit wird der Prozeß aber bei jedem Signalwechsel
ausführend. Die Einschränkung auf die positive Taktflanke geschieht innerhalb des Prozesses mit der
Abfrage IF(takt'EVENT AND takt='1'). Mit dem Attribut 'EVENT (gelesen: „Tick Event“) läßt sich
überprüfen, ob gerade ein Event, also ein Wechsel auf dem Signal takt, stattgefunden hat und die UndVerknüpfung mit takt='1' selektiert dann noch die positven Taktflanken aus.
Das Beispiel formuliert ein ideales Verhalten. Es ist frei von Laufzeiten, sodaß die Zuweisung bereits
im nächsten Simulationsdelta wirksam ist. Ebenso müssen die Setup- und Hold-Zeiten während der
Simulation nicht überprüft werden, da synthesefähiger VHDL-Code kein Timing enthält. Die exakte
Timing-Analyse erfolgt erst im Anschluß an die Synthese (Bild 1-46Bild 1-46:).
1.6.3 Getaktete Prozesse
Da es sehr aufwendig wäre jedes Register oder Flip-Flop als einzelne Komponente in den
Schaltungsentwurf einzubauen, wird von den Synthesewerkzeugen am Prozeßaufbau automatisch erkannt,
ob Speicherelemente eingebaut werden sollen. Ein getakteter Prozeß mit den nachfolgenden
Eigenschaften, beschreibt sowohl den kombinatorischen Teil einer Schaltung, als auch die zur Abtastung
bzw. Synchronisation erforderlichen Register. Getaktete Prozesse sind an den zwei Merkmalen erkennbar:
• Der Prozess ist sensitiv auf den Takt.
• Der Prozess beginnt mit IF(takt'EVENT AND takt = '1') THEN ...
Danach folgen Signal- oder Variablenzuweisungen, die die Kombinatorik der Schaltung beschreiben.
b
a
c
d
Kombinatorik 1
a AND b OR c
y
D
Q
Kombinatorik 2
y+d
Ta k t
Prozess P1
Prozess P2
Prozess P3
Bild 1-42: Getaktete Prozesse
54
z
D
Q
Sequentielle (Taktsynchrone) Logik
P1: PROCESS(takt)
BEGIN
IF(takt'EVENT AND takt = '1') THEN
-- auch sequentielle Anweisungen: Loops, Case, IF,...
y <= (a AND b) OR c;
END IF;
END PROCESS P1;
P2: PROCESS(takt)
BEGIN
IF(takt'EVENT AND takt = '1') THEN
z <= y + d;
END IF;
END PROCESS P2;
Ohne die Schaltung zu verändern, können beide Prozesse P1 und P2 in Bild 1-42 auch zusammengefaßt
werden, so daß kleine, funktional zusammengehörende, Module entstehen.
P3: PROCESS(takt)
BEGIN
IF(takt'EVENT AND takt = '1') THEN
y <= (a AND b) OR c;
z <= y + d;
END IF;
END PROCESS P3;
Interessant ist nun, wie das Simulationsergebnis des VHDL-Kodes und die synthetisierte Schaltung
aussehen. Für das Verhalten in der Simulation sind die Grundregeln der sequentiellen Anweisungen im
Abschnitt 4.5 von Wichtigkeit. Die Bearbeitung der Zuweisungen in getakteten Prozessen erfolgt mit jeder
positiven Taktflanke. Die Signale selbst verändern sich aber während der Prozessbearbeitung nicht. Der
neue Signalwert, nach der positiven Flanke, ergibt sich somit aus einer Verknüpfung der Signalwerte vor
der Taktflanke, dies entspricht der Schaltungsstruktur in Bild 1-42 und damit dem Verhalten realer
Schaltungen.
Da das VHDL-Modell frei von Verzögerungen ist, stehen die Taktflanke und der Signalwechsel in der
Simulationsausgabe genau übereinander. Dennoch ist der Signalwechsel eine Reaktion auf die Taktflanke ,
so daß zwischen der Ursache und Wirkung mindestens ein (unsichtbares) Simulationsdelta (∆) liegt. Diese
Delta ist ein Platzhalter für die reale Verzögerung am Register (Clock-to-Output).
Bild 1-43 veranschaulicht die Interpretation der Simulationsausgabe.
Bild 1-43: Simulation getakteter Prozesse
55
CAE: VHDL
Auch die Synthesewerkzeuge setzen die Prozessbeschreibung direkt um. Innerhalb der Anweisung
IF(takt'EVENT AND takt = '1') werden alle Signale, die auf der linken Seite einer Zuweisung stehen zu
Registerausgängen und die Verknüpfungen auf der rechten Seite der Zuweisungen bilden den
kombinatorischen Teil am Eingang des Registers.
Bild 1-44 zeigt das Simulationsergebnis des VHDL-Kodes für die getakteten Prozesse P1, P2 bzw. P3
und Bild 1-45 stellt ein synthetisiertes Ergebnis dar. In Bild 1-46 ist ein Beispiel für die Darstellung des
kritischen Pfades in der Timing-Analyse einer synthetisierten Schaltung zu sehen.
Bild 1-44: Simulationsergebnis des VHDL-Kodes ohne Timing
Bild 1-45: Synthetisierter Prozess mit kombinatorischem Teil und Registern
56
Sequentielle (Taktsynchrone) Logik
Bild 1-46: Identifizierung des „kritischen Pfades“. Beispiel aus dem Synopsys FPGA-Compiler
Reset in getakteten Prozessen
Register sind Speicherelemente, die nach dem Anlegen der Betriebsspannung üblicherweise einen
zufälligen Wert annehmen. Die korrekte Funktion der Schaltung ist meistens von einem definierten
Startwert am Ausgang der Register abhängig. Die Initialisierung der Register erfolgt mit einem speziellen
Signal (Reset), welches an alle Register angeschlossen ist. Diese Initialisierung darf nicht mit der
Initialisierung innerhalb von Signaldeklarationen verwechselt werden. Für die VHDL-Simulation ist ein
explizit vorgesehenes Reset-Signal meistens unvermeidlich, da mit dem Simulationsstart beispielsweise
Signale des Typs STD_LOGIC den Wert 'U' (undefined) annehmen. Rückgekoppelte Strukturen haben
dann ohne Reset keine Möglichkeit, den undefinierten Zustand zu verlassen, da eine Verknüpfung von 'U'
mit '0' oder '1' wieder ein 'U' ergibt. Für den realen Betrieb der Schaltung hingegen darf nach Power-Up
keine Situation mehr entstehen, in der ein Reset notwendig ist.
Zur Realisierung eines Resets stehen prinzipiell zwei Varianten zur Auswahl, der asynchrone und
synchrone Reset. Während beim asynchronen Reset das Zurücksetzen des Registers unabhängig vom Takt
erfolgt (Resetsignal hat Priorität), wird der synchrone Reset durch eine Verknüpfung im Datenpfad
realisert (Bild 1-47).
57
CAE: VHDL
Prozess mit asynchronem Reset
-- Prozess ist sensitiv auf das takt- und reset-Signal
PROCESS(takt, reset)
BEGIN
-- Reset hat Priorität vor Takt
IF(reset = '0') THEN
signal_out <= '0';
ELSIF(takt'EVENT AND takt = '1') THEN
signal_out <= signal_in;
END IF;
END PROCESS;
Prozess mit synchronem Reset
-- Prozess ist nur sensitiv auf das takt-Signal
PROCESS(takt)
BEGIN
IF(takt'EVENT AND
IF(reset = '0')
signal_out <=
ELSE
signal_out <=
END IF;
END IF;
END PROCESS;
takt = '1') THEN
THEN
'0';
signal_in;
Asynchroner Reset
Synchroner Reset
Reset
signal_in
D
Q
signal_out
takt
Reset
signal_in
takt
takt
takt
Reset
Reset
signal_out
'0'
signal_out
&
signal_out
D
Q
'0'
Bild 1-47: Synchroner und asynchroner Reset
Getakteter Prozeß mit Enable-Funktion
In vielen Fällen ist es wünschenswert, die Verarbeitung in getakteten Pozessen für eine oder mehrere
Taktperioden anzuhalten, bzw. nur dann eine Signalzuweisung vorzunehmen, wenn ein gültiges
Eingangssignal vorhanden ist. Ein spezielles Signal (Enable) übernimmt dabei die Steuerung und sorgt
dafür, daß bei einer Unterbrechung der Verarbeitung das Register seinen bisherigen Wert beibehält. Dies
wird erreicht indem der Ausgang des Registers auf den Eingang zurückgekoppelt wird (Bild 1-49).
58
Sequentielle (Taktsynchrone) Logik
Prozeß mit Enable-Funktion
PROCESS(takt)
BEGIN
IF(takt'EVENT AND takt = '1') THEN
IF(enable = '1') THEN
signal_out <= signal_in;
END IF;
END IF;
END PROCESS;
Bild 1-48: Simulation des Prozesses mit Enable-Signal
signal_in
D
Q
signal_out
Enable
Bild 1-49: Funktionsprinzip getakteter Prozesse mit Enable-Funktion:
Verarbeitung nur, wenn Enable='1'
Variablen in getakteten Prozessen
Wie schon ausgeführt, nehmen Variablen den Wert in einer Zuweisung sofort an. Dadurch nehmen sie in
getakteten Prozessen eine Sonderstellung ein. Zur Beantwortung der Frage, ob für eine Variable ein
Register synthetisiert wird, kommt es darauf an, wann die Variable im Prozessablauf beschrieben bzw.
gelesen wird. Erfolgt eine Variablenzuweisung im Prozeß vor einer Auswertung dieser Variablen, wird
kein Register synthetisiert, um ihren Wert zu speichern. Die Variable steht dann stellvertretend für einen
komplexen Ausdruck. Im umgekehrten Fall, wenn also die Variable zuerst gelesen wird, bevor eine
Zuweisung erfolgt, wird ein Register synthetisiert, um den Wert aus dem vorhergehenden Takt zu
speichern.
59
CAE: VHDL
VAR1: PROCESS(takt)
VARIABLE temp: STD_LOGIC;
BEGIN
IF(takt'EVENT AND takt = '1') THEN
-- Variable immer zuerst beschreiben, wenn die
-- Abspeicherung nicht erwünscht ist
IF(a = '1') THEN
temp := b AND c;
ELSE
temp := b OR c;
END IF;
-- Variable nach deren Zuweisung lesen
signal_out <= temp;
END IF;
END PROCESS VAR1;
VAR2: PROCESS(takt)
VARIABLE temp: STD_LOGIC;
BEGIN
IF(takt'EVENT AND takt = '1') THEN
IF(a = '1') THEN
temp := b AND c;
ELSE
-- Falls a='0', wird temp zuerst gelesen
-- (Wert vom Ausgang des Registers 'temp')
-- und dann beschrieben
temp := NOT temp;
END IF;
-- Zweite Auswertung von temp
-- entspricht jetzt dem Wert am Eingang des Registers
-- 'temp'
signal_out <= temp;
END IF;
END PROCESS VAR2;
a) Prozess VAR1
b) Prozess VAR2
Bild 1-50: Die unterschiedliche Behandlung von Variablen in der Synthese
60
Sequentielle (Taktsynchrone) Logik
1.6.4 Beispiel: Verarbeitung asynchroner Bussignale
In taktsynchronen Schaltungen erfordern Signale, die keinen Bezug zum Verarbeitungstakt haben,
besondere Betrachtung. Handelt es sich dabei um ein Einzelsignal (z.B. STD_LOGIC), kann das
asynchrone Signal mit Hilfe von zwei oder drei hintereinandergeschalteten Flip-Flops auf den
Verarbeitungstakt „aufsynchronisiert“ werden. Aufgrund der Abtastunsicherheit am ersten Flip-Flop der
Kette, kann ein Signalwechsel auf dem asynchronen Signal mit der maximalen Auflösung einer
Taktperiode des verarbeitenden Taktes erkannt werden.
&
signal_in
D
Q
D
Q
D
Q
D
Q
Takt2
Takt1
f(Takt2) >> f(Takt1)
Bild 1-51: Aufsynchronisation eines Einzelsignales (signal_in)
Dieses Prinzip ist bei asynchronen Bussignalen, z.B. STD_LOGIC_VECTOR oder INTEGER nicht mehr
anwendbar, da jedes Einzelsignal des Busses bei der Synchronisation einen zeitlichen Versatz von einer
Taktperiode erhalten kann. Das folgende Beispiel zeigt die Übergabe eines Buswertes zwischen zwei
Systemen mit unterschiedlichen Verarbeitungstakten Takt1 und Takt2. Nicht der Bus (im Beispiel count)
wird synchronisiert, sondern zwei Steuersignale, request und acknowledge, welche die Übergabe
anfordern bzw. bestätigen. Eine Invertierung des Signals request leitet die Übergabe ein und die
Invertierung von acknowledge zeigt an, daß der Übergabewert in einem Zwischenspeicher bereit liegt.
ARCHITECTURE SYNCH_ARCH OF SYNCH IS
SIGNAL count, temp, read_count : NATURAL RANGE 65535 DOWNTO 0;
SIGNAL request, request1, request2, request3: STD_LOGIC;
SIGNAL acknowledge, acknowledge1, acknowledge2,acknowledge3: STD_LOGIC;
BEGIN
SYS1: PROCESS(takt1)
BEGIN
IF(takt1'EVENT AND takt1 = '1') THEN
request1 <= request;
request2 <= request1;
request3 <= request2;
-- count soll übergeben werden
count <= count + 1;
-- Anfrage für Wertübergabe ?
IF((request3 XOR request2) = '1') THEN
temp <= count;
-- Zwischenabspeicherung anzeigen
acknowledge <= NOT acknowledge;
END IF;
END IF;
END PROCESS SYS1;
61
CAE: VHDL
SYS2: PROCESS(takt2)
BEGIN
IF(takt2'EVENT AND takt2 = '1') THEN
acknowledge1 <= acknowledge;
acknowledge2 <= acknowledge1;
acknowledge3 <= acknowledge2;
-- Wertübergabe anfordern
request <= NOT request;
-- Übernahme aus Zwischenspeicher erst nach erkannter
-- Bestätigung
IF((acknowledge3 XOR acknowledge2) = '1') THEN
read_count <= temp;
END IF;
END IF;
END PROCESS SYS2;
Bild 1-52: Verarbeitung asynchroner Bussignale. Ein einfaches Protokoll regelt die Übergabe
62
Typen
1.7 Typen
Ob Signale, Konstanten oder Variable, jedem dieser Objekte ist in VHDL ein ganz bestimmter Typ
zugeordnet. Objekttypen geben Auskunft über die Beschaffenheit des Datenflusses und geben
gleichermaßen eine Vorschrift, wie die Daten zu bearbeiten sind. VHDL gilt daher als stark typorientierte
Sprache. Neben den bereits vorhandenen Standardtypen lassen sich eigene Typdefinitionen leicht
hinzufügen oder von bestehenden Typen neue Definitionen, sogenannte Subtypes, ableiten. In der
Tabelle 1.6 sind die möglichen Typklassen genannt und die Tabellen 1.7 bis 1.15 beschreiben vordefinierte
Standardtypen aus dem Package STANDARD einschließlich der auf diese Typen anwendbaren
Operatoren.
Tabelle 1.6: Typklassen
Typklasse
Anwendung
Enumerator
Aufzählung
Integer
Ganze Zahlen
Floating Point
Gleitkommazahlen
Physical
Physikalische Größen mit Einheiten
Array
Ein- oder mehrdimensionale Felder
Record
Sammlung mehrerer Elemente
Access
Zugriff über Adressen (Pointer)
File
Dateien
1.7.1 Standard Typen
Vordefinierte Typen aus der Standard Bibliothek
Tabelle 1.7: BOOLEAN
BOOLEAN
Typdefinition
TYPE BOOLEAN IS (FALSE, TRUE);
Typklasse
Enumerator
Beschreibung
Zuständig für Vergleichsergebnisse und deren
Verknüpfung.
Boolesche Operatoren
Vergleichsoperatoren
Beispiel
NOT, AND, OR, NAND, NOR, XOR, XNOR
=, /=, <, <=, >, >=
SIGNAL a, b: INTEGER
...
IF((a > b) AND (c < d) THEN ...
IF((a > b) = TRUE) THEN ...
63
CAE: VHDL
Tabelle 1.8: BIT
BIT
Typdefinition
TYPE BIT IS ('0', '1');
Typklasse
Enumerator
Beschreibung
Darstellung der logischen Zustände 0 und 1.
Boolesche Operatoren
Vergleichsoperatoren
NOT, AND, OR, NAND, NOR, XOR, XNOR
Beispiel
=, /=, <, <=, >, >=
SIGNAL a,b,c: BIT;
...
IF(a = '0') THEN
b <= NOT c;
Tabelle 1.9: BIT_VECTOR
BIT_VECTOR
Typdefinition
TYPE BIT_VECTOR IS ARRAY
(NATURAL RANGE <>) OF BIT;
Typklasse
Array
Beschreibung
Eindimensionales Array aus BIT
Boolesche Operatoren
Vergleichsoperatoren
Arithmetische Operatoren
Verkettungsoperator
NOT, AND, OR, NAND, NOR, XOR, XNOR
Beispiel
=, /=, <, <=, >, >=
SLL, SRL, SLA, SRA, ROL, ROR
&
SIGNAL data: BIT_VECTOR(7 DOWNTO 0) ;
...
data <= "10100101";
Tabelle 1.10: CHARACTER
CHARACTER
Typdefinition
TYPE CHARACTER IS ( ... );
siehe Anhang C
Typklasse
Enumerator
Beschreibung
ASCII - Zeichen
Vergleichsoperatoren
=, /=, <, <=, >, >=
Beispiel
SIGNAL c: CHARACTER;
...
c <= 'A';
64
Typen
Tabelle 1.11: INTEGER
INTEGER
Typdefinition
TYPE INTEGER IS
RANGE -2147483648 TO 2147483647;
Typklasse
Integer
Beschreibung
32-Bit Zweier-Komplement Darstellung ganzer
Zahlen
Vergleichsoperatoren
Arithmetische Operatoren
Beispiel
=, /=, <, <=, >, >=
Vorzeichen +/-, ABS, +, -, *, /, MOD, REM, **
SIGNAL x,y: INTEGER;
...
y <= x + 4;
Tabelle 1.12: NATURAL
NATURAL
Typdefinition
SUBTYPE NATURAL IS INTEGER
RANGE 0 TO INTEGER'HIGH;
Typklasse
Subtype von Integer
Beschreibung
Von Integer abgeleiteter Subtype. Beschränkung auf
natürliche Zahlen. Berechnung von
Zwischenergebnissen erfolgen mit dem Basistyp
INTEGER. Bei einer Zuweisung darf begrenzter
Wertebereich des Subtypes nicht überschritten
werden.
Vergleichsoperatoren
Arithmetische Operatoren
Beispiel
=, /=, <, <=, >, >=
Vorzeichen +/-, ABS, +, -, *, /, MOD, REM, **
SIGNAL x,y: NATURAL;
...
y <= x + 4;
Tabelle 1.13: REAL
REAL
Typdefinition
TYPE REAL IS
RANGE -1.7014110E+38 TO 1.7014110E+38;
Typklasse
Floating Point
Beschreibung
Darstellung von Gleitkommazahlen
(nicht synthetisierbar)
Vergleichsoperatoren
Arithmetische Operatoren
=, /=, <, <=, >, >=
Beispiel
CONSTANT pi: REAL := 3.14
Vorzeichen +/-, ABS, +, -, *, /, **
65
CAE: VHDL
Tabelle 1.14: TIME
TIME
Typdefinition
TYPE TIME IS RANGE
-9223372036854775807 TO 9223372036854775807
UNITS fs;
Typklasse
Physical
Beschreibung
Darstellung von Zeiten:
Verzögerungszeiten, Simulationszeit
Vergleichsoperatoren
Arithmetische Operatoren
=, /=, <, <=, >, >=
Vorzeichen +/-, ABS, +, -, *, /
SIGNAL simulationszeit: TIME;
...
simulationszeit <= NOW + 10 ns;
Beispiel
CONSTANT delay: TIME := 5 ns;
...
d_out <= d_in AFTER delay;
Tabelle 1.15: STRING
STRING
Typdefinition
TYPE STRING IS ARRAY
(POSITIVE RANGE <>) OF CHARACTER;
Typklasse
Array
Beschreibung
Eindimensionales Feld aus CHARACTER
Vergleichsoperatoren
Verkettungsoperator
=, /=, <, <=, >, >=
Beispiel
66
&
SIGNAL w1, w2: STRING(4 DOWNTO 1) ;
SIGNAL w3: STRING(8 DOWNTO 1);
...
w1<= "dies";
w2 <= "+das";
w3 <= w1 & w2;
Typen
Vordefinierte Typen aus der IEEE-Bibliothek: Package IEEE.STD_LOGIC_1164
Tabelle 1.16: STD_ULOGIC
STD_ULOGIC
Typdefinition
TYPE STD_ULOGIC IS ( ... );
siehe Anhang C
Typklasse
Enumerator
Beschreibung
Neunwertiges Logiksystem ohne
Resolution-Function
Boolesche Operatoren
Vergleichsoperatoren
Beispiel
NOT, AND, OR, NAND, NOR, XOR, XNOR
=, /=, <, <=, >, >=
SIGNAL d_out, d_in: STD_LOGIC;
...
IF(ena = '1') THEN
d_out <= din;
ELSE
d_out <= 'Z';
Tabelle 1.17: STD_LOGIC
STD_LOGIC
Typdefinition
SUBTYPE STD_ULOGIC IS RESOLVED
STD_ULOGIC;
Typklasse
Subtype von STD_ULOGIC
Beschreibung
Neunwertiges Logiksystem mit
Resolution-Function
Boolesche Operatoren
Vergleichsoperatoren
Beispiel
NOT, AND, OR, NAND, NOR, XOR, XNOR
=, /=, <, <=, >, >=
SIGNAL d_out, d_in: STD_LOGIC;
...
d_out <= 'Z';
d_out <= d_in;
1.7.2 Enumerator
Enumeratoren sind vielseitig anwendbar und sorgen für einen lesbaren und verständlichen VHDL-Text.
Die Typdefinition eines Enumerators gibt in einer Liste allen Werten, die das Objekt später einmal
annehmen soll, einen symbolischen Namen. Häufig ist das Signal, welches den Zustand eines Automaten
darstellt, ein Enumerator. Das hat den Vorteil, daß die symbolischen Namen der Zustände auch in der
Simulation sichtbar bleiben und nicht durch unübersichtliche Bitkombinationen ersetzt werden. Außer den
reservierten Schlüsselwörtern in VHDL, ist jeder Name erlaubt.
TYPE current_state IS (ROT, GRUEN, GELB, BLAU);
Dieser Typ besitzt vier Literale: Rot, Grün, Gelb, Blau. Entsprechend der Aufzählung in der Liste
bekommt jedes Literal eine Positionsnummer, beginnend mit der Position 0. Anhand dieser
Positionsnummern ist nun auch ein Vergleich der Literale mit den Vergleichsoperatoren (=, /=, <, <=, >,
>=) möglich.
67
CAE: VHDL
Neben den symbolischen Namen kann die Aufzählungsliste eines Enumerators auch Character-Literale
enthalten. Character-Literale sind einzelne Buchstaben aus dem ASCII-Zeichensatz, umgeben von
einfachen Hochkommata:
TYPE BIT IS ('0', '1');
Der Vorteil der Character-Literale liegt in der Darstellung von Arrays in Form von Strings. Sie eignen sich
deshalb zum Aufbau eines Logiksystems aus Einzel- und Bussignalen.
einzel_signal <= '0';
ein_bus <= "101010101";
1.7.3 Physikalische Typen
Die physikalischen Typen sind eine Besonderheit in VHDL, denn hier ist die Angabe einer Maßeinheit
vorgeschrieben. Physikalische Typen definieren nicht nur den zulässigen Wertebereich, sondern benennen
auch die Grundeinheit und geben gleichzeitig eine Umrechnungsvorschrift für aufbauende Einheiten.
Aufgrund der Umrechnungstabelle können Einheiten in Ausdrücken gemischt werden (z.B. Addition von
Minuten und Nanosekunden).
TYPE TIME IS RANGE -9223372036854775807 TO 9223372036854775807
UNITS
-- Grundeinheit: Femto-Sekunden
fs;
ps = 1000 fs;
ns = 1000 ps;
us = 1000 ns;
ms = 1000 us;
sec = 1000 ms;
min = 60 sec;
hr = 60 min;
END UNITS;
...
SIGNAL start : TIME := 1 min + 10 ns;
Physikalische Typen haben zwar für die Synthese keine Bedeutung doch umso mehr im Aufbau von
Simulationsumgebungen (Test-Bench, s. Abschnitt 4.10). Dort sind z.B. Objekte des Typs TIME
verantwortlich für einen zeitlich korrekten Simulationsablauf.
1.7.4 Record
Ein Record ist, ähnlich der Struktur in C, eine Zusammenfassung von Elementen in einem Objekt. Die
einzigen auf Records anwendbaren Operatoren sind Tests auf Gleich- oder Ungleichheit (=, /= ). Der
Vergleich wird elementweise vorgenommen, mit anschließender Und-Verknüpfung der Teilergebnisse.
TYPE interface IS RECORD
data: STD_LOGIC_VECTOR(1 DOWNTO 0);
parity: STD_LOGIC;
END RECORD;
68
Typen
SIGNAL d_in, d_out: interface;
...
d_out <= d_in WHEN (d_in.parity = (d_in.data(1) XOR
d_in.data(0))) ELSE
...
Weitere Zuweisungsformen an Records, oder Elemente in Records sind:
d_out <= (data => "10", parity => '1');
d_out <= ("10", '1');
d_out.data <= "10";
d_out.parity <= '1';
1.7.5 Array
Ein Array ist ein Zusammenschluß von Elementen gleichen Typs. Die einzelnen Elemente werden über
einen Index angesprochen, wobei der Index selber ein Integer oder Enumerator sein kann. Bevor ein Signal
als Array deklariert werden kann, muß in einer Typdefinition festgelegt werden, welche Elemente das
Array aufnehmen kann und wie die Indizierung des Arrays erfolgt (Indextyp und Wertebereich). Man
unterscheidet zwei Arten von Typdefinitionen:
Constrained: Die Anzahl der Array-Elemente ist bereits in der Typdefinition exakt vorgegeben. Alle
Signale oder Variablen dieses Array-Typs haben dieselbe Größe.
TYPE wort IS ARRAY (NATURAL RANGE 15 DOWNTO 0) OF STD_LOGIC;
SIGNAL a, b: wort;
Unconstrained: Die Größe des Arrays wird in der Typdefinition nicht spezifiziert. Der Platzhalter
'RANGE <>' erlaubt eine spätere Festlegung, z.B. in der Signaldeklaration. Lediglich der Indextyp wird
benannt, z.B. NATURAL. Diese Vorgehensweise definiert eine Familie von gleichartigen Arrays, die sich
nur in ihrer Größe unterscheiden und eröffnet damit eine universelle Anwendung des Array-Typs.
TYPE STD_LOGIC_VECTOR IS ARRAY (NATURAL RANGE <>) OF STD_LOGIC;
...
SIGNAL daten: STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL adresse: STD_LOGIC_VECTOR(9 DOWNTO 0);
Das Beispiel deklariert ein Signal ’daten’ als eindimensionales Array mit acht Elementen aus
STD_LOGIC, wobei ein einzelnes Element mit einem Index aus dem Bereich 7 DOWNTO 0 adressiert
wird. Das erste Element besitzt den Index 7, das zweite Element 6 und das letzte 0. Diese absteigende
Folge der Indizes entspricht der üblichen Darstellung von Bussignalen. Der Index des angesprochenen Bits
ist dann gleich dem Exponenten in der Berechnung der binären Wertigkeit.
Der Zugriff auf ein einzelnes Element erfolgt entweder durch statische Indizierung, z.B.:
daten(0) <= '1';
oder dynamisch mittels einer Variablen, eines Signals oder Schleifenparameters:
69
CAE: VHDL
FOR i IN 7 DOWNTO 0 LOOP
daten(i) <= '0';
END LOOP;
Weitere gleichwertige Zuweisungsformen sind:
daten <=
daten <=
daten <=
daten(7)
daten (3
('1', '0', '1', '1', '0', '0', '0', '0');
"10110000";
('1', '0', '1', '1', OTHERS => '0');
<= '1'; daten(6) <= '0'; daten(5) <= '1'; daten (4) <= '1';
DOWNTO 0) <= "0000";
Zweidimensionales Array
Das nachfolgende Beispiel zeigt den Aufbau zweidimensionaler Arrays. Die erste Typdefinition vereinbart
zunächst ein eindimensionales Array (rom_daten). Die zweite Typdefinition nimmt davon vier Stück und
faßt sie zu einem „Array aus Arrays“ zusammen. Eine sinnvolle Anwendung zweidimensionaler Arrays
findet man im Aufbau kleiner Speicherstrukturen, z.B. RAM oder ROM.
TYPE rom_daten IS ARRAY ( NATURAL RANGE 7 DOWNTO 0) OF STD_LOGIC;
TYPE simple_rom IS ARRAY ( NATURAL RANGE 0 TO 3) OF rom_daten;
CONSTANT rom: simple_rom :=
('0', '0', '0', '0', '0',
('0', '0', '0', '0', '0',
('0', '0', '0', '0', '0',
('0', '0', '0', '0', '0',
(
'0',
'0',
'1',
'1',
'0'),
'1'),
'0'),
'1'));
SIGNAL daten: rom_daten;
SIGNAL adresse: NATURAL RANGE 7 DOWNTO 0;
...
FOR adr IN 0 TO 3 LOOP
daten <= rom(adr);
END LOOP;
1.7.6 Access
Access-Typen sind vergleichbar mit Adressen oder Pointern in anderen Programmiersprachen. Zwar sind
sie nicht synthetisierbar und wahrscheinlich auch die am wenigsten genutzten Datentypen, doch erlauben
sie den einfachen Aufbau komplexer Funktionen. Eine typische Anwendung von Pointern ist die
Modellierung von FIFOs (First In, First Out) oder der Aufbau von verketteten Listen innerhalb der
Simulationsumgebung (Test-Bench, Abschnitt 4.10). Desweiteren sind Access-Typen nur für Variablen in
Prozessen zugelassen.
Beim Umgang mit Access-Typen treten zwei vordefinierte Funktionen in Erscheinung: NEW und
DEALLOCATE. Die Funktion NEW allokiert Speicher für die Größe des Objektes und gibt einen Pointer
auf den Speicherbereich zurück. Von diesem Zeitpunkt an ist das Objekt über diesen Pointer ansprechbar.
Wird das Objekt während der Simulation nicht mehr benötigt, kann der reservierte Speicher wieder
zurückgegeben werden. Dazu löscht die Funktion DEALLOCATE das Objekt, auf den der Pointer zeigt,
und gibt den Speicher an das System zurück. Abschnitt1.7.8 4.7.8 zeigt am Beispiel einer verketteten Liste
den Gebrauch von Access-Types.
70
Typen
1.7.7 File
File-Objekte erlauben den direkten Zugriff auf das Dateisystem der aktuellen Rechnerumgebung.
Innerhalb einer VHDL-Beschreibung sind Dateien besonders geeignet um Testvektorsequenzen einzulesen
bzw. abzuspeichern (s. Abschnitt 4.10) oder Speicherblöcke, wie RAM und ROM, zu initialisieren. Die
Verwendung von Dateien sorgt für einen hohen Grad an Flexibilität der VHDL-Beschreibung, denn
Dateien können ohne erneutes Kompilieren beliebig verändert oder ausgetauscht werden. Die
Schnittstellen zu den Dateien bilden Prozeduren für den Datentransfer (read und write, zum Lesen bzw.
Schreiben) oder Funktionen um das Dateiende zu erkennen (endfile).
Der erste Schritt zum Anlegen eines File-Objektes ist die Definition des Dateiinhaltes. Als
Dateieinträge kommen z.B. skalare Typen, Records oder Arrays in Frage. Der zweite Schritt erzeugt ein
Objekt und verbindet den Objekt- mit dem Dateinamen (auch mit Pfadangabe) und legt die Richtung des
Datentransfers fest (IN oder OUT).
TYPE integer_datei IS FILE OF INTEGER;
TYPE text_datei IS FILE OF STRING;
-- VHDL87:
FILE my_integerfile: integer_datei IS IN "int.txt";
FILE my_textfile: text_datei IS OUT "/home/example/text.txt";
-- VHDL93:
FILE my_integerfile: integer_datei;
FILE my_textfile: text_datei;
file_open(my_integerfile, "int.txt", READ_MODE);
file_open(my_textfile, "/home/example/text.txt", WRITE_MODE);
file_open(status, my_integerfile, "int.txt", READ_MODE);
Während eine Datei im VHDL87-Standard implizit während der Elaborationsphase geöffnet wurde, stehen
im VHDL93-Standard explizit Prozeduren zum Öffnen und Schließen von Dateien zur Verfügung. An
dieser Stelle sind die Versionen des Standards nicht kompatibel. Eine zweite Form der Prozedur file_open
gibt zusätzlich einen Status zurück, welcher entweder das erfolgreiche Öffnen der Datei anzeigt
(OPEN_OK), oder auf Probleme hinweist:
• STATUS_ERROR: Datei wird bereits von anderer Stelle aus angesprochen
• NAME_ERROR: Datei existiert nicht
• MODE_ERROR: Datei kann mit angegebenem Mode nicht geöffnet werden
Zum Lesen, Schreiben oder Schließen der Datei wird die entsprechende Prozedur mit dem Namen des
File-Objektes aufgerufen. Eine Prüfung auf das Dateiende sollte sicherheitshalber jeder Leseoperation
vorangehen:
IF( NOT endfile(my_integerfile)) THEN
read(my_integerfile, integer_datum);
write(my_textfile, string_datum);
file_close(my_integerfile);
71
CAE: VHDL
1.7.8 Beispiel: Modellierung eines Stacks mit Access-Typen
PACKAGE listen_typen IS
TYPE daten IS ARRAY (7 DOWNTO 0) OF STD_LOGIC;
TYPE listenelement;
TYPE ptr_auf_listenelement IS ACCESS listenelement;
TYPE listenelement IS RECORD
listeneintrag: daten;
wogehtsweiter: ptr_auf_listenelement;
END RECORD;
END listen_typen;
USE WORK.listen_typen.ALL;
ENTITY liste IS
PORT (
takt: IN STD_LOGIC;
cs: IN STD_LOGIC;
rd_wr: IN STD_LOGIC;
din: IN daten;
dout: OUT daten);
END liste;
ARCHITECTURE liste_arch OF liste IS
BEGIN
LAST_IN_FIRST_OUT: PROCESS(takt)
VARIABLE listenanfang: ptr_auf_listenelement := NULL;
VARIABLE temp: ptr_auf_listenelement := NULL;
BEGIN
IF(takt = '1' AND takt'EVENT) THEN
-- Schreibmodus
IF(rd_wr = '0' AND cs = '0') THEN
-- Neuen Speicher allokieren
temp := NEW listenelement;
-- Daten eintragen
temp.listeneintrag := din;
-- Neues Element an Anfang der Liste
temp.wogehtsweiter := listenanfang;
listenanfang := temp;
-- Lesemodus
ELSIF (listenanfang /= NULL AND rd_wr= '1'AND cs = '0') THEN
-- Nehme den Anfang der Liste
temp := listenanfang;
-- Listeneintrag auslesen
dout <= temp.listeneintrag;
-- Setze Anfang der Liste auf Nachfolger
listenanfang := temp.wogehtsweiter;
-- Ausgelesenes Element entfernen
DEALLOCATE(temp);
END IF;
END IF;
END PROCESS LAST_IN_FIRST_OUT;
END liste_arch;
72
Typen
temp
neues Listenelement
daten
wogehtsweiter
listenanfang
NEW
Listenelement2
daten
wogehtsweiter
Listenelement1
daten
wogehtsweiter
listenanfang
NULL
neues Listenelement
daten
wogehtsweiter
Listenelement2
daten
wogehtsweiter
Listenelement1
daten
wogehtsweiter
NULL
Bild 1-53: Neues Element am oberen Ende der Liste einhängen
listenanfang
temp
Listenelement3
daten
wogehtsweiter
DEALLOCATE
Listenelement2
listenanfang
daten
wogehtsweiter
Listenelement1
daten
wogehtsweiter
NULL
Bild 1-54: Oberstes Element der Liste löschen
73
CAE: VHDL
Bild 1-55: Die verkettete Liste in der Simulation
74
Operatoren
1.8 Operatoren
VHDL bietet einen umfangreichen Satz unterschiedlicher Operatoren, um Vergleiche, boolesche
Gleichungen oder Arithmetik in RTL-Modellen zu formulieren. Operatoren stehen aber immer im
Zusammenhang mit den Objekten (Signal, Variable oder Array) eines ganz bestimmten Typs (BIT,
BOOLEAN, INTEGER). Bei der Definition eines Typs kommt es somit darauf an, auch einen Satz
Operatoren dafür bereitzustellen. Bei den vordefinierten Typen BIT und BOOLEAN, sowie Arrays aus
diesen Typen, geschieht dies automatisch. Wenn keine impliziten Operatoren existieren, muß man eigene
Operatoren hinzufügen oder das Verhalten eingebauter Operatoren verändern. Der einfache Enumerator
STD_LOGIC, ist der bekannteste Typ, für den innerhalb einer Package in der IEEE-Bibliothek Operatoren
implementiert sind. Eine Prüfung auf Gleich- bzw. Ungleichheit ist für alle Typen bereits standardmäßig
enthalten, da hier stets eine bitweise Prüfung ausreicht. Dagegen macht es sicher einen Unterschied, ob die
Addition beispielsweise auf Integerobjekte oder Enumeratoren angewendet wird. Zwar wird ein
Enumerator letztlich auch auf einzelne Bits abgebildet, doch ist für das richtige Ergebnis eine individuelle
Vorschrift nötig. Eine Übersicht der verfügbaren Operatoren gibt Tabelle 1.18. Einige vordefinierten
Operatoren für jeden Datentyp zeigt Tabelle 1.19.
Tabelle 1.18: Operatoren in VHDL
Operatorklasse
Operator
Boolean
NOT, AND, OR, NAND, NOR, XOR, XNOR
Vergleichen
=, /=, <, <=, >, >=
Schieben/Rotieren
SLL, SRL, SLA, SRA, ROL, ROR
Addition
+, -, & (Verkettung)
Vorzeichen
+, -
Multiplikation
*, /, MOD, REM
Verschiedene
**, ABS
Tabelle 1.19: Vordefinierte Typen und deren Standard-Operatoren
Operator
Typ
Bit
Boolean
Boolean
Vergleich
✔
✔
Arithmetik
Enumerator
✔
Integer
✔
✔
Arrays
✔
✔
✔
✔
Arrays aus Bit
oder Boolean
✔
75
CAE: VHDL
1.8.1 Standard Operatoren
Der Standard ordnet die Operatoren einzelnen Klassen zu. Unterschiedliche Prioritäten der Klassen
bestimmen dann die Bearbeitungsreihenfolge besonders dann, wenn gleichzeitig mehrere Operatoren in
einem Ausdruck vorkommen. So ist das Ergebnis im Beispiel
2+3*4
selbstverständlich 14 und nicht 20, da die Multiplikation höhere Priorität als die Addition besitzt. Enthält
ein Ausdruck mehrere Operatoren derselben Priorität, erfolgt deren Bearbeitung entsprechend ihrer
Reihenfolge im Ausdruck von links nach rechts:
2 - 3 + 4 wird daher als (2 - 3) + 4 interpretiert
.
Tabelle 1.20: Priorität der Standard Operatoren
Priorität
-
Operator
AND,NAND, OR, NOR, XOR, XNOR
=, /= , <, <=, >, >=
SLL, SRL, SLA, SRA, ROL, ROR
+, -, &
+, - (Vorzeichen)
*, /, MOD, REM
+
**, ABS, NOT
1.8.2 Boolesche Operatoren
Folgende boolesche Operatoren stehen zur Verfügung: NOT, AND, NAND, OR, NOR, XOR und XNOR.
Eine Verknüpfung liefert als Ergebnis TRUE oder FALSE. Im Gegensatz zu anderen
Programmiersprachen haben in VHDL, mit Ausnahme des NOT-Operators, alle booleschen Operatoren
gleiche Priorität. Eine entsprechende Klammerung ist daher bei gemischten Operatoren unbedingt
erforderlich.
carry <= (a AND b) OR (a AND c) OR (b AND c);
z <= NOT a AND b;
ist aufgrund der Vorrangigkeit des NOT Operators äquivalent zu
z <= (NOT a) AND b;
Weil invertierende Operatoren (NAND, NOR, XNOR) nicht assoziativ sind, muß in VHDL eine
Klammerung vorgenommen werden:
y<= a NAND b NAND c; -- ist nicht zulässig, da
y <= (a NAND b) NAND c; -- ein anderes Ergebnis liefert als
y <= a NAND (b NAND c);
76
Operatoren
Für Synthesewerkzeuge sind boolesche Operatoren problemlos zu handhaben, denn sie arbeiten ohnehin
mit einem Schaltungsmodell aus booleschen Gleichungen. Unter Umständen findet man die VHDLOperatoren in der internen Darstellung aus Summen- und Produkttermen wieder. Allerdings wird das
Resultat von vielen Faktoren beeinflußt. In Abhängigkeit von vorzugebenden Randbedingungen findet
eine Neustrukturierung der Schaltung während der Synthese statt. So entscheidet die Optimierung auf
Fläche oder Geschwindigkeit über die Umsetzung der Operatoren. Auch die Zieltechnologie beeinflußt die
Schaltung. Ist z. B. kein XOR-Gatter in der Zieltechnologie verfügbar, erfolgt eine Umwandlung in
Summen- (OR) bzw. Produktterme (AND):
xor = (NOT a AND b) OR (a AND NOT b)
Kommen hingegen Look-Up-Tables zum Einsatz, ist es nur eine Frage der Programmierung der RAMZellen, welcher Operator sich darin verbirgt.
Beispiel: XOR-Operator mit Look-Up-Table
y <= a XOR b;
Tabelle 1.21: XOR-Operator mit Look-Up-Table
Eingänge
(RAM-Adresse)
a
Ausgang
(RAM-Inhalt)
b
y
0
0
0
0
1
1
1
0
1
1
1
0
1.8.3 Vergleichsoperatoren
Tabelle 1.22: Vergleichsoperatoren
Operator
Bedeutung
=, /=
Gleich-, Ungleichheit
>, >=
Größer, Größer-Gleich
<, <=
Kleiner, Kleiner-Gleich
Ergebnis
TRUE oder
FALSE
Die häufigste Anwendung der Vergleichsoperatoren sind der Test numerischer Typen. Ein solcher Test hat
beispielsweise die Form (a < b) oder (a > 123). Da das Vergleichsergebnis vom Typ BOOLEAN ist,
nämlich TRUE oder FALSE, ist auch eine Mischung aus Vergleichsoperatoren und booleschen Operatoren
möglich:
IF((a XOR b) = '1') THEN ...;
77
CAE: VHDL
a (0 )
XNOR
b (0 )
=
a
M SB
a<b
_
a=b
&
a (1 )
b (1 )
XNOR
=
1
a /= b
a-b
b
a<b
a
b
a<b
1
a >= b
a
b
b<a
1
a <= b
Bild 1-56: Ein Zwei-Bit Vergleicher nach der Synthese
1.8.4 Überladen von Operatoren
Ein Operator ist nichts anderes als eine Funktion, deren Name dem Symbol des Operators entspricht.
Deshalb gibt es prinzipiell auch keinen Unterschied zwischen einem Überladen von Funktionen oder von
Operatoren. Das Überladen einer Funktion heißt, daß viele Funktionen zwar den gleichen Namen
benutzen, sich aber in den Typen der Parameter und des Rückgabewertes eindeutig unterscheiden. Der
Compiler kann aufgrund der Parameter und des Rückgabewertes (Anzahl und Typ) entscheiden, welche
Funktion an der jeweiligen Stelle die Richtige ist. Dasselbe gilt beim Überladen von Operatoren, bei denen
der Typ des Operanden den gewünschten Operator auswählt.
Allerdings gilt die Einschränkung, daß nur vordefinierte Operatoren überladen werden können. Auch
muß die Anzahl der Operanden des überladenen und des vordefinierten Operators übereinstimmen. Die
wichtigste Anwendung des Überladens besteht darin, einen neu eingeführten Typ mit einem Satz
geeigneter Operatoren auszustatten. Zu den am häufigsten benötigten Operatoren gehören sicherlich die
vergleichenden Operatoren (s. Tabelle 1.22) und die arithmetischen Operatoren:
Operator
+, +, *, /
MOD
REM
**
ABS
Beispiel
Addition, Subtraktion
Vorzeichen: Plus, Minus
Multiplikation, Division
Modulo Arithmetik
Rest nach Division
Potenzieren
Absolutwert (Betrag)
y <= a+b;
y = -a;
y <= a * b;
y <= a MOD b;
y <= a REM b;
y <= a ** 2;
y <= ABS(a);
Die folgenden Muster können als Vorlage zum Überladen der Operatoren herangezogen werden. Darin
steht type als Platzhalter für den neu definierten Typ, für den der Operator implementiert werden soll.
-- Unäre
FUNCTION
FUNCTION
FUNCTION
FUNCTION
78
Operatoren
"NOT" (r: type) RETURN type;
"-" (r: type) RETURN type;
"+" (r: type) RETURN type;
"ABS" (r: type) RETURN type;
Operatoren
-- Binäre Operatoren
FUNCTION "and" (l,r: type) RETURN type;
FUNCTION "or" (l,r: type) RETURN type;
FUNCTION "nand" (l,r: type) RETURN type;
FUNCTION "xor" (l,r: type) RETURN type;
FUNCTION "xnor" (l,r: type) RETURN type;
FUNCTION "=" (l,r: type) RETURN BOOLEAN;
FUNCTION "/=" (l,r: type) RETURN BOOLEAN;
FUNCTION "<" (l,r: type) RETURN BOOLEAN;
FUNCTION ">" (l,r: type) RETURN BOOLEAN;
FUNCTION "<=" (l,r: type) RETURN BOOLEAN;
FUNCTION ">=" (l,r: type) RETURN BOOLEAN;
FUNCTION "**" (l,r: type) RETURN type;
FUNCTION "*" (l,r: type) RETURN type;
FUNCTION "/" (l,r: type) RETURN type;
FUNCTION "+" (l,r: type) RETURN type;
FUNCTION "-" (l,r: type) RETURN type;
1.8.5 Beispiel: Addition von Enumeratoren mit überladenem '+' - Operator
Der '+'-Operator soll auf einen selbstdefinierten Typ (hier: FARBEN) so anwendbar sein, daß bei einer
Addition zweier Signale die resultierende Mischfarbe entsteht. Typ- und Funktionsdeklarationen, sowie
die Funktionsimplementierung werden am besten in einer Package (my_package) plaziert, siehe dazu auch
Abschnitt 4.11, womit nach dem Einbinden der Package durch die Anweisung USE
WORK.my_package.ALL der neue Operator sofort verfügbar ist. Die spezielle Additionsfunktion ist hier
mit Hilfe einer zweidimensionalen Tabelle (mix_tab) realisiert, welche für alle Eingangskombinationen
das richtige Resultat bereithält. Als Besonderheit ist anzumerken, daß ein Signal des Enumerators
FARBEN selbst als Index für die Tabelle herangezogen werden kann, weil die Aufzählungen in der Liste
(UNBEKANNT, GELB, BLAU, etc.) nur andere Bezeichnungen für die Integerwerte 0, 1, 2 etc. sind.
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
PACKAGE my_package IS -- Deklarationsteil der Package
TYPE FARBEN IS (UNBEKANNT, GELB, BLAU, ROT, GRUEN, VIOLETT, ORANGE);
FUNCTION "+" (l,r: FARBEN) RETURN FARBEN;
TYPE FARBEN_ARRAY IS ARRAY (FARBEN'LOW TO FARBEN'HIGH) OF FARBEN;
TYPE FARBEN_TAB IS ARRAY (FARBEN'LOW TO FARBEN'HIGH) OF FARBEN_ARRAY;
END my_package;
PACKAGE BODY my_package IS -- Implemtierungsteil der Package
CONSTANT mix_tab: FARBEN_TAB :=(
(UNBEKANNT,UNBEKANNT,UNBEKANNT,UNBEKANNT, UNBEKANNT,UNBEKANNT,UNBEKANNT),
(UNBEKANNT,GELB,
GRUEN,
ORANGE,
UNBEKANNT,UNBEKANNT,UNBEKANNT),
(UNBEKANNT,GRUEN,
BLAU,
VIOLETT,
UNBEKANNT,UNBEKANNT,UNBEKANNT),
(UNBEKANNT,ORANGE,
VIOLETT, ROT,
UNBEKANNT,UNBEKANNT,UNBEKANNT),
(UNBEKANNT,UNBEKANNT,UNBEKANNT,UNBEKANNT, GRUEN,
UNBEKANNT,UNBEKANNT),
(UNBEKANNT,UNBEKANNT,UNBEKANNT,UNBEKANNT, UNBEKANNT,VIOLETT, UNBEKANNT),
(UNBEKANNT,UNBEKANNT,UNBEKANNT,UNBEKANNT, UNBEKANNT,UNBEKANNT,ORANGE));
FUNCTION "+" (l, r: FARBEN) RETURN FARBEN IS
BEGIN
RETURN mix_tab(l)(r);
END "+";
END my_package;
79
CAE: VHDL
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE WORK.my_package.ALL;
ENTITY mix IS
PORT (
farbe1: IN FARBEN;
farbe2: IN FARBEN;
farbenmix: OUT FARBEN);
END mix;
ARCHITECTURE mix_arch OF mix IS
BEGIN
-- Anwendung des überladenen Operators
farbenmix <= farbe1 + farbe2;
END mix_arch;
Bild 1-57: Wirkung des überladenen Operators in der Simulation
Bild 1-58: Synthetisierter Operator
80
Unterprogramme (Subprograms)
1.9 Unterprogramme (Subprograms)
In klassischen Programmiersprachen (wie z.B. C) gehören Funktionen und Prozeduren zu den
hierarchiebildenden Elementen. Die Bearbeitungsschritte werden aufgeteilt und jede dabei entstehende
Aufgabe in Form eines Unterprogramms kodiert. Durch Aufruf der Unterprogramme, auch untereinander,
entsteht dann die Gesamtlösung. In VHDL entsteht eine Hierarchie nicht durch Unterprogramme, sondern
durch Entity-Architecture Paare bzw. Komponenten. Bevorzugte Anwendungen von Funktionen und
Prozeduren sind die Zusammenfassung von häufig benötigten Operationen (Makros), sowie die
Typkonvertierungen an Schnittstellen oder die Bereitstellung von Operatoren für neu definierte Typen.
Funktionen tauchen vor allem auf der rechten Seite in Signalzuweisungen auf oder auch eingebettet in
Bedingungen der IF- oder CASE-Konstrukte. Prozeduren hingegen sind eigenständige Anweisungen. Je
nachdem, ob innerhalb oder außerhalb eines Prozesses, stehen sie für eine sequentielle oder nebenläufige
Anweisung.
Funktionen und Prozeduren verwenden intern, wie auch die Prozesse, sequentielle Konstrukte.
Subprogram
Procedure
Function
my_proc(x,y,z);
y <= my_func(x)
Bild 1-59: Unterprogramme: Funktionen und Prozeduren
1.9.1 4.9.1 Funktionen (Functions)
Die Schnittstelle einer Funktion besteht aus ihren Übergabeparametern und einem Rückgabewert. Der
Rückgabewert kann einem Signal oder einer Variable zugewiesen oder innerhalb eines Vergleichs
ausgewertet werden. Die unterschiedlichen Anwendungsmöglichkeiten zeigt ein Beispiel zur Berechnung
und Auswertung einer Quersumme (Even Parity). Das höchstwertige Bit enthält dabei einen Wert '0' oder
'1' so, daß die Prüfsumme (XOR-Verknüpfung über alle Bits im Vektor) den Wert '0' annimmt.
-- Anwendung der Funktion parity in einer nebenläufigen Anweisung
d_out(8) <= parity(d_in(7 DOWNTO 0));
PROCESS(d_in)
BEGIN
-- Abfrage des Rückgabewertes der Funktion parity
IF(parity(d_in(7 DOWNTO 0)) /= d_in(8)) THEN
-- Verwendung innerhalb einer sequentiellen Anweisung
d_out <= parity(d_in(7 DOWNTO 0)) & d_in(7 DOWNTO 0));
END IF;
-- Alternativ an Stelle des IF-Konstruktes:
-- d_out <= parity(d_in(7 DOWNTO 0)) & d_in(7 DOWNTO 0));
END PROCESS;
Das gezeigte Beispiel geht davon aus, daß die Funktion bereits deklariert wurde. Dafür gibt es mehrere
Möglichkeiten:
81
CAE: VHDL
•
•
•
•
Im Deklarationsteil des Prozesses (vor BEGIN)
Im Deklarationsteil der Architecture
Im Deklarationsteil einer Package, s. a. Abschnitt 4.11
Selten im Package Body, weil sie dann nur dort verfügbar ist
Aufbau einer Funktion
Am vorigen Beispiel läßt sich zeigen, wie eine Funktion aufgebaut ist. Der Aufbau ähnelt einem Prozeß,
doch zeigt die Funktion, vor allem was Variablen angeht, ein anderes Verhalten. Die Funktionsdeklaration
besteht aus dem Funktionsnamen sowie aus Details zu den Übergabeparametern und dem Rückgabewert:
FUNCTION parity (d: IN STD_LOGIC_VECTOR) RETURN STD_LOGIC IS ...
Weil Übergabeparameter innerhalb der Funktion generell nur lesbar sind, müssen sie den Mode IN
besitzen. IN enspricht der Voreinstellung und muß daher nicht unbedingt angegeben werden. Der
Parameter d läßt die Breite des Vektors noch offen (unconstrained) und erlaubt damit später eine
universelle Anwendung der Funktion.
Der zweite Teil im Funktionsaufbau ist der Deklarationsteil, beispielsweise für lokale Variablen. So
könnte eine Variable result für das Funktionsergebnis vorgesehen werden, welches dann mit einer
RETURN-Anweisung zurückgegeben wird. Selbstverständlich muß der Typ dieser Variablen mit dem Typ
des Rückgabewertes in der Funktionsdeklaration übereinstimmen.
Im Unterschied zu Prozessen werden in Funktionen die Variablen mit jedem Aufruf neu initialisiert und
auch in der Synthese entsprechend berücksichtigt.
VARIABLE result : STD_LOGIC := '0';
Der dritte und letzte Teil der Funktion besteht aus dem Anweisungsbereich:
BEGIN
FOR i IN d'RANGE LOOP
result := result XOR d(i);
END LOOP;
RETURN result;
END;
1.9.2 Prozeduren (Procedures)
Auch Prozeduren gehören zu den Unterprogrammen und sind, was die Deklarationen und den Aufbau
betrifft, den Funktionen sehr ähnlich. Deklarationen von Prozeduren sind in jedem Deklarationsbereich
zulässig, doch am häufigsten findet man sie im Deklarationsteil einer Package. Der wohl auffälligste
Unterschied zu Funktionen besteht darin, daß die Parameter in Prozeduren den Mode IN, OUT oder
INOUT annehmen können. Doch dürfen diese Datenflußrichtungen nicht mit den Angaben bei den Ports
einer Entity verwechselt werden, da sie hier eine etwas andere Bedeutung haben. Da die Parameter jetzt in
der Lage sind sowohl Daten in die Prozedur zu transportieren (Mode IN), als auch Daten wieder nach
außen zu geben (Mode OUT), ist ein separater Rückgabewert überflüssig. Dies ist ein weiterer
wesentlicher Unterschied gegenüber Funktionen.
Um das vorige Beispiel wieder aufzugreifen, soll eine Prozedur die Quersumme (Even Parity) über
einen Eingangsvektor d_in prüfen und im Fehlerfall den Parameter fehler auf '1' setzen. Gleichzeitig
soll die Quersumme neu gebildet und an den Ausgangsvektor d_out zugewiesen werden. Zwei
Ausgabewerte lassen sich nur mit einer Prozedur übergeben.
PROCEDURE parity_generator(
d_in: IN STD_LOGIC_VECTOR,
82
Unterprogramme (Subprograms)
d_out: OUT STD_LOGIC_VECTOR,
fehler: OUT STD_LOGIC) IS
BEGIN
d_out := parity(d_in(d_in'LEFT-1 DOWNTO 0)) & d_in(d_in'LEFT-1 DOWNTO
0));
IF (parity(d_in(d_in'LEFT-1 DOWNTO 0)) /= d_in(d_in'LEFT)) THEN
fehler := '1';
ELSE
fehler := '0';
END IF;
END;
Alle Zuweisungen in diesem Beispiel sind Variablen-Zuweisungen. Die Begründung dafür liegt in der
Eigenschaft, daß OUT-Parameter einer Prozedur ohne zusätzliche Spezifikation Variablen sind. Diese
Prozedur kann deshalb nur in einer sequentiellen Umgebung mit Variablen aufgerufen werden.
Der dritte Mode INOUT hat eine besondere Bedeutung: Durch ihn kann ein Parameter Werte
übergeben, die innerhalb der Prozedur verändert und über die gleiche Schnittstelle wieder zurückgegeben
werden können. INOUT modelliert somit keine Tristate-Schnittstelle oder bidirektionale Signale, sondern
ermöglicht rekursive Algorithmen. In Funktionen ist es einfacher, ein Signal oder eine Variable zu lesen
und dem gleichen Signal bzw. der gleichen Variablen danach einen neuen Wert zuzuweisen:
y <= any_function(y);
Für dieselbe Anwendung in einer Prozedur müsste dieser Parameter mit dem Mode INOUT versehen
werden:
any_procedure(y);
Parameter von Funktionen und Prozeduren
Parameter der Unterprogramme können den Klassen SIGNAL, VARIABLE oder CONSTANT angehören.
Doch nicht alle Kombinationsmöglichkeiten von Klassen und Modi sind möglich (s. Tabelle 1.23). IN,
OUT und INOUT legen die Datenflußrichtung aus Sicht des Unterprogrammes fest und die Klasse
bestimmt, wie der einzelne Parameter des Unterprogrammes von außen anzuschließen ist.
Funktionsparameter unterliegen bezüglich ihrer Klasse keiner Einschränkung, haben aber grundsätzlich
nur den Mode IN. Ohne Angabe einer Klasse hat der Funktionsparameter die Klasse CONSTANT und
kann damit von außen sowohl mit einer Variablen, als auch einem Signal verbunden werden. Eine
Festlegung auf Signale oder Variablen wäre prinzipiell zwar möglich, doch würde dies die spätere
universelle Einsatzmöglichkeit der Funktion verhindern.
Diese Überlegung gilt auch für die IN-Parameter von Prozeduren. Prozedurparameter mit Mode OUT
oder INOUT können nicht der Klasse CONSTANT angehören, ihre Default-Klasse ist VARIABLE. Die
Deklaration des Parameters als Variable läßt nur eine sequentielle Verwendung zu, z.B. in Prozessen
anderen Prozeduren oder Funktionen.
83
CAE: VHDL
Tabelle 1.23: Klasse und Mode der Parameter von Unterprogrammen
und Anschluß des Parameters von außen
Verbindung des Parameters nach
außen mit:
Mode
Klasse
IN
VARIABLE
✔
SIGNAL
✔
CONSTANT
✔
(Default)
OUT
INOUT
✔
✔
(Default)
(Default)
✔
-
VARIABLE
SIGNAL
✔
-
✔
-
✔
-
✔
✔
Funktion
Prozedur
1.9.3 Beispiel: Wired-Or mit Resolution-Function
Resolution-Functions sind für all jene Fälle vorgesehen, in denen ein Signal mehrere Treiber (Quellen)
besitzt. Diese Funktionen entscheiden letztlich, welchen Wert das mehrfach getriebene Signal annimmt. In
jedem Simulationszyklus, in dem das Signal aktiv ist, d.h. eine Transaction ansteht, wird die ResolutionFunction implizit aufgerufen. Als Parameter bekommt die Funktion einen Vektor, der alle aktuellen
Treiberwerte enthält. Aus diesen berechnet der Funktionsalgorithmus schließlich einen Rückgabewert, die
Auflösung des ursprünglichen Treiberkonflikts. Für dieses Beispiel wird ein Typ MY_LOGIC und
MY_LOGIC_VECTOR definiert, sowie eine Funktion wired_or, welche alle beteiligten Treiber
miteinander Oder-verknüpft. Zum Schluß findet in der Definition des Subtype WIRED_LOGIC eine
Verknüpfung der Resolution-Function mit MY_LOGIC statt. Jedes Signal vom Typ WIRED_LOGIC
kennt damit die Werte des Typs MY_LOGIC und hat zudem die Resolution-Function automatisch mit
eingebaut (vgl. Definition von STD_LOGIC im Anhang C).
PACKAGE wired_or_package IS
TYPE MY_LOGIC IS ('0', '1');
TYPE MY_LOGIC_VECTOR IS ARRAY (NATURAL RANGE <>) OF MY_LOGIC;
-- Funktionsdefinition
FUNCTION wired_or (beteiligte_treiber: MY_LOGIC_VECTOR) RETURN
MY_LOGIC;
-- Jedes Signal vom Typ WIRED_LOGIC verwendet implizit
-- wired_or als Resolution Function
SUBTYPE WIRED_LOGIC IS wired_or MY_LOGIC;
END wired_or_package;
84
Unterprogramme (Subprograms)
PACKAGE BODY wired_or_package IS
-- Funktionsdeklaration (Implementierung)
FUNCTION wired_or(beteiligte_treiber: MY_LOGIC_VECTOR) RETURN
MY_LOGIC IS
BEGIN
IF(beteiligte_treiber'LENGTH = 0) THEN RETURN '0';
ELSE
-- Für alle beteiligten Treiber
FOR i IN beteiligte_treiber'RANGE LOOP
-- Ergebnis steht fest, wenn ein Treiber den Wert
-- '1' treibt
IF(beteiligte_treiber(i) = '1') THEN RETURN '1';
END IF;
END LOOP;
-- Keine '1' gefunden: Ergebnis bleibt '0'
RETURN '0';
END IF;
END;
END wired_or_package;
a
b
q <= a OR b
OR c
q
c
Bild 1-60: Oder-Verknüpfung mit Wired-Or Resolution-Function
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE WORK.wired_or_package.ALL;
ENTITY test IS
PORT(
a,b, c: IN WIRED_LOGIC;
q: OUT WIRED_LOGIC);
END test;
ARCHITECTURE test_arch OF test IS
BEGIN
q <= a;
q <= b;
q <= c;
END test_arch;
85
CAE: VHDL
Bild 1-61: Wirkung der Resolution-Function in der Simulation
86
Test-Umgebung (Test-Bench)
1.10 Test-Umgebung (Test-Bench)
Die Funktionsprüfung eines VHDL-Modells beginnt idealerweise bereits während seiner Entwicklung.
Sind es anfänglich die Eingangssignale, wie Takt und Daten, welche die Schaltung stimulieren und erste
Verhaltensmuster in der Simulationsausgabe sichtbar machen, so interessieren im weiteren Verlauf der
Entwicklung vor allem die Signalformen an den Ausgängen. Zum Aufbau einer Simulationsumgebung, im
VHDL-Sprachgebrauch heißt sie Test-Bench, ist VHDL bestens geeignet und kann ihre Fähigkeiten ohne
Einschränkung voll entfalten. Von außen betrachtet, bildet die Test-Bench eine geschlossene Hülle um das
Testobjekt und erzeugt damit insgesamt ein autarkes System. Aus Sicht des Prüflings soll die Testbench
eine möglichst vollständige Nachbildung seiner Umwelt darstellen.
Das wichtigste Merkmal der Test-Bench ist daher eine leere Portliste in einer übergeordneten
Hierarchieebene, worin der Prüfling als Komponente instanziert ist. Aufgabe der Test-Bench ist es dann,
sinnvolle Eingangssignale zu erzeugen und die Ausgänge dergestalt zu überwachen, daß eine zeitraubende
visuelle Kontrolle entfallen kann. Drei Bereiche bestimmen daher den Aufbau einer Test-Bench (s. Bild 162):
• Stimuli-Modell: Anlegen der Testvektoren an den Prüfling
• Prüfling
• Response-Modell: Vergleichen oder abspeichern der Simulationsergebnisse,
Test-Bench
Response
Stimuli
Prüfling
IN OUT
Bild 1-62: Aufbau einer Test-Bench mit eingebettetem Prüfling
1.10.1Das Stimuli-Modell
Der Begriff Stimuli bezeichnet die Abfolge von Testvektoren, welche in einem definierten zeitlichen
Abstand den Eingängen des Prüflings sinnvolle Signalwerte zuweisen. Testvektoren können als
vorbereitete Tabellen im VHDL-Text eingebaut sein, oder erst zur Laufzeit als Ergebnis einer
Berechnungsvorschrift in Erscheinung treten. Eine interessante Alternative besteht darin, Testvektoren als
File abzulegen, und zeilenweise während der Simulation einzulesen (s. Abschnitt 4.10.3). Diese Methode
hat bei einer Modifikation der Testvektoren den Vorteil, daß der VHDL-Text nicht neu kompiliert werden
muß und verschiedene Testszenarien durch einfachen File-Austausch realisierbar sind.
87
CAE: VHDL
Beispiel für Stimuli in Tabellenform:
-- Typ einer Tabellenzeile
TYPE testvektor IS RECORD
in1, in2, in3: STD_LOGIC;
END RECORD;
-- Deklaration der Tabelle aus beliebig vielen
-- Tabellenzeilen
TYPE testvektoren IS ARRAY(NATURAL RANGE <>) OF testvektor;
-- Anlegen und Initialisieren der Tabelle
CONSTANT stimuli: testvektoren :=
(('0', '0', '0'),
('0', '0', '1'),
...
);
CONSTANT zeitinkrement: TIME := 50 ns;
PROCESS
BEGIN
-- Abarbeiten der Tabelleneinträge
FOR i IN stimuli'RANGE LOOP
in0 <= stimuli(i).in0;
in1 <= stimuli(i).in1;
in2 <= stimuli(i).in2;
WAIT FOR zeitinkrement;
END LOOP;
WAIT;
END PROCESS;
Pulserzeugung 'On-Line':
-- Taktgenerator
PROCESS(takt_intern)
BEGIN
IF(takt_intern = '0') THEN
takt_intern <= '1' AFTER 20 ns;
ELSE
takt_intern <= '0' AFTER 20 ns;
END IF;
END PROCESS;
takt <= takt_intern;
88
Test-Umgebung (Test-Bench)
-- Resetgenerator
PROCESS
BEGIN
reset <= '0';
WAIT FOR 100 ns;
reset <= '1';
WAIT;
END PROCESS;
1.10.2Response-Modell
Zu einem automatischen Simulationsablauf gehört auch ein Response-Modell, das selbständig alle
Signalwerte an den Ausgängen überwacht und mit dem Erwartungswert vergleicht. Die Umsetzung der
eben formulierten Zielvorgabe ist weitaus schwieriger, als es auf den ersten Blick erscheinen mag. Die
Kenntnis über die Erwartungswerte allein reicht nicht aus, vielmehr gilt es auch den Zeitpunkt für einen
Soll-Ist-Vergleich zu bestimmen.
In diesem Zusammenhang muß jeweils eine Antwort auf die Fragen gefunden werden, wann die
Simulation eingeschwungen ist und sinnvolle Ergebnisse liefert und wieviele Takte jeweils benötigt
werden, bis die Ausgänge auf bestimmte Eingangswerte in den verschiedenen Schaltungszuständen
reagieren. Eine Möglichkeit der Überprüfung der Ergebnisse besteht darin, die Algorithmen, die den
Prüfling beschreiben, in dem Response-Modell noch einmal zu implementieren.
Damit ist ein Vergleich der Ergebnisse in allen Phasen des Entwurfszylus möglich (s.a. Bild 1-5). Mit
diesem Verfahren wird aber kein Test auf generelle Funktionalität der Schaltung in ihrem Umfeld
durchgeführt. Ebenso ist die Gefahr sehr groß, daß die beiden verwendeten Modelle in gleicher Weise
fehlerhaft sind. Aus diesem Grund bietet es sich an, das Referenzmodell komplett auf eine andere Art zu
beschreiben, zumal es nicht synthetisierbar sein muß. In beiden Methoden wird ein Vergleich
durchgeführt, der mit einer Assert-Anweisung geeignete Meldungen erzeugt.
Eine weitere Möglichkeit der Ergebniskontrolle ist die Abspeicherung der Ergebnisse mit einer
einmaligen Sichtkontrolle, wobei die Ergebnisse dann als Referenz für weitere Vergleiche verwendet
werden können. Da die Sichtkontrolle für sehr große Entwürfe nicht anwendbar ist, können die
abgespeicherten Daten auch Off-Line mit anderen Werkzeugen (z. B. C, C++ Programmen) ausgewertet
werden.
1.10.3Package TextIO
Für eine flexible und universelle Gestaltung der Test-Bench sind File-I/O-Funktionen unerläßlich. Anstelle
einer harten Kodierung im VHDL-Text ist es oft sinnvoller, Stimuli- und Response-Daten während der
Simulation aus einem File einzulesen, oder die Ergebnisse in einer Datei zu protokollieren. VHDL besitzt
mit der Package TextIO aus der Standardbibliothek STD ein einfaches I/O-System mit zeilenorientierten
Schreib- und Lesefunktionen. Die wichtigsten Unterprogramme daraus sind:
PROCEDURE readline (FILE f: TEXT; l: INOUT LINE);
PROCEDURE read (l: INOUT LINE; value: OUT INTEGER);
PROCEDURE writeline (FILE f: TEXT; l: INOUT LINE);
PROCEDURE write (l: INOUT LINE; value: IN INTEGER);
FUNCTION endfile (FILE f: TEXT) RETURN BOOLEAN;
89
CAE: VHDL
Tabelle 1.24: Vordefinierte Typen der Zeileneinträge
Typ
Beispiel
BIT
value := '0';
BIT_VECTOR
value := "1010";
CHARACTER
value := 'A';
STRING
value := "abcdef"
INTEGER
value := 123;
TIME
value := NOW;
Die Prozedur readline liest aus dem Textfile f eine komplette Zeile und speichert sie in der Variablen l. Aus
der Textzeile l entnimmt nun read, links beginnend, mit jedem Aufruf den nächsten Eintrag und wandelt
ihn in den entsprechenden VHDL-Typ. Danach wird der Lesepointer automatisch auf den nächsten Eintrag
gesetzt. Überladene Prozeduren unterstützen die wichtigsten Typen (s. Tabelle 1.24). Entsprechend
umgekehrt verlaufen die Schritte beim Schreiben: Die Prozedur write überträgt einen Wert in die Textzeile
l und writeline schreibt die auf diese Weise zusammengestellte Textzeile in den File f. Nachfolgender
Prozess zeigt eine erste Anwendung von read- und writeline in einer File-Kopierfunktion.
USE STD.TEXTIO.ALL;
ENTITY file_copy IS
END file_copy;
ARCHITECTURE copy_arch OF file_copy IS
BEGIN
COPY: PROCESS
FILE source_file: TEXT OPEN READ_MODE IS "file1.txt";
FILE target_file: TEXT OPEN WRITE_MODE IS "file2.txt";
VARIABLE zeile: LINE;
BEGIN
WHILE(NOT endfile(source_file)) LOOP
readline(source_file, zeile);
writeline(target_file, zeile);
END LOOP;
WAIT;
END PROCESS COPY;
END copy_arch;
90
Test-Umgebung (Test-Bench)
P1:PROCESS
BEGIN
takt <= ...
P2:PROCESS
BEGIN
IF(ctrl= ...
Prüfling
...
takt
daten_i
ctrl
...
P3: PROCESS
BEGIN
ASSERT(a=b)
REPORT...
...
daten_o
„monitor.txt“
„stimuli.txt“
„response.txt“
Bild 1-63: TextIO-Funktionen in der Test-Bench
1.10.4Beispiel: Test-Bench
Das Beispiel zeigt eine vollständige Test-Bench. Geprüft werden soll eine Komponente ’multiplizierer’
mit der taktabhängigen Funktion: out1 = in1 x in2. Es wird hier vorausgesetzt, daß Entity und Architecture
dieser Komponente bereits in der Arbeitsbibliothek WORK enthalten sind. Als Stimuli an Port in1 wird
der Dateieintrag als String gelesen und anschließend in den Typ STD_LOGIC gewandelt. Dieser Umweg
ist erforderlich, da im Standard-VHDL keine Prozedur readline enthalten ist, die direkt STD_LOGIC
einlesen kann.
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE STD.TEXTIO.ALL;
ENTITY test_bench IS
-- Leere Portliste, Test-Bench hat keine Schnittstellen
-- nach außen
END test_bench;
ARCHITECTURE test_bench_arch OF test_bench IS
SIGNAL in1: STD_LOGIC_VECTOR(7 DOWNTO 0) := (OTHERS => '0');
SIGNAL in2, out1: NATURAL RANGE 0 TO 4;
SIGNAL takt: STD_LOGIC := '0';
91
CAE: VHDL
COMPONENT multiplizierer
PORT(takt: IN STD_LOGIC;
in1: IN STD_LOGIC_VECTOR(7 DOWNTO 0);
in2: IN NATURAL RANGE 0 TO 4;
out1: OUT NATURAL RANGE 0 TO 1023);
END COMPONENT;
BEGIN
-- Taktgenerator
takt <= NOT takt AFTER 50 ns;
STIMULI: PROCESS(takt)
FILE testpattern: TEXT OPEN READ_MODE IS "stimuli.txt";
VARIABLE zeile: LINE;
VARIABLE leerzeichen: CHARACTER;
VARIABLE var1: STRING(8 DOWNTO 1);
VARIABLE var2: NATURAL RANGE 0 TO 4;
BEGIN
IF(takt'EVENT AND takt = '1') THEN
IF(NOT endfile(testpattern)) THEN
readline(testpattern, zeile);
read(zeile, var1);
in1 <= string2std_logic(var1);
-- überspringen des Leerzeichens
read(zeile, leerzeichen);
read(zeile, var2);
in2 <= var2;
ELSE
in1 <= (OTHERS => '0');
in2 <= 0;
END IF;
END IF;
END PROCESS STIMULI;
92
Test-Umgebung (Test-Bench)
RESPONSE: PROCESS(takt)
FILE vergleichspattern: TEXT OPEN READ_MODE IS "response.txt";
VARIABLE zeile: LINE;
VARIABLE var: NATURAL RANGE 0 TO 1023;
BEGIN
IF(takt'EVENT AND takt = '1') THEN
IF(NOW > 100 ns) THEN
IF(NOT endfile(vergleichspattern)) THEN
readline(vergleichspattern, zeile);
read(zeile, var);
ASSERT var = out1
REPORT „Vergleich fehlerhaft!"
SEVERITY WARNING;
END IF;
END IF;
END IF;
END PROCESS RESPONSE;
MONITOR: PROCESS(takt)
FILE protokoll: TEXT OPEN WRITE_MODE IS "monitor.txt";
VARIABLE zeile: LINE;
VARIABLE leerzeichen: CHARACTER := ' ';
VARIABLE var1: NATURAL RANGE 0 TO 1023;
VARIABLE simulationszeit: TIME;
BEGIN
IF(takt'EVENT AND takt = '1') THEN
var1 := out1;
simulationszeit := NOW;
write(zeile, var1);
write(zeile, leerzeichen);
write(zeile, simulationszeit);
writeline(protokoll, zeile);
END IF;
END PROCESS MONITOR;
DUT: multiplizierer PORT MAP(takt, in1, in2, out1);
END test_bench_arch;
„stimuli.txt“
00000000 0
00000001 1
00000011 2
00000100 2
00001000 3
00001111 4
11111111 4 ...
„response.txt“
„monitor.txt“
0
1
6
8
24
60
1020 ...
0 50 NS
0 150 NS
0 250 NS
1 350 NS
6 450 NS
8 550 NS
24 650 NS ...
Bild 1-64: Dateiformate in der Test-Bench
93
CAE: VHDL
FUNCTION char2std_logic (ch: IN CHARACTER) RETURN STD_LOGIC IS
BEGIN
CASE ch IS
WHEN 'U' | 'u' => RETURN 'U';
WHEN 'X' | 'x' => RETURN 'X';
WHEN '0' => RETURN '0';
WHEN '1'=> RETURN '1';
WHEN 'Z' | 'z' => RETURN 'Z';
WHEN 'W' | 'w' => RETURN 'W';
WHEN 'L' | 'l' => RETURN 'L';
WHEN 'H' | 'h' => RETURN 'H';
WHEN '-' => RETURN '-';
WHEN OTHERS =>
ASSERT FALSE
REPORT „Illegal Character found „ & ch
SEVERITY ERROR;
RETURN 'U';
END CASE;
END;
FUNCTION string2std_logic (s: STRING) RETURN STD_LOGIC_VECTOR IS
VARIABLE vector: STD_LOGIC_VECTOR(s'LEFT - 1 DOWNTO 0);
BEGIN
FOR i IN s'RANGE LOOP
vector(i-1) := char2std_logic(s(i));
END LOOP;
RETURN vector;
END;
Bild 1-65: Simulationsausgabe in der Test-Bench
94
Packages und Libraries
1.11 Packages und Libraries
VHDL unterstützt die Kapselung von Daten, Komponenten, Prozeduren und Funktionen in der sog.
Package. Eine Package faßt mehrfach benutzte Deklarationen zusammen und sorgt für deren Verteilung
unter mehreren Entwurfseinheiten. Im Vergleich zur Library, die mehr für die Wiederverwendung ganzer
Entwürfe verantwortlich ist, verwaltet eine Package eher das dafür notwendige Handwerkszeug. Dies ist
vergleichbar mit den Include-Dateien in anderen Programmiersprachen.
1.11.1Aufbau von Packages
Eine Package besteht aus zwei Teilen:
• Package Declaration
• Package Body
Die Package Declaration bildet die Schnittstelle zur Package. Vergleichbar einem Inhaltsverzeichnis wird
hier aufgelistet, was in der Package alles enthalten ist. Sie behandelt die sichtbaren Aspekte der Package
und gibt durch Typ- oder Funktionsdeklarationen (Prototypen) den Anwendern der Package eine Art
Bedienungsanleitung in die Hand.
Im Package Body sind die Implementierungen der Funktionen und Prozeduren untergebracht. Dem
Anwender der Package genügt meist nur die Kenntnis, daß die gewünschte Funktion verfügbar ist und
welche Ein- und Ausgangsparameter sie hat. Falls die Package nur Konstanten oder Typdefinitionen
enthält, kann dieser Teil entfallen, da er optional ist.
Um auf die Inhalte einer Package zugreifen zu können, muß in einer USE-Anweisung die Bibliothek
(Library) genannt werden und darin die Teile der Package, die für den aktuellen Entwurf sichtbar gemacht
werden sollen. Die wohl häufigste Anweisung dieser Art ist der Einbau der Package STD_LOGIC_1164
aus der IEEE-Bibliothek um den darin vereinbarten Typ STD_LOGIC verwenden zu können:
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
Abschnitt 4.11.3 enthält ein vollständiges Beispiel zum Aufbau und zur Anwendung einer Package (vgl.
Anhang C).
1.11.2Bibliotheken (Libraries)
Ein komplexer VHDL-Entwurf ist in aller Regel auf eine Vielzahl einzelner Textdateien verteilt. Jede
Textdatei enthält dabei eine oder mehrere Entwurfseinheiten (Design-Units), von denen es fünf Klassen
gibt:
•
•
•
•
•
Entity Deklaration
Architecture
Configuration Deklaration
Package Deklaration
Package Body
Der VHDL-Compiler prüft in einer ersten Phase die Syntax und Semantik jeder Entwurfseinheit, bevor in
der zweiten Phase eine Übersetzung des VHDL-Textes in ein Format stattfindet, welches nur noch vom
Rechner selbst interpretiert werden kann. Häufig führt die Verarbeitungskette von VHDL, Trennung der
Entwurfseinheiten und Übersetzung nach C++, zu einem linkfähigen Assemblercode. Erst beim Start des
Simulators wird durch die Auswahl einer Configuration oder Angabe eines Entity-Architecture Paares
bestimmt, was simuliert werden soll. Aufgrund dieser Angaben kann der Simulator die entsprechenden
Objektfiles auswählen und zu einem simulationsfähigen Verbund linken.
Alle für die Simulation notwendigen Dateien bilden zusammen eine Library (Bibliothek), wobei
95
CAE: VHDL
Aufbau und Verwaltung der Bibliothek in der Verantwortung des Simulators oder Rechnersystems liegt
und nicht vom VHDL-Standard vorgeschrieben ist. Der VHDL-Standard regelt nur das Einbinden von
Bibliotheken und den Zugriff auf darin enthaltene Entwurfseinheiten. Die Bibliothek bekommt einen
logischen Namen, unter dem sie aus VHDL angesprochen wird. Die Verbindung des logischen Namens
mit dem physikalischen Speicherplatz ist dann Aufgabe des Simulators.
Eine besondere Bedeutung hat die Bibliothek mit der logischen Bezeichnung WORK. Diese Bibliothek
repräsentiert die aktuelle Arbeitsbibliothek und bestimmt wo die übersetzten VHDL-Texte abgelegt
werden. Bevor also der Compiler starten kann, muß der symbolische Namen WORK auf eine existierende
Bibliothek zeigen.
Es gibt eine vordefinierte Bibliothek: STD. Sie enthält die Packages STANDARD und TEXTIO (s.a.
Anhang C). Darüber hinaus ist das neunwertige Logiksystem des IEEE-Standards weit verbreitet. Seine
Definition mit überladenen Operatoren findet man in der Bibliothek IEEE als Package
STD_LOGIC_1164. Diese Bibliothek ist jedoch nicht Bestandteil der Sprache selbst.
VHDL
Textdatei
übersetzte
VDHL-Datei:
Format ist
werkzeugabhängig
VHDL
Compiler
WORK
SIM_LIB
IEEE
STD
Bibliotheken:
Bild 1-66: Typischer Ablauf beim Compilieren einer VHDL-Textdatei
1.11.3Beispiel: Überladener Operator in einer Package
Dieses Beispiel implementiert eine Funktion zur Addition zweier STD_LOGIC_VECTOR - Operanden.
Weil diese Funktion mehrfach benötigt wird, ist sie Bestandteil einer Package. Von außen ist nur der
überladene + - Operator sichtbar. Die Funktion vector_to_int im Package-Body ist dort nur lokal
verfügbar. Sie tritt nur im Zwischenergebnis in Erscheinung und ist deshalb nicht von allgemeinem
Interesse.
LIBRARY IEEE,
USE IEEE.STD_LOGIC_1164.ALL;
-- Package Declaration
PACKAGE math IS
FUNCTION "+" (l, r: STD_LOGIC_VECTOR) RETURN INTEGER;
-- FUNCTION vector_to_int(S: STD_LOGIC_VECTOR) RETURN INTEGER;
END math;
96
Packages und Libraries
PACKAGE BODY math IS
FUNCTION vector_to_int(S: STD_LOGIC_VECTOR) RETURN INTEGER IS
VARIABLE result: INTEGER := 0;
VARIABLE product: INTEGER := 1;
BEGIN
FOR i IN S'RANGE LOOP
IF(S(i) = '1') THEN
result := result + product;
END IF;
product := product * 2;
END LOOP;
RETURN result;
END vector_to_int;
FUNCTION "+" (l, r: STD_LOGIC_VECTOR) RETURN INTEGER IS
BEGIN
RETURN( vector_to_int(l) + vector_to_int(r));
END;
END math;
-- Hier beginnt die nächste Kompiliereinheit
-- Libraries und Packages neu einbinden
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE WORK.math.ALL;
ENTITY addierer IS
PORT(
in1, in2: STD_LOGIC_VECTOR(7 DOWNTO 0);
out1: INTEGER);
END addierer;
ARCHITECTURE addierer_arch OF addierer IS
BEGIN
out1 <= in1 + in2;
END addierer_arch;
97
CAE: VHDL
1.12Advanced VHDL
1.12.1Generics
Die Wiederverwendung von bereits entwickelten, simulierten und in der Praxis erprobten Schaltungen ist
ein immer häufiger genanntes Ziel. ASICs und FPGAs sind heute in der Lage, hochkomplexe Systeme auf
einem kleinen Stück Silizium zu integrieren. Aber erst mit der Wiederverwendung vorhandener
Komponenten läßt sich das üppige Angebot an Logikzellen voll ausschöpfen. Oft scheitert der Versuch der
Wiederverwendung von Komponenten aber an geänderten Randbedingungen, oder an der früher
gewählten Architektur, die durch die damals zur Verfügung stehende Technologie bedingt war.
Mit Hilfe von Generics werden Schaltungen parametrierbar. Generics sind eine spezielle Art von
Konstanten, um die Eigenschaften von Komponenten zu beeinflussen. So kann beispielsweise bei einem
Entwurf in 8-Bit Architektur ein zukünftiger Einsatz in einer 16-Bit Umgebung berücksichtigt werden. Es
ist auch denkbar, mehrere Komponenten mit ähnlichen Funktionen in einem Entwurf zusammenzufassen
und die speziellen Eigenschaften dann beim Instanzieren der jeweiligen Komponente durch geeignete
Generics einzustellen.
Generics werden in der Entity-Deklaration vor der Portliste deklariert. Damit sind Generics sowohl in
der Schnittstellenbeschreibung als auch in der zugehörigen Architecture verwendbar. Um den Gebrauch
von Generics zu zeigen, wird der Parity-Generator aus Abschnitt 1.5.3 so modifiziert, daß er an Signalen
mit beliebiger Busbreite eingesetzt werden kann.
ENTITY parity_gen IS
GENERIC (breite: NATURAL);
PORT (
data_in: IN STD_LOGIC_VECTOR(breite-1 DOWNTO 0);
data_out: OUT STD_LOGIC_VECTOR(breite DOWNTO 0));
END parity_gen;
...
gen: PROCESS(data_in)
VARIABLE parity: STD_LOGIC;
BEGIN
par := '0';
FOR i IN breite-1 DOWNTO 0 LOOP
parity := parity XOR data_in(i);
END LOOP
data_out <= parity & data_in;
END PROCESS gen;
...
Innerhalb der Architecture gibt es keinen Unterschied zwischen regulären Konstanten und Generics. Erst
beim Anlegen einer Instanz der Komponente wird in der Generic Map ein konkreter Wert festgelegt.
par1: parity_gen
GENERIC MAP(breite => 8)
PORT MAP (a, b);
par2: parity_gen
GENERIC MAP(breite => 16)
PORT MAP (c, d);
98
Advanced VHDL
1.12.2Attribute
Attribute sind wichtige Hilfsmittel, um Typen oder Objekte mit einer Art Hintergrundinformation zu
belegen, bzw. ihre speziellen Merkmale oder Eigenschaften sichtbar zu machen.
So gibt es beispielsweise Attribute, um den ersten oder letzten Wert einer Typdeklaration zu erfragen,
oder die Position eines Enumeratorliterals zu bestimmen. Ebenso hilfreich sind Attribute, um Funktionen
oder Prozeduren möglichst allgemeingültig auszulegen, beispielsweise durch Abfrage der Arraygröße
(Attribut ‘LENGTH) eines Übergabeparameters. Wohl am häufigsten kommt das Attribut ‘EVENT vor,
um einen Zustandswechsel auf einem (Takt-) Signal zu detektieren.
Tabelle 1.25 klassifiziert die Attribute und die darauf folgenden Tabellen 1.26 bis 1.33 erläutern die
wichtigsten Attribute.
Tabelle 1.25: Klassen vordefinifierter Attribute
Attributklasse
Ergebnis ist ...
Value
ein konstanter Wert
Function
der Rückgabewert eines Funktionsaufrufes
Signal
ein neues Signal gleichen Typs
Type
ein Typname
Range
ein Bereich
TYPE aufsteigend IS 0 DOWNTO 255;
SUBTYPE fallend IS aufsteigend RANGE 255 DOWNTO 0;
TYPE farben IS (rot, gruen, gelb, blau);
SIGNAL data: STD_LOGIC_VECTOR(7 DOWNTO 0);
SIGNAL clk: STD_LOGIC;
Attributklasse: Value
Tabelle 1.26: Attribute für Typen Integer oder Enumerator (auch Subtypes davon)
Attribut
Ergebnis
Beispiel
'LEFT
Linker Wert des Bereichs
oder der Aufzählung
aufsteigend'LEFT=0
farben'LEFT=rot
'RIGHT
Rechter Wert des Bereichs
oder der Aufzählung
aufsteigend'RIGHT=255
farben'RIGHT=blau
'HIGH
Maximum des Bereichs oder
Eintrag mit höchster
Positionsnummer
aufsteigend'HIGH=255
farben'HIGH=blau
'LOW
Minimum des Bereichs oder
Eintrag mit kleinster
Positionsnummer
aufsteigend'LOW=0
farben'LOW=rot
'ASCENDING
TRUE bei aufsteigendem
Bereich, anderenfalls FALSE
aufsteigend'ASCENDING=TRUE
fallend'ASCENDING=FALSE
99
CAE: VHDL
Tabelle 1.27: Value-Attribute für Arrays (constrained)
Attribut
Ergebnis
Beispiel
'LENGTH
Anzahl Elemente in Vektor
data'LENGTH=8
Attributklasse: Function
Tabelle 1.28: Function-Attribute für die Typen Physical oder Enumerator
Attribut
Ergebnis
Beispiel
'POS(v)
Positionsnummer des
Eintrags v in der
Aufzählungsliste
farben'POS(gelb)=2
'VAL(v)
Name des Eintrags in der
Aufzählungsliste mit
Positionsnummer v
farben'VAL(1)=gruen
'SUCC(v)
Name des Eintrags in der
Aufzählungsliste mit nächst
höherer Positionsnummer
bzgl. v
farben'SUCC(gelb)=blau
'PRED(v)
Name des Eintrags in der
Aufzählungsliste mit nächst
niedriger Positionsnummer
bzgl. v
farben'PRED(gruen)=rot
'LEFTOF(v)
Linker Nachbar von v
farben'LEFTOF(blau)=gelb
'RIGHTOF(v)
Rechter Nachbar von v
farben'RIGHTOF(rot)=gruen
Tabelle 1.29: Function-Attribute für Arrays
Attribut
Ergebnis
Beispiel
'LEFT
Index des Elementes am
linken Rand des Arrays
data'LEFT=7
'RIGHT
Index des Elementes am
rechten Rand des Arrays
data'RIGHT=0
'LOW
Kleinster Indexwert im Array
(absolut)
data'LOW=0
'HIGH
Größter Indexwert im Array
(absolut)
data'HIGH=7
100
Advanced VHDL
Tabelle 1.30: Function-Attribute für Signal-Objekte
Attribut
Ergebnis
Beispiel
'EVENT
TRUE, wenn das Signal
im aktuellen
Simulationsdelta seinen
Wert verändert (Event)
clk'EVENT AND clk = '1'
Signalwechsel auf '1'
'ACTIVE
TRUE, wenn das Signal
im aktuellen
Simulationsdelta aktiv ist
(Transaction oder Event)
'LAST_EVENT
Zeitlicher Abstand zum
vorigen Event
CONSTANT t_setup:TIME:=5 ns;
data'LAST_EVENT < t_setup;
'LAST_VALUE
Wert vor dem letzten
Event
clk'EVENT AND clk = '1' AND
clk'LAST_VALUE = '0'
Signalwechsel von '0' nach '1'
Attributklasse: Signal
Tabelle 1.31: Signal-Attribute
Attribut
Ergebnis
'DELAYED(t)
Neues Signal mit gleichem
Typ und gleicher
Ereignistabelle, jedoch
verzögert um die Zeit t
Beispiel
SIGNAL clk_skew: STD_LOGIC;
clk_skew = clk'DELAYED(1 ns);
'STABLE(t)
TRUE, wenn in der
vergangenen Zeit t kein
Event vorhanden war
IF(data'STABLE(t_setup) = TRUE)
'QUIET(t)
TRUE, wenn in der
vergangenen Zeit t das
Signal nicht aktiv war
IF(data'QUIET(t_setup) = TRUE)
Ohne Angabe von t wird ein Simulationsdelta angenommen
Attributklasse: Type
Tabelle 1.32: Type-Attribut
Attribut
Ergebnis
Beispiel
'BASE
Liefert zu einem Subtype den
Basistyp
fallend'BASE=aufsteigend
fallend'BASE'LEFT=0
101
CAE: VHDL
Attributklasse: RANGE
Tabelle 1.33: Range-Attribute
Attribut
Ergebnis
Beispiel
'RANGE
Liefert den Indexbereich
eines Arrays
FOR i IN data'RANGE
-- im Beispiel: 7 DOWNTO 0
'REVERSE_
RANGE
Liefert den Indexbereich
eines Arrays mit
vertauschten
Bereichsgrenzen
102
FOR i IN data'REVERSE_RANGE
-- im Beispiel: 0 TO 7
Literatur
1.13Literatur
[BAK93]Baker, Lonis: VHDL Programming with Advanced Topics; John Wiley & Sons, Inc. 1993.
ISBN 0-471-57464-3.
[BHA94]Bhasker, Jayaram: A VHDL Primer. Prentice Hall 1994. ISBN 0-13-181447-8.
[LRM93]VHDL Language Reference Manual; IEEE Std 1076-1993. ISBN 1-55937-376-8
[RUS95] Rushton, Andrew: VHDL for Logic Synthesis; McGraw Hill 1995. ISBN 0-07-709092-6
103
CAE: VHDL
2 Graphische Verhaltensspezifikation
2.1 Einführung
Seit Anfang der 80er Jahre wurde die graphische Schaltungseingabe für digitale Schaltungen auf
Gatterebene zunehmend durch Hardwarebeschreibungssprachen ersetzt. Die treibenden Kräfte waren
dabei die aufkommenden Synthesewerkzeuge, für die eine Eingabesprache notwendig ist. Um eine Vielfalt
der Eingabesprachen zu vermeiden wurden die Hardwarebeschreibungssprachen VHDL und Verilog
genormt. Die Algorithmen, die durch diese Hardwarebeschreibungssprachen kodiert werden können,
werden im elektronischen Schaltungsentwurf schon seit langem als Überlegungsgrundlage graphisch
dargestellt.
2.1.1 Übersichtliche Darstellung
Die graphische Darstellung von Algorithmen und Verhaltensstrukturen hat den Vorteil, daß diese
Algorithmen bzw. Verhaltensstrukturen übersichtlicher als in einer textlichen Beschreibung dargestellt
werden können. Die graphische Darstellung von Abläufen ist eher an die menschliche Auffassungsgabe
angepaßt, während die kodierte Darstellung von Abläufen eher an die Rechnerstrukturen angepaßt ist.
Obwohl VHDL eine Hardwarebescheibungssprache ist, leidet auch sie, bei komplexeren zu
beschreibenden Strukturen, an mangelnder Übersichtlichkeit. Der folgende VHDL-Kode und die
graphische Darstellung in Bild 2-1 stellen einen Aufwärts-Abwärts-Zähler mit synchronem Reset und
synchroner Lademöglichkeit, sowie einer Zählfreigabe dar.
ARCHITECTURE flow OF counter IS
SIGNAL temporaer NATURAL RANGE 15 DOWNTO 0;
BEGIN
process1 : PROCESS
BEGIN
WAIT UNTIL(clk'EVENT AND clk = '1' AND clk'LAST_VALUE = '0');
uebertrag <= '0';
IF (reset = '0') THEN
temporaer <= 0;
ELSE
IF (laden = '1') THEN temporaer <= lade_wert;
ELSE
IF (zaehler_aktiv = '1') THEN
CASE auf_ab IS
WHEN '0' =>
IF (temporaer = 15) THEN
temporaer <= 0;
uebertrag <= '1';
ELSE
temporaer <= temporaer + 1;
END IF;
WHEN '1' =>
IF (temporaer = 0) THEN
temporaer <= 15;
uebertrag <= '1';
ELSE
temporaer <= temporaer - 1;
END IF;
WHEN others =>
NULL;
END CASE;
END IF;
END IF;
END IF;
END PROCESS process1;
zaehler_wert <= temporaer;
END flow;
104
Einführung
Bild 2-1: Graphische Darstellung eines parallel ladbaren Auf-Ab-Zählers.
2.1.2 Gliederung des Entwurfs
Bei der heute vorherrschenden TOP-DOWN-Entwurfsmethodik unterstützt bzw. erzwingt die graphische
Verhaltenspezifikation die Strukturierung eines Entwurfs. Vor allem bei größeren Schaltungsentwürfen ist
man gezwungen eine Strukturierung des Entwurfes durchzuführen, alleine schon deshalb, weil die Anzahl
der graphischen Elemente pro Darstellungseite sinnvollerweise nicht zu groß sein sollte.
Die Gliederung des Entwurfs kann in einer Aufteilung in parallele Funktionseinheiten auf einer
Hierarchieebene sowie auch hierarchisch in den jeweiligen Funktionseinheiten erfolgen.
2.1.3 Der Entwurfszyklus mit graphischer Spezifikation
Der in Bild 1-5 des Kapitels 1 dargestellte Entwurfszyklus einer Schaltung bleibt prinzipiell erhalten. Der
Unterschied zu folgendem Bild 2-2 besteht lediglich darin, daß die Spezifikationsphase und die
Entwurfsphase mit einer textorientierten Bescheibung (z.B. VHDL) oder zuvor mit einer graphischen
Beschreibung durchgeführt werden kann. Aus der graphischen Beschreibung wird automatisch eine
textorientierte Bescheibung (i.a. VHDL oder Verilog) erzeugt. Dabei ist zu betonen, daß die graphische
Verhaltensbeschreibung lediglich als graphische Repräsentation einer VHDL- oder Verilog-Beschreibung
anzusehen ist. So ist in der graphischen Eingabe in der Regel teilweise schon VHDL- bzw. Verilog-Syntax
zu verwenden. Es ist mit den graphischen Werkzeugen allein nicht möglich eine Schaltung zu entwickeln,
ohne Kenntnisse der entsprechenden Hardwarebeschreibungssprache.
105
CAE: VHDL
Simulation
graphische Beschreibung
Beschreibung mit VHDL
Simulation
Systemebene
Beschreibung mit VHDL
Ergebnis
Register-Transfer-Ebene
Fehlerkorrektur
graphische Beschreibung
Fehlerkorrektur
evt. ungeeigneter Algorithmus
Änderung der Spezifikation
Optimierung kritischer Pfad
Entwurfsphase
Spezifikationsphase
Idee
Ergebnis
File A
Vergleich:
File A = File B?
File B
Bild 2-2: Entwurfszyklus einer Schaltung mit VHDL (siehe auch Bild 4.5)
2.2 Prinzipielle graphische Darstellungsmöglichkeiten
Die Strukturierung eines Entwurfs kann durch Blockdiagramme dargestellt werden. Die funktionale
Beschreibung von Komponenten des Entwurfs kann durch Wahrheitstabellen, Flußdiagramme sowie
Zustandsdiagramme in graphischer Darstellung erfolgen.
2.2.1 Blockdiagramme
Blockdiagramme strukturieren einen Entwurf in der Regel nach Funktionseinheiten. In einem komplexen
Schaltungsentwurf wird mit Hilfe von Blockdiagrammen die Schaltungsstruktur festgelegt, wobei die
Blöcke durch Signale miteinander verbunden sind. Blockdiagramme können dabei hierarchisch aufgebaut
sein, d.h. jeder Block in einem Blockdiagramm kann seinerseits in weitere Blöcke zerlegt sein.
Wird ein Block nicht aus weiteren Blöcken aufgebaut, ist also die unterste Hierarchieebene erreicht,
muß diesem Block eine funktionale Beschreibung zugeordnet werden. Diese funktionale Beschreibung
kann aus einer Wahrheitstabelle, aus einem Flußdiagramm, aus einem Zustandsdiagramm oder aus HDLText bestehen.
106
Prinzipielle graphische Darstellungsmöglichkeiten
Bild 2-3: Strukturdarstellung eines UART mit einem Blockdiagramm
2.2.2 Wahrheitstabellen
Wahrheitstabellen, die häufig auch als Funktionstabellen bezeichnet werden, dienen zur Beschreibung von
Schaltnetzen (kombinatorischen Schaltungen). Damit können z.B. Dekoder und Multiplexer übersichtlich
dargestellt werden.
Bild 2-4: Beispiel einer Tabelle für einen Decoder
Der daraus automatisch erzeugte VHDL-Kode ist im folgenden dargestellt, wobei die Eingabe der
Prozeßdeklarationen (Process declarations) in einem nicht dargestellten Eingabefenster für die
Wahrheitstabelleneigenschaften (Truth tabel properties) eingegeben werden.
107
CAE: VHDL
LIBRARY ieee ;
USE ieee.std_logic_1164.all;
USE ieee.numeric_std.all;
ARCHITECTURE table OF Memdecsto IS
BEGIN
truth_process: PROCESS(adress)
cs1 <= '0';
cs2 <= '0';
cs3 <= '0';
cs4 <= '0';
BEGIN
IF (adress >= X"0000" ) AND (adress < X"2000") THEN
cs1 <= '1';
ELSIF (adress >= X"2000" ) AND (adress < X"3000") THEN
cs2 <= '1';
ELSIF (adress >= X"3000" ) AND (adress < X"7000") THEN
cs3 <= '1';
ELSIF (adress >= X"7000" ) THEN
cs4 <= '1';
END IF;
END PROCESS truth_process;
END table;
Im folgenden ist das Beispiel eines Multiplexers und des daraus erzeugten VHDL-Kodes dargestellt:
Bild 2-5: Beispiel einer Tabelle für einen Multiplexer
LIBRARY ieee ;
USE ieee.std_logic_1164.all;
ARCHITECTURE table OF Mux IS
BEGIN
truth_process: PROCESS(s, d, c, b, a)
BEGIN
IF (s = "00") THEN y <= a;
ELSIF (s = "01") THEN y <= b;
ELSIF (s = "10") THEN y <= c;
ELSE y <= d;
END IF;
END PROCESS truth_process;
END table;
108
Prinzipielle graphische Darstellungsmöglichkeiten
Aus den Beispielen ist ersichtlich, daß die graphische Darstellung der Schaltnetze die Konzentration auf
die wesentliche Funktionsdarstellung gestattet, während die Verhaltensbeschreibung der Schaltnetze mit
Hilfe von VHDL, die in dieser Weise auch von Hand geschrieben werden müßte, aufwendiger ist. Die
automatische Erzeugung des VHDL - Kodes aus der graphischen Darstellung, nimmt dem Entwickler sehr
viel Schreibarbeit und Syntaxüberlegungen ab.
Die Generierung des VHDL-Kodes kann in der Regel beeinflußt werden. So ist es z.B. möglich, statt
der IF-Abfragen die CASE-Anweisung zu verwenden.
LIBRARY ieee ;
USE ieee.std_logic_1164.all;
ARCHITECTURE table OF Mux IS
BEGIN
truth_process: PROCESS(s, d, c, b, a)
BEGIN
CASE s IS
WHEN "00" =>
y <= a;
WHEN "01" =>
y <= b;
WHEN "10" =>
y <= c;
WHEN OTHERS =>
y <= d;
END CASE;
END PROCESS truth_process;
END table;
Die Schaltungsynthese der beiden VHDL-Varianten kann zu unterschiedlichen Schaltungen führen. Eine
generelle Aussage ist aber sehr schwer möglich, da die Ergebnisse von dem Synthesewerkzeug und der
Zieltechnologie abhängen.
2.2.3 Flußdiagramme
Flußdiagramme können, aufgrund ihrer Darstellungsart, zur Beschreibung jedes beliebigen sequentiellen
Ablaufes verwendet werden. In VHDL wird sequentielles Verhalten in Prozessen definiert. Daher werden
Flußdiagramme in VHDL auf Prozesse abgebildet.
In einem Flußdiagramm werden typischerweise Anweisungselemente, Entscheidungselemente,
Warteelemente und Schleifenelemente verwendet, die durch Pfeile miteinander verbunden sind. Obwohl
mit Flußdiagrammen alle Funktionen beschreibbar sind, werden sie neben der Darstellung von Multiplexoder Datenpfadstrukturen für die Generierung von sog. Testbenches zum Testen der Schaltung in der
Spezifikations- u. Entwurfsphase verwendet.
Ähnlich wie in den Blockdiagrammen, sollte die Anzahl der Elemente pro Darstellungsseite gering und
damit die Darstellung übersichtlich gehalten werden. Für größere Entwürfe ist es daher sinnvoll auch
Flußdiagramme hierarchisch aufzubauen. Das Flußdiagramm des Zählers aus Bild 2-1:Bild 2-1 ist in Bild
2-6 in hierarchischer Form dargestellt.
109
CAE: VHDL
Bild 2-6: Hierarchische Darstellung des Zählers aus Bild 2-1:Bild 2-1
Der automatisch generierte VHDL-Kode des Zählers ist identisch mit dem aus dem Flußdiagramm in
Bild 2-1Bild 2-1: generierten VHDL-Kode.
110
Prinzipielle graphische Darstellungsmöglichkeiten
2.2.4 Zustandsdiagramme
Zustandsdiagramme dienen zur graphischen Darstellung von Zustandsautomaten, wobei ein
Zustandsautomat ein Beschreibungsmodell eines Schaltwerkes ist. Das Schaltwerk wird dabei durch eine
endliche Anzahl von Zuständen und Zustandsübergängen repräsentiert. Die Zustandsautomaten, als sehr
gut entwickelte und strukturierte Bescheibungsmethode für Systeme, gestatten im Prinzip die Darstellung
aller zu lösenden Aufgaben.
Automatenprinzipien
Es haben sich im wesentlichen zwei Automatenstrukturen durchgesetzt, die auch kombiniert angewendet
werden:
• Mealy-Automat
• Moore-Automat
Diese beiden Automatenstrukturen unterscheiden sich lediglich in der Erzeugung der Ausgangssignale.
Aus diesem Grund spricht man häufig auch von Mealy- oder Moore-Verhalten des entsprechenden
Ausgangssignales.
Im folgenden soll kurz auf die Unterschiede der Automaten eingegangen werden, um die
unterschiedliche graphische Darstellung in den Zustandsdiagrammen erläutern zu können.
Das Zustandsdiagramm für den Mealy-Automat
Die Ausgangssignale des Mealy-Automaten sind sowohl vom Zustand des Automaten, als auch von den
Eingangssignalen abhängig. Bei einem Mealy-Automaten können sich also die Ausgangssignale ändern,
sobald sich ein Eingangssignal ändert. Ebenso ändern sich die Ausgangssignale evt. auch bei einem
Zustandswechsel. Während die Zustandswechsel nur zu äquidistanten Zeiten, z.B. bei einer aktiven
Taktflanke, stattfinden, kann sich das Ausgangssignal, aufgrund von sich ändernden Eingangssignalen,
auch zwischen den aktiven Taktflanken ändern. Damit können Ausgangssignale beim Mealy-Automat ein
asynchrones Verhalten aufweisen, obwohl ein synchroner Entwurf vorliegt.
Eingangssignale
n
Schaltnetz
m
Ausgangssignale
Schaltnetz
Zustands- u
variablen
Register
Takt
Bild 2-7: Modell eines Mealy-Automaten
An einem einfachen Beispiel eines Zählers, soll das Zustandsdiagramm für einen Mealy-Automat in Bild
2-8 dargestellt werden. Der Zähler soll jeweils nach 3 Taktimpulsen einen Ausgangsimpuls abgeben,
solange ein Eingangssignal auf dem logischen Wert 1 steht. Geht das Eingangssignal auf den logischen
Wert 0, soll der Zählvorgang in dem jeweiligen Zählzustand verharren.
111
CAE: VHDL
Bild 2-8: Zustandsdiagramm eines einfachen Mealy-Automaten
Die Ausgangssignale sind den Zustandsübergängen zugeordnet. Wie für den Mealy-Automat üblich, ist
damit die Abhängigkeit des Ausgangssignales vom Zustand des Automaten und des momentanen
Eingangssignales ausgedrückt.
Um einen Eindruck zu erhalten, wie typischerweise ein so beschriebener Mealy-Automat in einen
VHDL-Kode umgesetzt wird, ist im folgenden der, aus dem Zustandsübergangsdiagramm in Bild 2-8
automatisch erzeugte VHDL-Kode dargestellt.
112
Prinzipielle graphische Darstellungsmöglichkeiten
LIBRARY ieee ;
USE ieee.std_logic_1164.all;
ARCHITECTURE fsm OF Mod_3_Zaehler IS
TYPE state_type IS (s0,s1,s2);
SIGNAL current_state, next_state : state_type ;
BEGIN
clocked : PROCESS (clk,reset)
BEGIN
IF (reset = '0') THEN
current_state <= s0;
ELSIF (clk'EVENT AND clk = '1') THEN
current_state <= next_state;
END IF;
END PROCESS clocked;
nextstate : PROCESS (current_state, x)
BEGIN
IF current_state = s0 AND (x='1') THEN next_state <= s1;
ELSIF current_state = s0 THEN next_state <= s0;
ELSIF current_state = s1 AND (x = '1') THEN next_state <= s2;
ELSIF current_state = s1 THEN next_state <= s1;
ELSIF current_state = s2 AND (x = '1') THEN next_state <= s0;
ELSIF current_state = s2 THEN next_state <= s2;
ELSE next_state <= s0;
END IF;
END PROCESS nextstate;
output : PROCESS (current_state, x)
BEGIN
y <= 0;
CASE current_state IS
WHEN s2 =>
IF (x = '1') THEN
y <= '1';
END IF;
WHEN OTHERS =>
NULL;
END CASE;
END PROCESS output;
END fsm;
Auch an diesem Beispiel ist ersichtlich, daß die Beschreibung mit Hilfe von graphischen Werkzeugen
übersichtlicher ist als die textliche Bescheibung. Die graphische Darstellung gestattet es, sich auf das
Wesentliche zu konzentrieren.
Bemerkenswert dabei ist allerdings, daß die Signalabfragen und Signalzuweisungen in HDL-Syntax
erfolgen müssen. Dies belegt die Aussage, daß die graphische Verhaltensbeschreibung lediglich eine
übersichtlichere Bescheibungsart gegenüber der textlichen Verhaltensbescheibung ist. So ist es
unabdingbar, daß sich ein Schaltungsentwickler in der jeweils verwendeten HDL-Beschreibungssprache
auskennt.
113
CAE: VHDL
Das Zustandsdiagramm für den Moore-Automat
Beim Moore-Automat hängen die Ausgangssignale nur vom momentanen Zustand des Automaten ab. Im
Gegensatz zum Mealy-Automat ändern sich beim Moore-Automat die Ausgangssignale also nur zu
äquidistanten Zeitpunkten, nämlich bei den aktiven Taktflanken, die einen Zustandswechsel bewirken.
Eingangssignale
n
Schaltnetz
m
Ausgangssignale
Schaltnetz
Zustands- u
variablen
Register
Takt
Bild 2-9: Modell eines Moore-Automaten
Wie beim Mealy-Automat, soll auch hier am Beispiel eines Zählers, der nach 3 Taktsignalen ein
Ausgangssignal abgibt, das Zustandsdiagramm für einen Moore-Automaten in Bild 2-10 dargestellt
werden.
Bild 2-10: Zustandsdiagramm eines einfachen Moore-Automaten
Die Ausgangssignale werden den Zuständen zugeordnet. Damit ist ausgedrückt, daß das Ausgangssignal
nur vom jeweiligen Zustand abhängig ist, was der Definition des Moore-Automaten entspricht.
114
Prinzipielle graphische Darstellungsmöglichkeiten
Zustandsdiagramme für einen gemischten Automaten
Da die Grundstruktur der beiden Automaten identisch ist, kann die Automatenstruktur bezüglich der
Ausgänge auch gemischt werden. Allerdings ist eine Signalzuweisung zu einem Signal, das
Mealyverhalten aufweisen soll immer nur in den Zustandsübergängen oder zu einem Signal, das
Mooreverhalten aufweisen soll immer nur in den Zuständen möglich, d.h. die Signale müssen typenrein
entweder Moore- oder Mealyverhalten aufweisen.
Bild 2-11: Modell eines Automaten mit gemischtem Verhalten
Darstellungsmöglichkeiten in Zustandsdiagrammen
Im folgenden werden Darstellungsmöglichkeiten in Zustandsübergangsdiagrammen aufgeführt, die in den
meisten graphischen Werkzeugen enthalten sind. Das Ziel dieser Zusatzmöglichkeiten zu den klassischen
Darstellungselementen von Zustandsdiagrammen ist die Erhöhung der Übersichtlichkeit und die
Minimierung des Eingabeaufwandes.
Zustandsübergangsprioritäten
Um Mehrdeutigkeiten bei Aussprüngen aus einem Zustand zu vermeiden werden den Zustandsübergängen
häufig noch Prioritäten zugeordnet. Dadurch ist oft eine einfachere Anschrift der Übergangsbedingungen
möglich. Diese Prioritätsvorgaben werden im HDL Kode in eine IF...THEN...ELSE Kette umgesetzt (siehe
Bild 2-12).
Bild 2-12: Beispiel für Zustandsübergangspriorisierung
115
CAE: VHDL
Die Ziffern an den Zustandsübergängen geben die Priorität der Abarbeitung an, wobei die kleinste
Ziffer die höchste Priorität angibt.
Bedingungslose Übergänge
Für den Fall, daß bei mehrfachen, bedingten Aussprüngen aus einem Zustand ein Aussprung auf jeden Fall
durchgeführt werden soll, ist es möglich einen bedingungslosen Zustandsübergang einzufügen (siehe
Zustand s2 in Bild 2-13). Der Zustandsübergang aus dem Zustand s2 in den Zustand s0 wird immer
ausgeführt, da der Übergang mit der Priorität 3 ein bedingungsloser Übergang ist.
Bild 2-13: Beispiel für bedingungslose Übergänge
Ebenso ist es selbstverständlich auch möglich einen Zustandsübergang ohne Bedingung und
Anweisung einzufügen, der damit lediglich eine Verzögerung um eineTaktperiode bewirkt (siehe Zustand
s1 in Bild 2-13).
Zustandsübergangsfaktorisierung
Eine weitere Möglichkeit die Übersichtlichkeit der Graphik zu erhöhen und den Aufwand der
Bedingungsanschriften zu vermindern, besteht in der Möglichkeit der Zustandsübergangsfaktorisierung.
Dabei können Zustandsübergänge, die von mehreren Bedingungen abhängen und bei denen einzelne
Bedingungen gleich sind, aufgesplittet werden.
Bild 2-14: Beispiel für Zustandsübergangsfaktorisierung
116
Prinzipielle graphische Darstellungsmöglichkeiten
Das in Bild 2-13 dargestellte Zustandsdiagramm ist dem in Bild 5.14 dargestellten Zustandsdiagramm
äquivalent.
Interrupt
Ein Interrupt stellt ein Element dar, das es gestattet mit den gleichen Bedingungen aus allen Zuständen
auszuspringen. Damit ist es in der Graphik möglich eine große Anzahl von Zustandsübergängen
einzusparen, die sonst eingezeichnet werden müßten (siehe auch Bild 2-15).
,
Bild 2-15: Beispiel für das Element Interrupt
Hierarchie
Ebenso wie bei der Flußdiagrammdarstellung ist es auch in der Zustandsdiagrammdarstellung sinnvoll pro
Darstellungsseite nicht zu viele Elemente darzustellen. Aus diesem Grund kann auch in den
Zustandsdiagrammen eine Hierarchie aufgebaut werden.
Bild 2-16: Beispiel für hierarchische Zustandsdiagramme (Oberste Hierarchie)
117
CAE: VHDL
Die Hierarchie wird hier dadurch gebildet, daß ein Zustand als hierarchischer Zustand deklariert wird,
unter dem sich weitere Zustände befinden. Es liegt in dem Geschick des Entwicklers, daß Zustände
sinnvoll zusammengefaßt werden und dadurch tatsächlich die Übersichtlichkeit erhöht wird (siehe Bild 216, 2-17 und 2-18).
Bild 2-17: Beispiel für hierarchische Zustandsdiagramme (Patternsearch)
Bild 2-18: Beispiel für hierarchische Zustandsdiagramme (Patternsend)
Nebenläufige Zustandsdiagramme
Falls das Verhalten einer Schaltung durch nicht zusammenhängende Zustandsdiagramme dargestellt
werden kann, bieten die meisten graphischen Werkzeuge die Möglichkeit mehrere Zustandsdiagramme
parallel in einem Block zu realisieren. Diese Zustandsdiagramme teilen sich die gleichen Schnittstellen,
die gleichen lokalen Deklarationen und Referenzen.
Einstellung von speziellen Eigenheiten zur Generierung des HDL Kodes
Wie in dem Kapitel 1 „Entwurfsspezifikation durch Hochsprachen“ dargestellt ist, sind in den
Hochsprachen noch Signaldeklarationen und Kodierungsfestlegungen der Zustände durchzuführen, sowie
noch Angaben bezüglich der Taktansteuerung und des Resetverhaltens notwendig. Ebenso muß dem
Generierungswerkzeug noch Information bezüglich des HDL-Stils, ob z.B. case- oder if-Anweisungen
verwendet werden sollen, gegeben werden.
118
Prinzipielle graphische Darstellungsmöglichkeiten
Da die Anzahl der Möglichkeiten in all diesen Fällen endlich ist, können sie in den graphischen
Werkzeugen über entsprechende Dialogfenster eingegeben werden. Die Dialogfenster gestatten damit eine
schnelle Festlegung der gewünschten Eigenschaften. Da die Eingabe dieser Eigenschaften sehr stark von
dem verwendeten Werkzeug abhängt und in der Regel selbsterklärend ist, soll hier nicht näher darauf
eingegangen werden.
Insgesamt kann der graphischen Verhaltensspezifikation mit den geschilderten Hilfsmitteln ein große
Produktivitätssteigerung bescheinigt werden, wobei auch dem Aspekt der Dokumentation mit diesem
Methoden sehr gedient ist.
119
Literatur
2.3 Literatur
[HAC96]Hachtel, G.D., Somenzi, F.: Logic Synthesis and Verifiction Algorithms.
Kluwer Academic Publishers, 1996. ISBN: 0-7923-9746-0
[REN98] Mentor-Graphics: Mentor/Renoir Tutorial 98.4 . Okt. 1998.
[SUM97]Summit Design Inc.:Visual HDL User‘s Guide. Beaverton, OR. 1997
120
Einführung
3 Synthese
3.1 Einführung
Hardwarebeschreibungssprachen unterstützen den Entwicklungsablauf auf einer höheren und abstrakteren
Ebene, als es bisher auf Gatterebene der Fall war. Die Schnittstelle zum Halbleiterhersteller ist aber
weiterhin die Gatternetzliste. Die Logiksynthese verbindet beide Ebenen, indem sie aus dem Quellcode der
Hochsprache automatisch eine optimierte Schaltung auf Gatterebene erzeugt. Dieser Vorgang ist
zweigeteilt:
• Übersetzung: Konvertierung der RTL-Beschreibung in eine generische, d.h. technologieunabhängige
und noch nicht optimierte Netzliste.
• Optimierung: Transformation der generischen Netzliste auf eine Zieltechnologie. Das Ergebnis muß die
Anforderungen in Bezug auf Fläche und Geschwindigkeit erfüllen.
Voraussetzung für eine erfolgreiche Synthese ist nicht nur ein effizienter Optimierungsalgorithmus. Es
muß auch eine Übereinkunft darüber geben, welche Hardware aus den speziellen
Hochsprachenkonstrukten entsteht, nicht zuletzt um die Reproduzierbarkeit der Schaltung zu
gewährleisten. Jedes Synthesewerkzeug beruft sich daher auf einen sog. Style-Guide, der zum größten Teil
aus einer Sammlung von VHDL-Beispielen besteht. Dieses Handbuch erklärt nicht in erster Linie, welche
Gatterstrukturen aus einem IF- oder CASE-Konstrukt entstehen. Vielmehr gibt es eine Anleitung, wie z.B.
ein Prozeß aussehen muß, um einen kombinatorischen Teil in Verbindung mit Registern zu beschreiben,
oder welche Anweisungen der Hochsprache nach der Synthese einen Pin mit Tri-State-Eigenschaften
ergeben, um nur zwei Beispiele herauszugreifen.
Die VHDL-Kodefragmente in Bild 3-1 zeigen auch die Abhängigkeit der Synthese von der Qualität des
Eingabekodes. Während im ersten Vorschlag ein Addierer, Subtrahierer und Multiplexer notwendig ist,
kommt die zweite Alternative mit nur einem Addierer und einem Multiplexer am Eingang b aus. Diese
strukturellen Unterschiede kann ein Optimierungswerkzeug kaum ausgleichen.
...
c := a +
d := a IF(sub =
result
ELSE
result
END IF;
b;
b;
'1') THEN
<= d;
...
IF(sub = '1') THEN
b := - b;
END IF;
result <= a + b;
<= c;
Bild 3-1: Zwei VHDL-Ausschnitte mit gleicher Funktionalität, aber mit unterschiedlichen Ergebnissen
nach der Synthese
Abschnitt 3.2 enthält beispielhaft einen Auszug aus den Kodierrichtlinien von Altera bzw. Synopsys.
Anschließend folgen im Abschnitt 3.3 einige Empfehlungen, um die Synthese durch sinnvolle
Partitionierung des Designs zu unterstützen. Thema der darauffolgenden Abschnitte ist die Optimierung
mit Erläuterung prinzipieller Algorithmen und Strategien. Den Schluß bildet eine Tabelle mit einer
Auflistung synthesefähiger VHDL-Konstrukte.
121
CAE: VHDL
3.2 Kodierbeispiele für synthesefähigen VHDL-Kode
D-Flip-Flop mit asynchronem Reset (Bild 3-2)
DFFA: PROCESS(clk, reset)
BEGIN
IF(reset = '0') THEN
data_out <= (OTHERS => '0');
ELSIF(clk'EVENT AND clk = '1') THEN
data_out <= data_in;
END IF;
END PROCESS DFFA;
DFFA
data_in
data_out
Q
D
clk
reset
Bild 3-2: D-FF mit asynchronem Reset
D-Flip-Flop mit synchronem Reset (Bild 3-3)
DFFS: PROCESS(clk, reset)
BEGIN
IF(clk'EVENT AND clk = '1') THEN
IF(reset = '1') THEN
data_out <= (OTHERS => '0');
ELSE
data_out <= data_in;
END IF;
data_in
reset
DFFS
&
END IF;
D
data_out
Q
clk
END PROCESS DFFS;
Bild 3-3: D-FF mit synchronem Reset
Kombinatorik mit Flip-Flop (Bild 3-4)
SYNC: PROCESS(clk, reset)
BEGIN
IF(reset = '0')
THEN
data_out <= (OTHERS => '0');
ELSIF(clk'EVENT AND clk = '1') THEN
data_out <= in1 XOR in2;
END IF;
END PROCESS SYNC;
in1
DFFA
in1 XOR in2
data_out
D
Q
clk
in2
reset
Bild 3-4: Kombinatorik und Flip-Flop
122
Kodierbeispiele für synthesefähigen VHDL-Kode
Flip-Flop mit Rückkopplung (Bild 3-5)
PROCESS(clk)
BEGIN
IF(clk'EVENT AND clk = '1') THEN
IF(ena = '1') THEN
data_out <= a;
END IF;
END IF;
END PROCESS;
data_out
D
a
ena
Q
clk
Bild 3-5: Flip-Flop mit Rückkopplung
Multiplexer mit Eingängen gleicher Priorität (Bild 3-6)
MUXA: PROCESS(sel, a, b)
BEGIN
CASE sel IS
WHEN '0' => data_out <= a;
WHEN '1' => data_out <= b;
-- Für alle anderen Zustände 'X', 'Z', 'U', ...
WHEN OTHERS => NULL;
a
END CASE;
END PROCESS MUXA;
data_out
b
sel
Bild 3-6: Multiplexer
Multiplexer mit priorisierten Eingängen (Bild 3-7)
MUXB: PROCESS(a,b,c,sel1,sel2)
BEGIN
IF(sel1 = '1') THEN
data_out <= a;
ELSIF(sel2 = '1') THEN
data_out <= b;
ELSE
data_out <= c;
END IF;
END PROCESS MUXB;
a
data_out
b
c
sel1
sel2
Bild 3-7: Hierarchischer Multiplexer
123
CAE: VHDL
Bidirektionale Datenschnittstelle mit Tri-State-Treiber (Bild 3-8)
ENTITY CPUIF IS
PORT( data_io: INOUT STD_LOGIC_VECTOR(7 DOWNTO 0);
cs: IN STD_LOGIC;
rd: IN STD_LOGIC);
END CPUIF;
ARCHITECTURE CPUIF_ARCH OF CPUIF IS
BEGIN
-- Das Lesen der Schnittstelle ist immer möglich
data_in <= data_io;
TRI: PROCESS(cs, rd, data_out)
BEGIN
-- Schreiben, wenn cs und rd aktiv
IF(cs = '0' AND rd = '0') THEN
data_io <= data_out;
ELSE
data_io <= (OTHERS => 'Z');
END IF;
END PROCESS TRI;
data_io
data_in
END CPUIF_ARCH;
data_out
rd
cs
1
Bild 3-8: Tri-State
124
Partitionierung
3.3 Partitionierung
Wichtige Voraussetzung für ein gutes Syntheseergebnis ist eine sorgfältige Partitionierung des Designs.
Damit ist die sinnvolle Aufteilung der Gesamtschaltung in einzelne Module gemeint, wobei gleichzeitig
auch eine Hierarchie, d.h. Verschachtelung der Module entstehen kann. Je früher die Partitionierung im
Entwicklungsprozess berücksichtigt wird, umso einfacher gestaltet sich nachher der Syntheseablauf. Eine
Änderung der Modulgrenzen erfordert oft eine Neudefinition der Schnittstellen und ist zeitraubend und
fehlerträchtig. Zwar gibt es keine exakten Regeln für die richtige Partitionierung, doch helfen
nachfolgende Empfehlungen, um die richtige Entscheidung zu treffen.
Kombinatorische Teile, die miteinander in Beziehung stehen, in einem Modul zusammenfassen (s.a.
Bild 3-9).
kritischer Pfad
D
Q
D
Q
Kombinatorik
Bild 3-9: Zusammengehörende Kombinatorik in einem Modul
Das Beispiel in Bild 3-9 zeigt drei kombinatorische Teile, welche direkt oder indirekt Bestandteil des
kritischen Pfades sind, damit ist der Signalpfad mit der größten zeitlicher Verzögerung gemeint. In diesem
Fall hat der Synthesecompiler bei der Optimierung mehr Freiheitsgrade um den kritischen Pfad durch
günstigere Zusammenfassung der Kombinatorik zu entschärfen. Eine wesentlich ungünstigere
Ausgangssituation zeigt Bild 3-10. Der Compiler kann dort über Hierarchiegrenzen hinweg keine Logik
verlagern oder zusammenfassen.
kritischer Pfad
D
Q
D
Q
Bild 3-10: Ungünstige Modulaufteilung
125
CAE: VHDL
Eine weitere Schwierigkeit der in Bild 3-10 durchgeführten Aufteilung ist die Verteilung der
verfügbaren Gesamtverzögerungszeit (i.a. eine Taktperiode) auf die einzelnen kombinatorischen Teile
unter Berücksichtigung der Treiberstärken und der Anzahl der angeschlossenen Lasten. Um jedes Modul
dennoch separat optimieren zu können, ist jeweils an der Schnittstelle q in Bild 3-11 eine genaue Vorgabe
der maximalen Verzögerungszeit (output- oder input-Delay), sowie der zu erwartenden Last und
Treiberstärke erforderlich. Bild 3-11 verwendet dazu einige Anweisungen des Synopsys-Compilers.
4 ns
D
Q
D
q
CLK
q
5 ns
Output-Delay
set_output_delay 5 -clock CLK q
set_load 0.5 q
Q
CLK
Input-Delay
set_input_delay 4 -clock CLK q
set_drive 0.2 q
Bild 3-11: Berücksichtigung der Verzögerungszeiten und Treiberstärken
Gemeinsam benutzbare Operatoren zusammenhalten (Resource Sharing)
Das nachfolgende VHDL-Beispiel verwendet zwei Additions-Operationen (a + b, bzw. c + d). Abhängig
von den Optimierungsvorgaben entscheidet der Compiler, ob für beide Operationen unabhängige Addierer
eingesetzt werden. Wenn die Geschwindigkeitsoptimierung im Vordergrund steht, wird der Compiler zwei
unabhängige Addierer wählen. Wenn eine möglichst kleine Fläche im Vordergrund steht, kann evt. ein
gemeinsames Rechenwerk für beide Additionen verwendet werden. Um dem Compiler die Entscheidung
nicht vorwegzunehmen, bzw. möglichst viele Freiheitsgrade offen zu lassen, ist daher die gleichzeitige
Betrachtung aller beteiligten Komponenten in einem gemeinsamen Modul sinnvoll.
IF(select = '1') THEN
q <= a + b;
ELSE
q <= c + d;
END IF;
a
a
+
b
D
c
Q
q
b
c
+
d
select
Bild 3-12: Resource Sharing
126
d
select
+
D
Q
q
Partitionierung
Getrennte Module für unterschiedliche Optimierungsziele
Das beste Syntheseergebnis erhält man, wenn Schaltungsteile mit unterschiedlichen Optimierungszielen,
das sind im wesentlichen eine geringe Fläche oder hohe Geschwindigkeit, auch in getrennten
Hierarchieebenen untergebracht sind. Beide Ziele lassen sich im allgemeinen nicht gleichzeitig
verwirklichen, da sie sich normalerweise widersprechen.
Optimierungsziele werden in Form von Constraints, das sind die zu erfüllenden Randbedingungen, dem
Compiler vorgegeben. Die Anweisung set_max_area = 0 veranlaßt den Compiler beispielsweise, eine
möglichst kleine Schaltung zu synthetisieren, evt. auch in Verbindung mit der Methode des Structuring
(vgl. Abschnitt 3.5).
Vor allem in taktsynchronen Schaltungen spielt die Geschwindigkeit, also die Taktfrequenz, bei der die
Schaltung betrieben werden kann, eine übergeordnete Rolle. Dazu muß dem Compiler der Name des
Taktnetzes und die angestrebte Taktperiode mitgeteilt werden.
Getrennte Module für unterschiedliche Optimierungsstrategien
Nicht nur unterschiedliche Optimierungsziele sind ein Grund dafür, die betroffenen Logikbereiche in
getrennten Modulen unterzubringen, auch die richtige Optimierungsstrategie kann dazu beitragen, die
letzten Reserven in Geschwindigkeit oder Fläche eines Modules zu nutzen. Es sind zwei Strategien,
Flattening oder Structuring üblich(s.a. Abschnitt 3.5). Module mit ausgeprägter Struktur, z.B. tiefe
Exklusiv-Oder Verknüpfungen, sollten nicht ausgeflacht werden. Das Ergebnis würde weder in der Fläche
noch in der Geschwindigkeit eine Verbesserung zeigen. Bereiche mit gering ausgeprägten Strukturen
(Random Logik), also ohne Addierer oder größeren Multiplexern, sind zum Ausflachen besser geeignet.
Register an allen Ausgängen
Register an allen Modulausgängen reduzieren die Anzahl der Optimierungsvorgaben und vereinfachen
dadurch den gesamten Syntheseablauf. Mit welcher Treiberstärke am Moduleingang zu rechnen ist, ist
durch Einhalten dieser Empfehlung schnell festzustellen, entspricht sie doch in der Regel dann der
Treiberstärke eines Flip-Flops. Auch die Eingangsverzögerung am Modul, bezogen auf den Takt, ist nur
abhängig von der Verzögerung des Registers (clock-to-output, tco) im vorigen Modul und nicht von einer
unbekannten und variablen Anzahl kombinatorischer Verknüpfungen (vgl. Bild 3-13). Der kritische Pfad
befindet sich somit nur innerhalb eines Moduls. Die zeitliche Optimierung wird wesentlich erleichtert, z.B.
durch einen neuen Optimierungslauf mit geänderten Vorgaben.
s e t_ d riv e d riv e _ o f(D F F /Q ) a ll_ in p u ts()
s e t_ in p u t_ d e la y t co -c lo c k C L K
D
CLK
Q
tc o
D
Q
CLK
Bild 3-13: Register am Modulausgang
127
CAE: VHDL
3.4 Modifizieren von Hierarchien
Nicht immer gelingt es, die Partitionierung vor dem Entwurf zu planen, um eine ideale Aufteilung der
Logik in einzelne Module mit entsprechender Hierarchie zu erreichen. Beispielsweise passen
Komponenten aus früheren Designs nicht exakt in das aktuelle Konzept, oder eine Designänderung
verändert auch die Partitionierung. In solchen Fällen kann es notwendig werden, die Hierarchie ganz oder
teilweise aufzuheben und anschließend neu zu ordnen. Eine Neuordnung kann auf zweierlei Wegen
erreicht werden: Entweder durch Änderung des VHDL-Entwurfes, oder mit Hilfe geeigneter Anweisungen
während der Synthese. Der Synopsys-Compiler kennt für diese Aufgabe die Kommandos group bzw.
ungroup und erlaubt damit auch experimentell eine optimale Partitionierung zu finden.
Ungroup-Kommando
Das ungroup-Kommando entfernt eine bestehende Hierarchie, wenn z.B. viele Module mit nur geringem
Inhalt den Optimierungsprozess behindern. Das Beispiel in Bild 3-14 zeigt diesen Fall. Nach der ungroupAnweisung befinden sich alle Zellen auf derselben Hierarchieebene.
ungroup -all
&
&
1
1
Bild 3-14: Anwendung des ungroup-Kommandos
Group-Kommando
Aus einer flachen Anordnung lassen sich mit dem group-Kommando neue Hierarchieebenen herausbilden.
Mit dieser Anweisung können Objekte aus der aktuellen Hierarchiestufe zu einem neuen Modul
zusammengefaßt werden. Dieses Kommando ist sehr vielseitig, denn Objekte können sowohl einzelne
Gatter, als auch Blöcke, Prozesse oder Komponenten unmittelbar nach dem Einlesen des Source-Codes
sein.
U2
U1
g ro u p
{U 1, U 2}
&
1
Bild 3-15: Anwendung des group-Kommandos
128
neues M odul
1
&
Optimierung
3.5 Optimierung
Um die besten Optimierungsergebnisse zu erzielen, ist es wichtig, dem Compiler Informationen über die
realen Umgebungsbedingungen des Entwurfes mitzuteilen. Constraints sind Deklarationen, die die
speziellen Optimierungsziele für eine konkrete Schaltung definieren. Ob das gesteckte Ziel erreicht wurde,
läßt sich anhand von wenigen charakteristischen Größen wie Timing, Fläche oder Kapazitäten leicht
beurteilen oder mit vorherigen Ergebnissen vergleichen. Während der Optimierung kommen zwei Typen
von Constraints zur Anwendung:
Design Rule Constraints
Diese technologieabhängigen Vorgaben existieren implizit aufgrund der gewählten Bibliothek. Es handelt
sich dabei um generelle Regeln, um eine korrekte Funktion zu gewährleisten. Für den Compiler haben
diese Vorgaben höchste Priorität. In Tabelle 6-1 sind einige wichtige Constraints zusammengefaßt.
Tabelle 3-1: Design-Rule Constraints in Synopsys
Constraint
Funktion
set_max_transition
Maximal zulässige Zeit für einen Pegelwechsel auf
einem Netz (=> Buffering)
set_max_fanout
Summe aller an einem Moduleingang (Port)
angeschlossenen Lasten (=> Treiberstärke).
set_fanout_load
Erwartete Last am Ausgang eines Moduls
set_max_capacitance
Maximal zulässige Kapazität auf einem Netz
Optimization Constraints
Sie geben explizit die individuellen Optimierungsziele vor (Tabelle 6-2). Reale Vorgaben bezüglich Fläche
und Geschwindigkeit haben bessere Chancen tatsächlich in der Schaltung umgesetzt zu werden.
Tabelle 3-2: Optimization Constraints in Synopsys
Constraint
Funktion
set_max_delay
Maximale Verzögerung im kombinatorischen Teil
set_max_area
Maximale Fläche (z.B. in Gatteräquivalenten)
set_input_delay
Externe Verzögerung am Moduleingang bezgl. Takt
create_clock
Bestimmt ein Moduleingang als Taktquelle und
spezifiziert die Taktperiode
set_clock_skew
Setzt an allen Flip-Flops einen Unschärfebereich für
die aktive Taktflanke
129
CAE: VHDL
3.5.1 Auswirkung der Optimierungsvorgaben
Die Synthese ist ein komplexer Prozeß mit vielen veränderbaren Parametern. Manche davon sind diskret,
andere wiederum kontinuierlich. Zu den diskreten Parametern gehören die RTL-Architektur,
Logikfunktionen sowie zahlreiche Schalterstellungen im Syntheseprogramm, um den Synthesealgorithmus
statisch zu beeinflussen. Kontinuierliche Parameter beschreiben z.B. Ein- oder Ausgangsverzögerungen,
kapazitive Lasten oder Taktperioden.
Um die prinzipielle Arbeitsweise eines Syntheseprogramms darzustellen, ist es nicht sinnvoll, alle
Parameter gleichzeitig zu verändern. In den Bildern 3-16 bis 3-18 sind hauptsächlich die Fläche und die
Geschwindigkeit eines gegebenen Designs von Interesse. Nach [LAN98] wird hierbei angenommen, daß
die erzielte Taktperiode, die annähernd der maximalen Verzögerung (Delay) im kritischen Pfad entspricht,
ein Maß für die Geschwindigkeit darstellt und alle anderen zeitlichen Parameter davon abhängen.
Bild 3-16: Verzögerung im kritischen Pfad (Delay) als Funktion der Optimierungsvorgabe (Constraint)
Bild 3-16 zeigt für ein vorgegebenes maximales Delay (Constraint) die aktuell erreichte Verzögerung im
kritischen Pfad der synthetisierten Netzliste. Nur im Bereich unterhalb der idealisierten Linie ist die
Vorgabe erfüllt. Optimieren heißt, die Vorgaben zu unterschreiten und nicht exakt zu erfüllen. Deshalb geht
die 45-Grad Linie von einem bestimmten Punkt an in die Waagerechte. Dann erfüllt das ohne besondere
Anstrengungen erzielte Design mit kleinster Fläche die relativ entspannten Vorgaben.
Bild 3-17: Fläche des Designs bei erfüllter zeitlicher Vorgabe als Funktion der Constraints
130
Optimierung
Bild 3-17 ergänzt die Darstellung in Bild 3-16, indem für jede synthetisierte Netzliste deren Fläche in
Abhängigkeit der Constraints aufgetragen ist. Je kleiner die Vorgabe des Delays im kritischen Pfad ist,
umso größer wird die Fläche infolge der Parallelisierung und des Einbaus zusätzlicher Buffer.
Beide Ergebnisse aus Bild 3-16 und Bild 3-17 zusammengefaßt, ergeben die charakteristische
Darstellung in Bild 3-18. Diese Kurve entsteht aus der Verbindung einzelner Punkte, wobei jeder Punkt
einem Syntheseergebnis (Netzliste) für ein bestimmtes Constraint entspricht. Auffällig ist das Verhalten
am linken Rand der Kurve. Beim Versuch das schnellste Design zu synthetisieren, lassen die Ergebnisse
teilweise chaotisches Verhalten vermuten. Eine geringfügige Änderung im Constraint hat dann große
Auswirkung auf die resultierende Fläche. In zufallsgesteuerten Algorithmen ist dieses Verhalten öfter
anzutreffen.
Bild 3-18: Fläche als Funktion der Verzögerung im kritischen Pfad
131
CAE: VHDL
3.5.2 Optimierungsstrategien
Entwürfe digitaler Schaltungen sind in der Regel hierarchisch aufgebaut. Die oberste Ebene dieser
Hierarchie (Top-Level) besteht dann meist nur noch aus Blöcken, die miteinander verbunden sind. Manche
Blöcke beinhalten Verarbeitungsstufen im Datenpfad und andere Blöcke sind ausschließlich für die
Steuerung zuständig. In ihrem inneren Aufbau unterscheiden sich die Blöcke ebenfalls. Beide Aspekte
erfordern eine individuelle Behandlung während der Synthese. In stark strukturierten Komponenten (z.B.
einem größeren Addierer) ist es beispielsweise wünschenswert, die Struktur zu erhalten und in der
Optimierung sogar darauf aufzubauen. In nicht strukturierten Blöcken ist es Aufgabe des
Synthesewerkzeugs, redundante Logik zu entfernen und damit die Struktur der Schaltung zu verbessern.
Die Struktur einer Schaltungsbeschreibung ist sowohl auf logischer Ebene, als auch auf Gatterebene
erkennbar. Die logische Ebene ist technologieunabhängig und wird durch einen Satz boolescher
Gleichungen dargestellt. Die Gatterebene einer Schaltung entsteht aus der konkreten Implementierung der
logischen Ebene unter Einbeziehung der gewünschten Technologie.
Die Optimierung auf logischer Ebene hat zum Ziel, die Struktur des digitalen Schaltungsentwurfes zu
verbessern, ohne die Funktion selbst zu verändern. Im ersten Ansatz führt eine Reduzierung der
Produktterme sowohl zu kleineren Schaltungen, als auch zu geringeren Durchlaufzeiten. Weil die Fläche
und die Geschwindigkeit die Hauptkriterien während einer Schaltungsoptimierung sind, bekommt die
dabei verfolgte Strategie eine bedeutende Rolle. Die Optimierungsstrategie besteht aus einer sinnvollen
Anwendung folgender Schritte oder einer Kombination aus beiden:
• Ausflachen (flattening)
• Strukturieren (structure), vgl. [SFS94]
V H D L -B e sc h re ib u n g
A u sfla c h e n
S tru k tu rie re n
L o g isc h e E b e n e :
b o o le s c h e G le ic h u n g e n
G a tte re b e n e :
N e tz lis te
Te c h n o lo g ie
m appen
N e tz liste in Z ie lte c h n o lo g ie
Bild 3-19: Die einzelnen Schritte von der VHDL-Beschreibung zur fertigen Netzliste.
Flattening
Das Ausflachen (Flattening) bezeichnet einen Optimierungsschritt, der geklammerte Ausdrücke in
booleschen Gleichungen durch Anwendung des Distributivgesetzes auflöst, sowie alle Zwischenvariablen
entfernt. Im Vordergrund steht dabei vor allem eine Geschwindigkeitsoptimierung.
132
Optimierung
Beispiel:
Vor dem Ausflachen:
out = uv
u = a + b(c + f)
v = d + e
Nach dem Ausflachen:
out = ad + bcd + bdf + ae + bce + bef
Das Ergebnis des Ausflachens ist, wie auch das Beispiel zeigt, eine Summe von Produkttermen. Damit
erhält man jedoch nicht immer die schnellste Schaltungsvariante, da manche Variablen in mehr als einem
Produktterm vorkommen und für den jeweiligen Treiberausgang dann aufgrund vieler angeschlossener
Gattereingänge eine erhöhte Last zustandekommt (großes Fan-Out).
Diesen Aspekt verdeutlicht Bild 3-20a.
a
a
x
&
b
b
&
y
&
z
y
&
c
c
d
x
&
a)
d
z
&
b)
Bild 3-20: Auswirkung des Ausflachens a) mit anschließender Strukturierung in b)
Dem Ausflachen kann sich ein Algorithmus zur Minimierung der booleschen Gleichungen anschließen
(s.a Abschnitt 3.5). Optimierungswerkzeuge können jeden einzelnen Ausgang separat minimieren, oder
alle Ausgänge gleichzeitig im Minimierungsprozeß betrachten. Der erste Fall liefert für den individuellen
Ausgang ein optimales Ergebnis. Im zweiten Fall werden mehrfach identisch vorhandene Produktterme
gemeinsam genutzt. Identische Produktterme treten häufig auf, wenn viele Ausgänge eine Abhängigkeit
von fast allen Eingängen aufweisen. Eine Minimierung wirkt sich in diesem Fall besonders positiv auf die
Gesamtgröße aus. In Bild 3-20b hat sich die Gesamtgröße in dem Beispiel zwar nicht verändert, doch sind
die Lasten wesentlich günstiger verteilt.
133
CAE: VHDL
Structuring
Oberstes Ziel in diesem Schritt ist das Auffinden möglichst vieler Gemeinsamkeiten in der
Schaltungsstruktur. Damit gewinnt eine zuvor flache Darstellung durch Einführung von
Zwischenvariablen wieder an Struktur. Die Zwischenvariable steht dabei für eine Teilfunktion, die dann an
mehreren Stellen zur vereinfachten Schreibweise der booleschen Gleichungen wieder eingesetzt werden
kann. Die Folge einer gemeinsamen Verwendung von Teilfunktionen ist ein flächensparender Entwurf,
erhöht aber in manchen Fällen die logische Tiefe im zeitkritischen Pfad.
Beispiel:
Vor Strukturierung:
Nach Strukturierung:
x = ab + ac
x=av
y=b+c+d
y=d+v
z=bce
z=ve
v=b+c
Die Strukturierung wird häufig im ersten Optimierungslauf empfohlen. Danach kann durch eine gezielte
Vorgabe von Verzögerungszeiten zwischen dem Ein- und Ausgang der kritische Pfad weiter optimiert
werden, ohne die unkritischen Bereiche zu verändern. Der Versuch, die zusätzlichen Randbedingungen zu
erfüllen, kann zu einer Vergrößerung des Flächenbedarfes führen, da die Laufzeitoptimierung mit der
Parallelisierung gekoppelt ist. Diese Parallelisierung bedeutet ein Duplizieren des einen oder anderen
Pfades.
Bild 3-21: Strukturierung ohne Vorgaben zu Verzögerungen
Bild 3-22: Optimierung des zeitkritischen Pfades
134
Optimierung
3.5.3 Optimierung zweistufiger Logik
Die zur Zeit deutlich sichtbaren Fortschritte in der Logiksynthese und -optimierung haben ihren Ursprung
in der Betrachtung zweistufiger kombinatorischer Schaltungen. Die bekannteste Darstellungsform dieser
Art ist die Summe von Produkttermen, auch disjunktive Normalform genannt. Dabei bilden gemäß der
booleschen Algebra Und-Verknüpfungen in der ersten Stufe Produktterme, deren Verknüpfungsergebnisse
in einer zweiten Stufe mit Oder-Gattern zusammengefasst (addiert) werden, wobei strenggenommen evt.
vorhandene Inverter an den Gattereingängen bereits eine dritte Stufe bilden. Weitere äquivalente Formen
zweistufiger Kombinatorik sind OR/AND, NOR/NOR, NAND/NAND oder AND/XOR.
y = (a AND b) OR (NOT a AND c)
Produktterme
Bild 3-23: Zweistufige Kombinatorik als Summe von Produkten: y = ab + ac
a
b
a
b
&
>1
c
&
&
c
A N D /O R
a
b
c
O R /A N D
y
&
N A N D /N A N D
>1
>1
&
y
&
a
b
>1
c
>1
y
>1
y
N O R /N O R
Bild 3-24: Äquivalente Formen zweistufiger Logik
Die praktische Umsetzung einer zweistufigen Kombinatorik findet man in Bausteinen mit PAL-Struktur
(Programmable Array Logic). Aufgrund des direkten Abbildes der mathematischen Beschreibung auf ein
Stück Silizium, eignen sich diese Strukturen besonders für eine automatische Logik- und Layoutsynthese.
Die relativ einfache Struktur macht sich in den realisierten Schaltungen durch kurze
Durchlaufverzögerungen und hohe Schaltungsgeschwindigkeiten positiv bemerkbar. In diesem Fall
können die Schaltungsgeschwindigkeiten vorab bereits sehr gut abgeschätzt werden.
Allerdings wächst die zweistufige Logik von regulären Operatoren (Addition, Subtraktion, Parity) mit
der Anzahl der Eingänge überproportional an. So entstehen zur Paritybildung bei der XOR-Verknüpfung
von N Signalen insgesamt 2N-1 Produktterme. Dennoch bilden optimierte zweistufige Darstellungsformen
häufig den Startpunkt für mehrstufige Synthesealgorithmen.
Das Ziel einer Optimierung zweistufiger Logik ist, eine zur Ausgangsfunktion äquivalente boolesche
Funktion zu finden, mit einem Optimum bezüglich Anzahl Produktterme und Literale (das sind direkt oder
negiert auftretende Variablen einer booleschen Funktion). Größtes Interesse gilt daher der
Aufgabenstellung, einen Satz Poduktterme zu finden, der einerseits die Ausgangsfunktion vollständig
beschreibt und andererseits möglichst wenig Redundanz enthält. Die Lösung gliedert sich in zwei
Teilschritte:
• Zerlegung der zu optimierenden booleschen Funktion in einen Satz Produktterme, sodaß keiner dieser
Produktterme in einem anderen enthalten ist (prime implicants).
• Auswahl eines Minimalsatzes von Produkttermen zur vollständigen Beschreibung der ursprünglichen
Funktion
135
CAE: VHDL
Quine-McCluskey-Algorithmus
Ein Algorithmus, der sich an den beiden Schritten orientiert, ist die Methode von Quine-McCluskey (vgl.
[DEV94]). Der Ansatz beruht auf einer geeigneten Zusammfassung von Produkttermen:
z = (abc) + (abc) = (a + a)bc = (1)bc = bc
Moderne Minimierungsprogramme basieren auf dieser Methode, deren Funktionsweise am besten anhand
eines Beispiels erläutert wird. In dem gewählten Beispiel sollen bestimmte Ausgangswerte eines vier Bit
breiten Zählers (abcd) ausdekodiert werden. Betrachtet wird also eine boolesche Funktion y, die sich aus
den Mintermen in der Tabelle 3-3 zusammensetzt (Ein Minterm ist ein Produktterm, der alle
Eingangsvariablen enthält):
y = p1 + p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9
Tabelle 3-3: Funktionsbestimmende Minterme
Zählerstand
Dezimal
Minterm
Binär:
abcd
0
0000
p1 = a b c d
5
0101
p2 = a b c d
7
0111
p3 = a b c d
8
1000
p4 = a b c d
9
1001
p5 = a b c d
10
1010
p6 = a b c d
11
1011
p7 = a b c d
14
1110
p8 = a b c d
15
1111
p9 = a b c d
Im ersten Schritt erfolgt ein paarweiser Vergleich, um Produktterme zu finden, die sich nur in einer
Position unterscheiden. Diese Position wird durch ein 'X' ersetzt (s. Tabelle 3-4).
Tabelle 3-4: Zusammenfassung der Minterme, 1. Iteration
0, 8
X000
A
5, 7
01X1
B
7, 15
X111
C
8, 9
100X
8, 10
10X0
9, 11
10X1
10, 11
101X
10, 14
1X10
14, 15
111X
136
Optimierung
Die Terme im Ergebnis aus dem ersten Schritt werden in der zweiten Iteration wieder in gleicher Weise
miteinander verglichen. Hier jedoch mit der Einschränkung, daß für eine Zusammenfassung zweier Terme
ein 'X' jeweils die gleiche Position belegt und die Terme sich sonst nur in einer weiteren Position
unterscheiden dürfen (ersetzt durch weiteres 'X'). Nach dieser Zusammenfassung bleiben fünf Terme übrig
(A - E), die keine Gemeinsamkeit mehr erkennen lassen.
Tabelle 3-5: 2. Iteration
8,9 10,11
8,10 9,11
10XX
D
10,11 14,15
1X1X
E
Aus den fünf übriggebliebenen Termen gilt es nun, einen Mindestsatz auszuwählen, um alle Minterme
zu realisieren. Dazu wird im letzten Schritt in der Tabelle 3-6 festgehalten, welcher Minterm in den
Termen A - E enthalten ist (markiert durch das Häkchen ✔). Aus den Spalten A - E können anschließend
soviele Spalten entfernt werden, daß in jeder Zeile mindestens ein Häkchen stehenbleibt. In diesem Fall
betrifft es die Spalte C, deren Häkchen auch Bestandteil der Spalten B und E sind. Gibt es beim
Herausstreichen von Spalten mehrere Möglichkeiten, so sollte die Spalte gestrichen werden, deren
Produktterm die meisten Literale enthält (oder geringste Anzahl 'X'en). Die restlichen Spalten bilden
schließlich die gesuchte Lösung.
Tabelle 3-6: Übriggebliebene Produktterme A-E sind „prime implicants“
A,B,D,E sind essentiell, C ist redundant
A
X000
Minterm
0000
D
10XX
E
1X1X
✔
0101
✔
0111
✔
1000
C
X111
B
01X1
✔
✔
✔
1001
✔
1010
✔
✔
1011
✔
✔
✔
1110
1111
✔
✔
Die Lösung zeigt eine deutlich geringere Anzahl von Produkttermen:
y=A+B+D+E
y = (b c d) + (a b d) + (a b) + (a c)
137
CAE: VHDL
3.5.4 Optimierung sequentieller Logik
Lag der Schwerpunkt im vorangegangenen Abschnitt mehr auf der Optimierung kombinatorischer Logik,
so werden nun Prinzipien zur Optimierung sequentieller Schaltungen betrachtet. Ein Modell für synchrone
sequentielle Schaltungen ist der Zustandsautomat, denn seine Zusammensetzung aus kombinatorischem
Teil und Registern ist typisch für diese Beschreibungsform.
Eingänge
Ausgänge
Kombinatorik
Zustand
Q
nächster
Zustand
D
Takt
Register
Bild 3-25: Aufbau eines Zustandsautomaten
Zustandsautomaten lassen sich am besten durch die Zustände und die momentanen Eingangssignale
beschreiben. Die Verknüpfung aus Eingang und aktuellem Zustand (vgl. Bild 3-25) bestimmt, welchen
Zustand der Automat im nächsten Takt annimmt. Eine bewährte Darstellungsform für Automaten ist
deshalb das Zustands-Übergangsdiagramm. Aufgrund ihres Abstraktionsgrades sind solche Diagramme
eher Verhaltensbeschreibungen und haben keinen direkt sichtbaren Bezug zur benötigten Fläche bzw.
erreichbaren Taktfrequenz.
e
* /1
&
z
S2
S1
e /0
e /1
D
Q
z (n ) = N O T (z (n -1 ) e (n ) )
Bild 3-26: Sequentielles Schaltungsmodell: Zustands-Übergangsdiagramm und synchrones Schaltwerk
Minimierung der Zustände
Eine Reduzierung der Gesamtkomplexität eines Automaten korreliert meistens mit einem sparsameren
Umgang von Resourcen. Weniger Zustände führen zu weniger Speicherelementen. Weniger Zustände
bedeuten im allgemeinen aber auch weniger Zustandsübergänge, was sich während einer
Flächenoptimierung positiv auswirkt.
Algorithmen zur Zustandsminimierung sind ausführlich in [MIC94] beschrieben. Ein einfaches
Beispiel verdeutlicht die Vorgehensweise, um aus einem gegebenen Übergangsdiagramm einen Automaten
zu gewinnen, der einerseits gleiches Verhalten zeigt, andererseits aber nur eine Mindestanzahl Zustände
aufweist.
138
Optimierung
0/1
S3
S1
1/1
S4
0/0
1/1
0/0
0/1
0/1
1/0
S2
1/1
S5
1/1
Bild 3-27: Zustands-Übergangsdiagramm vor der Optimierung
Tabelle 3-7: Zustandstabelle vor der Optimierung
Eingang
Zustand
nächster
Zustand
Ausgang
0
S1
S3
1
1
S1
S5
1
0
S2
S3
1
1
S2
S5
1
0
S3
S2
0
1
S3
S1
1
0
S4
S4
0
1
S4
S5
1
0
S5
S4
1
1
S5
S1
0
Der erste Schritt zur Zustandsminimierung durchsucht die Tabelle nach Zuständen, die für einen
bestimmten Eingangswert denselben Ausgangswert und Folgezustand besitzen. Dies trifft in dem Beispiel
für die Zustände S1 und S2 zu. Damit können diese Zustände zu einem gemeinsamen Zustand
zusammengefaßt werden.(vgl. Tabelle 3-8).
139
CAE: VHDL
Tabelle 3-8: Zustandstabelle nach Optimierung
Eingang
nächster
Zustand
Zustand
Ausgang
0
S12
S3
1
1
S12
S5
1
0
S3
S12
0
1
S3
S12
1
0
S4
S4
0
1
S4
S5
1
0
S5
S4
1
1
S5
S12
0
0/1
S3
1 /1
S1
S4
0 /0
1 /1
0 /0
0 /1
0 /1
1 /1
1 /0
S2
S5
1 /1
Bild 3-28: Zustands-Übergangsdiagramm nach Zusammenfassung von S1 und S2
Zustandskodierung
Eine geeignete Zustandskodierung zu finden, ist schwieriger, als es auf den ersten Blick erscheinen mag.
Das Problem besteht darin, unter Berücksichtigung des Speichertyps (D-, JK- oder Toggle-Flip-Flop), für
jeden Zustand ein binäres Äquivalent so zu bestimmen, daß Fläche und Geschwindigkeit des Automaten
optimiert werden. Man unterscheidet drei Kodierungsarten:
• 1-Hot Kodierung: 0001, 0010, 0100
Jeder Zustand wird durch genau ein Flip-Flop repräsentiert
Vorteil: Hohe erreichbare Geschwindigkeit
• Binäre Kodierung: 00, 01, 11, 11
Mit n Zustandsregister (Flip-Flop) sind 2n Zustände möglich
Vorteil: Geringere Gesamtfläche
• Gray-Kodierung: 000 001 011 010
Beim Zustandswechsel ändert nur ein Zustandsregister seinen Wert
140
Optimierung
Beispiel: Zustandsautomat mit Angabe der Kodierungsart
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
ENTITY counter IS
PORT(
reset:
IN STD_LOGIC;
clk:
IN STD_LOGIC;
cnt:
IN STD_LOGIC;
co:
OUT STD_LOGIC);
END counter;
ARCHITECTURE counter_arch OF counter IS
TYPE STATES IS (S0, S1, S2, S3);
ATTRIBUTE enum_encoding: STRING;
ATTRIBUTE enum_encoding OF STATES : TYPE IS
--- 1-Hot:
-"0001 0010 0100 1000";
-- Anwenderspezifische Kodierung:
"1111 1110 1100 1000";
---
-- Gray-Kodierung
"000 001 011 010";
SIGNAL current_state: STATES;
BEGIN
FSM: PROCESS(clk, reset)
BEGIN
IF(reset = '1') THEN
current_state <= S0;
co <= '0';
ELSIF(clk'EVENT AND clk = '1') THEN
-- co auf '0', nur in S3 überschreiben
co <= '0';
CASE current_state IS
WHEN S0 =>
IF(cnt = '1') THEN current_state <=
WHEN S1 =>
IF(cnt = '1') THEN current_state <=
WHEN S2 =>
IF(cnt = '1') THEN current_state <=
WHEN S3 =>
IF(cnt = '1') THEN current_state <=
S1; END IF;
S2; END IF;
S3; END IF;
S0;
co<='1'; END IF;
END CASE;
END IF;
END PROCESS FSM;
END counter_arch;
141
CAE: VHDL
3.6 Retiming
Retiming Algorithmen behandeln ebenfalls das Problem, die Taktfrequenz und Fläche eines synchronen
Schaltwerks zu optimieren. Zwei prinzipielle Möglichkeiten stehen zur Auswahl:
Lage der Register verändern, ohne die Kombinatorik zu verändern (Bild 3-29)
Ziel einer Lageveränderung der Register ist es aus mehreren kleinen Logikblöcken einen größeren
Kombinatorikblock zu bilden. Dadurch ergeben sich im nächsten Schritt der Schaltungsoptimierung mehr
Freiheitsgrade zur Minimierung (s.a. 3.5.3).
&
D Q
&
D Q
&
&
D Q
Bild 3-29: Veränderung der Lage und der Anzahl der Register, bei gleicher Funktion
Gleichmäßige Verteilung der Kombinatorik auf vorhandene Register (Bild 3-30)
Die gleichmäßige Verteilung der Kombinatorik hat zum Ziel, die Laufzeiten in den einzelnen
Logikblöcken ungefähr gleich aufzuteilen und damit eine Laufzeitoptimierung zu erreichen.
K o m b in a to rik
D Q
D Q
D Q
D Q
Bild 3-30: Ausbalancieren der Logik
Bild 3-31 zeigt eine weitere Anwendung dieser Optimierungsstrategie. Diese auch als Pipelining
bezeichnete Strategie kann die VHDL-Beschreibung stark vereinfachen. Ist bereits beim Entwurf
abzusehen, daß für einen Kombinatorikblock die Einhaltung von Signallaufzeiten innerhalb der
Taktperiode nicht möglich ist, kann der Compiler veranlaßt werden eine Aufteilung der Logik
vorzunehmen.
Das nachfolgende Beispiel zeigt die notwendigen Syntheseanweisungen (für den Synopsys-Compiler).
Diese Methode des Piplelinings wird allerdings nicht von allen Compilern unterstützt.
142
Retiming
a
a*b
c
D Q
d
D Q
b
CLK
D Q
c D Q
d
CLK
Bild 3-31: Pipelining: Trennen einer großen Kombinatorik in zwei kleinere Teile
Beispiel: Multiplizierer mit Pipeline
SIGNAL a, b: NATURAL RANGE 255 DOWNTO 0;
SIGNAL c: NATURAL RANGE 65535 DOWNTO 0;
...
PROCESS(takt)
BEGIN
IF(takt'EVENT AND takt = '1') THEN
c <= a * b;
d <= c;
END IF;
END PROCESS;
/* Auszug eines Scripts zum Pipelining (Synopsys) */
/* Einlesen des Source-Files */
read -f vhdl ./pipe.vhdl
...
/* Taktnetz nicht verändern , keine Buffer etc. */
set_dont_touch_network CLK
/* Erstes Mapping ohne Constraints */
compile -map_effort low
/* Constraint für Takt */
create_clock CLK -period 20
/* gleichmäßige Verteilung der Kombinatorik */
balance_register
/* Compilieren unter Berücksichtigung des Timings */
compile -map_effort high
143
CAE: VHDL
3.7 Umsetzung auf die Zieltechnologie (Technology Mapping)
Umsetzung auf die Zieltechnologie bezeichnet den Vorgang, das noch technologieunabhängige, aber
bereits optimierte boolesche Netzwerk mit den verfügbaren Gattern einer speziellen Bibliothek (ASIC
oder FPGA) zu implementieren. Die Auswahl erfolgt unter dem Gesichtspunkt, Geschwindigkeit und
Fläche betreffende Vorgaben nicht zu überschreiten.
Dieser Vorgang bildet den Abschluß der Synthese und soll die Struktur der Schaltung nicht mehr
gravierend verändern. Die Algorithmen haben die Aufgabe, entlang des kritischen Pfades die schnellsten
verfügbaren Gatter einzusetzen, bzw. außerhalb des kritischen Pfades eine möglichst flächenoptimale
Gatterkombination zu finden. Die Bibliotheken, welche die Zieltechnologie repräsentieren, sollen für den
Anwender leicht austauschbar sein, gleichzeitig müssen die Algorithmen damit zurecht kommen, daß die
Bibliotheken nicht immer dieselbe Auswahl an Gattern vorhalten.
Bibliotheken
Bibliotheken beinhalten typischerweise eine größere Ansammlung von Gattern (Inverter, Nand, Xor) und
sequentiellen Elementen (Flip-Flops). Jedem Element ist ein Wert zugeordnet, der die Fläche des Gatters
angibt (gemessen in µm2, Rastergröße oder normiert auf die Fläche eines Inverters oder Nand-Gatters) und
anteilig auch die Verdrahtungsfläche mitberücksichtigt. Zusätzliche Parameter beschreiben das zeitliche
Verhalten des Gatters, besonders die Verzögerung vom Eingang zum Ausgang sowie die Auswirkung beim
Anschluß weiterer Lasten. Das Verzögerungsmodell aus [DEV94] kennt für jedes Gatter drei Parameter:
Di,g = Ai,g + Bi,g * C
darin ist D die resultierende Verzögerung zwischen Ein (i) - und Ausgang (g), A die lastunabhängige
Verzögerung, B die lastabhängige Verzögerung und C die kapazitive Last des Gatters. Eine Indizierung ist
notwendig, wenn die Verzögerungen von einzelnen Eingängen des Gatters zum Ausgang nicht identisch
sind, kann jedoch entfallen, wenn sich die Eingänge nicht unterscheiden. Bild 3-32 zeigt die Symbole
einiger Bibliothekselemente mit äquivalenten Darstellungen aus Nand2-Gattern und Invertern sowie
Faktoren zur Fläche und zur Verzögerung.
Bild 3-32: Bibliotheksauszug
144
Umsetzung auf die Zieltechnologie (Technology Mapping)
3.7.1 Mapping
Vor dem eigentlichen Mappingprozeß wird das optimierte, aber noch technologieunabhängige boolesche
Netzwerk in eine Fom konvertiert, die nur bestimmte Basisfunktionen enthält. Häufig verwendet man als
Basisfunktionen ein Nand-Gatter mit zwei Eingängen (Nand2) und einen Inverter. Auch die Bibliothek
enthält für jedes Element eine äquivalente Darstellung aus Basisfunktionen, sog. Pattern. Für komplexere
Elemente, wie das Nand4-Gatter, gibt es mehrere äquivalente Darstellungen, die ebenfalls Bestandteil der
Bibliotheken sind.
&
1
&
1
&
&
1
&
&
1
Bild 3-33: Unterschiedliche Pattern für ein Nand4-Gatter
Durch die Einschränkung auf die Basisfunktionen ergeben sich bei der Konvertierung mitunter viele
verschiedene Darstellungen. Jede davon kommt als Initial-Netzwerk für den Mappingalgorithmus in
Frage.
Im nächsten Schritt wird das Netzwerk so aufgetrennt, daß keine Verzweigungen mehr vorhanden sind
(vgl. Bild 3-34). Alle Ausgänge, auch die soeben beim Auftrennen entstandenen, bilden dann einen
Startpunkt für den Mappingalgorithmus.
Beginnend an einem solchen Startpunkt, wird jeder Knoten mit allen Pattern aus der Bibliothek
verglichen. Für den Fall, daß mehrere Bibliothekselemente geeignet sind, entscheiden die Flächen- oder
Verzögerungsfaktoren, welches Element für ein optimales Gesamtergebnis besser ist.
&
&
&
Verzweigung
&
&
1
1
&
&
&
&
1
&
3
2
&
&
1
1
Bild 3-34: Verzweigungen im Netzwerk auftrennen
Weil die Basisfunktionen meistens ebenfalls in der Bibliothek als Gatter verfügbar sind, gibt es
mindestens eine (triviale) Lösung. Die passenden Bibliothekselemente für den Zweig mit Startpunkt 3 aus
Bild 3-34 sind in Bild 3-35 eingetragen. Zwei Lösungen zeichnen sich dabei ab. Die Lösung in Bild 3-36a
ist vorzuziehen, wenn der angenommene zeitkritische Pfad entschärft werden soll, da ein Nand2 im
Vergleich zum Nand3 günstigere Verzögerungsfaktoren besitzt (vgl. Bild 3-32). Andererseits weist die
Alternative in Bild 3-36b eine geringere Fläche auf.
145
CAE: VHDL
Nand2
&
&
Nand2
1
Inverter
&
3
Nand2
Nand3
Bild 3-35: Passende Bibliothekselemente
kritischer Pfad
Nand2
&
&
Nand2
a)
1
Nand2
&
&
3
Nand2
Inverter
b)
Bild 3-36: Zwei Lösungsvarianten für unterschiedliche Zielsetzung
a) optimiert kritischen Pfad
b) optimiert Fläche
146
Nand3
&
3
Synthesefähige Konstrukte, Attribute, Typen und Operatoren
3.8 Synthesefähige Konstrukte, Attribute, Typen und Operatoren
Zeichenerklärung:
✔ : Von Synthesewerkzeug unterstützt
—: Von Synthesewerkzeug ignoriert
✕: nicht unterstützte Anweisung
Tabelle 3-9: VHDL-Konstrukte in der Synthese
VHDL-Konstrukt
AFTER Anweisung in sequentiellen oder
nebenläufigen Signalzuweisungen
—
Aggregates: Record und Array
✔
ALIAS
✔
Allocator
✕
Architecture Body
✔
ASSERT: sequentiell oder nebenläufig
—
ATTRIBUTE: Deklaration und Spezifikation
✔
BLOCK
✔
CASE
✔
COMPONENT: Deklaration und Instanzierung
✔
CONFIGURATION: Deklaration
—
CONFIGURATION: Spezifikation
✔
CONSTANT: Deklaration
✔
ENTITY: Deklaration
✔
EXIT
✔
FILE: Deklaration
—
FUNCTION
✔
GENERATE
✔
GENERIC
✔
IF
✔
LIBRARY
✔
LOOP: ohne Vorgabe der Iterationen
✕
LOOP: (FOR)
✔
LOOP:(WHILE)
✕
NEXT
✔
147
CAE: VHDL
Tabelle 3-9: VHDL-Konstrukte in der Synthese
VHDL-Konstrukt
PACKAGE: Deklaration und Body
✔
PROCEDURE
✔
PROCESS
✔
Qualified Expression
✔
RETURN
✔
SIGNAL: Einfache, bedingte und selektierte
Zuweisung
✔
SIGNAL: Sequentielle Zuweisung
✔
SIGNAL: Deklaration (nicht in Packages)
✔
SUBTYPE
✔
TYPE: Deklaration und Konvertierung
✔
USE
✔
VARIABLE: Deklaration und Zuweisung
✔
WAIT (nur ein Wait im Prozess)
✔
Tabelle 3-10: VHDL-Attribute in der Synthese
ObjektKlasse
Array
148
Attributname
'HIGH
✔
'LEFT
✔
'LENGTH
✔
'LOW
✔
'RANGE
✔
'REVERSE_RANGE
✔
'RIGHT
✔
Synthesefähige Konstrukte, Attribute, Typen und Operatoren
Tabelle 3-10: VHDL-Attribute in der Synthese
ObjektKlasse
Signal
Type
Attributname
'ACTIVE
✕
'DELAYED
✕
'EVENT
✔
'LAST_ACTIVE
✕
'LAST_EVENT
✕
'LAST_VALUE
✔
'QUIET
✕
'STABLE
✔
'TRANSACTION
✔
'BASE
✔
'HIGH
✔
'LEFT
✔
'LEFTOF
✔
'LOW
✔
'POS
✔
'PRED
✔
'RIGHT
✔
'RIGHTOF
✔
'SUCC
✔
'VAL
✔
Tabelle 3-11: VHDL-Typen in der Synthese
Typklasse
Enumerator
Typ
BIT
✔
BOOLEAN
✔
CHARACTER
✔
STD_LOGIC
STD_ULOGIC
✔
INTEGER
✔
NATURAL
✔
Integer
149
CAE: VHDL
Tabelle 3-11: VHDL-Typen in der Synthese
Typklasse
Floating Point
Typ
REAL
✕
Physical
Array
✕
BIT_VECTOR
✔
STD_LOGIC_VECTOR
STD_ULOGIC_VECTOR
✔
Records
✔
File
✕
Access
✕
Tabelle 3-12: In der Synthese unterstützte Operatoren für
BIT und BIT_VECTOR
STD_LOGIC_VECTOR (mit entsprechendem Package)
Operatorklasse
Logisch
AND, OR, NAND, NOR, XOR, XNOR, NOT
Vergleich
=, /=, <, <=, >, >=
Addition
+, -, &
Vorzeichen
+,-
Shift
SL, SRL, SLA, SRA, ROL, ROR
Multiplikation
*, /, MOD, REM
Verschiedene
**, ABS
150
Literatur
3.9 Literatur
[ASG95] Altera: Altera/Synopsys User Guide. Application Note, July 1995.
[DEV94] Devadas, S., Ghosh, A., Keutzer, K.: Logic Synthesis.
McGraw-Hill, 1994. ISBN: 0-07-016500-9
[HAC96]Hachtel, G.D., Somenzi, F.: Logic Synthesis and Verifiction Algorithms.
Kluwer Academic Publishers, 1996. ISBN: 0-7923-9746-0
[LAN98] Landman, H.A.: Visualizing the Behavior of Logic Synthesis Algorithms.
Toshiba America Electronic Components, Inc. San Jose, California.
[MIC94] De Micheli, G.: Synthesis and Optimization of Digital Circuits.
McGraw-Hill, 1994. ISBN: 0-07-113271-6
[NEL94] Nelson, K.: High-Level Design Methodology Overview.
Synopsys Methodology Notes, March 1994.
[SFS94] Synopsys: Flattening and Structuring.
A Look at Optimization Strategies. Application Note 1994
[VAH94]
Vahtra, K.: ASIC Design Partitioning.
Synopsys Methodology Notes, January 1994.
151
Anhang B: VHDL - Syntax
4 Anhang B: VHDL - Syntax
AGGREGATES: Liste von Elementen, eingeschlossen in Klammern, getrennt durch Komma.
Syntax: (value1, value2, ...) oder (element1 => value1, element2 => value2, ...)
Beispiel: SIGNAL X : BIT;
SIGNAL Y : BIT_VECTOR (7 DOWNTO 0);
Y <= (7 => '1', 6 DOWNTO 3 => '0', 2 => X,
OTHERS => '0');
ALIAS DECLARATION: Alternative Bezeichnung eines Objektes, kein neues Objekt.
Syntax: ALIAS aliasname : aliastyp IS objektname ;
Beispiel: ALIAS parity : BIT IS daten(7);
ARCHITECTURE: Beschreibt das Verhalten einer Entity.
Syntax: ARCHITECTURE architecture_name OF entity_name IS
declarations
BEGIN
concurrent statements
END architecture_name ;
Beispiel: ARCHITECTURE arch_my OF entity_my IS
SIGNAL A, B, C, D, Y INTEGER;
BEGIN
A <= B + C;
Y <= A - D;
END arch_my ;
ARRAY: Besteht aus Elementen vom selben Typ. Die Deklaration eines Objektes vom Typ Array muß sich
auf einen existierenden Arraytyp beziehen.
Syntax: TYPE typname IS ARRAY (range) OF elementtyp;
Beispiel: TYPE word IS ARRAY (15 DOWNTO 0) OF BIT;
SIGNAL w: word;
... Die Objekte „string“, „bit_vector“ und „std_logic_vector“ sind z.B. als unbeschränkte
Arraytypen definiert:
TYPE bit_vector IS ARRAY(NATURAL RANGE <>)OF BIT;
SIGNAL Addr: bit_vector(15 DOWNTO 0);
ASSERTION STATEMENT: Testet eine Boolesche Bedingung (condition). Ist sie falsch, dann wird eine
Meldung ausgegeben und der Simulator kann über den severity level (note, warning, error und
failure) gesteuert werden.
Syntax: ASSERT condition REPORT string SEVERITY severitylevel;
Beispiel: ASSERT DATA <= X"00FF" REPORT "DATA ist größer als 00FF!" SEVERYTY note;
152
CAE: VHDL
ATTRIBUTES: Liefern zusätzliche Informationen zu einem Objekt.
Syntax: object'attribute_name
Beispiel: Vordefinierte Attribute für scalar types and array types:
X'HIGH
Die obere Grenze von X
X'LOW
Die untere Grenze von X
X'LEFT
Die linke Grenze von X
X'RIGHT
Die rechte Grenze von X
Vordefinierte Attribute für array types und array sub types:
X'RANGE
Der Bereich von X
X'REVERSE_RANGE
Der Bereich von X gedreht.
X'LENGHT
X'HIGH - X'LOW + 1 als Integer.
Vordefinierte Attribute für alle Signale X:
X'EVENT
True (Boolescher Wert), wenn sich X ändert
X'ACTIVE
True (Boolescher Wert), wenn Wertzuweisung an X
X'LAST_EVENT
Zeit der letzten Anderung von X
X'LAST_ACTIVE
Zeit der letzten Wertzuweisung an X
X'LAST_VALUE
Wert vor der letzten Wertzuweisung an X
X'DELAYED(T)
X verzögert um die Zeit T
X'STABLE(T)
True (Boolescher Wert), wenn X ungeändert in T
X'QUIET(T)
True (Boolescher Wert), wenn in T keine Zuweisung
X'TRANSACTION
Wertänderung eines BIT, wenn Zuweisung an X
BLOCK: nebenläufige Anweisung, die andere nebenläufigen Anweisungen enthält und zu einer Einheit
zusammenfaßt.
Syntax: label: BLOCK (optional_guard_condition)
declarations
BEGIN
concurrent statements
END BLOCK label;
Beispiel: bsp: BLOCK (en = '1')
BEGIN
Y <= GUARDED X;
END BLOCK bsp;
CASE: Wählt eine Anweisungssequenz aus einer Anzahl von Anweisungssequenzen aus.
Syntax: [case_label] CASE expression IS
WHEN choice =>
sequential statements
{WHEN choice =>
sequential statements}
END CASE [case_label];
Beispiel: CASE address IS
WHEN "000" => decode <= X"01";
WHEN "011" => decode <= X"F3";
WHEN "100" => decode <= X"33";
WHEN OTHERS => decode <= X"00";
END CASE;
153
Anhang B: VHDL - Syntax
COMPONENT DECLARATION: Deklaration eines Design Entity Interface, das in einer component
instantiation verwendet werden kann.
Syntax: COMPONENT component_name [IS]
GENERIC (List);
PORT (List);
END COMPONENT;
Beispiel: COMPONENT parity
GENERIC (n: INTEGER);
PORT (w: IN STD_ULOGIC_VECTOR (n-1 DOWNTO 0);
p: OUT STD_ULOGIC);
END COMPONENT;
COMPONENT INSTANTIATION: Einsetzen einer Komponente in einem hierarchischem Entwurf.
Syntax: instance_label: [COMPONENT] component_name
GENERIC MAP (generic_association_list)
PORT MAP (port_association_list);
Beispiel: U1: parity
GENERIC MAP (n => 8)
PORT MAP (w => DATA_BYTE, p => PARYITY_BIT);
CONFIGURATION DECLARATION: Deklaration, die die Hierarchie eines Entwurfs beschreibt.
Syntax: CONFIGURATION config_name OF entity_name IS
FOR architecture_name
FOR instance_label: component_name
USE ENTITY
library_name.entity_name(arch_name);
FOR arch_name
... lower-level configuration specifications ...
END FOR;
END FOR;
END FOR;
END config_name;
Beispiel: LIBRARY DIGPARTS;
CONFIGURATION struct_con OF struct_entity IS
FOR struct_arch
FOR U1,U2: REG
USE ENTITY WORK.UNIVREG;
END FOR;
FOR ALL: FULLADD
USE ENTITY DIGPARTS.OPTFULLADD;
FOR OPTIMIZED
FOR ALL: AND2
USE ENTITY DIGPARTS.AND2;
END FOR;
FOR ALL: OR2
USE ENTITY DIGPARTS.OR2;
END FOR;
END FOR;
END FOR;
END FOR;
END;
154
CAE: VHDL
CONFIGURATION SPECIFICATION: Stellt eine Verbindung einer Komponente oder von Instanzen
einer Komponente zu einer Entwurfseinheit her.
Syntax: FOR instance_label: component_name
USE ENTITY library_name.entity_name(arch_name);
Beispiel: FOR U1: fulladd USE ENTITY WORK.fadd(arch_fadd);
CONSTANT DECLARATION: Deklariert den Namen und den Wert einer Konstanten.
Syntax: CONSTANT constant_name : type [:= expression];
Beispiel: CONSTANT address_width: INTEGER := 8;
ENTITY: Beschreibt die Ein- u. Ausgänge einer Designeinheit
Syntax: ENTITY entity_name IS
GENERIC (generic_list);
PORT (port_list);
END [ENTITY] entity_name;
Beispiel: ENTITY fulladd IS
GENERIC (DELAY: time := 5ns);
PORT (a,b,c_in: IN BIT;
sum, c_out: OUT BIT);
END fulladd;
EXIT:
Verursacht die Beendigung einer loop, in die es eingebettet ist.
Syntax: EXIT [loop_label] [WHEN condition];
Beispiel: loop1: FOR i IN loop_var DOWNTO 0 LOOP
EXIT loop1 WHEN i > 15;
mem(i) <= (OTHERS => '0');
END LOOP;
EXPRESSION: Ist eine Formel, die die Berechnung eines Werts erlaubt.
Syntax: relation {logical_operator relation} ;
logical_operator ::= AND | OR | XOR | NAND | NOR | XNOR.
relation ::= shift_expression [relational_operator shift_expression]
relational_operator ::= = | /= | < | <= | > | >=
shift_expression ::=simple_expression [shift_operator simple_expression]
shift_operator ::= sll | srl | sla | sra | rol | ror
simple_expression ::= [sign] term {adding_operator term}
sign ::= + | , adding_operator ::= + | - | &
term ::= factor {multiplying_operator factor}
multiplying_operator ::= * | / | mod | rem
factor ::= primary [** primary] | ABS primary | NOT primary
primary ::= name | literal | aggregate | function_call | qualified_expression | type_conversion |
allocator | (expression)
FILE DECLARATION: Deklariert ein File und verbindet es mit einem File in der Arbeitsumgebung.
Syntax: FILE logical_name : file_type IS mode "file_name";
Beispiel: FILE intext: TEXT IS IN "stimuli.txt";
FILE erg_file: TEXT IS OUT"results.txt";
155
Anhang B: VHDL - Syntax
FOR LOOP: Wird zur Implementierung von sich wiederholenden Anweisungen verwendet.
Syntax: [loop_label :] FOR parameter IN range LOOP
sequential statements
END LOOP [loop_label];
Beispiel: FOR i IN 7 DOWNTO 0 LOOP
IF i = 2 THEN
NEXT;
ELSE
fifo(i) <= (OTHERS => '0');
END IF;
END LOOP;
FUNCTIONS: Unterprogramm, das einen Wert an das aufrufende Programm liefert und einen Ausdruck
(expression) darstellt.
Syntax: FUNCTION function_name (parameter_list) RETURN type IS
declarations
BEGIN
sequential statements
END function_name;
Beispiel: FUNCTION boolean_to_bit(a: BOOLEAN) RETURN BIT IS
BEGIN
IF a THEN
RETURN '1';
ELSE
RETURN '0';
END IF;
END boolean_to_bit;
END boolean_to_bit;
GENERATE: Wird zur iterativen oder bedingten Erzeugung von Schaltungsteilen verwendet.
Syntax: [gen_label] : FOR parameter IN range GENERATE
[{declarative_item}
BEGIN]
concurrent statements
END GENERATE [gen_label];
Beispiel: gen_bsp: FOR k IN 4 DOWNTO 1 GENERATE
y(k) <= a(k) AND b(k);
END GENERATE gen_bsp;
GENERICS: werden zur Zuweisung von bausteinindividuellen Daten verwendet.
Syntax: siehe Entity
Beispiel: siehe Entity
156
CAE: VHDL
IF STATEMENT: Anweisung zur Steuerung des sequentiellen Abaufes in einem Prozeß, einer Funktion
oder einer Prozedur. Die IF-Anweisung wählt eine oder keine Anweisungsfolge aus.
Syntax: [if_label:] IF condition_1 THEN
sequential_statements
{ELSIF condition_n THEN
sequential_statements}
[ELSE
sequential_statements]
END IF [if_label] ;
Beispiel:
ARCHITECTURE archmux4to1 OF mux4to1 IS
BEGIN
mux: PROCESS (a, b, c, d, s)
BEGIN
IF s = "00" THEN x <= a;
ELSIF s = "01" THEN x <= b;
ELSIF s = "10" THEN x <= c;
ELSE s = "11" THEN x <= d;
END IF;
END PROCESS mux;
END archmux4to1;
LIBRARY CLAUSE: benennt logische Namen für einzubindende Designbibliotheken.
Syntax: LIBRARY library_name_1, library_name_2, .. ;
Beispiel: LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
LITERALS: ist der Wert eines Datentyps.
Syntax: siehe Beispiel.
Beispiel: CONSTANT count: INTEGER:= 32;
CONSTANT end: real:= 15.0;
int1 <= 16#FA#
int2 <= 2#1111_1010#
great_number := 2.8E6
-- Integerzahlen
-- Realzahlen
-- Zahl zur Basis 16
-- Zahl zur Basis 2
-- Exponentendarstellung
NAMES: dienen als Identifizierer.
Syntax: werden aus Buchstaben, Ziffern und dem Unterstrich gebildet und müssen mit einem Buchstaben
beginnen.
Beispiel: RTL_Unit
Bus_16Bit
NEXT STATEMENT: bietet die Möglichkeit zur vorzeitigen Beendigung einer Iteration innerhalb einer
loop.
Syntax: [label:] NEXT [loop_label] [WHEN condition];
Beispiel: siehe FOR LOOP.
157
Anhang B: VHDL - Syntax
NULL: verursacht, daß keine Aktion stattfindet.
Syntax: [label :] NULL;
Beispiel: CASE state IS
WHEN s1 | s2 | s4 => next_state <= s0
WHEN OTHERS => NULL;
END CASE;
PACKAGE DECLARATIONS: definiert das Interface zu einer Package.
Syntax: PACKAGE package_name IS
declarations
END [PACKAGE] [package_name];
Beispiel: PACKAGE my_package IS
TYPE my_logic IS ('0', '1', 'X', 'Z');
TYPE my_logic_vector IS ARRAY (NATURAL RANGE <>) OF my_logic;
FUNCTION resolved (drivers: my_logic_vector) RETURN my_logic;
CONSTANT propagation_delay: time;
END my_package;
PACKAGE BODY: ist die Sammlung von Unterprogrammen, die in der package deklariert sind.
Syntax: PACKAGE BODY package_name IS
declarations
deferred constant declarations
subprogram bodies
END [PACKAGE BODY] [package_name];
Beispiel: PACKAGE BODY my_package IS
FUNCTION resolved (drivers: my_logic_vector) RETURN my_logic IS
BEGIN
-- code for the funktion
END resolved;
END my_package;
PROCEDURE: Unterprogramm, das mehrere Werte übergeben kann und eine Anweisung ist.
Syntax: PROCEDURE procedure_name (parameter_list) IS
declarations
BEGIN
sequential statements
END procedure_name;
Beispiel: PROCEDURE my_dff (SIGNAL d: STD_LOGIC_VECTOR;
SIGNAL clk, rst: STD_LOGIC;
SIGNAL q, qb: OUT STD_LOGIC_VECTOR) IS
BEGIN
IF rst = '1' THEN
q <= (OTHERS => '0');
qb <= (OTHERS => '1');
ELSIF clk'EVENT AND clk = '1' THEN
q <= d;
pb <= NOT d;
END IF;
END my_dff;
158
CAE: VHDL
PROCESS: gestattet die Darstellung von Teilen eines Entwurfs mit sequentiellen Anweisungen.
Syntax: [process_label :] PROCESS [(sensitivity_list)] [IS]
declarations
BEGIN
sequential statements
END PROCESS [process_label];
Beispiel: comp: PROCESS (a,b)
BEGIN
IF a = b THEN
equal <= '1';
ELSE
equal <= '0';
END IF;
END PROCESS comp;
QUALIFIED EXPRESSION: wird zur expliziten Festlegung des Typs eines Ausdrucks verwendet, um
Mehrdeutigkeiten zu vermeiden.
Syntax: type'(expression)
Beispiel: string'("0110")
bit_vector'("0110")
SIGNAL DECLARATION: deklariert Signale und evt. einen Initialisierungswert.
Syntax: SIGNAL signal_name_list : type [:= expression] ;
Beispiel: SIGNAL a, b, c : BIT;
SIGNAL count : INTEGER RANGE 0 TO 256;
SIGNAL ASSIGNMENTS:
delay ::= TRANSPORT | [REJECT time_expression] INERTIAL
waveform ::= waveform_element {, waveform_element} | UNAFFECTED
waveform_element ::= value_expression [AFTER time_expression] | NULL [AFTER time_expression]
CONCURRENT SIGNAL ASSIGNMENT: weist einem Signal einen Wert zu, sobald sich eines der
Signale auf der rechten Seite der Anweisung ändert. Liegt außerhalb eines Prozesses.
Syntax: [label :] signal_name <= [delay] waveform;
Beispiel: y <= a XOR b;
y <= '0', a XOR b AFTER 5 ns, '1' AFTER 20ns;
CONDITIONAL SIGNAL ASSIGNMENT: weist einem Signal einen Wert zu, sobald sich eines der
Signale auf der rechten Seite der Anweisung ändert und eine Bedingung erfüllt ist.
Syntax: [label :] signal_name <=
[GUARDED][delay] waveform1 WHEN condition1 ELSE
[GUARDED][delay] waveform2 WHEN condition2 ELSE
[GUARDED][delay] waveform3;
Beispiel: y <= a XOR b
WHEN c = '0' ELSE
a OR b
WHEN c = '1' ELSE
a AND b;
159
Anhang B: VHDL - Syntax
SELECTED SIGNAL ASSIGNMENT: weist einem Signal einen Wert zu, sobald sich bei gewissen
Steuersignalen ein Wert ändert.
Syntax: WITH expression SELECT
signal_name <= [GUARDED][delay] waveform1 WHEN choice1,
[GUARDED][delay] waveform2 WHEN choice2;
Beispiel: WITH a_integer SELECT
y <= a
WHEN 0,
b
WHEN 1 TO 3,
c
WHEN 4 | 8 | 11,
d
WHEN OTHERS;
SEQUENTIAL SIGNAL ASSIGNMENT: Die Signalzuweisung liegt innerhalb eines Prozesses. Die
Wertzuweisung an das Signal wird erst beim Verlassen des Prozesses ausgeführt. Falls mehrere
Zuweisungen auf ein Signal vorhanden sind, wird nur die letzte Zuweisung ausgeführt.
Syntax: [label :] signal_name <= waveform;
Beispiel: y <= a XOR b;
y <= a XOR b AFTER 5 ns;
SUBTYPE DECLARATION: Ein subtype ist ein type mit Einschränkungen.
Syntax: SUBTYPE subtype_name IS base_type RANGE range_constraint;
Beispiel: SUBTYPE ind IS INTEGER RANGE 10 TO 20;
TYPE CONVERTION: gestattet die Konvertierung des Wertes eines type in den Wert eines anderen type.
Syntax: target_type (expression) -- für vordefinierte Konvertierungen.
conversion_funktion (expression) -- für selbstdefinierte Konvertierungen.
Beispiel: VARIABLE N: INTEGER;
VARIABLE a: REAL;
n := INTEGER(3.8);
a := REAL(2);
TYPE DECLARATION: Ein type besteht aus einer Ansammlung von Werten, die in der type declaration
definiert werden.
Syntax: TYPE type_name IS type_definition;
Beispiel: TYPE my_integer IS RANGE 0 TO 64;
TYPE my_state IS (S1, S2, HALT, WEITER);
USE CLAUSE: wird verwendet, um libraries und packages zu importieren.
Syntax: USE library_name.unit_name.item;
Beispiel: LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
VARIABLE DECLARATION: deklariert Variable und evt. einen Initialisierungswert
Syntax: [SHARED] VARIABLE variable_name_list : type [:=expression];
Beispiel: VARIABLE a, b, c : BIT;
VARIABLE count : INTEGER RANGE 0 TO 256;
160
CAE: VHDL
VARIABLE ASSIGNMENT: weist einer Variablen unmittelbar einen Wert zu.
Syntax: [label :] variable_name := expression;
Beispiel: y := a XOR b;
y := '1';
WAIT STATEMENT: ist eine sequentielle Anweisung in einem Prozess und unterbricht die
Weiterführung des Prozess.
Syntax: [label :] WAIT [ON sensitivity_list][UNTIL condition][FOR time_exp.];
Beispiel: WAIT UNTIL CLK'EVENT and CLK = '1';
WAIT ON S1 FOR 5 ns;
WHILE LOOP: Wird zur Implementierung von sich wiederholenden Operationen verwendet.
Syntax: [loop_label :] WHILE condition LOOP
sequential statements
END LOOP [loop_label];
Beispiel: WHILE (i <= 7) LOOP
IF i = 2 THEN
NEXT;
ELSE
fifo(i) <= (OTHERS => '0');
END IF;
i := i + 1;
END LOOP;
161
CAE: VHDL
5 Anhang C: Packages
C.1 Package STD.STANDARD
package STANDARD is
type BOOLEAN is (FALSE, TRUE);
type BIT is ('0', '1');
type CHARACTER IS (
NUL,SOH,STX,ETX,EOT,ENQ, ACK,BEL,
BS,HT,LF, VT,FF,CR,SO,SI,
DLE,DC1,DC2,DC3,DC4,NAK,SYN,ETB,
CAN,EM,SUB,ESC,FSP,GSP,RSP,USP,
' ','!','"','#','$','%','&','''',
'(',')','*','+',',','-','.','/',
'0','1','2','3','4','5','6','7',
'8','9',':',';','<','=','>','?',
'@','A','B','C','D','E','F','G',
'H','I','J','K','L','M','N','O',
'P','Q','R','S','T','U','V','W',
'X','Y','Z',''[','\',']','^','_',
'''','a','b','c','d','e','f','g',
'h','i','j','k','l','m','n','o',
'p','q','r','s','t','u','v','w',
'x','y','z','{','|','}','~',DEL);
type SEVERITY_LEVEL is (NOTE, WARNING, ERROR, FAILURE);
type INTEGER is range -2147483648 to 2147483647;
type REAL is range -1.7e38 to 1.7e38;
type TIME IS range -9223372036854775808 to 9223372036854775807
units
fs;-- femtosecond
ps= 1000 fs;-- picosecond
ns= 1000 ns;-- nanosecond
us= 1000 ns;-- microsecond
ms= 1000 us;-- millisecond
sec= 1000 ms;-- second
min= 60 sec;-- minute
hr= 60 min;-- hour
end units;
subtype DELAY_LENGTH is TIME range 0 fs to TIME'HIGH;
impure function NOW return DELAY_LENGTH;
subtype NATURAL is INTEGER range 0 to INTEGER'HIGH;
subtye POSITIVE is INTEGER range 1 to INTEGER'HIGH;
type
type
type
type
STRING is array (POSITIVE range <>) of CHARACTER;
BIT_VECTOR is array (NATURAL range <>) of BIT;
FILE_OPEN_KIND is (READ_MODE, WRITE_MODE, APPEND_MODE);
FILE_OPEN_STATUS is (OPEN_OK, STATUS_ERROR, .NAME_ERROR, MODE_ERROR);
attribute FOREIGN: STRING;
end STANDARD;
162
Anhang C: Packages
C.2 Package STD.TEXTIO
package TEXTIO is
type LINE is access STRING;
type TEXT is file of STRING;
type SIDE is (RIGHT, LEFT);
subtype WIDTH is NATURAL;
file INPUT: TEXT open READ_MODE is „STD_INPUT“;
file OUTPUT: TEXT open WRITE_MODE is „STD_OUTPUT“;
procedure READLINE (file F: TEXT; L: inout LINE);
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
procedure
READ
READ
READ
READ
READ
READ
READ
READ
READ
READ
READ
READ
READ
READ
READ
READ
(L:
(L:
(L:
(L:
(L:
(L:
(L:
(L:
(L:
(L:
(L:
(L:
(L:
(L:
(L:
(L:
inout
inout
inout
inout
inout
inout
inout
inout
inout
inout
inout
inout
inout
inout
inout
inout
LINE;
LINE;
LINE;
LINE;
LINE;
LINE;
LINE;
LINE;
LINE;
LINE;
LINE;
LINE;
LINE;
LINE;
LINE;
LINE;
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
VALUE:
procedure WRITELINE (FILE F: TEXT; L:
out
out
out
out
out
out
out
out
out
out
out
out
out
out
out
out
BIT; GOOD: out BOOLEAN);
BIT);
BIT_VECTOR; GOOD: out BOOLEAN);
BIT_VECTOR);
BOOLEAN; GOOD: out BOOLEAN);
BOOLEAN);
CHARACTER; GOOD: out BOOLEAN);
CHARACTER);
INTEGER; GOOD: out BOOLEAN);
INTEGER);
REAL; GOOD: out BOOLEAN);
REAL);
STRING; GOOD: out BOOLEAN);
STRING);
TIME; GOOD: out BOOLEAN);
TIME);
inout LINE);
procedure WRITE (L: inout LINE; VALUE: in BIT;
JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH := 0);
procedure WRITE (L: inout LINE; VALUE: in BIT_VECTOR;
JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH := 0);
procedure WRITE (L: inout LINE; VALUE: in BOOLEAN;
JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH := 0);
procedure WRITE (L: inout LINE; VALUE: in CHARACTER;
JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH := 0);
procedure WRITE (L: inout LINE; VALUE: in INTEGER;
JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH := 0);
procedure WRITE (L: inout LINE; VALUE: in REAL;
JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH := 0; DIGITS: in NATURAL:=
0);
procedure WRITE (L: inout LINE; VALUE: in STRING;
JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH := 0);
procedure WRITE (L: inout LINE; VALUE: in TIME;
JUSTIFIED: in SIDE:= RIGHT; FIELD: in WIDTH := 0; UNIT: in TIME:= ns);
--
function ENDFILE (file F: TEXT) return BOOLEAN;
end TEXTIO;
163
CAE: VHDL
C.3 Package IEEE.STD_LOGIC_1164
(Standard Multivalue Logic System IEEE Std 1164-1993)
PACKAGE std_logic_1164 IS
TYPE std_ulogic IS (
'U',-- Uninitialized
'X',-- Forcing Unknown
'0',-- Forcing 0
'1',-- Forcing 1
'Z',-- High Impedance
'W',-- Weak Unknown
'L',-- Weak 0
'H',-- Weak 1
'-'-- Don't care
);
TYPE std_ulogic_vector IS ARRAY ( NATURAL RANGE <> ) OF std_ulogic;
FUNCTION resolved ( s : std_ulogic_vector ) RETURN std_ulogic;
SUBTYPE std_logic IS resolved std_ulogic;
TYPE std_logic_vector IS ARRAY ( NATURAL RANGE <>) OF std_logic;
SUBTYPE
SUBTYPE
SUBTYPE
SUBTYPE
X01 IS resolved std_ulogic RANGE 'X' TO '1';
X01Z IS resolved std_ulogic RANGE 'X' TO 'Z';
UX01 IS resolved std_ulogic RANGE 'U' TO '1';
UX01Z IS resolved std_ulogic RANGE 'U' TO 'Z';
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
"and"
"nand"
"or"
"nor"
"xor"
"xnor"
"not"
(
(
(
(
(
(
(
l
l
l
l
l
l
l
:
:
:
:
:
:
:
std_ulogic; r : std_ulogic
std_ulogic; r : std_ulogic
std_ulogic; r : std_ulogic
std_ulogic; r : std_ulogic
std_ulogic; r : std_ulogic
std_ulogic; r : std_ulogic
std_ulogic ) RETURN UX01;
)
)
)
)
)
)
RETURN
RETURN
RETURN
RETURN
RETURN
RETURN
UX01;
UX01;
UX01;
UX01;
UX01;
UX01;
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
"and"
"and"
"nand"
"nand"
"or"
"or"
"nor"
"nor"
"xor"
"xor"
"xnor"
"xnor"
"not"
"not"
(
(
(
(
(
(
(
(
(
(
(
(
(
(
l, r : std_logic_vector ) RETURN std_logic_vector;
l, r : std_ulogic_vector ) RETURN std_ulogic_vector;
l, r : std_logic_vector ) RETURN std_logic_vector;
l, r : std_ulogic_vector ) RETURN std_ulogic_vector;
l, r : std_logic_vector ) RETURN std_logic_vector;
l, r : std_ulogic_vector ) RETURN std_ulogic_vector;
l, r : std_logic_vector ) RETURN std_logic_vector;
l, r : std_ulogic_vector ) RETURN std_ulogic_vector;
l, r : std_logic_vector ) RETURN std_logic_vector;
l, r : std_ulogic_vector ) RETURN std_ulogic_vector;
l, r : std_logic_vector ) RETURN std_logic_vector;
l, r : std_ulogic_vector ) RETURN std_ulogic_vector;
l : std_logic_vector ) RETURN std_logic_vector;
l : std_ulogic_vector ) RETURN std_ulogic_vector;
FUNCTION To_bit
( s : std_ulogic; xmap : BIT := '0') RETURN BIT;
FUNCTION To_bitvector ( s : std_logic_vector ; xmap : BIT := '0') RETURN
BIT_VECTOR;
FUNCTION To_bitvector ( s : std_ulogic_vector; xmap : BIT := '0') RETURN
BIT_VECTOR;
FUNCTION To_StdULogic
( b : BIT
) RETURN std_ulogic;
FUNCTION To_StdLogicVector ( b : BIT_VECTOR) RETURN std_logic_vector;
164
Anhang C: Packages
FUNCTION To_StdLogicVector ( s : std_ulogic_vector ) RETURN std_logic_vector;
FUNCTION To_StdULogicVector ( b : BIT_VECTOR) RETURN std_ulogic_vector;
FUNCTION To_StdULogicVector ( s : std_logic_vector ) RETURN
std_ulogic_vector;
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
To_X01
To_X01
To_X01
To_X01
To_X01
To_X01
(
(
(
(
(
(
s
s
s
b
b
b
:
:
:
:
:
:
std_logic_vector ) RETURN std_logic_vector;
std_ulogic_vector ) RETURN std_ulogic_vector;
std_ulogic) RETURN X01;
BIT_VECTOR) RETURN std_logic_vector;
BIT_VECTOR) RETURN std_ulogic_vector;
BIT
) RETURN X01;
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
To_X01Z
To_X01Z
To_X01Z
To_X01Z
To_X01Z
To_X01Z
(
(
(
(
(
(
s
s
s
b
b
b
:
:
:
:
:
:
std_logic_vector ) RETURN std_logic_vector;
std_ulogic_vector ) RETURN std_ulogic_vector;
std_ulogic) RETURN X01Z;
BIT_VECTOR) RETURN std_logic_vector;
BIT_VECTOR) RETURN std_ulogic_vector;
BIT
) RETURN X01Z;
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
FUNCTION
To_UX01
To_UX01
To_UX01
To_UX01
To_UX01
To_UX01
(
(
(
(
(
(
s
s
s
b
b
b
:
:
:
:
:
:
std_logic_vector ) RETURN std_logic_vector;
std_ulogic_vector ) RETURN std_ulogic_vector;
std_ulogic) RETURN UX01;
BIT_VECTOR) RETURN std_logic_vector;
BIT_VECTOR) RETURN std_ulogic_vector;
BIT
) RETURN UX01;
FUNCTION rising_edge (SIGNAL s : std_ulogic) RETURN BOOLEAN;
FUNCTION falling_edge (SIGNAL s : std_ulogic) RETURN BOOLEAN;
FUNCTION Is_X ( s : std_ulogic_vector ) RETURN BOOLEAN;
FUNCTION Is_X ( s : std_logic_vector ) RETURN BOOLEAN;
FUNCTION Is_X ( s : std_ulogic) RETURN BOOLEAN;
END std_logic_1164;
165
CAE: VHDL
C.4 Package IEEE.NUMERIC_STD
(Synthesis Package IEEE Std 1076.3-1997)
type UNSIGNED is array (NATURAL range <>) of STD_LOGIC;
type SIGNED is array (NATURAL range <>) of STD_LOGIC;
Arithmetische Operatoren:
Operator
Typ des Operanden
Typ des Ergebnisses
ARG
abs(ARG)
SIGNED
Operator
(unär)
Typ des Argumentes
- ARG
SIGNED
Operatoren
(binär)
Typ der Operanden
L+R
L-R
L*R
L / Ra
L rem Ra
L mod Ra
a.
SIGNED(ARG'LENGTH-1 downto 0)
Typ des Ergebnisses
ARG
L
SIGNED(ARG'LENGTH-1 downto 0)
R
Typ des Ergebnisses
UNSIGNED
UNSIGNED
UNSIGNED(MAX(L'LENGTH,R'LENGTH)-1
downto 0)
SIGNED
SIGNED
SIGNED(MAX(L'LENGTH,R'LENGTH)-1 downto
0)
UNSIGNED
NATURAL
UNSIGNED(L'LENGTH-1 downto 0)
NATURAL
UNSIGNED
UNSIGNED(R'LENGTH-1 downto 0)
INTEGER
SIGNED
SIGNED(R'LENGTH-1 downto 0)
SIGNED
INTEGER
SIGNED(L'LENGTH-1 downto 0)
Einschränkung für die Synthese: R ist konstant und eine 2er Potenz
Vergleichsoperatoren:
Operatoren
Typ der Operanden
L
L>R
L<R
L >= R
L <= R
L=R
L /= R
166
R
Typ des Ergebnisses
UNSIGNED
UNSIGNED
BOOLEAN
SIGNED
SIGNED
BOOLEAN
UNSIGNED
NATURAL
BOOLEAN
NATURAL
UNSIGNED
BOOLEAN
INTEGER
SIGNED
BOOLEAN
SIGNED
INTEGER
BOOLEAN
Anhang C: Packages
Logische Operatoren:
Operator
Typ des Operanden
Typ des Ergebnisses
L
not L
Operatoren
UNSIGNED
UNSIGNED(L'LENGTH-1 downto 0)
SIGNED
SIGNED(L'LENGTH-1 downto 0)
Typ der Operanden
L
L and R
L or R
L nand R
L nor R
L xor R
L xnor Ra
a.
Typ des Ergebnisses
R
UNSIGNED
UNSIGNED
UNSIGNED(L'LENGTH-1 downto 0)
SIGNED
SIGNED
SIGNED(L'LENGTH-1 downto 0)
xnor ist nicht kompatibel mit IEEE Std 1076-1987
Rotationsoperatoren:
Operatoren
Typ der Operanden
ARG
ARG sll COUNT
ARG srl COUNT
ARG rol COUNT
ARG ror COUNT
COUNT
Typ des Ergebnisses
UNSIGNED
INTEGER
UNSIGNED(ARG'LENGTH-1 downto
0)
SIGNED
INTEGER
SIGNED(ARG'LENGTH-1 downto 0)
Rotationsfunktionen:
Funktionen
Typ der Argumente
ARG
ROTATE_LEFT
(ARG,COUNT)
ROTATE_RIGHT
(ARG,COUNT)
COUNT
Typ des Rückgabewertes
UNSIGNED
NATURAL
UNSIGNED(ARG'LENGTH-1 downto
0)
SIGNED
NATURAL
SIGNED(ARG'LENGTH-1 downto 0)
Schiebefunktionen:
Funktionen
Typ der Argumente
ARG
COUNT
Typ des Rückgabewertes
SHIFT_LEFT
(ARG,COUNT)
UNSIGNED
NATURAL
UNSIGNED(ARG'LENGTH-1 downto
0)
SHIFT_RIGHT
(ARG,COUNT)
SIGNED
NATURAL
SIGNED(ARG'LENGTH-1 downto 0)
167
CAE: VHDL
Grössenanpassung von Vektoren:
Typ der Argumente
Funktion
ARG
RESIZE
(ARG,NEW_SIZE)
NEW_SIZE
Typ des Rückgabewertes
UNSIGNED
NATURAL
UNSIGNED(NEW_SIZE-1 downto 0)
SIGNED
NATURAL
SIGNED(NEW_SIZE-1 downto 0)
Konvertierungsfunktionen:
Typ des Argumentes
Funktion
Typ des Rückgabewertes
ARG
TO_INTEGER
(ARG)
UNSIGNED
NATURAL
SIGNED
INTEGER
Typ des Argumentes
Funktion
ARG
TO_UNSIGNED
(ARG,SIZE)
SIZE
NATURAL
NATURAL
UNSIGNED(SIZE-1 downto 0)
Typ des Argumentes
Funktion
ARG
TO_SIGNED
(ARG,SIZE)
Typ des Rückgabewertes
SIZE
INTEGER
NATURAL
Typ des Rückgabewertes
SIGNED(SIZE-1 downto 0)
Elementvergleich (verarbeitet Don’t Cares, '-'):
Typ der Argumente
Funktion
Typ des Rückgabewertes
L,R
STD_MATCH
(L,R)
STD_ULOGIC
BOOLEAN
STD_LOGIC_VECTOR
BOOLEAN
STD_ULOGIC_VECTOR
BOOLEAN
UNSIGNED
BOOLEAN
SIGNED
BOOLEAN
Übersetzungsfunktionen:
Funktionen
Typ der Argumente
S
TO_01
(S,XMAP)
168
XMAP
Subtype des Rückgabewertes
UNSIGNED
STD_LOGIC
UNSIGNED(S'RANGE)
SIGNED
STD_LOGIC
SIGNED(S'RANGE)
Was this manual useful for you? yes no
Thank you for your participation!

* Your assessment is very important for improving the work of artificial intelligence, which forms the content of this project

Download PDF

advertisement