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 | end process; |
Natürlich wird hier die Zuweisung an y viel schneller fertig sein (das sind ja nur Verbindungen) als die Berechnung von z.
:
Bearbeitet durch Moderator
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 | variable var1:bit; |
3 | begin
|
4 | var1 := SIG1; |
5 | if var1='1' then |
6 | OUT <= '1'; |
7 | else
|
8 | OUT <= '0'; |
9 | end if |
10 | end process |
zu schreiben, aber nicht:
1 | process (SIG1) |
2 | signal tmp1:bit; |
3 | begin
|
4 | tmp <= SIG1; |
5 | if tmp='1' then |
6 | OUT <= '1'; |
7 | else
|
8 | OUT <= '0'; |
9 | end if |
10 | end process |
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;)
:
Bearbeitet durch Moderator
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 | signal tmp: integer; |
3 | begin
|
4 | if rising_edge(clk); |
5 | if init='1' then |
6 | tmp := startwert; |
7 | end if; |
8 | if tmp=3 then |
9 | tmp := tmp+offset; |
10 | end if; |
11 | if jump='1' then |
12 | tmp := addr-8; |
13 | speicher[tmp]<=wert; |
14 | elsif reduce='1' then |
15 | tmp := tmp+1; |
16 | end if; |
17 | end if; |
18 | end process |
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!
:
Bearbeitet durch Moderator
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.