Hallo zu alle!
Ich habe eine Anfängerfrage:
wenn in einem Prozess alle Anweisungen sequenziell ablaufen, werden dann
bei der folgenden Schreibweise 3 Multipliziereinheiten instantiiert?
process begin
result1 <= std_logic_vector(signed(x1)*signed(y1));
result2 <= std_logic_vector(signed(x2)*signed(y2));
result3 <= std_logic_vector(signed(x3)*signed(y3));
wait on x1;
end process;
und bei der:
process begin
result1 <= std_logic_vector(signed(x1)*signed(y1));
result2 <= std_logic_vector(signed(x2)*result1);
result3 <= std_logic_vector(result1*result2);
wait on x1;
end process;
wahrscheinlich auch?
Wann wird eigentlich nur 1 instantiiert? Wenn ich statt Signalen
Variablen benutzen werde?
Danke im Voraus für die Hilfe!
Beim Ersten ist alles nach einem Takt fertig.
Beim Zweiten sind genauso wie beim Ersten auch drei parallele
Multiplizierer, aber: Dein Ergebnis result3 ist erst nach 3 Takten das,
was du erwartest.
ABER: result2 und result3 haben schon vorher einen Wert, irgendeinen
oder einen Festen je nachdem was du vorgibst, aber erst nach dem 3. Takt
haben die ein Ergebnis das bei der Rechnung rauskommt/das du erwartest.
Wie macht man sowas seriell - also nur mit einem Multiplizierer?
Vielleicht so:
signal count: integer range 0 to 2:=0;
process begin
wait until rising_edge(clock);
if count < 2 then
count <= count +1;
end if;
if count = 0 then
result1 <= std_logic_vector(signed(x1)*signed(y1));
elsif count = 1 then
result2 <= std_logic_vector(signed(x2)*result1);
elsif count = 2 then
result3 <= std_logic_vector(result1*result2);
end if;
end process;
Syntax hat vermutlich Fehler aber du kannst hoffentlich erkennen was ich
meine. Jetzt wird nämlich in jedem Takt nurnoch eine Multiplikation
durchgeführt.
Das wait ist der Takt. Aber dass ein "wait on" auch geht, ist mir auch
neu. Welcher Synthesizer kann das?
> Wann wird eigentlich nur 1 instantiiert?
Dann, wenn die Multiplikation zeitgleich nur 2 Faktoren hat.
Ja, es geht um VHDL. Das Clock-Signal möchte ich eigentlich nicht
benutzen, das sollte ja auch irgendwie rein kombinatorisch gehen. Oder
irre ich mich da?..
Wäre es eigentlich möglich bei einem rein kombinatorischen Prozess
(getriggert z.B. auf Änderung von einem der Eingangssignalen)
festzustellen, dass die Multiplikation gerade fertig geworden ist? Und
dann reinkombinatorisch den Zustand umschalten? (Da stelle ich mir eine
Art von endlichem Zustandautomaten vor) Würde sowas mit Variablen
"Result" und zusätzlichen "Trigger-Signalen" auch vom Typ Variable
gehen?
Lothar Miller schrieb:> Das wait ist der Takt. Aber dass ein "wait on" auch geht, ist mir auch> neu. Welcher Synthesizer kann das?
Der von Synopsis hat da nicht bemekert...
Lothar Miller schrieb:> Dann, wenn die Multiplikation zeitgleich nur 2 Faktoren hat.
Genau da liegt mein Verständniss Problem: wenn in einem Prozess alle
Schritte sequenziell ablaufen, also, nacheinander, es sollten ya mit
Variablen klappen. Oder nicht?..
Ich hatte das eigentlich oben beschrieben ... und auch in einem Beispiel
grob skizziert wie man das macht, dass in jedem Takt nur eine
Multiplikation läuft.
VHDL ist nicht wie C oder so, dass Dinge die untereinander stehen
nacheinander ablaufen. Alles geht gleichzeitig, ausser du machst es
explizit wie ich oben in dem Beispiel anders.
-gb-
spinne schrieb:> Genau da liegt mein Verständniss Problem: wenn in einem Prozess alle> Schritte sequenziell ablaufen, also, nacheinander, es sollten ya mit> Variablen klappen. Oder nicht?..Gustl Buheitel schrieb:> if count = 0 then> result1 <= std_logic_vector(signed(x1)*signed(y1));> elsif count = 1 then> result2 <= std_logic_vector(signed(x2)*result1);> elsif count = 2 then> result3 <= std_logic_vector(result1*result2);> end if;> end process;>> Syntax hat vermutlich Fehler aber du kannst hoffentlich erkennen was ich> meine. Jetzt wird nämlich in jedem Takt nurnoch eine Multiplikation> durchgeführt.
Das dürfte nur zur Hälfte stimmen.
In Hardware werden ziemlich wahrscheinlich 3 Multiplikationseinheiten
angelegt, die mit jedem Takt multiplizieren, aber jedes Ergebnis wird
immer nur zu einem bestimmten Takt verwendet.
Es würde mich stark wundern, wenn der Compiler so intelligent wäre, dass
er erkennt, dass im Grunde nur immer nur eine Multiplikationseinheit
verwendet wird.
Dein Beispiel ist deshalb eher unsinnig.
spinne schrieb:> Genau da liegt mein Verständniss Problem: wenn in einem Prozess alle> Schritte sequenziell ablaufen, also, nacheinander, es sollten ya mit> Variablen klappen. Oder nicht?..
Ja, aber ...
Deine Beispiele sind aber nicht equivalent.
Dein erstes Beispiel berechnet 3 Multiplikationen x1*y1, x2*y2, x3*y3
Dein zweites Beispiel berechnet (x1*y1*x2)*(x1*y2), zumindest im
Prinzip, da die x und y Werte von verschiedenen Takten verwendet werden.
Was willst Du wirklich berechnen?
Gustl Buheitel, ich verstehe das alles schon und glaube, dass ich meine
Denkweise von C auf VHDL bereits umgestellt habe. Ich suche halt eine
Möglichkeit einige Multiplikationen parallel durchzuführen und einige
solche Schritte nacheinander zu machen. Dabei möchte ich möglichst
schnell machen, also nicht getaktet, sondern kombinatorisch. Und dafür
brauche ich z.B. ein gestzter Bit, den mir zeigt, dass die
Multiplikation vollstendig ausgeführt wurde und die Eingenge dürfen nun
gemultiplext werden. Villeicht irre ich mich und so was grundsetztlich
nicht möglich ist. Genau da brauche ich Hilfe von erfahrenen
VHDL-Entwickler.
Na dann mach es halt anders:
X <= Y * Z;
if count = 0 then
Y <= x1;
Z <= y1;
elsif count = 1 then
Y <= x2;
Z <= X;
elsif count = 2 then
Y <= Z;
Z <= X;
end if;
Ach so, ja was willst du denn in diesem Fall machen? Sollen die 3
Multiplikationen parallel laufen? Sollen die komplett nacheinander
laufen um Hardware zu sparen?
Klaus Falser, als Beispiel:
Ich möchte möglichst schnell x^4 und y^4 berechnen. Dabei möchte ich die
Anzahl der Multiplizierer auf 2 begrenzen, also, 2 Schritte zu machen:
x*x; x^2*x^2;
Frage: wann soll ich die Eingänge ändern? Wie erkenne ich, dass die
Multiplikation vertig ist und der Wert am Ausgang stimmt?
Wann die Multiplikation fertig ist sagen dir die Tools des FPGA
Herstellers, die machen eine Timing-Analysen und sagen dir mit welchem
Takt du das ganze noch betreiben kannst damit es funktioniert.
Ja, da bin ich sicher, das sowas funktionieren würde. Es gilt für z.B.
eine 16 Bit-Multiplikation: min 15ns bis max 75ns. Immer 75ns zu warten
wäre aber uneffizient. Besser wäre eine fertige Multiplikation zu
signalisieren. Geht das mit Sicherheit nicht?
Z.B.:
1
process1begin
2
3
result1:=std_logic_vector(signed(x1)*signed(y1));
4
a:=nota;
5
result2:=std_logic_vector(signed(x2)*signed(y2));
6
b:=notb;
7
result3:=std_logic_vector(signed(x3)*signed(y3));
8
c:=notc;
9
waitont;--t ein external Trigger
10
endprocess;
11
12
process2begin
13
waiton(aandbandc);-- a,b,c - shared variable
14
result4<=std_logic_vector(result1*result2);
15
result5<=std_logic_vector(result1*result3);
16
result6<=std_logic_vector(result2*result3);
17
18
endprocess;
Werden da 3 oder 6 Multiplizierer verwendet? Werden die Variablen a,b
und c eigentlich sofort nach der Multiplikation zugewiesen?
Das geschieht ALLES gleichzeitig - und dauert so lange wie es jeweils
dauert.
Also es passiert folgendes:
a:=not a;
b:=not b;
c:=not c;
geht schnell weil es nur ein NOT und keine Multiplikation ist.
result1 := std_logic_vector(signed(x1)*signed(y1));
result2 := std_logic_vector(signed(x2)*signed(y2));
result3 := std_logic_vector(signed(x3)*signed(y3));
Die drei brauchen etwas länger.
Also wirst du als Ergebins sehen, dass sich zuerst und zwar
gleichzeitig, die Werte von a,b und c ändern und erst danach erst werden
die Ergebnisse der Multiplikationen fertig sein. Die Ergebinsse der
Multiplikationen natürlich auch wieder einigermaßen gleichzeitig.
Die Ergebinsse aus process 2 werden wohl nicht gültig sein, da der
Process schon startet wenn a, b und c ihren Wert ändern - aber da sind
die Multiplikationen in process 1 noch nicht fertig.
Hm... Ok, aber was bedeuten dann, dass Processe sequenzielle Instanzen
sind? Das kapiere ich nun gar nicht. Ich stelle mir das so vor, dass
zuerst erste Multiplikation durchgeführt wird, Result1 wird zugewiesen
und unmittelbar danach wird Variable a zugewiesen und nur dann kann
Result2 ausgerechnet werden usw... Falls a,b und c gleichzeitig
zugewiesen werden, dass passiert auf jeden Fall früher, als die
Multiplikationen durchgeführt werden - was ist dann hier "sequenziell"
gemacht?
spinne schrieb:> Ja, da bin ich sicher, das sowas funktionieren würde. Es gilt für> z.B.> eine 16 Bit-Multiplikation: min 15ns bis max 75ns. Immer 75ns zu warten> wäre aber uneffizient. Besser wäre eine fertige Multiplikation zu> signalisieren. Geht das mit Sicherheit nicht?>> Z.B.:> process 1 begin>> result1 := std_logic_vector(signed(x1)*signed(y1));> a:=not a;> result2 := std_logic_vector(signed(x2)*signed(y2));> b:=not b;> result3 := std_logic_vector(signed(x3)*signed(y3));> c:=not c;> wait on t;--t ein external Trigger> end process;>> process 2 begin> wait on (a and b and c);-- a,b,c - shared variable> result4 <= std_logic_vector(result1*result2);> result5 <= std_logic_vector(result1*result3);> result6 <= std_logic_vector(result2*result3);>> end process;>> Werden da 3 oder 6 Multiplizierer verwendet? Werden die Variablen a,b> und c eigentlich sofort nach der Multiplikation zugewiesen?
Nimm es bitte nicht persönlich, aber man sieht, dass Du wirklich noch
einige Verständnisschwierigkeiten hast.
Zuerst einmal:
- "shared variable" : Vergiss das Wort und nimm es bitte die nächsten 5
Jahre nicht mehr in den Mund.
Mit VHDL baust Du Hardware, d.h. Du verdrahtest Hardware-Einheiten (eben
z.B. Multiplikatoren) miteinander. Diese Einheiten sind immer da, und
arbeiten kontinuierlich.
z.B. Dein Prozess 1
Dieser ergibt z.B. 3 Multiplizierer, deren Ergebnis in result1 bis
result3 gespeichert werden, wenn t sich ändert.
Dies ist so nicht synthetisierbar, weils Speicherelemente in FPGA's
üblicherweise nur auf eine Flanke reagieren, also entweder t ändert sich
von 0 auf 1 (rising edge) oder von 1 auf 0 (falling edge).
Wir reden also schon einmal über Hardware, die es nicht gibt.
Aber warum willst Du auf einen Trigger warten?
Wenn Du das ganze außerhalb eines Prozesses schreibst, dann arbeiten die
Multiplikationseinheiten die ganze Zeit und das Ergebnis ist voll
kombinatorisch.
Also zum Berechnen von
-- x * x
result1 <= std_logic_vector(signed(x)*signed(x));
-- und nun x^2 * x^2
result2 <= std_logic_vector(signed(result1)*signed(result1));
Das ergibt genau 2 Multiplikatoren, diese haben aber eine gewisse
Durchlaufzeit.
Das Ergebnis result2 wird sich immer dann ändern, wenn sich x ändert,
aber wann ändert sich x?
Und genau aus diesem Grunde verwendet man einen Takt, den Du so
vermeiden willst.
Wenn Du das Design taktest, dann ändert sich x genau zur Taktflanke und
der Multiplikator hat 1 Taktzyklus Zeit sein Ergebnis zu berechnen. Zur
nächsten Taktflanke muss das Ergebnis stabil sein, denn da wird es von
der nächsten Stufe weiterverarbeitet.
Klaus Falser schrieb:> Nimm es bitte nicht persönlich, aber man sieht, dass Du wirklich noch> einige Verständnisschwierigkeiten hast.
Klar, deswegen bin ich ja hier =) Ich bin noch ziemlich am Anfang, aber
habe vor, sich möglichst gut in VHDL einzuarbeiten. Leider wird diese
Sprache in meinem Studiengang nicht unterrichtet, deswegen muss ich
jetzt gleichzeitig teoretische kenntnisse und praktische Vorgehensweise
erwerben.
Und es bleibt für mich immer noch nicht ganz klar, was in Processen
nacheinander abläuft. Warum sind sie sequenzielle Instanzen, wenn sogar
die Reihenfolge von Zuweisungen nicht sequenziell ist?
Klaus Falser schrieb:> Dies ist so nicht synthetisierbar, weils Speicherelemente in FPGA's> üblicherweise nur auf eine Flanke reagieren, also entweder t ändert sich> von 0 auf 1 (rising edge) oder von 1 auf 0 (falling edge).> Wir reden also schon einmal über Hardware, die es nicht gibt.> Aber warum willst Du auf einen Trigger warten?
Doch, das ist synthetisierbar und funktioniert zwar in der Hardware. Den
Beisielcode kann ich bei Interesse am Montag hier posten. Den Process
wird mit jeder Änderung des Zustandes aufgerufen, man kann ja auch auf
Vektoren triggern, dann wird jede Änderung von einzelnen Bits
detektiert. Das hatte ich auch bereits mal erfolgreich verwendet.
Auf den Trigger will ich deswegen warten, weil die Multiplikation nur
dann stattfinden soll, wenn beide Signale aktuell sind. Genau das
signalisiert mir das Triggersignal. Warum möchte ich kein Takt
benutzen?.. Weil somit werden alle multiplikationen in gleicher Zeit
erledigt, und diese Zeit ist dann die maximale Zeit. Werde ich meine
Hardware schneller takten - werden die Ergebnisse nicht mehr stabil beim
ablesen. Ich meine nur, wenn es z.B. zwei 16-Bit Vektoren multipliziert
werden sollen, wird das Ergebnis von "0000000000000001" *
"0000000000000001" schneller stabil als "1010101011100101" *
"1111010001001101". Dann geht mehr als 90% Zeit verloren. Genau das
möchte ich vermeiden, falls es geht.
spinne schrieb:> Und es bleibt für mich immer noch nicht ganz klar, was in Processen> nacheinander abläuft.
Nur die Berechnung des Ergebnisses im Simulator läuft da
"nacheinander" ab. In der Hardware (CPLD, FPGA, ASIC) passiert immer(!!)
ist alles gleichzeitig.
spinne schrieb:> Ok, aber was bedeuten dann, dass Processe sequenzielle Instanzen sind?
Ich sage mal ein wenig provokant: dieses "sequenziell" und "parallel"
wurde nur von Professoren erfunden, die ihre Studenten aufs Glatteis
führen wollten.
Genauso wie das Wort "nebenläufig" in VHDL gar nichts mit "unwichtig"
oder "zweitrangig" zu taun hat. Es bedeutet vielmehr "gleichzeitig"...
Ich habe schon gestern überlegt, was zu schreiben, es dann aber
gelassen. Das Problem ist, das diese Begriffe nur in Zusammenhang mit
einer räumlichen und zeitlichen Dimension wirklich Sinn machen. Ich kann
das, falls gewünscht noch weiter ausführen.
Andererseits hat Lothar sinngemäß recht, wenn er diese Begriffe
abqualifiziert, da sie eine mehr oder weniger triviale Tatsache eher
verbrämen als deutlich machen. Ich liebe ja Fremdwörter, aber hier
stiften sie eher Verwirrung.
Du musst Dir zuallererst klarmachen, das Du Strukturen beschreibst und
die Übernahme/Übergabe von Signalen anhand von Taktsignalflanken. Mehr
ist das alles nicht, wenn man der traditionellen Auffassung folgt, und
das ist für einen Anfänger ratsam (bin selbst noch halber Anfänger
allerdings mit 30 Jahren Erfahrung in Elektronik und Software).
Da ich zu abseitigen Fragestellungen eine gewisse Affinität habe, habe
ich auch Verständnis für diese: "Und dafür brauche ich z.B. ein gestzter
Bit, den mir zeigt, dass die Multiplikation vollstendig ausgeführt
wurde" auch wenn Du sie erstmal als Notwendigkeit und nicht als
Abseitigkeit ansiehst.
Vielleicht hilft es Dir ein wenig, wenn Du Dir die Aufgabenstellung mal
kurz vereinfachst. Nimm mal an, es handele sich um ein XOR-Verknüpfung.
(Prinzipiell ist das nur eine wesentlich einfachere Kombinatorik)
Überleg mal, beim Warten in der Mensa-Schlange, wie Du ein Bit berechnen
würdest, das Dir anzeigt, das die Verknüpfung fertig ist.
Wie würdest Du das anfangen?
In der Praxis aber, wird das Verfahren, das hier schon von einigen
beschrieben wurde, angewandt. Nämlich anhand von Takten Eingangswerte
und Ausgangswerte erst dann zu sampeln, wenn die Laufzeit durch eine
Kombinatorik garantiert abgelaufen ist.
Oops, hier fehlt was:
>Vielleicht hilft es Dir ein wenig, wenn Du Dir die Aufgabenstellung mal>kurz vereinfachst. Nimm mal an, es handele sich um ein XOR-Verknüpfung.>(Prinzipiell ist das nur eine wesentlich einfachere Kombinatorik)>Überleg mal, beim Warten in der Mensa-Schlange, wie Du ein Bit berechnen>würdest, das Dir anzeigt, das die Verknüpfung fertig ist.>Wie würdest Du das anfangen?
Und als nächstes, überleg Dir mal, wie Du in diesem Design, ein Bit
bildest, das anzeigt, dass das Bit, das anzeigt, das die Verknüpfung
fertig ist, fertig ist. :-)
Nun, bei der Multiplikation ist doch die Länge der beiden Werte dafür
verantwortlich wie lange es dauert. Also bei einem 8x8 Bit
Multiplizierer dauert ein "11010110" * "10100110" wohl länger wie ein
"00000110" * "00000101".
Wenn als die Laufzeit der Multiplikation von der Länge der Faktoren
abhängt, dann kann man da schon Bits haben die anzeigen wann die
Multiplikation sicher fertig ist.
Man prüft dafür zuerst die Längen der Faktoren und gestaltet dann das
Bit.
Also eben mit einer Festen Verzögerung für "Beide Faktoren sind 4 Bits
lang".
Man hat dann viele Bits die nacheinander verzögert gesetzt werden, und
zwar je nachdem wie man das abstufen will für viele unterschiedliche
Längen der Faktoren. Das ist natürlich zusätzliche Logik und es muss
auch die Länge der Faktoren überprüft werden - es überhaupt nur einen
Sinn bei deutlich unterschiedlichen Faktoren, denn wenn sich deren Länge
nur um wenige Stellen unterscheidet, ist der Zeitgewinn umso kleiner.
Was soll das denn bringen mit dem Busy-Bit? Da müsste ja das System, das
die Operanden rein füttert und aufs Ergebnis wartet, viel schneller ein
als der Multiplizierer. Und das externe System läuft ja ganz sicher mit
einem Takt. Solange dieser Takt unter der Grenze für die Multiplikation
bleibt, die die Toolchain nach dem Place&Route ausrechnet, kannst du
davon ausgehen, dass die Multiplikation innerhalb eines Taktes "fertig"
ist. Aktuelle FPGAs schaffen so eine Multiplikation locker bei
250...500MHz Systemtakt.
Keine Ahnung was es bringen soll, der Threadersteller hatte danach
gefragt, und Multiplikationen angegeben die nacheinander laufen sollen
weil sie jeweils das Ergebnis der Vorherigen brauchen. Mit so einem Bit
könnte man das rein kombinatorisch machen.
Ich bin aber natürlich auch wie hier schon im 2. Post klar werden sollte
für eine getaktete Lösung.
Ist doch dann auch egal, ob man so ein Bit hat. Die Multiplikation muss
doch sowieso im nächsten Takt fertig sein. Und bei einer Multiplikation
sind doch alle Faktoren austauschbar (wenn man nicht irgendwelche
Überlauf-Geschichten evtl. zwischendurch abfangen will). Da kann man
genauso gut auch
1
d<=a*b*c;
hinschreiben. Die maximal ereichbare Taktfrequenz sinkt halt dann.
Ungetaktet können meines Wissens (bei Xilinx) sowieso keine HW-MUL
genommen werden.
Hmm schrieb:> Und als nächstes, überleg Dir mal, wie Du in diesem Design, ein Bit> bildest, das anzeigt, dass das Bit, das anzeigt, das die Verknüpfung> fertig ist, fertig ist. :-)
Das ist eigentlich ganz einfach, aber komplett sinnlos: Du
multiplizierst parallel 2 bekannte Zahlen, bei denen 'einfach' ein
hoeherwertiges Bit als in deiner eigentlichen Berechnung gesetzt ist.
Damit dauert diese Multiplikation laenger als die eigentlich
gewuenschte. Und wenn du dieses (bekannte) Ergebnis detektierst, hast du
einen Trigger der dir sicher sagen kann, dass deine eigentliche MULT
fertig ist.
Aber die dazu benoetigte Logik ist mindestens 10x so komplex wie die
eigentliche Multiplikation. Aber funktionieren wuerde es...
So als Hintergrund: In den 90ern gab es Experimente und sogar einen
wirklich in Silizium gegossenen ARM mit 'self-timed logic' (ich meine
der ARM war von der Uni Cambridge). Er hat funktioniert, war aber wegen
des Ballasts deutlich langsamer als ein 'Standard'-Rechner. Das hatten
die Jungs mal auf der Hot-Chips (1995 oder 96 oder 97) vorgestellt. Und
bei meiner Ex-Firma haben wir mit sowas auch experimentiert, es aber
dann sein lassen (zu komplex, Toolchains komplett ueberfordert, effektiv
durch das mehr an Logik kein Gewinn, ...)
>Das ist eigentlich ganz einfach, aber komplett sinnlos...
Ebenso sinnlos wie Deine Antwort.
An Dich war die Frage nicht gerichtet, sondern an den TO. Für DEN hat
diese Überlegung ein didaktischen Zweck.
Du dagegen musstest Dich anscheinen profilieren.
Meine Güte.
@ Hmm
Warum so angesäuert? berndl hat doch nur seine (durchaus nennenswerten)
Erfahrungen eingebracht. Er hat dich nicht persönlich angegriffen,
sondern nur die Sinnlosigkeit des Verfahrens, die du schon
herausgestellt hast, nochmal verdeutlicht...
spinne schrieb:> Ich meine nur, wenn es z.B. zwei 16-Bit Vektoren multipliziert werden> sollen, wird das Ergebnis von "0000000000000001" * "0000000000000001"> schneller stabil als "1010101011100101" * "1111010001001101".
Das kannst du nur dann behaupten, wenn du die Struktur des
Multiplizierers gut kennst. Bis dahin bleibt alles reine Vermutung...
Lothar Miller schrieb:> @ Hmm> Warum so angesäuert? berndl hat doch nur ...
Ja. Schon gut. Sorry. Hätte ich auch ohne Sauerteig formulieren können.
Ich hielte es für besser, wenn der TO selbst sich Gedanken macht,
anstatt, das andere ihm ihre Erfahrungen schildern (so relevant sie auch
sein mögen).
Seine Erfahrung war doch garnicht gefragt, sondern das der TO selbst mal
grübelt. Wenn man ihm das vorkaut, macht er keine eigenen Erfahrungen
(selbst wenn es nur gedankliche sind). So sehe ich das.
Hmm schrieb:> sondern das der TO selbst mal grübelt.
Ja, spinne sitzt gerade ganz arg im hardwaremäßigen Abseits fest.
spinne schrieb:> Genau da liegt mein Verständniss Problem: wenn in einem Prozess alle> Schritte sequenziell ablaufen, also, nacheinander, es sollten ya mit> Variablen klappen. Oder nicht?..
Du denkst immer noch in einer (irgendeiner) prozeduralen
(Schritt-für-Schritt) Programmiersprache.
> ich meine Denkweise von C auf VHDL bereits umgestellt habe.
Du musst auch nicht in VHDL "denken", sondern in "Hardware". Und dann
VHDL nehmen, um das Gedachte zu beschreiben. Deshalb heißt es ja auch
"Hardwarebeschreibungssprache"...
Und wenn in VHDL drei "*"-Zeichen stehen, werden da drei Multiplizierer
instantiiert. Fertig. Wenn du für 3 Multiplikationen nur 1
Multiplizierer nehmen willst, dann musst du den mit einem Multiplexer
auf die unterschiedlichen Faktoren und Ergebnisse umschalten...
Lothar Miller schrieb:>> Genau da liegt mein Verständniss Problem: wenn in einem Prozess alle>> Schritte sequenziell ablaufen, also, nacheinander, es sollten ya mit>> Variablen klappen. Oder nicht?..> Du denkst immer noch in einer (irgendeiner) prozeduralen> (Schritt-für-Schritt) Programmiersprache.
es ist doch eigentlich ganz einfach: Eine VARIABLE ist der D-Eingang
eines FFs, ein SIGNAL ist der Q-Ausgang eines FFs. Dass bei D natuerlich
noch alles andere vorher als Laufzeit dazu kommt sollte doch bitte klar
sein! Bei Q faengt die Zeitrechnung eben einfacherweise wieder bei 0
an...
berndl schrieb:> es ist doch eigentlich ganz einfach
Ja, und zwar ganz einfach falsch. Signale und Variablen können beide
auch in reinen kombinatorischen Verknüpfungen auftauchen und haben gar
nichts mit FFs zu tun.
Olga schrieb:> Ja, und zwar ganz einfach falsch. Signale und Variablen können beide> auch in reinen kombinatorischen Verknüpfungen auftauchen und haben gar> nichts mit FFs zu tun.
in einem PROCESS mit CLK sehr wohl. Und ja, concurrent statements sind
auch nur 'fliegende' Logiksignale.
Ich habe mir folgendes angegewoehnt: Ein PROCESS hat eine clock, evtl.
noch einen async. reset. Alles andere wird mit concurrent statements
beschrieben. Damit ist auch klar: Ein 'signal' ist innerhalb eines
Prozesses immer ein FF, ein 'signal' ausserhalb eines Prozesses immer
ein 'fliegendes' Logiksignal. 'variable' verwende ich nur in Testbenches
oder als temporaeres Signal innerhalb eines 'process'. Z.B. als Index in
ein Array, oder weil ich zu faul bin, das ganze mehrmals zu tippen. Aber
in diesem Zusammenhang niemals als speicherndes Element.
Und Fakt ist: eine VARIABLE ist immer der D-Eingang des FF im getakteten
process. Mit dem ganzen Rattenschwanz davor...
FPGA Noob
So wie ich den TO versteh will er eine Multiplikation kombinatorisch
(ohne Takt) laufen lassen und braucht irgendein Bit das anzeigt wenn die
Multiplikation sicher beendet ist.
Normal ist doch der Takt der worst case?
Ein Multiplizierer ist per se kombinatorisch. Mit Aufbrechen von
Kombinatorikpfaden und Einfügen von FFs kann er in einem getakteten
Design "schneller" gemacht werden.
Hallo,
> Ich habe mir folgendes angegewoehnt: Ein PROCESS hat eine clock, evtl.> noch einen async. reset. Alles andere wird mit concurrent statements> beschrieben.
Ein Process hat eine Sensitivity List: process(SIGNAL1, SIGNAL2,...)
SIGNAL_X kann ein eine Clock sein dann ist es ein synchroner Process und
wird in der Regel auf D-FFs abgebildet. Es muss aber keine Clock sein,
dann ist es ein asynchroner Process, z.B. ein Bus-Multiplexer:
process(Select, Bus1, Bus2)
Für die Sensitivity List gibt es klare Regeln. Ist der Process syncron
steht da nur die Clock! Eventuell noch ein asynchroner Reset, obwohl man
asynchrone Resets heutzutage eigentlich nicht mehr verwendet( XILINX hat
das in ihrem WhitePaper WP272 schön beschrieben). Ist der Process
asynchron, dann müssen in der Sensitivity List alle Signale stehen,
deren Wert GELESEN wird. Ansonsten produziert man da Latches und die
will man nicht.
Bsp:
RICHIG:
-------
process(Select, Bus1, Bus2)
begin
if Select='0' then
Output <= Bus1;
else
Output <= Bus2;
end if;
end process;
FALSCH:
-------
process(Select)
begin
if Select ='0' then
Output <= Bus1;
else
Output <= Bus2;
end if;
end process;
> Damit ist auch klar: Ein 'signal' ist innerhalb eines> Prozesses immer ein FF, ein 'signal' ausserhalb eines Prozesses immer> ein 'fliegendes' Logiksignal.
Nee, ein Signal in einem geclockten Process wird nur dann zu einem
Flip-Flop wenn es einem anderen Signal zugewisen wird. Man kann z.B. ein
RegisterSet mit synchroner Logik folgendermaßen beschreiben:
process(Clk)
begin
if rising_edge(CLK) then
if Reset = '1' then
Reg0 <= (others=>'0');
Reg1 <= (others=>'0');
Reg2 <= (others=>'0');
Reg3 <= (others=>'0');
elsif Write = '1' then
case Addr is:
when "00" => Reg0 <= Input;
when "01" => Reg1 <= Input;
when "10" => Reg2 <= Input;
when "11" => Reg3 <= Input;
when others => null;
end if;
end if;
end process;
Reset & Addr sind in diesem Fall zwei Signale, die aber nicht in FF
umgewandelt werden. Addr wird zu einem Multiplexer, bzw. Selector für
den Enable Eingang der Register.
>'variable' verwende ich nur in Testbenches> oder als temporaeres Signal innerhalb eines 'process'. Z.B. als Index in> ein Array, oder weil ich zu faul bin, das ganze mehrmals zu tippen. Aber> in diesem Zusammenhang niemals als speicherndes Element.
Man sollte zwar vorsichtig mit Variablen sein, es gibt aber kein Grund
sie nicht zu verwenden. In einem Counter z.B. können Variablen durchaus
hilfreich sein. Dort werden sie auch in speichernde Elemente
umgewandelt.
Beispiel:
process(clk)
variable count : integer := 0;
constant CMAX : integer := 10;
begin
if rising_edge(clk) then
if Reset = '1' then
count := 0;
elsif Increment = '1' then
if count = CMAX then -- wrap around
count := 0;
else
count := count +1;
end if;
end if;
OutSignal <= std_logic_vector(count);
end process;
Generiert einen sauberen Counter, es werden nur Variablen verwendet und
die Zuweising an das OutSignal wird ganz am Ende gemacht.
Es gibt von GAISLER Research sogar einen kompletten Processor, LEON3,
der nur 2 Prozesse verwendet und massiv auf Variablen aufbaut.
Der einzige formelle Unterschied ist, dass eine Variable ihren Wert
sofort annimmt, das Signal dagegen erst wenn der Process "suspendet"
wird.
Bsp:
----
process(CLK)
variable X, Y : std_logic;
begin
if rising_edge(clk) then
X := InputSignal;
Y := X;
end if;
OutputSignal <= Y;
end process;
Generiert genau 1 D Flip-Flop: InputSignal -> D-FF -> OutputSignal
process(CLK)
begin
if rising_edge(clk) then
X <= InputSignal;
Y <= X;
end if;
end process;
OutputSignal <= Y;
Generiet ein Shiftregister aus 2 D-FF: InputSignal -> D-FF(X) -> D-FF(Y)
-> OutputSignal
>> Und Fakt ist: eine VARIABLE ist immer der D-Eingang des FF im getakteten> process. Mit dem ganzen Rattenschwanz davor...
Nope! Kann auch zur Selektion dienen, zur Beschriebung kombinatorischer
Logik, etc.
Gruss,
DJ
Lothar Miller schrieb:> Ich sage mal ein wenig provokant: dieses "sequenziell" und "parallel"> wurde nur von Professoren erfunden, die ihre Studenten aufs Glatteis> führen wollten.> Genauso wie das Wort "nebenläufig" in VHDL gar nichts mit "unwichtig"> oder "zweitrangig" zu taun hat. Es bedeutet vielmehr "gleichzeitig"...
Nee, die Begriffe wurden nicht von Professoren erfunden um die Studenten
zu verwirren, sondern sind fundamentale Konzepte von (synchronem)
Digital Design, was recht wenig mit Software Design zu tun hat, auch
wenn VHDL und Verlog in ihrem Syntax aussehen wie Programmiersprachen.
Ich denke eher, das verwirrt die Leute da es sich um ein Konzept handelt
welches man von "normalen" Programmiersprachen nicht kennt.
Thread-Programming, MPI oder CUDA/OpenCL kommen dem noch am nächsten.
Zwar läuft in einem Design alles parall ab aufgrund der Propagation
elektrischen Signale aber durch getaktete Elemente wie Flip-Flops
bekommt man ein diskretes Zeitverhalten, eben eine Sequenz. Und dieses
Konzept braucht man, wenn man z.B. Counter, Statemachines, etc.
modelieren möchte.
Eventuell haben Professoren ein Problem damit die Begriffe richtig zu
erklären, die haben aber in jedem Fall ihre Berechtigung.
Hier ein Beispiel um es zu illustrieren. In VHDL istjedes Signal
Statement (Instantierung und Port Mapping mal
ausgenommen)gleichbedeutent mit einem Process.
X <= A * B ist das selbe wir
process(A,B)
begin
X <= A * B;
end process;
Man kann also ein Design komplett nur mit Prozessen beschreiben. Alle
diese Prozesse laufen PARALL ab, d.h. gleichzeitig/nebenläufig
(concurrent). Die Abarbeitung innerhalb eines Prozesses ist jedoch
SEQUENTIELL, d.h. der Code wird in der Reihenfolge durchlaufen, wie er
da steht. Da erst macht die Modelierung von synchronen Prozessen mit
einer Clock möglich. Der Prozess "hält an dieser Stelle" an, wartet bis
er eine Rising/Falling Edge sieht und läuft dann erst weiter. Dadurch
kann man Zeitverhalten modelieren, was anders nicht möglich ist.
Daher: ALLE Prozess laufen PARALLEL zueinander ab, aber innerhalb des
Prozess Statements SEQUENTIELL. Bei einem asynchronen Prozess stoppt der
Prozess nicht, er läuft genau ein mal durch, wenn ein Element in der
Sensitivity List sich ändert und läuft dann bis zum Ende durch. Anders
synchrone Prozesse. Die stoppen bei dem Rising/Falling Edge Statement
und läufen erst dann weiter wenn die das Taktsignal sehen. Nur solche
Prozesse kann man dazu verwenden nicht nur die aktuellen Inputs zu
verwenden, sondern auch den Output.
Einfachstes Beispiel ist ein Counter. Der Ausgang hängt von dem
aktuellen Wert ab, der inkrementiert wird. Man muss also den aktuellen
Wert kennen.
Anders ausgedrückt, immer da wo man den vorhergegangen Wert kennen muss,
also ein Feed-back hat, braucht man synchrone Logik und die ist per
Definition sequentiell, da der Prozess an der Clock stoppt.
Einfaches Beispiel:
process(clk)
begin
if rising_edge(clk) then
D <= not D;
end if;
end process;
Kann synthetisiert werden und gibt ein Signal D mit der halben Clock
Frequenz.
process(D)
begin
D <= not D;
end if;
Gibt einen Fehler im Synthesizer mit "Combinatorial Loop".
Gruss,
JD
John Doe schrieb:> Beispiel:> process(clk)> variable count : integer := 0;> constant CMAX : integer := 10;> begin> if rising_edge(clk) then> if Reset = '1' then> count := 0;> elsif Increment = '1' then> if count = CMAX then -- wrap around> count := 0;> else> count := count +1;> end if;> end if;> OutSignal <= std_logic_vector(count);> end process;
Hi,
schoener Nickname uebrigens :o)
Jetzt sind wir ja auch bzgl. des Titels des Threads (Verständnissproblem
sequeziell/parallel) wieder richtig...
Da fehlt erstens ein 'end if' und zweitens stoert mich, dass man mit der
'lokalen' Variablen sich halt eine Abhaengigkeit der Reihenfolge der
Statements einfaengt.
Mit Signalen wuerde ich auch schreiben koennen:
1
count<=count+1;
2
ifcount=CMAXthen
3
count<='0';
4
endif;
'count' sollte hier auch direkt dein 'OutSignal' sein, oder man kann
count auch einfach in einem concurrent statement dem Ausgangssignal
zuweisen.
Dein Beispiel
1
ifcount=CMAXthen
2
count:=0;
3
else
4
count:=count+1;
5
endif;
6
OutSignal<=std_logic_vector(count);
funktioniert natuerlich wunderbar. Weshalb ich das so aber nicht
verwende: Die Reihenfolge der statements ist entscheidend, und deshalb
verwende ich persoenlich diese Schreibweise nicht. Und jetzt stelle ich
mir mal vor, der 'process' waere etwas komplexer und dann noch nicht
getaktet sondern kombinatorisch... Es ist dann extrem schwer, den Ablauf
noch zu durchschauen. Deshalb meine 'simple minded' Vorgehensweise:
process nur als 'clocked' process, maximal mit async. Reset. variable
nur als Platz sparendes 'fliegendes' Signal im process. Ansonsten
Kombinatorik nur mit concurrent Statements.
Also mir gefaellt die Methode 'Gaisler/Leon' persoenlich ueberhaupt
nicht, weil sich eben 'lokale variable' ganz anders verhalten (koennen)
als 'global signal'.
Und richtig lustig wird es, wenn du 'variable' in einem nicht getakteten
'process' verwendest. Sowas habe ich in eingekauften Designs schon
gesehen und darueber auch schon mal einen Vormittag gebruetet... Ich bin
halt ein Anhaenger der keep-it-simple Philosophie...
berndl schrieb:> Hi,> schoener Nickname uebrigens :o)
Danke! John Doe war leider schon vergeben, da konnte ich meiner
Kreativität freien Lauf lassen ;-)
> Jetzt sind wir ja auch bzgl. des Titels des Threads (Verständnissproblem> sequeziell/parallel) wieder richtig...
Yepp!
> Da fehlt erstens ein 'end if' und zweitens stoert mich, dass man mit der> 'lokalen' Variablen sich halt eine Abhaengigkeit der Reihenfolge der> Statements einfaengt.
Stimmt, habe den Code schnell aus'm Kopf runtergeschrieben, da geht
schon mal was verloren.
> Mit Signalen wuerde ich auch schreiben koennen:>
1
>count<=count+1;
2
>ifcount=CMAXthen
3
>count<='0';
4
>endif;
5
>
Naja, die Reihenfolge spielt hier ja auch eine Rolle.
1
ifcount=CMAXthen
2
count<='0';
3
endif;
4
count<=count+1;
Reihenfolge vertauscht und der Code wird sich anders verhalten.
> 'count' sollte hier auch direkt dein 'OutSignal' sein, oder man kann> count auch einfach in einem concurrent statement dem Ausgangssignal> zuweisen.
Wenn 'count' ein internes Signal ist, dann kann man es natürlich, wie in
deinem Beispiel direkt zuweisen. Bei einem Output geht das ja nicht, da
auf 'count' auch lesend zugegriffen wird. O.k. Man kann "inout" oder
"buffer" verwenden, aber das ist hässlich.
> Dein Beispiel>
1
>ifcount=CMAXthen
2
>count:=0;
3
>else
4
>count:=count+1;
5
>endif;
6
>OutSignal<=std_logic_vector(count);
7
>
> funktioniert natuerlich wunderbar. Weshalb ich das so aber nicht> verwende: Die Reihenfolge der statements ist entscheidend, und deshalb> verwende ich persoenlich diese Schreibweise nicht. Und jetzt stelle ich> mir mal vor, der 'process' waere etwas komplexer und dann noch nicht> getaktet sondern kombinatorisch...Es ist dann extrem schwer, den Ablauf> noch zu durchschauen.
Ist vermutlich Geschmackssache. Die Abhängigkeit der Reihenfolge hat man
bei Signalen auch. Dazu kommt, dass Variablen direkt den Wert
übernehmen, Signal dagegen erst, wenn der Process durchgelaufen ist und
suspended wird. Beides hat seine Berechtigung und ist ne Frage wie man's
gewohnt ist. Jemand der lange mit Variablen arbeitet wird es vermutlich
einfacher finden damit zurecht zu kommen als mit Signalen. Ich
persönlich vermeide Variablen ebenfalls.
> Deshalb meine 'simple minded' Vorgehensweise:> process nur als 'clocked' process, maximal mit async. Reset. variable> nur als Platz sparendes 'fliegendes' Signal im process. Ansonsten> Kombinatorik nur mit concurrent Statements.
Ja, sehe ich genau so. Bis auf die Sache mit dem async. Reset! Der muss
endlich mal aus dem Kopf der Leute raus. Darf man zwar haben (wenn er
von extern kommt) aber dann bitte sauber (Synchronizer!) in die
entsprechende Clock-Domain einsynchronisren und als sync. Reset
verwenden.
> Also mir gefaellt die Methode 'Gaisler/Leon' persoenlich ueberhaupt> nicht, weil sich eben 'lokale variable' ganz anders verhalten (koennen)> als 'global signal'.> Und richtig lustig wird es, wenn du 'variable' in einem nicht getakteten> 'process' verwendest. Sowas habe ich in eingekauften Designs schon> gesehen und darueber auch schon mal einen Vormittag gebruetet... Ich bin> halt ein Anhaenger der keep-it-simple Philosophie...
Ja, - KISS : Keep It Simple, Stupid! - die goldene Regel des
Digitaldesigns. Oder mit den Worten von Sergei Koroljow (dem russischen,
daher leider ziemlich unbekannten aber genialen Gegenspieler von Werner
von Braun) „The Genialität of a construction lies in their simplicity.
Complicated to build everyone can.“
Ein Vorteil haben Variablen jedoch und zwar wenn es um die
Simulationszeit geht. Signale sind ziemlich aufwendig zu simulieren, da
sie für jeden DELTA-Cylce des Simulators evaluiert werden müssen.
Variablen sind da super schnell und sparen Speicherplatz. Das fällt bei
kleinen bis mittleren Designs nicht so ins Gewicht, aber große Designs
bei entsprechend hohen Frequenzen (und damit ein kleine
Simulatorzeiteinheit im Bereich von ps oder fs).... viel Speicher in den
Server und ne Nacht schlafen gehen. Prozessoren zu simulieren macht da
richtig viel Spaß, da treffen dann makroskopische Simulationszeiten im
Bereich Millisekunden oder Sekunden auf mikroskopische
Simulatorauflösungen im Bereich Piccosekunden... da sind Variablen dann
auf jeden Fall dein Freund.
Gruss,
JD
John Doe schrieb:> Nee, die Begriffe wurden nicht von Professoren erfunden um die Studenten> zu verwirren, sondern sind fundamentale Konzepte von (synchronem)> Digital Design
Naja, mir scheint, der Begriff "concurrent" wurde einfach nur mit dem
unüblichsten Wort übersetzt. Denn concurrent könnte heißen:
1
gleichzeitig
2
entsprechend
3
simultan
4
übereinstimmend
5
gleichlaufend
6
konkurrierend
7
zusammenwirkend
8
zusammenfallend
9
nebenläufig
10
nichtsequentiell
11
nebeneinander bestehend
12
durch einen Punkt gehend
13
zusammentreffend
Und schon das erste Wort beschriebe den eigentlichen Sinn besser...
> Werner von Braun
Das würde dem Wernher gar nicht gefallen...
http://de.wikipedia.org/wiki/Wernher_von_Braun> Ein Vorteil haben Variablen jedoch und zwar wenn es um die> Simulationszeit geht.
Hast du das schon mal ausprobiert? Denn lustigerweise taucht genau
dieses Argument (das ja früher(TM) auch für std_logic vs std_ulogic
hergenommen wurde) bei der Gaisler-Anhängerschaft nie auf...
berndl schrieb:> Und richtig lustig wird es, wenn du 'variable' in einem nicht getakteten> 'process' verwendest.
Und diese Variablen dann implizit speichern oder auch nicht...
Siehe den Klassiker
Beitrag "Re: Variable vs Signal"
Ja, nun ist das Wochenende zu Ende und ich wundere mich, wie viele
Antworten gab es auf meine Frage... Vielen Dank! Einiges ist klarer
geworden, aber nicht alles. Die Hauptfrage bleibt immer noch offen: was
genau sind Prozessen, für was steht "sequenziell" und zu welchem
Zeitpunkt werden Signale bzw. Variablen zugewiesen (bei kombinatorischen
Prozessen)?
1
processbegin
2
3
result1<=std_logic_vector(signed(x1)*signed(y1));
4
a<=nota;
5
waitonx1,y1;
6
endprocess;
Z.B., wann genau werden in so einem Prozess result1 und a zugewiesen?..
Und vor allem, wird das nun gleichzeitig passieren oder nicht?..
Lothar Miller schrieb:>> Ein Vorteil haben Variablen jedoch und zwar wenn es um die>> Simulationszeit geht.> Hast du das schon mal ausprobiert? Denn lustigerweise taucht genau> dieses Argument (das ja früher(TM) auch für std_logic vs std_ulogic> hergenommen wurde) bei der Gaisler-Anhängerschaft nie auf...
Ich, als bekennender Anhänger der Zwei-Prozess-Methode, hätte einfach
gar keine Lust die komplexe Logik, die ich mit der Zwei-Prozess-Methode
realisieren kann, nochmal "klassisch" zu realisieren, nur um die
Geschwindigkeit vergleichen zu können...
Mir graust es schon bei der Vorstellung daran, wie die "klassische"
Implementierung mit verteilten x-Prozessen aussieht.
Für mich zählt Wartbarkeit und daß diverse Fallstricke (z.B.
unvollständige Sensitvitätslisten, nicht initialisierte Signale,
unerwünschte Latches) elegant umgangen werden.
Aber jeder soll bitte so Hardware beschreiben, wie er es vermag. Ich muß
da niemanden bekehren :-)
Und um noch den Bogen zum ursprünglichen Problem zu spannen; Wenn man
den Multipizierer mehrfach nutzen möchte, könnte man folgendes Konstrukt
verwenden:
spinne schrieb:> Z.B., wann genau werden in so einem Prozess result1 und a zugewiesen?..> Und vor allem, wird das nun gleichzeitig passieren oder nicht?..
Es wird nichts "zugewiesen" da du nur rein kombinatorische Lokik
beschreibst. Result1 und a ändern sich immer wenn sich an den
Eingangssignalen was tut, in der Simulation sofort, auf der Hardware
nach den Gatterlaufzeiten. Den Prozess kannst du in dem Fall weglassen.
Das wait on wird sicherlich eh nur für die Simulation gebraucht, scheint
die Sensitivitätsliste zu ersetzen.
spinne schrieb:> Z.B., wann genau werden in so einem Prozess result1 und a zugewiesen?
Immer beim nächsten wait. Oder bei Prozessen ohne wait (also die
"klassischen") immer am Ende des Prozesses.
spinne schrieb:> wait on (a and b and c);> wait on x1,y1;
Ich glaube noch immer nicht, dass das wie erwartet synthetisierbar ist.
Denn das wäre ja ein Flipflop, das auf jede Änderung jedes der Signale
getaktet werden könnte...
>> Welcher Synthesizer kann das?> Der von Synopsis hat da nicht bemekert...
Das Synplify Pro, das bei Lattice dabei ist, kann es nicht. Das "wait
on" wird ganz einfach IGNORIERT. Es kann nach wie vor nur "wait until"
synthetisiert werden.
Aber vermutlich bin ich hier auf der falschen Spur und vermute ein
Flipflop, wo du gar keines willst. Nochmal dein Code:
1
processbegin
2
result1<=std_logic_vector(signed(x1)*signed(y1));
3
result2<=std_logic_vector(signed(x2)*signed(y2));
4
result3<=std_logic_vector(signed(x3)*signed(y3));
5
waitonx1;
6
endprocess;
Mit dem "wait on x1" kannst du im Simulator (und nur dort!) steuern,
dass die drei vorigen Rechenschritte nur dann "ausgeführt werden", wenn
sich x1 ändert.
Richtigerweise wird es in der Hardware aber so implementiert:
1
processbegin
2
result1<=std_logic_vector(signed(x1)*signed(y1));
3
result2<=std_logic_vector(signed(x2)*signed(y2));
4
result3<=std_logic_vector(signed(x3)*signed(y3));
5
waitonx1,y1,x2,y2,x3,y3;
6
endprocess;
Oder gleichbedeutend und traditionell so:
1
process(x1,y1,x2,y2,x3,y3)begin
2
result1<=std_logic_vector(signed(x1)*signed(y1));
3
result2<=std_logic_vector(signed(x2)*signed(y2));
4
result3<=std_logic_vector(signed(x3)*signed(y3));
5
endprocess;
Was aber ohne Prozess concurrent mit exakt gleichem Ergebnis auch gleich
so geschrieben werden könnte:
> Und vor allem, wird das nun gleichzeitig passieren oder nicht?..
Löse diesen unnötigen (und unvollständigen) Prozess einfach mal auf und
schreibe diese beiden Zeilen ohne Prozess concurrent hin:
1
result1<=std_logic_vector(signed(x1)*signed(y1));
2
3
a<=nota;
Und jetzt beantworte die Frage: was passiert hier gleichzeitig?
Antwort: in einem Teil des FPGAs wird ein Multiplizierer instanziiert,
an den am Eingang x1 und y1 und am Ausgang result1 angeschlossen sind,
und in einem anderen Teil bildet gleichzeitig ein Nicht-Gatter (das a)
eine kombinatorische Schleife...
Ok, ich stelle die Frage etwas anders:
1)Es geht jetzt nur um Hardware
2)t ist ein externes Triggersignal (Ein Taster auf meinem
Entwicklungsbord)
3)x1 und y1 sinde jeweils Taster, result1 - LEDs
4)a ist auch ein LED
5)es funktioniert alles in der Hardware und das Ergebnis der
Multiplikation stimmt.
6)a ändert sein Wert mit jeder steigenden Flanke des t.
Nun die Fragen:
1) sind a und result1 gleichzeitig gesetzt?
2) ist der Zeitpunkt der Zuweisung (oder wie sich das sonst nennt) in
etwe gleich "Zeitpunkt der rising_edge(t) + Multiplikationsdauer"?
Lothar Miller schrieb:> spinne schrieb:>> wait on (a and b and c);>> wait on x1,y1;> Ich glaube noch immer nicht, dass das wie erwartet synthetisierbar ist.> Denn das wäre ja ein Flipflop, das auf jede Änderung jedes der Signale> getaktet werden könnte...>>> Welcher Synthesizer kann das?>> Der von Synopsis hat da nicht bemekert...> Das Synplify Pro, das bei Lattice dabei ist, kann es nicht. Das "wait> on" wird ganz einfach IGNORIERT. Es kann nach wie vor nur "wait until"> synthetisiert werden.
Sorry, Lothar, das war mein Fehler, natürlich hatte ich überall wait
untill benutzt.
Lothar Miller schrieb:> Löse diesen unnötigen (und unvollständigen) Prozess einfach mal auf und> schreibe diese beiden Zeilen ohne Prozess concurrent hin: result1 <=> std_logic_vector(signed(x1)*signed(y1));>> a <= not a;
Warum denn ist hier ein Prozess unnötig?
spinne schrieb:> Warum denn ist hier ein Prozess unnötig?
Warum sollte einer nötig sein?
> natürlich hatte ich überall wait untill benutzt.
Also so etwa:
wait until x1,y1;
Das geht nicht, weil:
Lothar Miller schrieb:> das wäre ja ein Flipflop, das auf jede Änderung jedes der Signale> getaktet werden könnte...spinne schrieb:> t ist (Ein Taster auf meinem Entwicklungsbord)> ...> wait until rising_edge(t);
Aua...
Such mal nach "Postulate" hier im VHDL-Forum...
Ok, die Karten werden neu gemischt:
spinne schrieb:
1
architectureBehaveofMULis
2
signalb:std_logic;
3
begin
4
processbegin
5
waituntilrising_edge(t);
6
result1<=std_logic_vector(signed(x1)*signed(y1));
7
b<=notb;
8
endprocess;
9
10
a<=b;
11
end;
> 1) sind a und result1 gleichzeitig gesetzt?
Ja. So "gleichzeitig" wie möglich (ein paar ps sind da immer drin).
> 2) ist der Zeitpunkt der Zuweisung (oder wie sich das sonst nennt) in> etwe gleich "Zeitpunkt der rising_edge(t) + Multiplikationsdauer"?
Nein.
Du schaltest hier hinter den Multiplizierer noch für jedes Bit 1
Flipflop und du schaltest hinter ein Nicht-Gatter ein Fipflop. Und mit
der steigenden Flanke wird das vorher berechnete Ergebnis der
Multiplikation und der Negation (das sind die beiden Rechnungen, die im
Prozess auftauchen) in diese Flipflops gespeichert.
Sieh dir einfach mal den RTL-Schaltplan deines Designs an...
Lothar Miller schrieb:> Du schaltest hier hinter den Multiplizierer noch für jedes Bit 1> Flipflop und du schaltest hinter ein Nicht-Gatter ein Fipflop. Und mit> der steigenden Flanke wird das vorher berechnete Ergebnis der> Multiplikation und der Negation (das sind die beiden Rechnungen, die im> Prozess auftauchen) in diese Flipflops gespeichert.>> Sieh dir einfach mal den RTL-Schaltplan deines Designs an..
Also, dann müssen auch nach jedem IO-Pin auch FF's kommen? Sonst würde
ja das Ergebnis nicht stimmen. Falls es so ist, fange ich langsam an zu
verstehen, was eigentlich passiert. Dann kriege ich unmittelbar nach
jede steigende Flanke von t die vorherige werte von a und result1,
richtig?..
Lothar Miller schrieb:> spinne schrieb:>> t ist (Ein Taster auf meinem Entwicklungsbord)>> ...>> wait until rising_edge(t);> Aua...> Such mal nach "Postulate" hier im VHDL-Forum...
Da wird wahrscheinlich das "Einsynchronisieren" gemeint? Aber ich habe
gar kein anderen Taktsignal. Oder liegt mein Fehler woanders?..
spinne schrieb:> Also, dann müssen auch nach jedem IO-Pin auch FF's kommen?
Es sieht so aus:
1
2
t ----------------.
3
| _____
4
____ o---|> | viele Fliflops
5
x1 ----| \ | | |
6
| x >----+---|D Q|----------------- result1
7
y1 ----|____/ | |_____|
8
| _____
9
'---|> | ein Flipflop
10
| |
11
.--|D Q|---o------------- a
12
| |_____| |
13
| | b
14
'-----o<|----'
spinne schrieb:> Da wird wahrscheinlich das "Einsynchronisieren" gemeint?
Nimm einen Takt aus einem Taktoszillator. Synchronieisere den Taster ein
und setze eine Flankenerkennung drauf.
@Spinne
Ich hab´s jetzt nur überflogen und nicht alles genauestens durchgelesen,
das mal vorweg.
Mein Eindruck ist, dass du weisst, dass ein Prozess in VHDL "getriggert"
wird. Entweder durch die Sensitivity-Liste oder durch ein wait.
Das ist auch richtig, aber das ist etwas, was für die Simulation gedacht
ist. In VHDL kann man viel mehr an Funktionalität beschreiben, als dann
eigentlich so in Hadware abgebildet werden kann.
Die oben beschriebene Eigenschaft eines Prozesses kann man nicht ohne
Weiteres der Synthese vorlegen und hoffen, dass sie da "Richtige" daraus
bastelt.
Eine Zeitabhängigkeit kann auf realer Hardware nur vernünftig mit einem
Takt realisiert werden, auf dessen Flanke reagiert wird. Und genau das
sind dann Register. Natürlich gibt es auf der realen Hadware Laufzeiten
zwischen den Logikfunktionen, aber sich die in einem FPGA zunutze machen
zu wollen, ist etwas, was einem nur gelingt, wenn man recht tiefes
Verständnis von der Materie hat und erfodert Fingerspitzengefühl beim
Constraining.
Zu allem Übel sind diese Zeiten dann noch sehr temperaturabhängig.
Fazit: Sowas macht keiner freiwillig!
Wenn du einen ungetakteten Prozess bechreibst, bei dem alle Eingänge in
der Sensitivity-Liste auftauchen, dann macht die Synthese meines
Erachtens exakt das Gleiche daraus, wie wenn du es einfach concurrent
hinschreibst.
Die Tatsache, dass du kombinatorische Zuweisungen in einen Prozess
packst, macht diese noch lange nicht sequenziell.
Zurück zu deinem Problem.
Wenn du einen Multiplizierer merhfach verwenden willst, dann stelle dir
vor, wie du das in Hardware lösen würdest.
Du würdest EINEN Multiplizierer verwenden, einen Multiplexer
davorschalten und dann das Ergebnis der ersten Multiplikation in einem
Register zwischenspeichern. Im nächsten Schritt würdest du dann den Mux
umschalten und das gespeicherte Ergebnis mit dem nächsten Operand
verknüpfen.
Und genau diesen "nächsten Schritt" kannst du nur sinnvoll mit einem
Takt vom ersten Schritt separieren.
Genau, so habe ich mir das alles vorgestellt. Vielen Dank, Lothar, nun
ist für mich viel mehr klar geworden. Was die Einsynchronisierung
angeht, ich weiß, wie und wozu man das macht. Vielleicht, sieht das ganz
dumm und komisch aus, aber ich habe einfach nach einer Möglichkeit
gesucht ein getakteter Prozess durch ein kombinatorischen zu ersetzen
und dadurch mehr Effizienz zu gewinnen (variable statt feste
Abtastzeiten, weniger Stromverbrauch). Nun sehe ich, dass es nicht
möglich ist.
Schlumpf schrieb:> Fazit: Sowas macht keiner freiwillig!
Vielen Dank auch dir, Schlumpf, für deine Feedback!
Nun sehe ich meinen Gedankenfehler. Ich habe mir nur überlegt, dass so
was möglich wäre. Leider habe ich überhaupt keine Erfahrung in
Digitalelektronik und VHDL und konnte es mir nicht vorstellen, wie
schwierig es wäre, so was zu realisieren. Nun weiß ich das.
spinne schrieb:> Leider habe ich überhaupt keine Erfahrung in> Digitalelektronik
Hallo Spinne, das ist genau das Problem. Ohne Kenntnisse in de
Digitalelektronik kann man keinen vernünftigen synthetisierbaren
HDL-Code erzeugen. VHDL ist keine Programmiersprache, sondern eine
Beschreibungssprache mit der man eben genau Digitalelektronik
beschreibt.
Es ist ein Werkzeug, um mehr oder weniger komplexe Digitale
Zusammenhänge abstrakt darzustellen. Wenn man aber von dem, was man
beschreiben will, (noch) keine Kenntnisse hat, dann ist es eigentlich
auch zum Scheitern verurteilt, dies abstrakt darzustellen :-)
Als Einstieg in die Materie würde ich dir empfehlen, dass du dich
erstmal mit den Grundelementen der Digitaltechnik vertraut machst
(Zähler, Gatter, Mux, ...). Im nächsten Schritt kannst du dann diese
Elemente in VHDL darstellen. Dazu gibt es auch jede Menge Literatur.
Aktuell gibt es sogar ein Buch zum kostenlosen Download. Den Link dazu
findest du in einem der Threads hier.
Ein rein kombinatorisches Design kann man natürlich auch in einem FPGA
machen, aber dann ist es eben rein kombinatorisch. Sobald aber
Verknüpfungen und Funktionen in einer gewissen zeitlichen Ordung
durchgeführt werden sollen, dann braucht dein System eine Zeitbasis. Und
diese ist der Systemtakt.
Schlumpf schrieb:> Als Einstieg in die Materie würde ich dir empfehlen, dass du dich> erstmal mit den Grundelementen der Digitaltechnik vertraut machst> (Zähler, Gatter, Mux, ...
So schlimm sieht's bei mir auch nicht aus =) Theoretische
Grundkenntnisse sind ja vorhanden, leider nicht so viel, wie bei einem
Elektrotechnikstudium. Und ich bin gerade dabei, vorhandene Lücken zu
verschließen. Das VHDL-Synthese Buch von Reichardt/Schwarz versuche ich
schon seit einer Woche herunterzuladen, geht leider in der letzten Zeit
nicht. Schade, wäre ja sehr hilfreich.
spinne schrieb:> Das VHDL-Synthese Buch von Reichardt/Schwarz versuche ich schon seit> einer Woche herunterzuladen
Kauf es doch einfach. Ausdrucken kostet mehr...
Schlumpf schrieb:> Momentan gibt es dieses Buch für lau...>> http://www.degruyter.com/view/product/223820
Dieses Buch habe ich gerade gestern Abend heruntergeladen.
Lothar Miller schrieb:> spinne schrieb:>> Das VHDL-Synthese Buch von Reichardt/Schwarz versuche ich schon seit>> einer Woche herunterzuladen> Kauf es doch einfach. Ausdrucken kostet mehr...
Ich könnte ja es auch in der Hochschulbibliothek ausleihen, mache aber
mit Absicht nicht, weil ich praktisch nur unterwegs auf dem Tablet lese.
Hoffe, dass es noch Möglichkeit geben wird, das Buch bis zum Ende August
herunterzuladen...
Hallo!
Seit meinem letzten Beitrag habe ich ziemlich große Vorschritte gemacht
(im allg.Verständniss, als auch in meinem Projekt). Aber Probleme gibt
es immer noch genug.
Mit zwei davon kämpfe ich seit 1 Woche und bis jetzt hab keine Lösung
bzw. Antwort gefunden.
Das Herzstück meines Codes ist eine FSM mit derzeit 4 Zuständen. In 2
Zuständen wird einen Zähler mit verschiedenen werten geladen (abhängig
von der Anzahl der durchzuführenden Multiplikationen). Im Step1 wird den
Zähler einige Durchläufe machen, im Step2 nur eins. Nun zu den Fragen:
1). Als erstes, hat mir aufgefallen, dass mein Design nach der Synthese
unerwartet groß ist. Ich benutze nur 1 32-Bit Multiplizierer (laut
Syntesizer 3,7k LE), 3 37-Bit Addierer (ca.0,7k LE) und diverse Register
für geschätzt 2,5k LE max. Alles zusammen also ca 7k LE. Mein Design ist
laut Synthesizer 16k LE groß. Könnte das sein, dass alles 2 mal
implementiert wurde? Oder ist so was in Ordnung?
2). Ich kann die Funktionalität meines Codes nicht testen, weil es für
den zur Verfügung stehenden Entwicklungsbord zu groß ist und die
Simulation in ModelSim läuft nicht richtig. Die FSM funktioniert gar
nicht, keiner von Zuständen wird gesetzt. Unten der gekürzte Code, den
tadellos in der HW funktioniert, nicht aber in der Simulation. Woran
kann das denn liegen?
1
libraryIEEE;
2
3
useIEEE.std_logic_1164.all;
4
useIEEE.NUMERIC_STD.ALL;
5
6
entityParameterschaetzverfahren_Core_VHDLis
7
port(
8
clk:instd_logic;
9
I:instd_logic_vector(5downto0);-- I als Trigger - Signal vom Datenerfassung Core
Danke, aber das sollte bei mir richtig sein. Ich definiere clk als Takt
und ändere entsprechend I. Die beiden Signale sehe ich dann in der
Simulation als richtig belegt. Aber test bleibt immer auf UUUU.
Spinne schrieb:> nicht aber in der Simulation
Hast Du noch eine passende Testbench zu Deinem Code?
Damit können andere Forumsteilnehmer Deine Simulation nachvollziehen.
Duke
Duke Scarring schrieb:> Hast Du noch eine passende Testbench zu Deinem Code?> Damit können andere Forumsteilnehmer Deine Simulation nachvollziehen.>> Duke
Nein, habe ich nicht. Ich mach es so, dass ich im ModelSim einfach auf
den Signalname mit rechter Maustaste drücke und dann das Signal als Takt
definiere oder einen Wert vorgebe (Force...). Bis jetzt funktionierte
sowas immer richtig...
Spinne schrieb:> Duke Scarring schrieb:>> Hast Du noch eine passende Testbench zu Deinem Code?>> Damit können andere Forumsteilnehmer Deine Simulation nachvollziehen.>>>> Duke>> Nein, habe ich nicht. Ich mach es so, dass ich im ModelSim einfach auf> den Signalname mit rechter Maustaste drücke und dann das Signal als Takt> definiere oder einen Wert vorgebe (Force...). Bis jetzt funktionierte> sowas immer richtig...
force macht immer mal wieder Probleme:
Beitrag "Modelsim Clock Signal erzeugen"
ein paar zeilen vhdl sind besser.
MfG
Spinne schrieb:>> Hast Du noch eine passende Testbench zu Deinem Code?> Nein, habe ich nicht.
Mach das doch. Das kostet dich bei diesem Projekt keine halbe Stunde.
VHDL kannst du ja schon...
> (Force...). Bis jetzt funktionierte sowas immer richtig...
Ich bin froh, dass ich diesen Murks nie angefangen habe... ;-)
Spinne schrieb:> Ich benutze nur 1 32-Bit Multiplizierer (laut Syntesizer 3,7k LE), 3> 37-Bit Addierer (ca.0,7k LE) und diverse Register für geschätzt 2,5k LE> max. Alles zusammen also ca 7k LE. Mein Design ist laut Synthesizer 16k> LE groß.
Was sind das für Zahlen? was ist eine "k LE"? Ich rechne meine Designs
in einzelnen Flipflops (also nicht "kilo Flipflops") aus...
Dir sind die möglichen Probleme dieser Beschreibung bewusst?
1
ifI(5)='0'thenstate<=init;endif;-- Zuweisung an state
2
3
casestateis
4
wheninit=>
5
:
6
ifI(5)='1'thenstate<=step1;-- Zuweisung an state
7
:
8
when50=>
9
state<=done;-- Zuweisung an state
10
:
Stichwort: die letzte Zuweisung im Prozess "gewinnt"
Lothar Miller schrieb:> Spinne schrieb:>>> Hast Du noch eine passende Testbench zu Deinem Code?>> Nein, habe ich nicht.> Mach das doch. Das kostet dich bei diesem Projekt keine halbe Stunde.> VHDL kannst du ja schon...
Ok, ich werde mich damit mehr beschäftigen.
Lothar Miller schrieb:> Was sind das für Zahlen? was ist eine "k LE"? Ich rechne meine Designs> in einzelnen Flipflops (also nicht "kilo Flipflops") aus...
Ja, ich habe mit "k" kilo gemeint. "LE" steht für Logik Elemente, aber
ich weiß leider nicht auf 100%, was damit vom Hersteller gemeint war.
Mein FPGA hat 6000 LEs und 250000 Gatter(das sind wahrscheinlich "reine"
FFs), nach der Synthese wird mir den Logikverbrauch in LEs angezeigt.
Aber es geht eigentlich nur darum, dass das Design zu groß ist, egal in
welchen Einheiten.
Lothar Miller schrieb:> Dir sind die möglichen Probleme dieser Beschreibung bewusst? if I(5) => '0' then state <= init; end if; -- Zuweisung an state>> case state is> when init =>> :> if I(5) = '1' then state <= step1; -- Zuweisung an state> :> when 50 =>> state <= done; -- Zuweisung an state> :> Stichwort: die letzte Zuweisung im Prozess "gewinnt"
Hmm... Nun verstehe ich gar nicht, über was die Rede ist... Was für
Probleme kann es hier geben?.. Für eine Erklärung wäre ich sehr dankbar.
Ludolf Lustig schrieb:> force macht immer mal wieder Probleme...
Mag sein, ich versuche morgen einen Testbench zu schreiben (hab früher
noch nicht gemacht). Danke für die Hinweise!
spinne schrieb:> Lothar Miller schrieb:>> Dir sind die möglichen Probleme dieser Beschreibung bewusst?>> Stichwort: die letzte Zuweisung im Prozess "gewinnt"> Hmm... Nun verstehe ich gar nicht, über was die Rede ist... Was für> Probleme kann es hier geben?.. Für eine Erklärung wäre ich sehr dankbar.
Du hast hier 2 Probleme:
1. Auch wenn du schon ganz am Anfang über I(5) dem dem state den Wert
init zuweist, wird dieser Wert nur zwischengespeichert und der Prozess
trotzdem mit dem alten Wert von state durchlaufen.
2. Während des Durchlaufens kann der Wert von state immmer wieder
überschrieben werden. Erst am Ende des Prozesses wird der dann aktuelle
Wert aus dem Zwischenspeicher auf das Signal abgebildet.
Du wirst hier für das Signal antwort also immer nur den Wert 42 zu
sehen bekommen, weil "die letzte Zuweisung gewinnt":
1
processbegin
2
waituntilrising_edge(clk);
3
4
antwort<=8411;
5
6
ifnixthen
7
antwort<=0815;
8
elsifzero
9
antwort<=0;
10
endif;
11
12
antwort<=42;
13
endprocess;
Das bedeutet, dass du einen synchronen Reset am einfachsten am Ende
eines Prozesses beschreibst:
1
processbegin
2
waituntilrising_edge(clk);
3
-- normaler Ablauf
4
ifreset='1'then
5
-- Resetbehandlung
6
endif;
7
endprocess;
Oder in der traditionellen Art mit einer zusätzlichen umschließenden
if-Abfrage:
1
processbegin
2
waituntilrising_edge(clk);
3
ifreset='1'then
4
-- Resetbehandlung
5
else
6
-- normaler Ablauf
7
endif;
8
endprocess;
> Mag sein, ich versuche morgen einen Testbench zu schreiben (hab früher> noch nicht gemacht). Danke für die Hinweise!
Sieh dir das mal an:
http://www.lothar-miller.de/s9y/archives/89-BCD-nach-Binaer-wandeln.html
Eine einfache Aufgabe und die passende kleine Testbench dazu...
BTW: eine Testbench erkannt man daran, dass sie keine Ports hat. Die
Deklaration der Entity sieht also so aus: