mikrocontroller.net

Forum: FPGA, VHDL & Co. Puls Clock Crossing Problem


Autor: Matthias G. (mgottke)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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:
entity clk_crossing_puls is port
(
   -- Eingangsseite
   clk_in     : in  std_logic;
   puls_in    : in  std_logic;
   busy       : out std_logic;
   -- Ausgangsseite
   clk_out    : in  std_logic;
   puls_out   : out std_logic
);
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.

Autor: SuperWilly (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Wie groß darf / soll der Unterschied zwischen den beiden Takten denn 
sein ?

Gruß,
SuperWilly

Autor: noone (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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

Autor: Matthias G. (mgottke)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 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.

Autor: SuperWilly (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Mit Unterscbied zwischen den Takten meinte
ich Frequenz-Unterschied !

SuperWilly

Autor: Gast (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert

Autor: Matthias G. (mgottke)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Matthias G. (mgottke)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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?

Autor: Falk Brunner (falk)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@  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

Autor: Matthias G. (mgottke)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> 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

Autor: Lothar Miller (lkmiller) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.
:
:
signal ffpi : std_logic;
signal srpi0,srpi1 : std_logic;
signal srpo0,srpo1 : std_logic;
begin

  -- synchron zu clk_in
  process begin
     wait until rising_edge(clk_in);
     srpi0 <= ffpi;
     srpi1 <= srpi0;
  end process;

  process (puls_in, srpo1) begin
     if (srpo1='1') then            -- synchron zu clk_out
        ffpi <= '0';
     elsif rising_edge(puls_in) then -- synchron zu clk_in
        ffpi <= '1';
     end if;
  end process;

  process begin
     wait until rising_edge(clk_out);
     if (srpo1='1') then
        srpo0 <= '0';
        srpo1 <= '0';
     else
        srpo0 <= ffpi;
        srpo1 <= srpo0;
     end if;
  end process;

  puls_out <= srpo1;
  busy  <= '1' when (ffpi='1' or srpi0='1' or srpi1='1') else '0';
:
:

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.

Autor: Matthias G. (mgottke)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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:
architecture behavior of clk_crossing_puls is
   signal puls_in_r        : std_logic;                      -- zur Flankendetektion
   signal in_ff            : std_logic := '0';               -- Speicherung des Eingangspulses in einem FF
   signal in_to_out_r      : std_logic_vector(2 downto 0);   -- Sieberegister zur Synchronisation zum Ausgang hin
   signal out_to_in_r      : std_logic_vector(1 downto 0);   -- Sieberegister zur Synchronisation zum Eingang zurück
begin

   -- Eingangs FF zur Speichrung des Eingangssingals
   in_ff_proc: process(clk_in)
   begin
      if rising_edge(clk_in) then
         puls_in_r <= puls_in;                         -- zur Detektion der steigenden Flanke
         if (puls_in_r = '0') and (puls_in = '1') then -- bei steigender Flanke
            in_ff <= '1';
         elsif out_to_in_r(1) = '1' then
            in_ff <= '0';
         end if;
      end if;
   end process;

   busy <= '1' when (in_ff = '1') or ((puls_in_r = '0') and (puls_in = '1')) else '0'; -- Eingangs-FF verodert mit steigender Eingangs-Flanke

   -- Eingang zum Ausgang hin synchronisieren
   in_to_out_r_proc: process(clk_out)
   begin
      if rising_edge(clk_out) then
         in_to_out_r(0)          <= in_ff;
         in_to_out_r(2 downto 1) <= in_to_out_r(1 downto 0);
      end if;
   end process;

   -- Ausgangspuls aus dem in_to_out_r erzeugen
   puls_out <= '1' when in_to_out_r(2 downto 1) = "01" else '0';

   -- Ausgang zum Eingang hin zurück synchronisieren
   out_to_in_r_proc: process(clk_in)
   begin
      if rising_edge(clk_in) then
         out_to_in_r(0) <= in_to_out_r(2);
         out_to_in_r(1) <= out_to_in_r(0);
      end if;
   end process;

end behavior;
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.

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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...

Autor: Matthias G. (mgottke)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Autor: Matthias G. (mgottke)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
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.

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [vhdl]VHDL-Code[/vhdl]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.