Hallo Zusammen,
ich habe in einem Design an mehreren Stellen folgendes Problem:
Es müssen Daten (Register und andere) Signale von einer Clock-Domain in
eine andere überführt werden. Beide Clocks sind zueinander frei laufend.
Also keinerlei Abhängigkeiten zueinander. Auch die Frequenz des Clocks
kann auf der Daten-Source-Seite (Eingangs-Seite) größer oder kleiner
sein als auf der anderen Seite (Ausgagns-Seite) sein.
Da dieses Problemstellung etliche male im Design vorkommt, möchte ich
gerne ein Modul schreiben, welches ein Übernahmesignal Eingangsseitig in
einen 1-Takt langen Puls ausgangsseitig Transferiert. Der Puls
ausgangsseitig soll die Übernahme der Daten steuern.
Das Eingangsseitige Übernahmesignal ist ebenfalls ein Puls mit einer
Länge von einem oder aber auch mehreren Takten. Ausgewertet werden soll
dabei die steigende Flanke eingangsseitig.
Damit sich die Daten eingangsseitig während der Übergabe nicht ändern
können, soll ein Busy Signal eingangsseitig eine Datenaktualisierung
verhindern bzw. verzögern. Das Busy soll erst wieder aufgelöst werden,
wenn ausgangsseitig der Puls beendet ist.
Die Ein-, bzw. Ausgangssignale sollen natürlich Clocksynchron zum
jeweiligen Takt sein.
Die Entity sieht also so aus:
1
entityclk_crossing_pulsisport
2
(
3
-- Eingangsseite
4
clk_in:instd_logic;
5
puls_in:instd_logic;
6
busy:outstd_logic;
7
-- Ausgangsseite
8
clk_out:instd_logic;
9
puls_out:outstd_logic
10
);
Wie könnte nun aber das passende Design dazu aussehen, das möglichst
kleine Verzögerungszeiten realisiert aber dennoch resistent gegenüber
metastabilen Zuständen ist.
Vielen Dank schon mal für möglichen Lösungsansätze.
hoi...
wenn du xilinx verwendest kannste im coregenerator schauen ... unter
independent clocks fifo ... sonst stehen glaube ich im "advanced FPGA
design" von kilts auch sachen dazu drinne...
mfg
> Wie groß darf / soll der Unterschied zwischen den beiden Takten denn> sein ?
Er sollte möglichst klein sein. Wie groß er sein darf, so stellt sich
die Frage nicht. Die Frage ist, was ist möglich. Alles was an Zeit
benötigt wird, fehlt anderen Prozessen wie u.a. einer CPU die in dem
zyklischen Prozess eingebunden ist.
> wenn du xilinx verwendest kannste im coregenerator schauen ... unter> independent clocks fifo
Xilinx verwende ich diesmal (leider) nicht, aber Fifos sind an dieser
Stelle keine Lösung. Das Resultat brauche ich nämlich sofort. Insofern
benötige ich trotzdem noch ein Signal das mitteilt, dass die Daten da
sind. Ok, auf das Busy könnte man da verzichten, aber Blockrams sind
Mangelware und bei über 400 Bits und einer benötigten Puffertiefe 1 eine
Verschwendung. Im anderen Fall hänge Double-Buffer dran und dort ist
eine zwingende Rückwärtige Verriegelung (Busy) notwendig.
Danke für die App_Note, die muss ich mir erst einmal näher zu Gemüte
führen. Dazu werde ich morgen Berichten.
> Mit Unterscbied zwischen den Takten meinte> ich Frequenz-Unterschied !
Steht noch nicht endgültig fest, kann aber durchaus größer Faktor 2
sein. Von daher gesehen also beliebig.
So, nun habe ich den Flancter durchgearbeitet. Interressanter Ansatz.
Die Anwendung mit dem Interrupt ist ja schön und gut. Wie aber im Text
schon erwähnt ist der Ausgang teil-synchron bzw. teil-asynchron zu
beiden Clocks. Damit keine Metatstabile Zustände auftreten, müssten dann
am out noch zwei FF in Reihe geschaltet werden (mit dem out-clock
(reset-clock)). Um ein Puls von einem Takt länge zu erzeugen, noch ein
weiteres. Das Signal müsste dann nach den zwei FFs wieder zurück zur
Eingangsseite mit weiteren zwei FF geleitet werden. Diese dann mit dem
Eingangstakt (sysclk). Am Eingang kommt dann noch eine kleine
Statemachine.
Geht man mal davon aus, dass das CE des Eingangs-FF durch die
Statemachine mit Kombinatorischer Logig angesteuert wird, so gibt es da
keine Verzögerung. Das Ausgangssignal steht also nach der
Ausgangssynchronisation nach 2-3 Ausgangstakte zur Verfügung. Dann
dauert es wieder 2-3 Eingangstakte bis das Ausgangssignal rückwärtig zur
Statemachine einsynchronisiert ist. Nun dauert es auch noch 1
Eingangstakt bis das Busy, gesteuert durch die Statemachine, weggeht.
Im worst case dauert das ganze dann 4 Eingangs-Clocks + 3
Ausgangs-Clocks. Na ja, nicht gerade sehr schnell.
Vielleicht gehts ja doch auch schneller?
@ Matthias G. (mgottke)
>Vielleicht gehts ja doch auch schneller?
Ja, mach dein Design sauber synchron zu EINEM Takt. Alles andere geht
gegen den Baum ist ist den Zoff keine Nanosekunde wert.
Was glaubst denn, wofür du diese vielen asynchronen Takte brauchst?
MFG
Falk
> Ja, mach dein Design sauber synchron zu EINEM Takt. Alles andere geht> gegen den Baum ist ist den Zoff keine Nanosekunde wert.
Schön wärs, geht aber leider nicht. Es gibt im Design nun mal komplexe
Berechnungen die in Hardare ausgelagert sind. Durch die Komplexität und
der Bitbreite von 32 Bit ist nun mal keine Taktrate oberhalb einer
gewissen Frequenz möglich. In der Kette befindet sich aber auch eine CPU
die viel schneller ist. Wenn ich der nicht einen höheren Takt verpasse,
dann reicht das zur Verfügung stehende Zeitfenster nicht mehr aus um die
erforderlichen Berechnungen durchzuführen.
Also bitte nicht so verpauschalieren. Die Zeitlichen Anforderungen habe
ich mir schließlich nicht ausgedacht. Ein durchgängig synchrones Design
wäre mir auch lieber.
Viele Grüße Matthias
Es geht "nur" um das Valid-Signal?
Die Daten sind (über Busy) zum gleichen Zeitpunkt stabil?
Also langsam zum Mitschreiben.
Weil clk_in < clk_out sein kann, wird auch puls_in < t(clk_out) werden
können.
Dann würde ich das mit der üblichen Wischertechnik machen:
Zieldomäne ist clk_out. Mit dem puls_in setzt du setzt asynchron ein FF
(ffpi). Damit hast du auch kürzere Pulse als 1 clk_in gespeichert. Jetzt
muß dieses Flag synchronisiert werden (dieser saure Apfel bleibt dir).
Wenn der puls_out aktiv wird, wird die ganze Registerkette (aus
srpi(1,0) & ffpulsin) zurückgesetzt. Dann wird das Busy-Flag nach
Rücksynchronisierung wieder gelöscht. Hier könntest du evtl. noch ein FF
einsparen.
1
:
2
:
3
signalffpi:std_logic;
4
signalsrpi0,srpi1:std_logic;
5
signalsrpo0,srpo1:std_logic;
6
begin
7
8
-- synchron zu clk_in
9
processbegin
10
waituntilrising_edge(clk_in);
11
srpi0<=ffpi;
12
srpi1<=srpi0;
13
endprocess;
14
15
process(puls_in,srpo1)begin
16
if(srpo1='1')then-- synchron zu clk_out
17
ffpi<='0';
18
elsifrising_edge(puls_in)then-- synchron zu clk_in
Die andere Möglichkeit wäre, mit dem puls_in synchron zum clk_in alle
Daten in ein Synchronisations-Schieberegister zu laden und über zwei
Stufen in die clk_out Domäne zu takten. Dann wäre auch das busy-Signal
unnötig.
Danke Lothar,
Die beschriebene Variante habe ich in ähnlicher Weise mal umgesetzt. Die
Funktioniert auch.
> Die andere Möglichkeit wäre, mit dem puls_in synchron zum clk_in alle> Daten in ein Synchronisations-Schieberegister zu laden und über zwei> Stufen in die clk_out Domäne zu takten. Dann wäre auch das busy-Signal> unnötig.
Jetzt habe ich auch die variante 2 umgesetzt. Die ist im zeitlich
verlauf ehr schneller und es wird kein asynchrones Reset benötigt. Das
einsychronisieren übe zwei FFs in beide Richtungen bleibt in allen
Varianten erhalten. Allerdings brauche ich das Busy trotzdem.
Hier meine Lösung:
1
architecturebehaviorofclk_crossing_pulsis
2
signalpuls_in_r:std_logic;-- zur Flankendetektion
3
signalin_ff:std_logic:='0';-- Speicherung des Eingangspulses in einem FF
4
signalin_to_out_r:std_logic_vector(2downto0);-- Sieberegister zur Synchronisation zum Ausgang hin
5
signalout_to_in_r:std_logic_vector(1downto0);-- Sieberegister zur Synchronisation zum Eingang zurück
6
begin
7
8
-- Eingangs FF zur Speichrung des Eingangssingals
9
in_ff_proc:process(clk_in)
10
begin
11
ifrising_edge(clk_in)then
12
puls_in_r<=puls_in;-- zur Detektion der steigenden Flanke
13
if(puls_in_r='0')and(puls_in='1')then-- bei steigender Flanke
14
in_ff<='1';
15
elsifout_to_in_r(1)='1'then
16
in_ff<='0';
17
endif;
18
endif;
19
endprocess;
20
21
busy<='1'when(in_ff='1')or((puls_in_r='0')and(puls_in='1'))else'0';-- Eingangs-FF verodert mit steigender Eingangs-Flanke
wahrscheinlich lässt sich da nicht mehr viel rausholen. Höchstens man
arbeitet bei den Schieberegistern abwechselnd mit steigenden und
fallenden Flanke. Das ist aber bei Frequenzen über 120 MHz ein heißes
Eisen.
Hm... also wieso machst du die "langsamen" Berechnungen nicht in
mehreren schritten in einer Art Pipeline? Hätte zudem den vorteil das du
nach einer initalen latenz in jedem Takt einen Wert lesen kannst...
Hallo Läubi:
> Hm... also wieso machst du die "langsamen" Berechnungen nicht in> mehreren schritten in einer Art Pipeline? Hätte zudem den vorteil das du> nach einer initalen latenz in jedem Takt einen Wert lesen kannst...
Das ist an der Stelle zu komplex um das zu erklären. Da spielen noch
viele andere Faktoren im Design eine Rolle. Es sind dort mehrere CPUs im
Design vorhanden und auch die Speicheranbindung (DDR2-RAM) spielt mit
seinem Takt eine Rolle. Ich bin schon froh, dass sich die Taktraten auf
zwei Takte reduzieren lassen.
Was ich dir sonst noch empfehlen könnte wäre nen FSL Bus, Xilinx bietet
da nen IP Core an, der kommt auch beim MicroBlaze in Multicore
anwendungen zum Einsatz und unterstütz Asyncrone Ein/Ausgänge...
Pufertiefe ist einstellbar und recht leicht im interface, basiert mein
ich auf einem IBM Standar der auch offen ist.
Ich denke mit der Übernahme, dass das in meinem Fall schon die richtige
Lösung ist. Die Daten, die ich Übernehmen muss kommen von mehreren
Schnittstellen aktiv zur Übernahme, so dass dort eine riesen Palette an
Daten anliegt. Diese werden auch ständig von verschiedenen Quellen,
teilweise auch parallel, geändert. Es muss aber bei der Übernahme die
Synchronität eines Datensatzes gewährt bleiben. Das sind aber
Projektspezifische Probleme.
Aber danke für die Tips.