Moin,
letztens hatte ich eine Diskussion über VHDL-Beschreibungsarten. Ich
habe mir VHDL selber beigebracht, der andere hat es im Studium gelernt.
Darf man getaktete Zuweisung und Kombinatorik mischen oder sollte das in
zwei Prozessen getrennt sein.
Ein Beispiel habe ich hier anhand eines einfachen Zählers, der ständig
bis zum Maximalwert hochzählt und dann wieder bei 0 anfängt.
Ich würde das in einen Prozess schreiben
1
entityGemeinsamis
2
port(
3
clk:instd_ulogic;
4
reset:instd_ulogic;
5
Wert:outstd_ulogic_vector(7downto0)
6
);
7
endentityGemeinsam;
8
9
architectureVerhaltenofGemeinsamis
10
signalZaehler:integerrange0to255;
11
constantMaximal:integer:=20;
12
begin
13
process(clk)
14
begin
15
ifrising_edge(clk)then
16
ifreset='1'then
17
Zaehler<=0;
18
else
19
ifZaehler=Maximalthen
20
Zaehler<=0;
21
else
22
Zaehler<=Zaehler+1;
23
endif;
24
endif;
25
endif;
26
endprocess;
27
28
Wert<=std_ulogic_vector(to_unsigned(Zaehler,8));
29
endVerhalten;
Im Studium wurde es wohl so gelehrt, mit zwei Prozessen
1
entityGetrenntis
2
port(
3
clk:instd_ulogic;
4
reset:instd_ulogic;
5
Wert:outstd_ulogic_vector(7downto0)
6
);
7
endentityGetrennt;
8
9
architectureVerhaltenofGetrenntis
10
signalZaehler:integerrange0to255;
11
signalZaehler_nach:integerrange0to255;
12
constantMaximal:integer:=20;
13
begin
14
process(clk)
15
begin
16
ifrising_edge(clk)then
17
Zaehler<=Zaehler_nach;
18
endif;
19
endprocess;
20
21
process(reset,Zaehler)
22
begin
23
ifreset='1'then
24
Zaehler_nach<=0;
25
else
26
ifZaehler=Maximalthen
27
Zaehler_nach<=0;
28
else
29
Zaehler_nach<=Zaehler+1;
30
endif;
31
endif;
32
endprocess;
33
34
Wert<=std_ulogic_vector(to_unsigned(Zaehler,8));
35
endVerhalten;
Ob ich das in der zweiten Version so richtig hinbekommen habe, weiß ich
nicht ganz genau, weil ich diese Schreibweise eigentlich nicht verwende.
Zumindest funktioniert es in der Simulation.
Es geht darum, ob in diesem Beispiel das Zaehler+1 in dem getakteten
Teil stehen soll oder in einem eigenen Prozess.
Es gibt die Regel, dass man rein kombinatorische Prozesse oder rein
sequentielle Prozesse verwenden soll, aber das bezieht sich doch eher
auf sowas wie
1
process(clk)
2
begin
3
ifrising_edge(clk,[andereSignale])then
4
[Anweisungen]
5
endif;
6
7
Signal<=[KombinatorikausanderenSignalen]
8
endprocess;
oder?
Mir fällt auf, dass die zweite Variante eine Verzögerung von einem Takt
hat. Außerdem muss der kombinatorische Prozess eine Senstitivity-List
mit vielen Signalen haben, wodurch es leicht zu Fehlern kommen kann.
Deshalb bevorzuge ich die erste Version. Aber irgendeinen Grund muss es
doch geben, dass die zweite gelehrt wird.
Was meint ihr, welche ist besser, welche benutzt ihr, welchen Grund gibt
es, die eine zu benutzen und nicht die andere?
Ich persönlich finde die 1-Prozess-Methode leichter lesbar (komme aber
von C her und habe deshalb vielleicht andere Präferenzen).
Ansonsten kann ich noch den Guru (Lothar Miller) zitieren, er sagt über
die 2-Prozess-Darstellung: "Aber wer sich den Code ein halbes Jahr
später anschaut, wird den Kopf schütteln, und sich fragen, wie und was
da wann wozu verwendet wird". Der vollständige Text, sehr lesenswert
(wie auch alles andere auf seiner Seite):
http://www.lothar-miller.de/s9y/archives/43-Ein-oder-Zwei-Prozess-Schreibweise-fuer-FSM.html
Dussel schrieb:> Darf man getaktete Zuweisung und Kombinatorik mischen oder sollte das in> zwei Prozessen getrennt sein.
Man darf im Prinzip, was man will...
Aber man sollte es nicht "mischen" in dem Sinn, dass Takte und
Kombinatorik im Prozess beliebig durcheinander kommen (aus wenn das
tatsächlich umgesetzt werden könnte):
1
entityGemeinsamis
2
port(
3
clk:instd_ulogic;
4
reset:instd_ulogic;
5
Wert:outstd_ulogic_vector(7downto0)
6
);
7
endentityGemeinsam;
8
9
architectureVerhaltenofGemeinsamis
10
signalZaehler:integerrange0to255;
11
constantMaximal:integer:=20;
12
begin
13
process(clk,Zaehler)
14
begin
15
ifrising_edge(clk)then
16
ifreset='1'then
17
Zaehler<=0;
18
else
19
ifZaehler=Maximalthen
20
Zaehler<=0;
21
else
22
Zaehler<=Zaehler+1;
23
endif;
24
endif;
25
endif;
26
Wert<=std_ulogic_vector(to_unsigned(Zaehler,8));
27
endprocess;
28
29
endVerhalten;
Der Rest ist die Dskussion um die 1-Prozess vs. 2-Prozess-Beschreibung.
Hierzu ein paar Worte:
http://www.lothar-miller.de/s9y/archives/43-Ein-oder-Zwei-Prozess-Schreibweise-fuer-FSM.html> Mir fällt auf, dass die zweite Variante eine Verzögerung von einem Takt > hat.
Das kommt darauf an, welches Signal du ansiehst: den Eingang des
Flipflops oder seinen Ausgang. Solche unerwartete "Latency" hast du eher
mal, wenn du mit der 1-Prozess-Schreibweise neu beginnst...
Rolf E. schrieb:> Appsys ProAudioRené D. schrieb:> Gibt es einen Unterschied zwischen den beiden Anweisungen?
Das mit dem "wait" kapiert nicht jeder Synthesizer. Simutechnisch ist es
dasselbe. Meine ich.
Die Ein-, Zwei- und Dreiprozessschreibweise für Zustandsautomaten kenne
ich, aber handelt es sich bei dem gezeigten denn um einen
Zustandsautomaten?
Man kann natürlich die einzelnen Zählerstände als Zustände auffassen,
aber ein praktischer Zustandsautomat wird es dadurch doch noch nicht,
oder?
Rolf E. schrieb:> Ich persönlich finde die 1-Prozess-Methode leichter lesbar (komme aber> von C her und habe deshalb vielleicht andere Präferenzen).
Das geht mir genauso.
René D. schrieb:> Gibt es einen Unterschied zwischen den beiden Anweisungen?
Gibt's da auch was von Lothar Miller?
Selbstverständlich:
http://www.lothar-miller.de/s9y/archives/16-Takt-im-Prozess.html ;-)
Wir verwenden die 2Prozess-Methode ala Gaisler.
@TO:
Signale nicht in Sensitivity List:
Deshalb benutzt oben genannte Methode Records.
Ein Takt Delay:
Du machst eine Zuweisung von "Zaehler" statt "Zaehler_nach"
Das Reset kann/muss/sollte dann oben asynchron im clocked process
stehen. Der Reset Release muss synchron erfolgen.
Matthias schrieb:> Wir verwenden die 2Prozess-Methode ala Gaisler.
Ist das von denen so definiert worden? Ich kenne das ebenfalls als
zwei-Prozess-Beschreibung, schon aus der Software - ganz ohne Gaisler.
Ja, ist Teil des Coding Styles.
Ich weiß auch dass andere Riesen nicht nach 2 Prozess machen.
Es ist halt, wie so oft, Religion. Clock Domain Crossing erhöht
natürlich die Zahl an Prozessen...
In den Coding Standard steht nicht "tu es wie gaisler", aber ich würde
meinen, dass wir im Wesentlichen dem folgen.
Ich kenne beides und finde auch beides ok. Beides hat +\-, wobei mir
momentan die 2 Prozess Methode gut gefällt.
Wie gesagt: gefallen... kommt eh dasselbe raus, meistens hoffe ich.
Ich hab mich mit dem Thema auch schon öfters bschäftigt (meistens mit
Focus auf SV, aber das ist ja nur die Sprache), deshalb hier meine
(etwas längeren) 2Cents:
Ich bin auch ein Anhänger des "Gaisler Styles"
(http://www.gaisler.com/doc/vhdl2proc.pdf). Die wichtigsten Vorteile für
mich:
- im kombinatorischen Prozess steht rein die funktionale Beschreibung
des Moduls, clock und reset sehen im eigenen Prozess. Durch die
Schreibweise mit records entsteht nicht mehr Code als beim 1
Prozessmodell.
- ich kann auch nicht getaktete Signale steuern
- im kombinatorischen Prozess kann ich zugewiesene Variablen
weiterverwenden (ja, das wird selten benötigt)
- im Simulator kann ich sehr einfach zwischen Registern (alles was in
dem Register record ist) und kombinatorischen Signalen unterscheiden.
- Manchmal ist es hilfreich in der Simulation die next state signale
beobachten zu können
- ich definiere die Resetwerte als Konstante des Registerrecords, somit
kann ich die Resetwerte gleich unterhalb von der Definiton des
Registerrecords machen. Weiters kann ich den Reset öfters zuweisen ohne
alles neu zu schreiben. Z.B. wenn es neben dem asynchronen Reset noch
einen Synchronen (Soft) Reset gibt.
Das Problem mit dem Verwalten der Sensitivitylist gibts seit VHDL 2008
eigentlich nicht mehr. Es gibt "process(all)"
Die Argumente hier von Lothar
(http://www.lothar-miller.de/s9y/archives/43-Ein-oder-Zwei-Prozess-Schreibweise-fuer-FSM.html)
kann ich nicht nachvollziehen. Meiner Meinung nach wird in diesem Post
das 2 Prozessmodell nicht konsequent angewendet. Denn 'delay' ist das
einzige Signal, das im getakteten Prozess maintained wird. Warum? Hier
sollte nur "delay <= next_delay;" stehen und alles weitere wieder im
kombinatorischen.
Novice schrieb:> Ich hab mich mit dem Thema auch schon öfters bschäftigt (meistens mit> Focus auf SV,
Das scheint mir Wesentlich. Wenn SW angekoppelt wird, sind
zusammengefasste Datentypen vorteilhaft, da stimme ich zu. Allerdings in
Designs, in denen ein hartes timing im Modul generiert wird, nicht.
Alles, was IO ist, bekommt da nur einen Mehraufwand.
> - im Simulator kann ich sehr einfach zwischen Registern (alles was in> dem Register record ist) und kombinatorischen Signalen unterscheiden.
Das ist aber eine Frage der Beschreibung:
Wenn man Präfixes sauber benutzt, hat man keinen Nachteil, es bleibt
aber der Vorteil, dass es zwischen Record-Signalen und Variablen
ausserhalb von Records keinen Unterschied gibt.
> Z.B. wenn es neben dem asynchronen Reset noch> einen Synchronen (Soft) Reset gibt.
Etwas, was es in einem sinnvoll aufgezogenen FPGA-Design niemals gibt
:-)
Markus F. schrieb:> Das mit dem "wait" kapiert nicht jeder Synthesizer.
Doch, inzwischen schon. Vor 10 Jahren war das noch neu. Da gab es noch
welche, die Probleme hatten. Z.B. auch mit der Initialzuweisung an
Signale...
Novice schrieb:> Meiner Meinung nach wird in diesem Post das 2 Prozessmodell nicht> konsequent angewendet. Denn 'delay' ist das einzige Signal, das im> getakteten Prozess maintained wird. Warum? Hier sollte nur "delay <=> next_delay;" stehen und alles weitere wieder im kombinatorischen
Richtig, und genau das verstehen offenbar viele Anfänger nicht. Such
einfach mal hier im Forums nach "kombinatorische Schleife" oder
"combinatorial loop". Dieser Fehler taucht laufend auf. Und zu 99% warst
dann eben ein Zähler im kombinatorischen Prozess...
Novice schrieb:> . Z.B. wenn es neben dem asynchronen Reset noch einen Synchronen (Soft)> Reset gibt.
Welches FPGA kann das? Oder andersrum: warum sollte man etwas
beschreiben, was hinterher nur umständlich und aufwändig in Hardware
umgesetzt werden kann?
Ich sehe hier das selbe Problem wie bei der Softwareentwicklung: obwohl
die Prozessoren schneller werden, werden die Programme langsamer und
"fetter"...
Weltbester FPGA Pongo schrieb im Beitrag #4605911:
> Novice schrieb:>> Ich hab mich mit dem Thema auch schon öfters bschäftigt (meistens mit>> Focus auf SV,> Das scheint mir Wesentlich. Wenn SW angekoppelt wird, sind> zusammengefasste Datentypen vorteilhaft, da stimme ich zu. Allerdings in> Designs, in denen ein hartes timing im Modul generiert wird, nicht.> Alles, was IO ist, bekommt da nur einen Mehraufwand.
Mit SV meinte ich SystemVerilog.
@Lothar:
Aja, den entscheidenden Satz hab ich vorher überlesen:
>Jetzt könnte ich das Spiel genauso wie mit counter und next_counter machen,>aber diesmal bin ich zu faul und nehme den Verzögerungszähler delay einfach>gleich in den getakteten Teil:
Naja, mit dem Gaisler Style wird die Faulheit umgangen, da man das
Signal auch nur einmal definieren muss.
Aber wie schon weiter oben erwähnt, 1 vs. 2 Prozesse ist eine
Entscheidung des Entwicklers. Ein richtig oder falsch gibts nicht.
@Lothar:
Wird die 2 Prozess Methode richtig angewendet, gibt's die
Kombinatorische Schleife gar nicht, da typischerweise stehen sollte:
S.count := Reg.count + 1;
Aber ja, weißt ja eh, "wenn man es richtig macht"... ;-)
Markus F. schrieb:> Niemals nicht hab' ich das geschrieben!
Richtig, da zitiert die Foren-SW ein wenig ungünstig:
Markus F. schrieb:> MeineMarkus F. schrieb:> NiemalsMatthias schrieb:> Wir verwenden die 2Prozess-Methode ala Gaisler.
Gaisler schafft es hier, einen Beschreibungsstil (genau diese
2-Prozessmethode mit getrennter Weiterschaltlogik und Registern) der
schon früh am Ende des letzten Jahrtausends in jedem Lehrbuch stand, im
Jahre 2010 in neue Schläuche zu füllen und als frischen (und vor Allem:
eigenen) Wein zu verkaufen...
Dann wird es jetzt wohl Zeit, die Prozesslandschaft 4.0 zu definieren
und noch eine weitere kompliziertere Methode als NEU einzuführen. Immer
mal was Neues.
René D. schrieb:> Die letzt Zuweisung gewinnt.> Wenn der Reset am Ende des Process ist, dann enfällt das else in der> höchsten if-Ebene.
Das ist eine Sache, die viele Firmen in ihren Richtlinien ausschließen,
nämlich, dass die Reihenfolge der Anweisungen eine Rolle spielen darf.
Bevorzugt werden ALLE Signale in allen Zweigen klar und ausdrücklich
beschrieben. Wenn es zu kompliziert wird und die Bedinungen zu unschön
überlappen, dann werden eben mehrere Prozesse definiert und zwar einer
für jedes Signal.
Das erleichtert das Debuggen, weil explizite Zuweisungen existieren und
Denkfehler bei der manuellen Simulation, die man ja quasi durchführen
muss, nicht aufkommen oder ausdrüclich einprogrammiert hätten werden
müssen.
In der Gesamtbetrachtung ist es so, daß es momenten noch recht egal ist,
welche Schreibweisen der oberen verwendet wird, aber das ändert sich,
wenn komplexe Rechenalgorithmen eingebaut werden sollen.
In der Mehrprozessschreibweise muss man dann immer die
Vorher-Nachhersignale definieren. Braucht kein Mensch.Wir benutzten ja
die Hochsprache, um Zuweisungen implizit zu vollziehen und nicht die
FF-Struktur aufmalen zu müssen.
Jürgen S. schrieb:> In der Mehrprozessschreibweise muss man dann immer die> Vorher-Nachhersignale definieren. Braucht kein Mensch.Wir benutzten ja> die Hochsprache, um Zuweisungen implizit zu vollziehen und nicht die> FF-Struktur aufmalen zu müssen.
Naja, schau dir mal den Gaisler Style genauer an.
Die Querverbindung kam über die Gaisler-Pamphlete und die fragwürdige
Schlussfolgerung einiger Jünger, daß wenn eine Gaisler-Idee gut ist, der
Rest auch gut sein müsse.
Eine spontane Umfrage in der hiesigen Abteilung ergab im Übrigen:
3 kennen das gar nicht
4 kennen das, benutzen es aber nicht
2 kennen das, benutzen es nur teilweise
1 kennt es und hält sich weitgehend dran
Das ist ein Anteil von 14% derer, die es kennen. In ganz Deutschland
gibt es also unter allen 44000 embedded Ingenieuren rund 6000
Gaisler-Jünger.
Bagwhan hatte zu seinen Besten Zeiten 100mal mehr. Da gibt es also noch
Überzeugungsarbeit zu leisten :D