Ich habe eine Verständnisschwierigkeit, die sich bestimmt ganz schnell
aufklären lässt:
Wie erreicht man, dass die Signalzuweisungen in einem
aktaktenden-Register-Prozess alle gleichzeitig passieren, sodass race
conditions nicht auftreten können?
Beispiel:
process (CLK)
begin
if CLK='1' and CLK'event then
SIG1_cs <= SIG1_ns;
...
Sig100_cs <= Sig1_ns;
end process
Die _ns Signale sind Rechenergebnisse (nicht abgataktet), die _cs
Signale sind Taktsynchron zu beschreiben, von eben diesem Prozess. Die
_cs Signale werden außerdem nur gelesen und stehen in den jew.
Sensitivlisten. So weit, so bekannt. SIG1_cs bis SIG100_cs stellen sehr
viele FFs dar, die prinzipiell alle gleichzeitig über
taktflankengesteuerte FFs mit ihren _ns Werten beschrieben werden,
aufgrund ihrer Anzahl aber vermutlich über die ganze Chipfläche verteilt
liegen. D.h. das CLK Signal braucht verschieden lange um die FFs zu
erreichen.
Des Weiteren gibt es den Prozess
process (SIG1_cs, SIG50_cs)
variable calc:bit;
begin
calc := SIG1_cs;
calc := calc and SIG50_cs;
... oder ähnliche sehr kurze Berechnungen
SIG50_ns <= calc;
end process
Angenommen das CLK Signal braucht "sehr kurz" um zum
taktflankengesteuerten FF zu gelangen, das SIG1_cs durch SIG1_ns ersetzt
und aufgrund phys. Entfernung braucht es "sehr lange" um zum FF zu
gelangen, das SIG50_cs durch den Wert von SIG50_ns ersetzt.
Wird SIG1_cs beschrieben startet die Berechnung für SIG50_ns.
Woher weiß man nun, ob SIG50_cs den Wert erhält, den SIG50_ns VOR der
Taktflanke hatte oder ob es den Wert erhält, den SIG50_ns bekommen hat
NACHDEM die Taktflanke SIG1_cs beschrieben hat und durch den
Berechnungsprozess ein neuer Wert für SIG50_ns berechnet wurde?
Ich würde pauschal sagen, die Zeitdifferenz zwischen dem ersten
Erreichen des CLK-Signals an einem _ns Abtakt-FF und dem letzten
Erreichen des CLK-Signals an einem _ns Abtakt-FF muss kleiner sein, als
die kürzeste Signallaufzeit durch einen Prozess mit kombi. Logik.
Denn im genannten Beispiel oben, entscheidet ja einzig und allein die
Länge der Signallaufzeit im Berechnungsprozess darüber ob SIG50_cs nun
den Wert bekommt, den SIG50_ns hatte BEVOR oder NACHDEM SIG1_cs
beschrieben wurde.
Für die Äußerung von Gedanken und Anregungen wäre ich sehr dankbar.
Naja, das weiß die Toolchain nach dem Place and Route. Du gibst im
einfachsten Fall für ein komplett synchrones Design mit nur einem Takt
ein Constraint vor, wie hoch die Periodendauer des Taktes ist. Und wenn
alles durchgelaufen ist, sagt dir die Toolchain, ob das Timing passt,
oder ob du Setup- bzw. Hold-Verletzungen hast. Normalerweise ist der
Clock Skew auf den dedizierten Clock-Routing Pfaden so optimiert, dass
alle FlipFlops auf dem gesamten FPGA nahezu gleichzeitig erreicht
werden. Bei mehreren Takten und deren Takt-Übergängen oder asynchronen
Schweinereien wirds natürlich dann etwas aufwendiger. Aber innerhalb des
FPGA sollte man tunlichst alles sychron halten.
Zum einen hast du ein Taktnetz, aus dem alle relevanten
FFs ihr Clock-Signal beziehen. Dies erfogt nicht perfekt,
so dass für alle FFs eine Spanne von [-Delta1 .. +Delta2]
gilt (folgt aus Device-Specs). Zum anderen sind deine
Signale mit einer Laufzeit (durch die Kombinatorik/lokale
Netzwerk) behaftet (kann nach Place&Route berechnet werden).
Nimmt man als Takt/Periodenlänge ClkLen, dann muss für
den einfachsten Fall alle Signallaufzeiten kleiner
ClkLen-Delta2-Delta1-FFHold-FFSetup sein (FFHold/FFSetup
aus Device-Specs), ein synchrones Design vorausgesetzt.
Denn ab ca. T+Delta2 sind alle "alten" Infos vollständig
vorhanden und müssen bis T+ClkLen-Delta1 durch die
Kombinatorik/lokales Netz gelaufen sein.
Deine Toolchain kennt sowohl die Laufzeiten der Signale durch die ganze
Logik und Routing als auch die Laufzeiten auf dem Taktnetz.
Wobei der Skew auf dem Taktnetz eher gering ausfällt.
Wenn du eine synchrone Beschreibung machst, in der der Ausgang des einen
Registers durch eine Logik etc auf den Eingang eines zweiten Registers
führt und diese beiden Register am selben Takt "hängen", dann weiss die
Toolchain, dass sie es so routen muss, dass es passt.
Also dass mit dem ersten Takt der Ausgang des ersten Registers seinen
Wert annimmt und mit "Eintreffen" des nächsten Taktes am Zielregister
das Datum auf jeden Fall rechtzeitig am Zielregister anliegen muss.
Ob die zur Verfügung stehende Laufzeit sich aufgrund des Taktskews
zwischen den beiden Registern ein wenig verlängert oder verkützt,
berücksichtig die Toolchain.
Falls es nicht passt, bekommst du eine Meldung.
Fazit: Du musst dir eigentlich gar keinen Kopf drum machen.
Beschreibe alles schön synchron und dann passt das.
Daniel R. schrieb:> Wie erreicht man, dass die Signalzuweisungen in einem> aktaktenden-Register-Prozess alle gleichzeitig passieren, sodass race> conditions nicht auftreten können?
Das ist die Aufgabe des FPGA-Designers. Dass das nicht passiert, dass
die Taktverteilung symmetrisch ist, dass du dir keine Gedanken darum
machen musst, dafür bezahlst du deinem FPGA-Lieferanten Geld. Und dann
musst du selber nur noch dafür sorgen,
Aber wenn du ein ASIC machst, dann bist du selber dafür
verantwortlich, wie der Takt durch dein Design läuft. Du hast dann
Hilfmittel und musst selber das Taktnetz auf solche Zustände hin
untersuchen.
Daniel R. schrieb:> allein die Länge der Signallaufzeit im Berechnungsprozess
Ein Prozess hat keine Laufzeit! (da sollten viele Ausrufezeichen hin!)
Du hast hier die falsche Denkweise! Ein Prozess wird einfach in
Kombinatorik und Flipflops umgesetzt. Und natürlich hat diese
Kombinatorik Durchlaufzeiten, die korrelieren aber in keinster Weise
mit der Reihenfolge der Zuweisungen im Prozess. Nehmen wir mal den hier:
1
process(a,b,c,d)begin
2
z<=a+b*c;
3
y<=d;
4
endprocess;
Natürlich wird hier die Zuweisung an y viel schneller fertig sein (das
sind ja nur Verbindungen) als die Berechnung von z.
Lothar Miller schrieb:> Ein Prozess hat keine Laufzeit! (da sollten viele Ausrufezeichen hin!)> Du hast hier die falsche Denkweise! Ein Prozess wird einfach in> Kombinatorik und Flipflops umgesetzt. Und natürlich hat diese> Kombinatorik Durchlaufzeiten, die korrelieren aber in keinster Weise> mit der Reihenfolge der Zuweisungen im Prozess. Nehmen wir mal den hier:> process (a,b,c,d) begin> z <= a+b*c;> y <= d;> end process;> Natürlich wird hier die Zuweisung an y viel schneller fertig sein (das> sind ja nur Verbindungen) als die Berechnung von z.Lothar Miller schrieb:> Daniel R. schrieb:process (TRIGGER, CLK)> variable vCLK:bit;> begin> vCLK := CLK;> ... einige Anweisungen <--- Diese Annahme ist GRUNDLEGEND falsch!> ... zur Verzögerung> CLK <= vCLK;> end process> Denn die Anweisungen in einem Prozess haben in der Hardware keine> "Ausführungsreihenfolge".> Das hier:process (a,b,c,d,e)> variable v:bit;> begin> v := a;> w <= a or b and c or d or e;> x <= v and c or d xor e;> y <= v or a and b or c;> z <= v;> end process> Ergibt exakt die selbe Hardware wie das hier:process (a,b,c,d,e)> variable v:bit;> begin> v := a;> z <= v;> y <= v or a and b or c;> x <= v and c or d xor e;> w <= a or b and c or d or e;> end process
Ok, Signalzuweisungen in einem Prozess werden vom Synthesetool als
nebenläufige Verdrahtungen mit oder ohne kombi. Logik umgesetzt (davon
hängt dann ab in welcher Reihenfolge die Zuweisungen tatsächlich
passieren). Also von den Zuweisungen
y <= v or a and b or c;
z <= v;
wird die zweite sicherlich zuerst abgeschlossen sein, da hier nur ein
Draht notwendig ist und keine Logikzellen, wie bei der ersten Zuweisung.
Aber anders verhält es sich doch wenn man Variablen verwendet. Die
Zuweisungen an Variablen laufen in der Reihenfolge ab, wie sie angegeben
wurden. Deshalb ist es möglich:
1
process(SIG1)
2
variablevar1:bit;
3
begin
4
var1:=SIG1;
5
ifvar1='1'then
6
OUT<='1';
7
else
8
OUT<='0';
9
endif
10
endprocess
zu schreiben, aber nicht:
1
process(SIG1)
2
signaltmp1:bit;
3
begin
4
tmp<=SIG1;
5
iftmp='1'then
6
OUT<='1';
7
else
8
OUT<='0';
9
endif
10
endprocess
denn hier passiert die Zuweisung an tmp nebenläufig oder anders gesehen
am "Ende" des Prozesses. Ist SIG1 ein abgetaktetes Signal, verschiebt
sich die kombi. Logik um einen Takt, da erst beim nächsten ausführen des
unteren Prozesses der an tmp übergebene Wert in der if-Abfrage verwendet
werden kann.
Schreibt man also einen langen Block von Anweisungen, die auf Variablen
rumrechnen, werden die vom Synthesetool erzeugten Logikblöcke in die
Tiefe gestapelt sein und nicht als parallele Zuweisungen realisiert
werden.
Signalzuweisungen erzeugen jew. parallel ausgeführte (relativ kurze)
kombi. Blöcke.
Variablenberechnungen erzeugen längere Verkettungen von Logikblöcken mit
längerer Signallaufzeit.
Richtig?
Dass die Ausgänge solcher langen Verkettungen während der Berechnung vor
sich hin glitchen kann ich mir auch gut vorstellen;)
Daniel R. schrieb:> zu schreiben, aber nicht:
Ja klar: hier ist die Sensitivliste falsch, weil unvollständig.
Wir hatten das schon im
Beitrag "Re: Variable vs Signal"> denn hier passiert die Zuweisung an tmp nebenläufig oder anders gesehen> am "Ende" des Prozesses.
Genau deswegen muss der Prozess noch einmal durchlaufen werden. Das
sagt die Sensitivliste dem Simulator. Die Synthese macht es eh' schon
"richtig"...
> Schreibt man also einen langen Block von Anweisungen, die auf Variablen> rumrechnen, werden die vom Synthesetool erzeugten Logikblöcke in die> Tiefe gestapelt sein und nicht als parallele Zuweisungen realisiert> werden.
Keine sorge: die Synthese klopft diese Struktur schon flach. Ein
"komplizierter" Prozess mit Variabeln wird mit Signalen genauso
aufwendig umgesetzt...
> Signalzuweisungen erzeugen jew. parallel ausgeführte (relativ kurze)> kombi. Blöcke.> Variablenberechnungen erzeugen längere Verkettungen von Logikblöcken mit> längerer Signallaufzeit.> Richtig?
Nein.
Es ist eher so: weil mit der "sofortigen" Änderung von Variablen ein
Beschreibungsstil ermöglicht wird, der viele Ebenen beinhaltet, werden
solche Prozesse in der realen Hardware oft aufwendig. Nehmen wir mal das
da:
1
process(clk)
2
signaltmp:integer;
3
begin
4
ifrising_edge(clk);
5
ifinit='1'then
6
tmp:=startwert;
7
endif;
8
iftmp=3then
9
tmp:=tmp+offset;
10
endif;
11
ifjump='1'then
12
tmp:=addr-8;
13
speicher[tmp]<=wert;
14
elsifreduce='1'then
15
tmp:=tmp+1;
16
endif;
17
endif;
18
endprocess
Hier kann man tmp sofort beliebig manipulieren und "gleich" wieder
darauf zugreifen. In der Realität muss der Synthesizer aber diese
Berechungen alle parallel ausführen und gleichzeitig machen.
Weil diese "sofortige" Manipulation mit Signalen nicht erfolgt, muss man
sich vorher ein paar Gedanken zur Strucktur machen. Und das schadet
meist nicht!
Eine eigene Frage?
Dieser Thread ist über 12 Jahre alt.
Stell sie in einem eigenen Thread – dort lesen mehr Leute mit, und sie wird öfter und schneller beantwortet.