|
|
VHDL[Bearbeiten] VHDL als ProgrammierspracheVHDL ist im Grunde eine ganz normale Programmiersprache, die kompiliert und ausgeführt werden kann. In dieser Funktion wird VHDL haupsächlich zum Schreiben von Simulationen (Testbenches) verwendet. Näheres dazu im Artikel VHDL Testbench. [Bearbeiten] VHDL als HardwarebeschreibungsspracheIn VHDL lassen sich auch digitale Schaltungen beschreiben. Die Beschreibung wird von einer Synthesesoftware in eine Netzliste umgesetzt. Der grundlegende Unterschied zur Verwendung von VHDL als Programmiersprache ist, dass man nicht beliebigen Code schreiben kann, sondern sich an bestimmte Strukturen halten muss die der Synthesizer kennt und in Hardware umsetzen kann. Wie diese Strukturen im allgemeinen aussehen ist weiter unten beschrieben, Details erfährt man in der Dokumentation der Software, z. B. dem XST User Guide. [Bearbeiten] Grundregeln für synthetisierbaren VHDL-CodeFolgende Grundregeln sollten vor allem Anfänger auf jeden Fall einhalten:
[Bearbeiten] Beispiel: Kombinatorischer Prozess
[Bearbeiten] Beispiel: Getakteter Prozess ohne Reset
[Bearbeiten] FAQ[Bearbeiten]
|
process(clk) begin if clk = '1' and clk'event then --snip end if; end process; |
und Variante 2:
process(clk) begin if rising_edge(clk) then --snip end if; end process; |
Welche ist zu empfehlen?
Antwort: Kurz gesagt bei der Synthese gibt es keine Unterschiede, in der Simulation kleine. Verwende die besser lesbare Variante. Die mit rising_edge() (bzw. falling_edge()) gilt allgemein als die bessere (Lesbarkeit und Simulationsgenauigkeit).
Wer es genau wissen will: Die klassische Variante ist nur korrekt für Signale die nur die Werte '1' und '0' annehmen können. Bei Signalen vom Typ std_logic (der Standardtyp für Signale) werden Flanken erkannt, die tatsächlich keine sind. Zum Beispiel beim Treiben eines PullUps ('H' -> '1' keine tatsächliche Flanke aber für clk = '1' and clk'event) oder beim Simulationsstart ohne Initialisierungswert für die Signale ('U' -> '1').(Signale mit Initialisierungswert werden so deklariert: signal a: std_logic := '0'; ). Die Funktionen rising_edge() und falling_edge() konvertieren den std_logic Wert vor dem Vergleich auf '1' bzw. '0' und simulieren so keine Flanke wo in der echten Hardware auch keine ist.
Beispiele:
-- Nutzung von Signalen -- die letzte Anweisung ist gültig und überschreibt alle vorhergehenden Anweisungen -- steht real in der Signaldefinition über begin signal a: std_logic; process(clk) begin if rising_edge(clk) then a <= a and b; a <= a and c; end if; end process; -- Ergebnis: a <= a and c, die Zeile zuvor wird ignoriert |
-- Nutzung von Variablen -- Aufeinanderfolgende Anweisungen werden sofort logisch wirksam und in die neue Anweisung einbezogen, die Anweisungen werden somit verkettet process(clk) variable a: std_logic; begin if rising_edge(clk) then a := a and b; a := a and c; end if; end process; -- Ergebnis: a <= a and b and c, Verkettung der Anweisung wie in normalen Programmiersprachen |
Sig1 := A + B; Sig2 := C + D; Sig3 := E + F; Result1 <= Sig1 - Sig2; Result2 <= Sig1 - Sig3; |
ist i.d.R. besser als
Result1 <= (A + B) - (C + D); Result2 <= (A + B) - (E + F); |
Wenns komplexer wird, ist es auch einfacher, daran was zu ändern.
if (opcode = add) then res <= a+b; else -- opcode = sub res <= a-b; end if; |
oder
if (opcode = add) then var1 := b; else var1 := -b; end if; res <= a+var1; |
Im ersten Fall wird ggf. ein Addierer und ein Subtrahierer (noch'n Addierer) eingebaut und das Ergebniss gemultiplext, im zweiten Fall wird eventuell nur ein Addierer eingebaut und der b-Eingang des Addierers gemultiplext. Sind a und b beispielsweise 32-Bit-Vektoren, dann macht das HW-mäßig schon was aus. Gute Synthesetools sollten dies aber mittlerweile automatisch machen, so das in beiden Fällen dasselbe rauskommt (war nicht immer so). (BTW, ich selber tendiere normalerweise zur ersten Variante, da besser lesbar - und man die HW-Implementierung nicht notwendigerweise vorwegnehmen soll, aber da hat jeder seine eigene Meinung zu ...)
Bei Analogen Filtern ist es oft notwendig, iterative Schleifen zu verwenden, um die Ergebnisse zu erlangen. Nur die Verwendung von Variablen und Loops gestattet es, komplexe Rechenergebnisse von "analoger" Reaktionsgeschwindigkeit vom Simulator zu erlangen, ohne Simulationszeit oder gar Takte vergehen zu lassen.
Simulatoren rechnen i.d.R. mit Variablen schneller als mit Signalen. Hat man also viele Prozesse mit Signalen vs. Variablen in einem großen Design, dann kann das schon was ausmachen ... Siehe auch oben bei asynchronen Prozessen, die mit Signalen ggf. wesentlich öfters durchlaufen werden. Auch wenn also ein System mit Signalen abzubilden ist, so empfiehlt sich der Einsatz von Variablen mitunter dennoch.
Ausnahme: I.d.R. sind nur auf Signalen und getakteten Prozessen basierende Modelle voll und leicht synthesierbar. Soll z. B. ein Modell in einem HIL System getestet werden, ist die Verwendung von Signalen mit entsprechender Berücksichtigung des timing angezeigt.
Kodierschaltungen (Coder) sind Schaltungen mit einem mehrstelligen Ein- und Ausgang. In der Schaltung werden keine FF oder andere Speicher benutzt. Ein typisches Beispiel ist die Wandlung einer Binärzahl in eine Binär Codierte Dezimalzahl. Eine sehr übersichtliche Schreibweise benutzt ein Konstanten-Feld.
Vor- und Nachteile verschiedener VHDL-Varianten einen Coder zu beschreiben werden hier besprochen.
If-Bedinungen sind außerhalb eines Prozesses nicht möglich. Lösung:
vector_or <= '0' when oder_vector = X"0000" else '1'; |
Dies nennt der Fachman bedingte Zuweisung (conditional assignment).
Auf null setzen:
count <= (others => '0'); |
Auf eins setzen:
count <= (others => '1'); |
Um einen std_logic_vector auf "00000..." oder "1111..." zu vergleichen, kann die (others => '0') Schreibweise nicht verwendet werden, weil die Vektorbreite dabei nicht definiert ist. Hier muss also ein Bereich angegeben werden:
if VECTOR = (15 downto 0=>'0') then.. |
oder
if VECTOR = (VECTOR'range=>'0') then... |
Diese Schreibweise gilt universell für alle Libs.
Werte ungleich "000.." und "1111..." müssen bei Verwendung der NUMERIC_STD.ALL etwas aufwendiger umgewandelt werden (hier der Wert 77):
if VECTOR = std_logic_vector(to_unsigned(77,VECTOR'length)) then... |
Wird die herstellerabhängige Synopsis-Lib STD_LOGIC_UNSIGNED.ALL verwendet, kann auch einfach so geschrieben werden:
if VECTOR = 0 then... |
Genauso einfach geht ein Vergleich auf z. B. den Wert 77:
if VECTOR = 77 then... |
Report kann nur Strings verarbeiten, deswegen muss ein std_logic_vector in einen String verwandelt werden:
report integer'image(to_integer(unsigned(rdata))); |
http://de.wikibooks.org/wiki/VHDL