Guten Morgen!
Ich arbeite zurzeit an einem Dual-Clock-DMA, welcher zwischen zwei
Clock-Domains Daten hin- und herschieben kann (jeweils ein Avalon-Master
pro Clock-Domain). Die Daten werden mit Hilfe einer Dual-Clock-FIFO
zwischen beiden Domains übertragen, sodass der Signalpfad zwischen
beiden Avalon-Master-Modulen timing-technisch wohldefiniert ist.
Nun habe ich ein Control-Slave-Modul, welches in einer der beiden
Clock-Domains arbeitet und per Register-Map den DMA konfiguriert und
startet. Folgendes Design habe ich mir hierfür erarbeitet:
* Beide Avalon-Master setzen ein Idle-Signal auf High, wenn sie bereit
für die nächste Operation sind. Wird in dieser Zeit ein Start-Signal auf
High gezogen (für einen Takt), startet der Avalon-Master mit der
Datenübertragung. Einige Kontrollsignale wie Start-Adresse und
Byte-Count, die wie das Start-Signal vom Control-Modul kommen, werden
erst dann als Valid anerkannt, wenn der Handshake erfolgt ist. Ab dann
darf das Control-Modul die Kontrollsignale (Start-Adresse etc.) nicht
ändern, bis das Modul wieder in den Idle wechselt.
* Innerhalb einer Clock-Domain werden die Signale ohne CDC übernommen.
Zwischen zwei Clock-Domains werden zurzeit 2-stufige CDCs verwendet.
Hiermit bläst sich das Modul jedoch drastisch auf, da jedes Signal
mehrere Register benötigt.
Nun zu meiner Frage: Dadurch, dass erst die Kontrollsignale gesetzt
werden, und mindestens ein Takt später das Start-Signal gesetzt wird,
kann ich davon ausgehen, dass in der zweiten Clock-Domain die Signale
stabil genug sind, um ohne CDC auszukommen. Nur das Start- und
Idle-Signal muss speziell behandelt werden, bspw. mit CDC beim
Start-Signal. Wie kann ich per Constraints definieren, dass Quartus
dies erlaubt?
dono schrieb:> Dadurch, dass erst die Kontrollsignale gesetzt> werden, und mindestens ein Takt später das Start-Signal gesetzt wird,> kann ich davon ausgehen, dass in der zweiten Clock-Domain die Signale> stabil genug sind, um ohne CDC auszukommen.
Nicht zwingend. Das kommt auf das Verhältnis der beiden Takte zueinander
und den Routing Delays an.
Dafür gibt es maxdelay.
Für sowas verwendet man auch eigentlich vector synchronizer.
https://www.edn.com/Pdf/ViewPdf?contentItemId=4435339
Figure 11.
dono schrieb:> Wie kann ich per Constraints definieren, dass Quartus> dies erlaubt?
Inwiefern "erlaubt"?
req und ack werden jeweils mit zwei Registern synchronisiert, und
mindestens N Takte angelegt, wobei N abhängig ist vom Verhältnis der
Taktfrequenzen. Zwischen data_clk1 und data_clk2 wird maxdelay
verwendet.
Wie muss dann die Constraint aussehen?
> dono schrieb:>> Wie kann ich per Constraints definieren, dass Quartus>> dies erlaubt?>> Inwiefern "erlaubt"?
Damit die Timing Analysis nicht meckert, und Quartus die Signale nicht
versucht, unnötig zu optimieren.
Ich kenne Quartus nicht, aber in Vivado gibt es ein "Timing Ignore"
Constraint (TIG), mit dem man einzelne Pfade von der Timinganalyse
ausnehmen kann. Gibts bei Quartus sicher auch.
Wahrscheinlich hast du schon selbst dran gedacht, aber zur Vorsicht: Das
was du vorhast, funktioniert nur dann, wenn du die Signale in der
Zieldomain tatsächlich nur dann einsamplest wenn sie stabil sind. Es
nutzt dir nichts, wenn du sie in jedem Takt einsamplest und nur
gelegentlich auswertest. Dann kann das Eingangsregister schon längst
metastabil sein.
Du brauchst also in den Eingangsregistern ein clock enable oder einen
Multiplexer, das/der von irgendwo gesteuert wird. Ein wenig Overhead
bekommst du also auf jeden Fall.
set_max_delay ist dafür bei Quartus m.E. nicht, bzw. nur bedingt
geeignet.
set_max_delay berücksichtigt den clock skew, was bei CDC zu eher
unbefriedigenden (um nicht zu sagen: falschen) Ergebnissen führt.
Angeblich gibt/gabs mal ein set_data_delay (das praktisch dem Xilinx
set_max_delay -datapath only, das den clock skew ignoriert) entsprach.
Ich habe allerdings noch keine TimeQuest-Version gefunden, die das
unterstützen würde.
Ich würde den speziellen Datenpfad einfach timing-technisch
(set_false_path) auftrennen. Da kommt erfahrungsgemäß am wenigsten
Blödsinn raus.
dono schrieb:> * Innerhalb einer Clock-Domain werden die Signale ohne CDC übernommen.> Zwischen zwei Clock-Domains werden zurzeit 2-stufige CDCs verwendet.> Hiermit bläst sich das Modul jedoch drastisch auf, da jedes Signal> mehrere Register benötigt.
Wird ein CDC auch für parallele Daten wie Start-Adresse etc. genutzt?
Wenn ja, dringend anders designen und wie schon geschrieben:
Vancouver schrieb:> Wahrscheinlich hast du schon selbst dran gedacht, aber zur Vorsicht: Das> was du vorhast, funktioniert nur dann, wenn du die Signale in der> Zieldomain tatsächlich nur dann einsamplest wenn sie stabil sind.
Z. B. begründet im Paper "14-ways-to-fool-your-synchroniser" Abschnitt
3.12 (Jaja, ich weiss, ich hab dieses Paper schon an anderer Stelle
erwähnt, aber ich finds halt kurz und gut geschrieben)
Vancouver schrieb:> Wahrscheinlich hast du schon selbst dran gedacht, aber zur Vorsicht: Das> was du vorhast, funktioniert nur dann, wenn du die Signale in der> Zieldomain tatsächlich nur dann einsamplest wenn sie stabil sind. Es> nutzt dir nichts, wenn du sie in jedem Takt einsamplest und nur> gelegentlich auswertest. Dann kann das Eingangsregister schon längst> metastabil sein.> Du brauchst also in den Eingangsregistern ein clock enable oder einen> Multiplexer, das/der von irgendwo gesteuert wird. Ein wenig Overhead> bekommst du also auf jeden Fall.
Genau so mache ich es. Ich warte auf das "req"-Signal und übernehme erst
dann die Signale in die Register.
Markus F. schrieb:> Ich würde den speziellen Datenpfad einfach timing-technisch> (set_false_path) auftrennen. Da kommt erfahrungsgemäß am wenigsten> Blödsinn raus.
So werde ich es wohl machen. Das "req"-Signal kommt einige Takte nach
den Kontrolldaten an, sodass ich mit einem set_false_path wohl auf der
sicheren Seite bin. Irgendwo muss man ja auch realistisch einsehen, dass
es nicht nicht klappen sollte.
Christophz schrieb:> Wird ein CDC auch für parallele Daten wie Start-Adresse etc. genutzt?
Im neuen Design nicht mehr. Dort wird nur ein CDC für das "req"-Signal
verwendet, die parallelen Daten werden als false_path konfiguriert.
Christophz schrieb:> Z. B. begründet im Paper "14-ways-to-fool-your-synchroniser" Abschnitt> 3.12 (Jaja, ich weiss, ich hab dieses Paper schon an anderer Stelle> erwähnt, aber ich finds halt kurz und gut geschrieben)
Tolles Paper! Hatte eine Diskussion mit einem Kollegen, kann mit ihm das
gleich mal durchspielen.
Fazit: Ich teste das Design nächste Woche auf der Hardware aus, sobald
ich alle Signale in der .sdc richtig konfiguriert habe. Vielen Dank.