240 16 1MB
German Pages 229 Year 2010
FPGA-Design mit Verilog von Dipl.-Ing. (FH) Harald Flügel
Oldenbourg Verlag München
Dipl.-Ing. (FH) Harald Flügel studierte Nachrichtentechnik an der Fachhochschule Karlsruhe. Danach arbeitete er über 20 Jahre mit unterschiedlichen Zuständigkeiten in der Entwicklung eingebetteter Systeme mit dem Schwerpunkt auf FPGA-Design. Derzeit arbeitet er bei der Firma Arrow, einem Distributor für elektronische Bauteile. Dort ist er als Technical Marketing Manager für programmierbare Logik zuständig.
Bibliografische Information der Deutschen Nationalbibliothek Die Deutsche Nationalbibliothek verzeichnet diese Publikation in der Deutschen Nationalbibliografie; detaillierte bibliografische Daten sind im Internet über abrufbar.
© 2010 Oldenbourg Wissenschaftsverlag GmbH Rosenheimer Straße 145, D-81671 München Telefon: (089) 45051-0 oldenbourg.de Das Werk einschließlich aller Abbildungen ist urheberrechtlich geschützt. Jede Verwertung außerhalb der Grenzen des Urheberrechtsgesetzes ist ohne Zustimmung des Verlages unzulässig und strafbar. Das gilt insbesondere für Vervielfältigungen, Übersetzungen, Mikroverfilmungen und die Einspeicherung und Bearbeitung in elektronischen Systemen. Lektorat: Anton Schmid Herstellung: Anna Grosser Coverentwurf: Kochan & Partner, München Gedruckt auf säure- und chlorfreiem Papier Gesamtherstellung: Grafik + Druck GmbH, München ISBN 978-3-486-59234-4
Vorwort Wofür braucht man eine weitere Hardwarebeschreibungssprache neben VHDL? Gibt es Aufgaben, die sich in VHDL nicht erledigen lassen? Fehlt etwas Wichtiges in VHDL? Diese Fragen mag man sich anlässlich der Vorstellung dieses Buches schon stellen. Und sie lassen sich kurz und knapp beantworten: VHDL ist ausreichend. Diese Sprache ist für die Beschreibung eines FPGAs und der zugehörigen Testumgebung gut einsetzbar. Wer VHDL beherrscht, ausreichend Entwicklungserfahrung damit hat und mit der Sprache zufrieden ist, der möge sie weiter benutzen. Es gibt allerdings eine Reihe von FPGA-Entwicklern, die mit VHDL so ihre Schwierigkeiten haben, denen VHDL etwas sperrig vorkommt, langatmig und sehr formal. Diese Entwickler haben eine Alternative: Verilog. Diese Sprache liest sich wesentlich einfacher als VHDL, ist leichter verständlich und deutlich kompakter. Nicht umsonst ist Verilog weltweit die Nummer Eins bei den Hardwarebeschreibungssprachen. Die beiden Sprachen Verilog und VHDL entstanden ungefähr zur gleichen Zeit. Während Verilog mit dem Ziel der Simulation von digitalen Schaltungen entworfen wurde, war der Ansatz bei VHDL etwas umfangreicher gefasst. Das Ziel war der Entwurf einer Sprache zur Simulation von Systemen jedwelcher Art. So gibt es in VHDL selbst zunächst gar kein Zahlensystem. Erst dadurch, dass man bestimmte Bibliotheken zur Simulation bzw. Synthese benutzt, kann man Zahlenwerte oder elektrische Signale überhaupt erfassen. Die Tatsache, dass Verilog und VHDL nahezu zeitgleich entstanden und bis heute konkurrieren, erinnert etwas an die Konkurrenz der beiden Programmiersprachen C und Pascal in den 80er-Jahren des vergangenen Jahrhunderts. Es gibt auch einige Parallelen zwischen den Programmiersprachen und den Hardwarebeschreibungssprachen. Bei der Syntax, den Schlüsselwörtern und den Operatoren gibt es eine große Ähnlichkeit von Pascal und VHDL einerseits sowie von C und Verilog andererseits. Pascal und VHDL sind im Vergleich mit den jeweiligen Konkurrenten klar die methodischeren und strengeren Sprachen. Bei den Programmiersprachen konnte sich diese feste Strukturierung bei den damals vielleicht etwas chaotischen Programmierern nicht recht durchsetzen, denn Pascal spielt heute kaum noch eine Rolle. Und auch der Durchbruch von VHDL kam nicht daher, dass die Sprache bei den Programmierern besondern beliebt war. Die Verwendung der Sprache VHDL war einige Zeit lang zwingende Voraussetzung für den Erhalt von Entwicklungsaufträgen für ASIC-Designs bei einigen großen Auftraggebern. Später wurde VHDL vor allem in Europa von den Hochschulen als die methodisch bessere betrachtet und für die Lehre verwendet. Viele große Unternehmen in Europa haben sich dieser Wahl angeschlossen, und so ist Europa die Hochburg von VDHL geworden. Im Zeitalter der Globalisierung lösen sich regionale Grenzen aber
VI
Vorwort
mehr und mehr auf, und so finden sich auch europäische Ingenieure immer öfter mit Verilog konfrontiert. Dieses Buch ist kein Referenzhandbuch für die Sprache Verilog in deutscher Sprache. Viele Aspekte von Verilog werden nicht erwähnt oder nur kurz angerissen. Das betrifft in erster Linie diejenigen Elemente der Sprache, die bei einem FPGA-Design keine Rolle spielen. Statt dessen wurde das Buch als Lehrbuch konzipiert, und zwar als Lehrbuch für FPGADesign mit der Sprache Verilog. Es beinhaltet daher auch methodische Aspekte wie z.B. den Entwicklungsablauf mit Simulation und Synthese sowie Entwurfsrichtlinien für kombinatorische und sequenzielle Logik. Als weiterführende Literatur kann das Verilog Language Reference Manual empfohlen werden, am besten zunächst in der Originalversion von 1995. Dieses Manual findet man vielfach frei zum Download im Internet. Eine Suche nach "OVI LRM" führt meist zu einer ganze Reihe von Treffern.
Inhalt Vorwort
V
1
Einleitung
1
1.1
Was ist ein FPGA? ..................................................................................................... 1
1.2
Evolution programmierbarer Logikbausteine ............................................................. 1
1.3
Evolution von Verilog ................................................................................................ 3
1.4 1.4.1 1.4.2 1.4.3 1.4.4
Programme und Modelle ............................................................................................ 5 Programme ................................................................................................................. 6 Nicht-synthesefähige Modelle .................................................................................... 7 Synthesefähige Modelle ............................................................................................. 7 Sprachumfang für Simulation und Synthese .............................................................. 8
1.5
Abstraktionsebenen .................................................................................................... 8
1.6
Aufbau des Buchs....................................................................................................... 9
2
Programmelemente
2.1
Leerzeichen und Kommentar ................................................................................... 11
2.2 2.2.1
Ganze Zahlen............................................................................................................ 12 Sonderzeichen in Zahlen .......................................................................................... 12
2.3
Fließkommazahlen ................................................................................................... 13
2.4 2.4.1
Zeichenketten ........................................................................................................... 13 Sonderzeichen in Zeichenketten ............................................................................... 14
2.5 2.5.1
Bezeichner ................................................................................................................ 14 Sonderzeichen in Bezeichnern ................................................................................. 14
2.6
Schlüsselwörter und Systemnamen .......................................................................... 15
2.7
Operatoren ................................................................................................................ 15
2.8
Compilerdirektiven und Makros............................................................................... 15
3
Datentypen
3.1
Wertebereich ............................................................................................................ 17
11
17
VIII
Inhalt
3.2
Signalnetz ................................................................................................................. 18
3.3
Register .................................................................................................................... 20
3.4
Vektoren ................................................................................................................... 20
3.5
Implizite Signaldeklaration ...................................................................................... 24
3.6
Speicher.................................................................................................................... 25
3.7 3.7.1 3.7.2 3.7.3
Weitere Datentypen.................................................................................................. 26 Integer ...................................................................................................................... 26 Time ......................................................................................................................... 26 Real .......................................................................................................................... 26
3.8
Parameter ................................................................................................................. 27
4
Ausdrücke
4.1
Arithmetische Operatoren ........................................................................................ 30
4.2
Vergleich nach Größe .............................................................................................. 31
4.3
Vergleich auf Gleichheit .......................................................................................... 32
4.4
Logische Operatoren ................................................................................................ 32
4.5
Bit-weise Operatoren ............................................................................................... 34
4.6
Reduktion ................................................................................................................. 35
4.7
Verschieben.............................................................................................................. 35
4.8
Bedingung ................................................................................................................ 36
4.9
Aneinanderreihung ................................................................................................... 36
4.10
Wiederholung ........................................................................................................... 37
4.11
Operandengröße ....................................................................................................... 37
4.12
Priorität .................................................................................................................... 38
4.13 4.13.1 4.13.2 4.13.3
Zuweisungen ............................................................................................................ 39 Dauerhafte Zuweisungen ......................................................................................... 39 Prozedurale Zuweisungen ........................................................................................ 39 Besonderheiten bei der Synthese.............................................................................. 40
5
Verhaltensbeschreibung
5.1
Prozesse.................................................................................................................... 43
5.2 5.2.1 5.2.2 5.2.3 5.2.4
Kontrollstrukturen .................................................................................................... 47 if, else ....................................................................................................................... 47 case........................................................................................................................... 49 casez, casex .............................................................................................................. 51 while......................................................................................................................... 52
29
43
Inhalt
IX
5.2.5 5.2.6 5.2.7 5.2.8
wait ........................................................................................................................... 52 for ............................................................................................................................. 53 forever ...................................................................................................................... 53 repeat ........................................................................................................................ 53
5.3
Blockierende und nicht-blockierende Zuweisung .................................................... 54
5.4
Sequenzielle und parallele Blöcke............................................................................ 55
5.5
Ereignisbasierte Simulation ...................................................................................... 56
6
Hierarchie
6.1
Definition ................................................................................................................. 59
6.2
Instanzierung ............................................................................................................ 61
6.3
Parameter.................................................................................................................. 63
6.4
Skalierbare Modelle ................................................................................................. 67
6.5
Modul der obersten Ebene ........................................................................................ 68
6.6
Simulation Top und Synthesis Top .......................................................................... 69
7
Tasks und Funktionen
7.1
Funktionen................................................................................................................ 72
7.2
Tasks ........................................................................................................................ 75
8
Kombinatorische Logik
8.1
... modelliert mit Gattern .......................................................................................... 79
8.2
... modelliert mit Schaltern ....................................................................................... 80
8.3
... modelliert mit Zuweisungen ................................................................................. 81
9
Sequenzielle Logik
9.1
Einfache Register ..................................................................................................... 91
9.2
Zähler ....................................................................................................................... 93
9.3
Schieberegister ......................................................................................................... 96
9.4
Taktdomänen ............................................................................................................ 97
9.5
Anfangszustand ...................................................................................................... 100
9.6
Externe Takte ......................................................................................................... 102
9.7 9.7.1 9.7.2 9.7.3
Empfehlungen für sequenzielle Logik.................................................................... 105 Kombinatorischer Takt ........................................................................................... 105 Sequenzieller Takt .................................................................................................. 106 Latches ................................................................................................................... 106
59
71
79
89
X
Inhalt
10
Zustandsautomaten
109
10.1
Automatentypen ..................................................................................................... 109
10.2
Zustandskodierung ................................................................................................. 110
10.3
Beschreibung eines Zustandsautomaten ................................................................. 111
10.4
Zusammenarbeit von Automat und Zähler ............................................................. 115
10.5 10.5.1 10.5.2 10.5.3 10.5.4 10.5.5
Tricks und Kniffe ................................................................................................... 118 One-Hot-Kodierung ............................................................................................... 118 case (1) ................................................................................................................... 120 Sonderzustand ........................................................................................................ 121 Eingangssignale ..................................................................................................... 122 Reset ....................................................................................................................... 122
11
Compilerdirektiven, Makros und Meta-Informationen
11.1
`define .................................................................................................................... 125
11.2
`ifdef, `else, `endif .................................................................................................. 126
11.3
`include .................................................................................................................. 126
11.4
`timescale ............................................................................................................... 127
11.5
Gültigkeit von Compilerdirektiven und Makros .................................................... 127
11.6
Toolspezifisch gesetzte Merker .............................................................................. 127
11.7 11.7.1 11.7.2 11.7.3 11.7.4 11.7.5
Meta-Informationen ............................................................................................... 128 full_case ................................................................................................................. 130 parallel_case ........................................................................................................... 130 translate_off / translate_on ..................................................................................... 130 keep ........................................................................................................................ 131 Attribute ................................................................................................................. 131
12
Vorgefertigte Module
12.1
FIFO ....................................................................................................................... 135
12.2
Multiplikation......................................................................................................... 140
12.3
Division von Fließkommazahlen ........................................................................... 142
13
Entwicklungsablauf
13.1
Simulator ................................................................................................................ 147
13.2
Synthese ................................................................................................................. 147
13.3
Timing-Analyse ..................................................................................................... 150
13.4
Verzeichnisse ......................................................................................................... 151
13.5
RTL und gate level ................................................................................................. 152
125
133
145
Inhalt
XI
13.6
Bidirektionale Signale ............................................................................................ 153
14
Systemtasks und -funktionen
14.1 14.1.1 14.1.2 14.1.3 14.1.4 14.1.5 14.1.6 14.1.7
$display und $write ................................................................................................ 155 Spezielle Druckzeichen .......................................................................................... 156 Ausgabeformate ..................................................................................................... 156 Größe der Datenausgabe ........................................................................................ 158 X und Z .................................................................................................................. 158 Signalstärke ............................................................................................................ 159 Hierarchischer Name .............................................................................................. 159 Zeichenkette ........................................................................................................... 159
14.2
$strobe .................................................................................................................... 159
14.3
$monitor ................................................................................................................. 160
14.4
$time ....................................................................................................................... 160
14.5
$realtime ................................................................................................................. 161
14.6
$stime ..................................................................................................................... 161
14.7
$printtimescale ....................................................................................................... 161
14.8
$timeformat ............................................................................................................ 162
14.9
$stop ....................................................................................................................... 163
14.10
$finish ..................................................................................................................... 163
14.11
$rtoi und $itor ......................................................................................................... 164
14.12
$realtobits und $bitstoreal ...................................................................................... 164
15
Dateien
15.1
Dateiausgabe .......................................................................................................... 165
15.2
Dateieingabe ........................................................................................................... 166
16
Beispiele
16.1
Ansteuerung einer 7-Segment-LED-Anzeige ......................................................... 170
16.2
Peripheriebaustein .................................................................................................. 177
16.3
Manchester-Dekoder .............................................................................................. 183
16.4
Kreuzkorrelation..................................................................................................... 190
17
Konventionen
17.1
Signalnamen ........................................................................................................... 199
17.2
Variablen von Testprogrammen ............................................................................. 200
17.3
Kommentare ........................................................................................................... 200
155
165
169
199
XII
Inhalt
17.4
Hierarchie ............................................................................................................... 201
17.5
Formatierung .......................................................................................................... 201
18
Nicht erwähnte Eigenschaften
18.1
Besondere Schlüsselwörter .................................................................................... 203
18.2
scalared und vectored ............................................................................................. 203
18.3
Modellierung mit Gattern und Schaltern ................................................................ 204
18.4
Signalstärken .......................................................................................................... 204
18.5
UDP........................................................................................................................ 204
18.6
Prozedurale dauerhafte Zuweisungen .................................................................... 204
18.7
Makromodule ......................................................................................................... 205
18.8
Port Collapsing ....................................................................................................... 206
18.9
Benannte Blöcke .................................................................................................... 206
18.10
Vollständige hierarchische Namen......................................................................... 207
18.11
Geltungsbereich ..................................................................................................... 207
18.12
Verzögerungsanweisung ........................................................................................ 208
18.13
Verzögerung ........................................................................................................... 208
18.14
Module path delay (specify)................................................................................... 209
18.15
Timing check und Notifier ..................................................................................... 210
18.16
Compilerdirektiven ................................................................................................ 210
18.17
Disabling Named Blocks and Tasks....................................................................... 211
18.18
PLI ......................................................................................................................... 211
18.19
Design Management .............................................................................................. 211
19
Literaturverzeichnis
213
20
Sachregister
215
203
1
Einleitung
1.1
Was ist ein FPGA?
Die vier Buchstaben FPGA sind die Abkürzung der Begriffs „Field Programmable Gate Array“. „Field Programmable“ soll dabei in etwa „draußen programmierbar“ bedeuten, und dieses „draußen“ ist alles außerhalb der Fertigungsstätte des FPGAs. Damit ist gemeint, dass bei einem FPGA, im Gegensatz zu vielen anderen elektronischen Schaltkreisen, die logische Funktion nicht bereits bei der Fertigung festgelegt wird, sondern eben erst draußen, wenn das FPGA von einem Anwender in dessen Gerät verbaut wird. Der zweite Teil der Abkürzung, „Gate Array“, bezeichnet eine Gattung von integrierten Schaltungen, bei der aus ein und dem selben Master-Chip unterschiedliche ICs hergestellt werden. So kann man die Kosten für die teuren Fotomasken für die Herstellung des Master-Chips auf mehrere ICs umlegen. Dieser Gedanke der Kostenteilung ist letztlich die Grundlage für alle Arten programmierbarer Logikbausteine. Der Halbleiterhersteller fertigt diese Bausteine in großer Stückzahl und damit kostengünstig. In ihrem Inneren befinden sich frei programmierbare Logikelemente, sodass der eine Entwickler damit vielleicht eine Schaltung zur schnellen Erfassung physikalischer Größen baut und der andere eine Echounterdrückung für eine Übertragungsstrecke. Die Bausteine sind beim Kauf zunächst leer wie ein DVD-Rohling, es kommt darauf an, was man in sie hineinprogrammiert.
1.2
Evolution programmierbarer Logikbausteine
Programmierbare Logikbausteine gibt es etwa seit 1975. Damals wurden die meisten digitalen Schaltungen noch mit TTL-Standardbausteinen aufgebaut. Die SN74-TTL-Familie von Texas Instruments und anderen Herstellern enthielt eine Vielzahl unterschiedlicher Logikbausteine, aus denen die Entwickler die geeigneten Bausteine aussuchen und richtig miteinander verbinden mussten. Um komplexere logische Ausdrücke mit solchen Standard-ICs zu ermitteln, waren im Allgemeinen mehrere Bausteine nötig. Im Gegensatz zu diesen Standardbausteinen boten die damaligen PLDs (programmable logic device, programmierbarer Logikbaustein) die Möglichkeit, komplexere logische Ausdrücke in einem einzelnen Baustein zu ermitteln. Damit waren deutlich kompaktere Baugruppen
2
1 Einleitung
möglich. Die Bausteine besaßen etwa 10 bis 20 Eingangssignale und maximal 10 Ausgangssignale, wahlweise mit D-Flipflop oder ohne. Allerdings waren die Bausteine recht teuer, und sie verbrauchten deutlich mehr Strom als die Standardbausteine. Die Entwicklungsmethoden waren damals recht rudimentär. Es gab dedizierte Programmiergeräte von der Größe eines PCs und größer, die über eine Tastatur und einen Bildschirm verfügten. Dort konnte man die gewünschten logischen Funktionen direkt eintippen. Die Syntax sah dabei etwa so aus: /Y15 = X2 & X3 & /X4 + X5 & /X6
Das bedeutete: Das Ausgangssignal an Pin 15 geht auf low wenn Pin 2 und Pin 3 high sind und Pin 4 low ist oder wenn Pin 5 high ist und Pin 6 low. Mehr Komfort gab es in dieser Zeit nicht. Zustandsautomaten mussten von Hand entworfen und logische Ausdrücke von Hand optimiert werden. Das Ausfüllen von Karnaugh-Veitch-Diagrammen und das Aufstellen der Produktterme waren das tägliche Brot der Hardwareentwickler. Und wenn die logischen Funktionen aller Ausgangssignale definiert waren, dann wurde der Baustein in einen Sockel des Programmiergeräts gesteckt und programmiert. Die umständliche Art der Entwicklung, der hohe Preis der Bausteine und der Entwicklungssysteme und die Tatsache, dass die Bausteine im Allgemeinen nur einmal programmierbar waren, standen einer schnellen Verbreitung programmierbarer Logikbausteine im Weg. Die Bausteine fanden zwar ihren Platz in der Hardwareentwicklung, dominierend war er allerdings zunächst nicht. Parallel zu der Verbreitung der programmierbaren Bausteine entwickelten sich mehrere Programmiersprachen dafür. Zu nennen sind hier, ohne Anspruch auf Vollständigkeit, PALASM, ABEL, CUPL und LOGE (später LOG/iC). Diese proprietären Sprachen zielten primär auf eine komfortablere Syntax für die Eingabe der Logik, orientierten sich aber stark an der damals recht festen Und-Oder-Struktur der PLDs. Für die Sprachen gab es Entwicklungsprogramme, die auf dem PC liefen, und aus den Beschreibungen die Programmierdaten für die PLDs berechneten. Die Programmiergeräte waren jetzt wirklich nur noch zur Programmierung da, nicht mehr, um die logische Funktion zu bestimmen. Die Fortschritte der Halbleitertechnologie ermöglichten im Laufe der Zeit programmierbare Bausteine mit deutlich mehr Logikressourcen. Im Jahr 1985 erblickte das erste FPGA das Licht der Welt. Im Gegensatz zu den frühen PLD enthielt es mehr Logikelemente (64) als Pins, wobei die Fähigkeiten dieser Elemente beschränkt war, sodass man mehrere solcher Elemente hintereinanderschalten musste, um einen komplexeren logischen Ausdruck aus mehr als drei Eingangssignalen zu ermitteln. Einen weiteren wesentlichen Unterschied gab es noch zwischen PLD und FPGA: Die PLD wurden wirklich programmiert, sie behielten die Programmierung dauerhaft bei. FPGA hingegen speicherten die Programmierung nicht. Sie mussten jedes Mal nach dem Einschalten neu konfiguriert werden. Heutige FPGA sind zwar prinzipiell ähnlich aufgebaut wie die ersten Vertreter dieser Gattung, verfügen aber über wesentlich mehr Logikelemente. Als Vergleichsmaßstab hat sich
1.3 Evolution von Verilog
3
dabei über die Jahre ein Logikelement mit vier Eingängen und einem Ausgang, bei Bedarf mit einem D-Flipflop, herauskristallisiert. Selbst das kleinste Mitglied einer aktuellen Bausteinfamilie verfügt über mehrere Tausend solcher Logikelemente. Nach oben scheinen keine Grenzen gesetzt, 100.000 solcher Elemente sind keine Seltenheit mehr und die Million ist in Sichtweite (2010). FPGA-Bausteine sind zu einem Technologietreiber geworden, wie es PC-Prozessoren schon lange sind. Die neuesten Fertigungstechnologien werden mit solchen Bausteinen aus der Taufe gehoben, und die Anzahl an Logikelementen, die sie beinhalten, wächst und wächst. War es Anfang der 90er-Jahre noch unvorstellbar, einen ganzen Mikrocontroller in ein FPGA zu bringen, so war es ca. ab dem Jahr 2000 kein technisches Problem mehr, sondern nur zu teuer. Heute belegt ein Mikrocontroller nur noch einen kleinen Anteil eines FPGA, selbst des kleinsten Exemplars. Wegen dieser Stärken hat die programmierbare Logik heute einen ganz anderen Stellenwert. Früher wurden nur Standard-IC mit einfachen Gatterschaltungen durch programmierbare Logikbausteine ersetzt, heute dagegen werden FPGA anstelle von kompletten ASIC oder Mikrocontrollern eingesetzt. Die modernen FPGA beinhalten aber mehr als nur eine große Menge gleichartiger Logikelemente. Neben diesen Elementen gibt es in den meisten Bausteinfamilien: RAM PLL Multiplizierer Mit Hilfe solcher zusätzlichen Module kann man heute komplette digitale Systeme in ein FPGA integrieren. Aus den RAM-Modulen kann man Dual-Port-Speicher oder FIFO gestalten, und dank der PLL kann man die einzelnen Funktionsblöcke eines FPGA-Designs mit unterschiedlichen Takten laufen lassen. Die Multiplizierer haben vor allem den Zweck, das Produkt zweier Binärzahlen schneller zu berechnen, als es eine synthetisierte Gatterlogik auf der Basis der Logikelemente könnte. Mit den Fähigkeiten der Bausteine wuchsen auch die Anforderungen an die Entwicklungsmethode. Die alten PLD-Entwicklungssysteme gibt es nicht mehr, und die proprietären Sprachen sind nahezu vollständig ausgestorben. Lediglich VHDL und Verilog sind für die Simulation und Synthese von FPGA-Designs übrig geblieben.
1.3
Evolution von Verilog
Die Sprache Verilog Hardware Description Language wurde in den Jahren 1983 und 1984 von Phil Moorby bei Automated Integrated Design Systems (später Gateway Design Automation) als Simulationssprache entworfen. Es gab damals nur ein Produkt, das mit dieser Sprache arbeitete, nämlich den Logiksimulator Verilog-XL. Im Jahr 1989 wurde Gateway von Cadence übernommen. Ein Jahr später entschied man bei Cadence, Verilog HDL und das zugehörige Programmierinterface PLI öffentlich freizugeben. Open Verilog International
4
1 Einleitung
(OVI) wurde mit den Ziel gegründet, die Verbreitung von Verilog zu fördern und einen offenen Standard zu definieren. Cadence übergab OVI die Quelldokumente des Handbuchs von Cadence Verilog-XL, und dieses Handbuch wurde das erste Referenzhandbuch der Sprache Verilog. Im Jahre 1993 wurde die Version 2.0 des Referenzhandbuchs von OVI verabschiedet, in dem es gegenüber der ersten Version nur wenige Erweiterungen gab. Dieses Handbuch wurde beim Institute of Electrical and Electronics Engineers (IEEE) zur Standardisierung eingereicht. Das IEEE gründete eine Arbeitsgruppe zur Ausarbeitung, und im Jahre 1995 wurde der offizielle IEEE-Standard 1364-1995 verabschiedet. Das Ziel war dabei nicht, weitere Erweiterungen einzubringen, sondern den damaligen Stand der Sprache als Standard festzuschreiben. Bereits zwei Jahre später begann die Arbeit an der ersten größeren Erweiterung der Sprache Verilog. Neben der Beseitigung kleinerer Fehler und Unstimmigkeiten galt es, Erweiterungen einzubringen, die auf der einen Seite den Anwendern deutliche Vorteile bei der Entwicklung der Schaltungen brachten, auf der anderen Seite aber auch von den Herstellern von Simulations- und Syntheseprogrammen mit vertretbarem Aufwand umgesetzt werden konnten. In erster Linie wurden daher solche Erweiterungen definiert, die die Hersteller bereits für die VHDL-Versionen ihrer Programme entwickelt hatten. Auf diese Weise sind eine ganze Reihe von Konstrukten von VHDL in Verilog eingeflossen. Der daraus hervorgegangene Standard Verilog-2001 (IEEE 1364-2001) ist derzeit (2010) der am meisten verbreitete Evolutionsgrad der Sprache. Der derzeit letzte Evolutionsgrad ist Verilog-2005, ebenfalls ein IEEE-Standard. Die Änderungen gegenüber Verilog-2001 sind allerdings gering. Neben dieser geraden Evolutionslinie hat sich eine Abwandlung von Verilog gebildet, genannt SystemVerilog, normiert als IEEE-1800. SystemVerilog ist eine Erweiterung der Sprache Verilog auf breiter Front. Zum einen sind neue, hardwarenahe Datentypen und Sprachkonstrukte hinzugekommen, die einem das Modellieren von Hardware erleichtern. Am oberen Ende der Abstraktionsebenen wurden abstrakte Datentypen und die objektorientierte Programmierung eingeführt, um umfangreiche Testprogramme komfortabel schreiben zu können. An diesem Ende verhält sich SystemVerilog zu traditionellem Verilog wie C++ zu C. Insofern ist es für das Erlernen von SystemVerilog hilfreich, sowohl Verilog als auch C++ zu kennen. Ein Namensvetter von SystemVerilog ist SystemC, und da Verilog große Ähnlichkeit zu C hat, könnte man diese Ähnlichkeit auch zwischen SystemVerilog und SystemC vermuten. Diese gibt es inzwischen auch, aber die Ausgangspunkte der Entwicklung lagen weit auseinander. Zunächst: SystemC ist keine eigenständige Sprache, sondern eine Klassenbibliothek für C++. Eine SystemC-Beschreibung kann man in einem C++-Compiler übersetzen und zusammen mit dem SystemC-Simulationskern zu einem Programm binden, das das Systemverhalten über der Zeit errechnet. Eine Zyklus-akkurate Simulation war allerdings zunächst nicht der Fokus von SystemC, sondern eine hohe Simulationsgeschwindigkeit. Erst nach und nach wurde SystemC bei seiner Weiterentwicklung für Konstrukte geöffnet, die es ermöglichen,
1.4 Programme und Modelle
5
das Verhalten der Hardware taktgenau zu beschreiben. Genau dies ist notwendig für eine automatische Generierung einer Schaltung aus einer Beschreibung. SystemVerilog und SystemC sind ausdrücklich nicht Bestandteil dieses Buchs. Es orientiert sich an den beiden Verilog-Standards IEEE 1364-1995 und -2001. Dabei werden die einzelnen Sprachelemente immer zuerst im „alten“ Standard Verilog-1995 beschrieben und dann wird auf die Verbesserungen von Verilog-2001 eingegangen. So kann der Leser bei der Entwicklung eigener Modelle und Programme die Vorteile von Verilog-2001 nutzen, versteht aber auch Modelle und Programme, die im älteren Standard geschrieben wurden.
1.4
Programme und Modelle
Was macht man mit einer Software-Programmiersprache? Man schreibt Programme damit. Genau genommen werden die ausführbaren Programme mittels Compiler und Linker erzeugt und in der Programmiersprache wird lediglich der Quellcode geschrieben, aber auf diese Feinheit kommt es hier nicht an. Es gilt festzuhalten: Programme werden in Programmiersprachen geschrieben. Die Leute, die das tun, nennt man Programmierer, und wenn sie das tun, nennt man das programmieren. Alles in allem eigentlich banal, aber durchgängig benannt. Wie sieht die gleiche Betrachtung bei den Hardwarebeschreibungssprachen aus? Die Antwort ist nicht so einfach, denn mit diesen Sprachen macht man unterschiedliche Dinge. Zum einen kann man damit ganz normale, prozedurale Programme schreiben, wie mit einer Programmiersprache auch. In den Hardwarebeschreibungssprachen gibt es Variablendefinitionen, Zuweisungen, Kontrollstrukturen und Unterprogramme, also alle notwendigen Zutaten für prozedurale Programme. Sicher bieten die traditionellen Programmiersprachen mehr Komfort und die Entwicklungsumgebungen bieten mehr Bibliotheksfunktionen, aber elementar ist der Unterschied nicht. In den Hardwarebeschreibungssprachen werden aber auch Modelle geschrieben, und ein Modell ist etwas grundlegend anderes als ein Programm. Es ist aber leider nicht so, dass Modelle wesentlich anders aussehen würden als Programme, denn beim Modellieren werden die gleichen Zutaten verwendet wie beim Programmieren. Worin liegt der Unterschied? Und wovon soll ein Modell gemacht werden? Ganz allgemein kann man von einem Modell sprechen, wenn eine Sache, unabhängig davon ob sie real existiert oder nicht, durch eine Erklärung im Rahmen einer wissenschaftlich handhabbaren Theorie in einer definierten Form abgebildet wird. Oft kann man nicht alle Aspekte der Sache in einem Modell abbilden und muss daher die Komplexität reduzieren, oder abstrahieren. Im Falle einer digitalen Schaltung aber, und um nichts anderes geht es hier, können das Modell und die Schaltung hinsichtlich der logischen Funktion (also ohne Berücksichtigung von Verzögerungszeiten) identisch sein. Die wissenschaftlich handhabbare Theorie ist dabei Syntax und Semantik einer Hardwarebeschreibungssprache, und die Erklärung ist der Text, den man in der Sprache schreibt.
6
1 Einleitung
Ein Modell muss daher vollständig beschreiben, wie sich eine digitale Schaltung als Folge auf Signale an den Eingängen den Modells verhält. Dabei ist es zunächst egal, ob diese Schaltung automatisch mit einem Schaltungsgenerator aus dem Modell erzeugt werden kann oder nicht. Es gibt also genau genommen drei Arten von Objekten, die man in Hardwarebeschreibungssprachen beschreiben kann: Programme Nicht synthesefähige Modelle Synthesefähige Modelle
1.4.1
Programme
Bei einem Programm wird stets eine Anweisung nach der anderen ausgeführt. Diese sequenzielle Abarbeitung ist quasi eines der Grundfeste der Programmierung. Wenn in einem linearen Programmstück ohne Sprünge geschrieben steht (Programmiersprache C): a = 1; b = 2; c = a + b;
so kann man sicher sagen, dass bei der Abarbeitung der dritten Anweisung der Variable c der Wert 3 zugewiesen wird, denn die beiden darüber stehenden Anweisungen wurden zuvor ausgeführt. Die sequenzielle Abarbeitung der Befehle eines Programms hat z.B. auch zur Folge, dass man den Wert zweier Variablen nicht vertauschen kann, ohne eine dritte Variable zu benutzen. Das kann dann so aussehen (Programmiersprache C): temp = a; a = b; b = temp;
Es gibt zwar Prozessoren, die die Inhalte zweier Register mit einem einzigem Assemblerbefehl vertauschen können, aber es gibt in C kein Konstrukt, das direkt dazu führt, dass dieser Befehl in das ausführbare Programm geschrieben wird. Bei einen Modell ist das anders. Ein Modell besteht zwar auch aus einer Anzahl von Zuweisungen, Bedingungen, etc. aber alle Zuweisungen werden zeitgleich ausgeführt. Sollen in einem Modell die Werte zweier Variablen vertauscht werden, so sieht das so aus (Verilog): a b
Bedeutung a, um b Bits nach links geschoben a, um b Bits nach rechts geschoben
In beiden Fällen werden die Vektoren mit Nullen nachgefüllt. In Verilog-2001 sind die arithmetischen Schiebeoperationen hinzugekommen. Diese werden gebraucht, um vorzeichenbehaftete Zahlen bzw. Vektoren, die eine vorzeichenbehaftete Zahl darstellen, korrekt zu schieben.
36
4 Ausdrücke
Tabelle 4.12: Arithmetische Schiebeoperatoren Ausdruck a > b
Bedeutung a, um b Bits nach links geschoben a, um b Bits nach rechts geschoben
Beim Verschieben nach links führen logische und arithmetische Schiebeoperationen zum gleichen Ergebnis. Beim Verschieben nach rechts wird beim arithmetischen Schieben das höchstwertige Bit nicht mit 0 aufgefüllt, sondern mit seinem vorhergehenden Wert. Dadurch bleibt das Vorzeichenbit erhalten.
4.8
Bedingung
Beim Bedingungsoperator handelt es sich um den aus C bekannten ternären Operator: Bedingung ? Wahr-Ausdruck : Falsch-Ausdruck Wenn die Bedingung wahr ist, dann wird der Wahr-Ausdruck berechnet und als Ergebnis genommen. Ist die Bedingung dagegen falsch, dann wird der Falsch-Ausdruck berechnet und als Ergebnis genommen. In C gibt es, außer diesen beiden, keine weiteren Möglichkeiten. In Verilog ist der Operator aber komplexer, da die Bedingung auch x oder z sein kann. In diesem Fall werden sowohl Wahr-Ausdruck als auch Falsch-Ausdruck ermittelt und die Ergebnisse Bit für Bit verglichen. Der kürzere der beiden Ausdrücke wird dabei mit Nullen aufgefüllt. Wenn an einer Stelle beide Ausdrücke 0 sind, dann ist auch das Ergebnis an dieser Stelle 0. Wenn an einer Stelle beide Ausdrücke 1 sind, dann ist auch das Ergebnis an dieser Stelle 1. In allen anderen Fällen ist das Ergebnis an dieser Stelle x.
4.9
Aneinanderreihung
Mit der Aneinanderreihung (concatenation) kann man Ergebnisse mehrerer Ausdrücke zusammenfassen, z.B. um sie einem Vektor zuzuweisen. Die einzelnen Ausdrücke stehen dabei in geschweiften Klammern, getrennt durch Kommata. Die Breite in Anzahl von Bits jedes einzelnen Ausdrucks muss konstant sein, unbekannte Breiten sind nicht erlaubt. wire [7:0] a, b, c; assign c = {a[3:0], 2'b01, b[5:4]};
4.10 Wiederholung
4.10
37
Wiederholung
Mit der Wiederholung (replication) kann man einen Ausdruck mehrmals an sich selbst aneinander reihen. wire [7:0] a, b, c; assign c = {2'b01, {3{a[6:5]}}};
4.11
// 0, 1, and three times {a[6:5]}
Operandengröße
Auffällig an Verilog im Vergleich zu C oder VHDL ist, dass es in Verilog so etwas wie einen expliziten type cast, eine Typumwandlung, nicht gibt. In Verilog kann man problemlos einem Vektor einen Skalar zuordnen oder auch einem Skalar einen Vektor. Auch Operanden unterschiedlicher Breite lassen sich zuordnen oder miteinander vergleichen. All das geht, weil es eine einfache Regel für die Operandengrößen gibt: Die Operanden werden zur größten Bitbreite, die in einem Ausdruck vorkommt, vergrößert und dabei mit Nullen aufgefüllt. Und wenn ein Ausdruck in einer Zuweisung verwendet wird, so wird auch die Variable links des Zuweisungszeichen bei der Ermittlung der Anzahl von Stellen berücksichtigt. Dabei ist allerdings zu beachten, dass auch Zwischenergebnisse mit dieser Anzahl von Stellen berechnet werden. Das kann dazu führen, dass ein anderes Ergebnis berechnet wird als man das vielleicht erwarten würde. wire [7:0] a, b, c; assign c = a + b >> 1;
Der Entwickler hat sich vielleicht gedacht: Da a und b jeweils 8 Bit breite Vektoren sind, kann das Ergebnis der Addition, wenn man es um 1 Bit nach rechts verschiebt, wieder nur 8 signifikante Bits besitzen. Diese passen in den Ergebnisvektor c, und damit ist die Anweisung korrekt. Das stimmt aber leider nicht. Das Endergebnis des Ausdrucks hat zwar immer nur 8 gültige Bits, aber das Zwischenergebnis a + b benötigt nun einmal 9 Bits. Die automatische Breitenbestimmung betrachtet aber nur die Operanden und ermittelt 8 Bit als größte Bitbreite. Das ergibt im Beispiel: a = 8'hc0; b = 8'h50; c = 8'h08;
// ((8'hc0 + 8'h50) & 8'hff) >> 1
38
4 Ausdrücke
Man kann einen Trick anwenden, um den Ausdruck das gewünschte Ergebnis erzeugen zu lassen: wire [7:0] a, b, c; assign c = a + b + 9'd0 >> 1;
Durch die Einführung einer 9 Bit breiten Null wird das Ergebnis nicht verändert, aber nun wird die rechte Seite der Zuweisung in einer Breite von 9 Bit berechnet. Bei der Zuweisung zu der 8 Bit breiten Variable c wird dann zwar das oberste Bit ignoriert, aber das ist nach der Schiebeoperation ohnehin 0. a = 8'hc0; b = 8'h50; c = 8'h88;
4.12
// ((9'hc0 + 9'h50 + 9'd0) & 9'h1ff) >> 1
Priorität
Die Operatoren haben eine definierte Rangordnung, in der sie ausgewertet werden, wenn sie in einem Ausdruck vorkommen. Die Operatoren mit der höchsten Priorität werden zuerst ausgewertet, danach die der nächst niedrigeren Ebene usw. Innerhalb einer Ebene werden die Operatoren in der Reihenfolge ausgewertet, wie sie von links nach rechts gelesen in einem Ausdruck vorkommen. Einzige Ausnahme hiervon ist der ternäre Operator ?, der von rechts nach links ausgewertet wird. Tabelle 4.13:Priorität der Operanden (mit Operanden von Verilog-2001) Operatoren + - ! ~ & ~& | ~| ^ ~^ ^~ (1 Operand) ** */% + - (2 Operanden) > > < >= == != === !== & (2 Operanden) ^ ^~ ~^ (2 Operanden) | (2 Operanden) && || ?: { } {{ }}
Priorität höchste . . . . . . . . . . . . niedrigste
4.13 Zuweisungen
39
Möchte man einen Ausdruck anders auswerten lassen als es die Rangordnung vorsieht, dann muss man Teile des Ausdrucks in runden Klammern einschließen. Einige einfache Beispiele: A + B – C
B wird zu A addiert. Von diesem Zwischenergebnis wird C subtrahiert. A + B / C
B wird wegen der höheren Rangordnung zunächst durch C dividiert. Danach wird dieses Zwischenergebnis von A abgezogen. (A + B) / C
Mit Klammern kann man die Reihenfolge der Operationen abändern.
4.13
Zuweisungen
In den Beispielen zu den unterschiedlichen Operatoren ist die Zuweisung bereits mehrfach vorgekommen. Das Zeichen dafür ist das einfache Gleichheitszeichen =. Der primäre Zweck eines Ausdrucks ist es ja, ein Ergebnis zu liefern, das einer Variablen zugewiesen werden kann. Dabei wird in Verilog die weit verbreitete Syntax verwendet, die auf der linken Seite des Zeichens eine Variable fordert und auf der rechten Seite einen Ausdruck. Es gibt in Verilog allerdings zwei grundlegend unterschiedliche Zuweisungen: Dauerhafte Zuweisungen (continuous assignment) und prozedurale Zuweisungen (procedural assignment).
4.13.1
Dauerhafte Zuweisungen
Bei den dauerhaften Zuweisungen wird einer Variablen einmalig und dauerhaft ein Ausdruck zugewiesen. Die Syntax dafür ist: assign Variable = Ausdruck ;
Dabei muss die Variable ein Skalar oder ein Vektor vom Datentyp wire sein. Die Bedeutung dieser Zuweisung reicht weiter als man zunächst denkt, wenn man SoftwareProgrammiersprachen gewohnt ist. Eine dauerhafte Zuweisung wird nicht nur einmal ausgeführt, sondern eben „immer“. Eine solche Zuweisung ist nicht Teil eines prozeduralen Programms, sondern quasi ein eigener Handlungsstrang. Für einen Simulator bedeutet eine dauerhafte Zuweisung, dass jedes Mal, wenn sich eine der Eingangsvariablen des Ausdrucks ändert, der Wert neu ermittelt und der Variablen zugewiesen werden muss. Ein Synthesetool hingegen setzt den Ausdruck in eine kombinatorische Gatterschaltung um.
40
4.13.2
4 Ausdrücke
Prozedurale Zuweisungen
Die prozeduralen Zuweisungen benötigen eine Variable eines speichernden Datentyps auf der linken Seite, also reg, integer, real usw. Im Gegensatz zu den dauerhaften Zuweisungen müssen die prozeduralen Zuweisungen stets in eine umschließende strukturierte Prozedur eingebettet sein. Die prozeduralen Zuweisungen, die zugehörigen umschließenden Prozeduren und die dabei möglichen Kontrollstrukturen sind das eigentliche Kernstück der Sprache Verilog. Alle drei Komponenten zusammen dienen dem Zweck, eine logische Funktion nicht mit dem etwas unflexiblen assign-Statement machen zu müssen, sondern mittels eines Algorithmus zu formulieren. Wegen der zentralen Bedeutung der drei genannten Komponenten ist ihnen das ganze nächste Kapitel Verhaltensbeschreibung gewidmet.
4.13.3
Besonderheiten bei der Synthese
Man muss sich bei der Verwendung der Hardwarebeschreibungssprachen VHDL und Verilog immer wieder vor Augen halten, dass diese Sprachen als Simulationssprachen konzipiert wurden. Die Verwendung für die Synthese ist zunächst nicht beabsichtigt gewesen. Das führt dazu, dass man beim Erstellen von Modellen stets berücksichtigen muss, was eine Anweisung für die Simulation bedeutet, und was hinsichtlich der Synthese. Am Anfang dieses Kapitels stand bei den Vergleichen nach Größe folgende Liste für das Ergebnis: Alle Vergleiche ergeben ein skalares Ergebnis von 0, wenn der Vergleich falsch ist 1, wenn der Vergleich wahr ist x, wenn mindestes ein Bit eines der Operanden x oder z ist. Diese Liste beschreibt das Verhalten der Vergleichsoperatoren gemäß der Definition der Sprache Verilog. Damit ist das Verhalten in der Simulation definiert. Eine synthetisierte Schaltung verhält sich aber geringfügig anders. Ein Beispiel: wire [1:0] wire [1:0] wire assign
a; b; a_greater_b; a_greater_b = a > b;
4.13 Zuweisungen
41
Übergibt man dieses Modell einem Schaltungsgenerator zur Synthese in einem handelsüblichen FPGA, so erzeugt dieser eine solche oder eine ähnliche Schaltung:
Bild 4.1: Gatterschaltung eines 2-Bit-Größenvergleichers
Es ist leicht ersichtlich, dass der Ausgang dieser Schaltung nicht x sein kann, wenn ein Bit der Operanden a oder b unbekannt oder hochohmig ist. In Gatterschaltungen gibt es nun mal nur die binären Werte 0 und 1. Ein x kann nicht ausgegeben werden und eine Erkennung der Werte x und z gibt es auch nicht. Im Klartext: Die Synthese einer Gatterschaltung berücksichtigt nur die Bedeutung der Operatoren für die binären Werte 0 und 1. Die einzige Ausnahme hiervon ist die bewusste Herbeiführung eines hochohmiges Zustands mit Hilfe einer assign-Anweisung und einer Bedingung, wie es im folgenden Beispiel für einen 8 Bit breiten Bus der Fall ist. wire [7:0] tristate_bus; wire [7:0] bus; wire bus_enable; assign tristate_bus = bus_enable ? bus : 8'hzz;
Diese Beschränkung der Synthese auf die binären Werte 0 und 1 hat weitreichende Folgen. Schaltungsgeneratoren ignorieren alle Wirkungen der Eingangswerte x und z und erzeugen solche Werte auch nicht (bis auf die eben beschriebene Ausnahme). Das führt dazu, dass die beiden Operatoren für Vergleiche auf Gleichheit (== und ===) zur exakt identischen Schaltung synthetisiert werden. Betrachtet man aber den vollständigen Wertebereich, dann entspricht die synthetisierte Schaltung keinem der beiden Operatoren genau! Eine weitere Folge der Beschränkung ist der Unterschied der Simulationen des Quelltextes und der daraus erzeugten Gatterschaltung, wenn der volle Wertebereich zum Einsatz kommt.
5
Verhaltensbeschreibung
Die bislang eingeführten Sprachkonstrukte erlauben die Modellierung von Hardware auf einem recht niedrigen Niveau. Es gibt noch weitere Sprachelemente wie die Modellierung mit Gattern und Schaltern sowie UDP (user defined primitive), die für noch niedrigere Ebenen geeignet sind. Solche Sprachelemente werden normalerweise nicht dazu benutzt, eine logische Funktion zu beschreiben und daraus eine Gatterschaltung berechnen zu lassen, sondern um eine existierende Gatter- oder Transistorschaltung für eine Simulation zu modellieren. All diese Elemente sind allerdings nicht geeignet, ein System auf einer abstrakteren Ebene zu beschreiben. Das gilt sowohl für die Schaltung selbst als auch für ein Testprogramm, das einen komplexen Signalverlauf erzeugen soll, um damit die korrekte Funktion des Designs zu überprüfen. Für solche Aufgaben braucht man eine höhere Art der Beschreibung, die Verhaltensbeschreibung.
5.1
Prozesse
Die Beschreibung des Verhaltens eines Systems erfolgt in Verilog mit zwei strukturierten Prozeduren (structured procedures) namens initial und always. Mit jedem Aufruf dieser Prozeduren wird ein eigenständiger Handlungsstrang angelegt. Alle Handlungsstränge laufen in der Simulation als nebenläufige Prozesse zeitgleich ab. Die Prozeduren initial und always haben keine Parameter; ihr einziger Zweck ist es, eine Anweisung oder eine Reihe von Anweisungen einmal (initial) oder immer wieder (always) auszuführen. Alle angelegten nebenläufige Prozesse werden vom Simulator zum Simulationszeitpunkt 0 ausgeführt. Dadurch, dass in der Reihe der Anweisungen auch Verzögerungsanweisungen (delay control) und Ereignisabhängigkeiten (event control) stehen können, kann man damit durchaus einen längeren Zeitraum der Simulation überdecken, auch wenn der Prozess nur einmal ausgeführt wird. So ist der Haupt-Handlungsstrang eines Testprogramms üblicherweise ein initial-Block, dessen letzte Anweisung der Stopp der Simulation ist. In allen nebenläufigen Prozessen können an beliebigen Stellen Verzögerungsanweisungen und Ereignisabhängigkeiten stehen, bei Bedarf auch mehrere davon, beliebig gemischt. Eine Verzögerungsanweisung besteht aus einem Rautezeichen #, gefolgt von einer Zahl oder einem Ausdruck, der eine Zahl ergibt. Die Zahl gibt die Verzögerung in Vielfachen der Si-
44
5 Verhaltensbeschreibung
mulationseinheit an. Diese Simulationseinheit muss zuvor mit der `timescale-Direktive definiert werden. `timescale 1ns/1ps ... parameter d = 10, e = 20; reg r; ... #10 r = 1; #((d+e)/2) r = 0;
// time base = 1ns, resolution = 1ps
// set r to 1 after 10 ns delay // set r to 0 after 15 ns delay
Wenn bei der Simulation eine Verzögerungsanweisung zur Ausführung kommt, so wird der Prozess, in dem die Anweisung steht, für die angegebene Zeit angehalten. Eine Ereignisabhängigkeit besteht aus einem At-Zeichen @, gefolgt von einer geklammerten und mit dem Wort or verknüpften Liste einzelner Ereignisse. Zulässige Ereignisse sind – – –
posedge negedge Ausdrücke
Unter muss man sich dabei einen Ausdruck vorstellen, der zu einem 1-Bit-Wert aufgelöst wird. Mit diesen zulässigen Ereignissen kann man Abhängigkeiten bilden, die bei jeder Pegeländerung eines Signals eintreten oder bei Änderungen des Signals in eine bestimmte Richtung, also bei steigender oder fallender Flanke. Dabei tritt erwartungsgemäß ein mit posedge bezeichnetes Ereignis ein, wenn sich das danach genannte Signal von 0 nach 1 ändert, aber auch bei der Änderungen von 0 nach x und von x nach 1. Analoges gilt für negedge. Beispiele für Ereignisabhängigkeiten sind @(posedge clock) @(posedge clock or posedge reset or posedge set) @(input_1 or input_2)
Das Wort or bei den beiden letzten Beispielen hat nichts mit einem logischen Oder der genannten Signale zu tun. Es soll das Oder der Ereignisse bezeichnen. Dies ist etwas verwirrend. Daher wurde in Verilog-2001 eine zweite Art der Aufzählung hinzugenommen. Seitdem gibt es auch die Aufzählung mit Kommata. @(input_1, input_2)
5.1 Prozesse
45
Kommt bei der Simulation eine Ereignisabhängigkeit zur Ausführung, so wird der Prozess, in dem die Anweisung steht, angehalten, bis das Ereignis eintritt. Mit den beiden Prozeduren, der Verzögerungsanweisung, der Ereignisabhängigkeit und den Kontrollstrukturen lassen sich beliebig komplexe Verhaltensweisen beschreiben. Wenn die Beschreibung allerdings eine logische Funktion darstellen soll, die ein Synthesetool in eine Schaltung umsetzen kann, dann scheiden viele denkbare Konstrukte aus. Ganz grob kann man sagen:
initial-Blöcke werden von Schaltungsgeneratoren normalerweise komplett ignoriert. Somit scheidet diese Prozedur auch für die Festlegung des Resetzustands aus.
In einem always-Block darf es nur eine Ereignisabhängigkeit geben, und diese muss die erste Anweisung des Blocks sein. Man nennt sie dann Abhängigkeitsliste (sensitivity list).
Im ersten realen Beispiel einer Verhaltensbeschreibung wird ein Taktgeber beschrieben, der zwei konstante Takte von 5 MHz und 10 MHz erzeugen soll. Ein solcher Taktgeber kann Teil eines Testprogramms sein, synthetisierbar ist er nicht. `timescale 1ns/1ps module top; reg mhz_5, mhz_10; initial begin mhz_5 = 1'b0; mhz_10 = 1'b0; end always begin #10 ; mhz_5 = !mhz_5; end always begin #5 ; mhz_10 = !mhz_10; end endmodule
// // // //
time base = 1ns, resolution = 1ps name of top level module reg variable for behavioral modelling execute once
// set initial values for // both variables // execute repeatedly // wait 10 ns, results in 20 ns period // toggle signal // execute repeatedly // wait 5 ns, results in 10 ns period
Im Gegensatz zu den Beispielen im Kapitel 4 sind die Zuweisungen dieses Beispiels keine assign-Anweisungen, denn sie sind nicht dauerhaft. Stattdessen wird das gewünschte Verhalten algorithmisch formuliert. Das bedeutet, dass es mehrere Zuweisungen zu ein und derselben Variablen gibt, in diesem einfachen Beispiel noch ohne Kontrollstrukturen wie if,
46
5 Verhaltensbeschreibung
else usw.. In dem Programm werden drei Handlungsstränge angelegt und parallel bearbeitet. Die initial-Anweisung setzt die Anfangswerte. Die beiden always-Anweisungen beinhalten in ihren begin/end-Klammern je eine Anweisung, die nur Zeit verstreichen lässt, und eine Anweisung zur Änderung des jeweiligen Signals. Die Festlegung eines Anfangswerts ist notwendig, weil alle Variablen vom Typ reg beim Start der Simulation den Wert x zugewiesen bekommen. Ohne die Definition des Startwerts 1'b0 würden die beiden always-Blöcke nach der Verzögerung den beiden Variablen die Negation von x zuweisen, also wieder x. In einem zweiten Beispiel soll nochmals der Unterschied zwischen den dauerhaften und prozeduralen Zuweisungen hervorgehoben werden. Eine einfache kombinatorische, binäre Verknüpfung wird dabei zwei Mal in unterschiedlicher Weise modelliert. wire reg
continuous; procedural;
assign
continuous = sig1 && sig2 || sig3;
always @(sig1 or sig2 or sig3) begin procedural = 1'b0; if (sig1 && sig2) procedural = 1'b1; if (sig3) procedural = 1'b1; end
// variable for continuous assignment // variable for procedural assignment
// // // //
always if one of the signals changes then evaluate this algorithm default state is 0 check for sig1 and sig2
// check for sig3
Nachdem es sich hier um ein sehr einfaches Beispiel handelt, ist die dauerhafte Zuweisung mit der assign-Anweisung kurz und übersichtlich: Eine Und-Verknüpfung und eine OderVerknüpfung. Bei der prozeduralen Zuweisung entsteht die Oder-Verknüpfung dadurch, dass die beiden if-Abfragen hintereinander stehen und die dadurch bedingte Zuweisung der Variablen jedes Mal den Wert 1 zuweist. Die Und-Verknüpfung steht direkt in der Bedingung der ersten if-Abfrage. Bei solch einfachen Beispielen ist die dauerhafte Zuweisung üblicherweise kürzer und einfacher zu verstehen als die prozedurale Zuweisung. Das ändert sich allerdings mit dem Grad der Komplexität. Je nach Abhängigkeitsliste lassen sich mit prozeduralen Zuweisungen nicht nur kombinatorische Gatterschaltungen modellieren, sondern auch getaktete Elemente (Register, Latches), wie zum Beispiel: always @(posedge clk) q regb) result = rega; else // else applies to preceding if result = regb;
Auch wenn es in Verilog eine klare Definition gibt, zu welchem if das else gehört, ist davon abzuraten, so etwas zu schreiben. Stattdessen schaffen begin/end-Klammern Klarheit. if (index > 0) begin if (rega > regb) result = rega; else result = regb; end
Verkettete if-else-if kommen in Beschreibungen häufig vor. Ein besonderes Schlüsselwort gibt es dafür nicht. if (Bedingung1) Anweisung1; else if (Bedingung2) Anweisung2; else if ... ... else Anweisungx;
5.2 Kontrollstrukturen
5.2.2
49
case
Wenn die Bedingungen einer verketteten if-else-Anweisung verwandt sind, bietet sich eine case-Anweisung an. case (Auswahlausdruck) Ausdruck1: Anweisung1; Ausdruck2: Anweisung2; … default: Anweisungx; endcase
Der default-Zweig ist optional. Es gibt keine weiteren Vorschriften bezüglich des Auswahlausdrucks und der Liste der Vergleichsausdrücke. So dürfte z.B. die default-Anweisung irgendwo mittendrin in der Liste stehen, es darf nur keine zwei davon geben. Es hat sich aber eingebürgert, sie als letzte Anweisung in die Liste zu schreiben. Es ist auch nicht vorgeschrieben, dass die Liste der Vergleichsdrücke alle möglichen Werte des Auswahlausdrucks abdecken muss. Auch ist erlaubt, dass ein bestimmter Wert des Auswahlausdrucks mehrere Vergleichsausdrücke „trifft“. In dem Fall wird aber trotzdem nur eine Anweisung ausgeführt, nämlich die in der Liste höher stehende. Als Beispiel einer case-Anweisung kann ein einfacher 2-Bit-Umsetzer von der Binärcodierung zur Gray-Kodierung dienen: reg [1:0] binary; reg [1:0] gray; always @(binary) begin case (binary) 2'b00: 2'b01: 2'b10: 2'b11: endcase end
gray gray gray gray
= = = =
2'b00; 2'b01; 2'b11; 2'b10;
Die case-Anweisung ist ein mächtiges Instrument, mit dem man vorsichtig umgehen muss, wenn außer den beiden binären Werten 0 und 1 noch x und z hinzukommen. Zunächst könnte man annehmen, dass mit der case-Anweisung auch beliebige logische Verknüpfungen formuliert werden können.
50
5 Verhaltensbeschreibung
Das Beispiel von Seite 46 könnte man vielleicht auch so schreiben (Achtung, falsch!): wire reg
continuous; procedural;
assign
continuous = sig1 && sig2 || sig3;
always @(sig1 or begin case ({sig1, 3'b11x: 3'bxx1: default: endcase end
sig2 or sig3)
// variable for continuous assignment // variable for procedural assignment
// always if signal changes // then evaluate this algorithm
sig2, sig3}) procedural = 1'b1; // don't care about sig3 procedural = 1'b1; // don't care about sig1 and sig2 procedural = 1'b0;
Nochmals: Das ist falsch! Die Beschreibung ist zwar syntaktisch korrekt und ein Simulator hat damit keine Probleme. Aber sie tut nicht das, was man erwartet, und sie ist nicht synthetisierbar. In der case-Anweisung wird geprüft, ob der Auswahlausdruck einem der Ausdrücke der Liste entspricht. Dabei wird in der ersten Zeile aber nicht nur geprüft, dass sig1 und sig2 beide 1 sind, sondern auch dass sig3 x ist! Das x ist kein don't care, sondern ein Sollwert für den Vergleich. Und im FPGA gibt es keine unbekannten Signale oder eine Vergleichslogik dafür. Das Gleiche gilt auch für die if-Anweisung. Die Anweisung if (rega === 2'b1x)
besagt ebenfalls, dass das Bit 0 des Vektors rega x sein muss, damit die Bedingung erfüllt ist. Wenn man den Vektor nur teilweise prüfen will und das Bit 0 dabei egal ist, dann bleibt beim derzeitigen Kenntnisstand nur der Ausdruck: if (rega[1] === 1'b1)
Im nächsten Kapitel werden andere Möglichkeiten einer teilweisen Prüfung vorgestellt.
5.2 Kontrollstrukturen
5.2.3
51
casez, casex
Die beiden Anweisungen casez und casex sind formal identisch zur case-Anweisung, unterscheiden sich aber in ihrer Wirkung. Die Anweisung casez ignoriert tri-state-Zustände in den Ausdrücken, casex ignoriert tri-state und unbekannte Zustände. Damit kann man das Beispiel von Seite 46 nun richtig schreiben: wire reg
continuous; procedural;
assign
continuous = sig1 && sig2 || sig3;
always @(sig1 or sig2 or sig3) begin casex ({sig1, sig2, sig3}) 3'b11x: procedural = 1'b1; 3'bxx1: procedural = 1'b1; default: procedural = 1'b0; end
// variable for continuous assignment // variable for procedural assignment
// always if one signal changes // then evaluate this algorithm // don't care about sig3 // don't care about sig1 and sig2
Manche Synthesetools haben Probleme mit dem default-Zweig: Sie ignorieren ihn einfach. Da dieser Zweig aber immer nur dann eine Wirkung hat, wenn kein anderer Fall zutrifft, kann man die Anweisung des default-Zweigs auch ganz aus der case-Anweisung herausnehmen. Das nun schon mehrfach zitierte Beispiel sieht dann so aus: always @(sig1 or sig2 or sig3) begin procedural = 1'b0; casex ({sig1, sig2, sig3}) 3'b11x: procedural = 1'b1; 3'bxx1: procedural = 1'b1; end
// always if signal changes // then evaluate this algorithm // former default case // don't care about sig3 // don't care about sig1 and sig2
Dem ein oder anderen Hardwareentwickler wird ein solches Konstrukt missfallen. In Worten gesprochen heißt das ja: Immer wenn sich an einem der Eingangssignale etwas ändert, dann setze den Ausgang zunächst einmal auf 0. Je nach Zustand der Eingangssignale setze den Ausgang dann wieder auf 1. Das gibt doch bestimmt unsaubere Signale mit kurzzeitigen Pegelwechseln! Doch so darf man das nicht betrachten. Kurzzeitige Pegelwechsel gibt es zwar immer bei kombinatorischer Logik, aber die kommen nicht von der Beschreibung der Schaltung, sondern von unterschiedlich langen Verzögerungszeiten der einzelnen Signale. Der Algorithmus in dem Beispielmodell ist kein sequenzieller Algorithmus. Man muss ihn eher so lesen: Fülle das Karnaugh-Veitch-Diagramm der Variable procedural in Abhängigkeit der Signale sig1, sig2 und sig3 zunächst mit Nullen. Dann schreibe eine 1 in die benannten 2 + 4 Felder und berechne eine Gatterschaltung, die das KV-Diagramm abbildet.
52
5.2.4
5 Verhaltensbeschreibung
while
Die while-Anweisung führt eine Anweisung aus, solange ein Bedingungsausdruck wahr ist. Wenn die Bedingung zu Beginn falsch ist, wird die Anweisung gar nicht ausgeführt. Auch hier die formale Definition: while (Ausdruck) Anweisung
Als Beispiel dient hier ein nicht synthetisierbares Code-Fragment zur Zählung der auf 1 gesetzten Bits in einem Byte. reg [7:0] tempreg; integer count; count = 0; tempreg = rega; while (tempreg) begin if (tempreg[0]) count = count + 1; tempreg = tempreg >> 1; end
// // // // //
working register result set result to 0 load working register check for not zero
// check bit 0 // shift working register
Bezüglich des Bedingungsausdrucks gilt das Gleiche wie bei der if-Anweisung: Der Bedingungsausdruck wird als erfüllt betrachtet, wenn das Ergebnis bekannt und ungleich 0 ist.
5.2.5
wait
Die wait-Anweisung verzögert die Ausführung der umschließenden strukturierten Prozedur, falls der Bedingungsausdruck falsch ist. Im Gegensatz zur Ereignisabhängigkeit ist die wait-Anweisung vom aktuellen Zustand des Ausdrucks abhängig und nicht von einer Änderung. Die formale Definition ist: wait (Ausdruck) Anweisung
Die beiden Zeilen: wait (sig1)
sig2 = 1'b1;
... @(posedge sig1) sig2 = 1'b1; führen zum gleichen Verhalten, sofern sig1 zu Beginn der Anweisungen 0 ist. Falls sig1 jedoch bereits 1 ist, führt die erste Zeile (wait) nicht zu einer Verzögerung des Prozesses. Die zweite Zeile (posedge) hält jedoch den Prozess an, bis wieder eine steigende Flanke des Signals sig1 eintritt.
5.2 Kontrollstrukturen
5.2.6
53
for
Die for-Anweisung ist sehr ähnlich zu der in der Programmiersprache C. Sie besteht aus dem Setzen eines Anfangswerts, der wiederholten Prüfung und der Veränderung. Das gleiche Beispiel zum Zählen der auf 1 gesetzten Bits in einem Byte sieht unter Benutzung der forAnweisung zum Beispiel so aus: reg [7:0] integer integer
tempreg; count; i;
count = 0; tempreg = rega; for (i = 0; i < 8; i = i + 1) begin if (tempreg[i]) count = count + 1; end
// working register // result
// set result to 0 // load working register
// check bit i
Den in C erfahrenen Programmierern wird aufgefallen sein, dass der ++-Operator nicht verwendet wurde. Das liegt einfach daran, dass es ihn in Verilog nicht gibt.
5.2.7
forever
Die forever-Anweisung entspricht einem while (1). Sie führt zu einer Endlosschleife.
5.2.8
repeat
Die repeat-Anweisung führt eine Anweisung sooft aus wie vorgegeben. Ihre formale Definition ist: repeat (Anzahl) Anweisung
Ein nicht synthetisierbares Beispiel zur Bildung einer ganzzahligen Potenzfunktion ist: potenz = 1; repeat (exponent) potenz = potenz * basis;
Die repeat-Anweisung ist letztlich nur eine vereinfachte Form der for-Anweisung. Eine Anweisung oder ein mit begin/end geklammerter Block von Anweisungen wird mehrfach ausgeführt, ohne das man dafür eine Laufvariable definieren muss.
54
5.3
5 Verhaltensbeschreibung
Blockierende und nicht-blockierende Zuweisung
Bei den Beispielen dieses Kapitels wurden ohne weitere Erklärungen zwei unterschiedliche Zuweisungszeichen verwendet: Das einfache Gleichheitszeichen = und der „Pfeil“ bestehend aus dem „kleiner“-Zeichen und dem Gleichheitszeichen. Im Referenzhandbuch werden dafür zwei Begriffe benutzt. Tabelle 5.1: Arten der Zuweisung Zuweisung =