VHDL - Beispiele und Links

Einführung

Die folgenden Ausführungen sollen einen kurzen Einblick in die Hardware-Beschreibungssprache VHDL geben (= VHSIC Hardware Description Language, VHSIC = Very High Speed Integrated Circuit).

Mit VHDL sind Design und Simulation von Hardware sehr effizient möglich. Es gibt zwar reichlich Literatur über VHDL, aber eine kurze, praktische Einführung in deutsch ist schwer zu finden. Meist wird der Anfänger mit einer Unmenge an Theorie und Details überhäuft, die für einen Einstieg völlig unnötig sind.

Ich empfehle für den Einsteiger die folgenden beiden Links:

http://www.fh-wedel.de/cis/archiv/seminare/ws96/vhdl/fraemke.htm
http://www.ti.cs.uni-bonn.de/html/Lehre/SoSem99/TechInfo2_SoSem99/ti2_99_kap2.pdf

und in Englisch:

http://www.gmvhdl.com/VHDL.html

Dem Fortgeschrittenen seien diese Links empfohlen:

http://mikro.e-technik.uni-ulm.de/vhdl/vhdl_infos.html
http://wwwlrh.fh-bielefeld.de/vhdl_vor/vhdl_vor.htm
http://www.fh-wedel.de/cis/archiv/seminare/ws96/vhdl/schoenfe.htm

Für den reinen "Hardwerker" dürfte der Einstieg ebenso ein Umdenken erfordern, wie z.B. für einen Programmierer von Microcontrollern. Man kann aber schon mit wenigen Grundelementen in VHDL ganz ordentliche Designs erstellen. Man muss sich einfach daran gewöhnen, Hardware mittels Software zu beschreiben.

VHDL Grundstruktur

Ein VHDL-Modul besteht prinzipiell aus einer Schnittstellenbeschreibung (Entity) und einer Architektur (Architecture).
In der Entity werden die Ein- und Ausgänge, also das Interface der "Schaltung" beschrieben. Es gibt verschiedene Signaltypen, z.B. für einzelne Leitungen oder Busse.
In der Architecture wird das Verhalten der Schaltung beschrieben, also wann ein Flip-Flop gesetzt werden soll oder ein bestimmtes Bitmuster am Ausgang erscheinen soll.

Das folgende Beispiel zeigt einen invertierenden Tristate-Buffer in VHDL:


Für den Tristate-Buffer werden 2 Eingänge und 1 Ausgang benötigt. Der VHDL-Typ std_logic wird für Einzelsignale (1 bit) verwendet. Im Gegensatz zum Typ bit kann er nicht nur die Werte '0' und '1' annehmen, sondern u.a. auch 'Z', also hochohmig.

Für dieses Beispiel werden keine zusätzlichen Signale benötigt. Die gesamte Funktion des Buffers läßt sich in einer Zeile ausdrücken:

Der Ausgang dout bekommt den invertierten Wert von din, falls t='0' ist, sonst geht der Ausgang auf Tristate. Nicht so schwierig - oder?

Das nächste Beispiel ist etwas anspruchsvoller: ein 4-bit Synchronzähler. Ein Reset-Eingang soll jederzeit das Rücksetzen auf 0 ermöglichen und ein Übertragsbit soll gesetzt werden, sobald der max. Zählerstand "1111" erreicht ist.

Aus diesem Beispiel könnte man fast einen ganzen VHDL-Einsteigerkurs rausholen! Der Zählerstand wird mit einem 4 bit - Signal (std_logic_vector) dargestellt. Wir verwenden erstmals ein zusätzliches Signal cnt_q(3 ... 0). Ich habe ein _q an den Namen angehängt, um zu kennzeichnen, dass es sich um Flip-Flops handelt (das hilft, wenn es mehr Signale werden).

Wenn Flip-Flops generiert werden sollen, dann benötigt man einen Prozess. Ein Prozess kann aber genausogut rein kombinatorisch sein. Dieses neue Element ist deshalb extrem wichtig in VHDL.

Wann kann sich der Zählerstand ändern? Entweder wenn rst='1' wird oder bei einer Taktflanke an clk. Der Prozess reagiert also auf Änderungen von rst und clk. Diese beiden Signale müssen in die 'sensitivity list' aufgenommen werden. Die höchste Priorität hat rst - es ist damit das erste Signal in der if-Abfrage und erzeugt einen asynchronen Reset.

An 'if rising_edge(clk)' erkennt der Compiler, dass Flip-Flops generiert werden sollen. Alle Signale, die innerhalb einer solchen if-Anweisung einen Wert zugewiesen bekommen, sind Register. Das Signal carry wird deshalb außerhalb des Prozesses erzeugt. Es soll ja genau beim Zählerstand "1111" auf '1' stehen. Würde man es in den Prozess hineinschreiben, bekäme man ein Flip-Flop, dass natürlich genau einen Takt später die '1' liefert.

Am Schluss gibt es noch eine Signalzuweisung cnt <= cnt_q. Das ist notwendig, weil cnt_q in der Architecture als zusätzliches Signal deklariert wurde und clk "nur" ein Port-Signal der Entity darstellt. Man kann cnt nicht direkt als Zählersignal verwenden, da es mit "out" als reiner Ausgang definiert wurde. Für die Addition und zur Carry-Generierung muss dieses Signal aber in der Architecture lesbar sein. Das ist bei einem Ausgang nicht der Fall!

Eine zweite Möglichkeit wäre, cnt als "buffer" zu definieren. Alle "buffer"-Signale sind "lesbare" Ausgänge. Ich habe mich hier für die erste Variante entschieden.

Das obige Beispiel lässt noch viele weitere Betrachtungen zu- was hier aber m.E. zu weit führen würde.

An dieser Stelle folgen demnächst noch einige weitere kleine Beispiele in synthesefähigem VHDL.

...