Forum: FPGA, VHDL & Co. Einsynchronisieren mit hörerer Taktrate sinnvoll?


von Anguel S. (anguel)


Lesenswert?

Hallo Leute!

Es ist bekanntlich nötig, externe asynchrone Signale erstmal über 2 FFs 
im FPGA einzusynchronisieren. Dieses Einsynchronisieren führt allerdings 
dazu, dass man dann eine Verzögerung von 2 Takten hat. Nehmen wir nun 
an, dass ein internes Modul, welches ein externes Triggersignal 
empfängt, mit einem Clock von nur 8 MHz getaktet wird. Das würde 
bedeuten, dass das Signal erst nach 2 * 125 ns synchronisiert dort 
ankommen würde.

Nun frage ich mich, ob es nicht sinnvoll ist, dass man das 
Einsynchronisieren mit Hilfe von 2 FFs durchführt, die allerdings mit 
einer höheren Frequenz arbeiten. Das hätte auch den Vorteil, dass man 
kürzere externe Triggerpulse erkennen kann. Da ich im FPGA über DCMs an 
anderer Stelle mit 100 MHz arbeite, könnte ich dann diese 100 MHz dazu 
nutzen, um mein Signal in nur 2 * 10 ns einzusynchronisieren.

Dieses auf den 100 MHz Clock einsynchronisierte Signal muss dann zwar 
"langgezogen" werden, um in die 8 MHz Domäne ordnungsgemäß transferiert 
zu werden, aber das würde IMHO höchstens zu einen weiteren 
Taktverzögerung von bis zu 125 ns führen. Das Domain-Crossing an sich 
von 100 MHz nach 8 MHz sollten Tools wie Xilinx ISE eigentlich 
automatisch machen, da das ganze ja intern über DCMs geht, deren 
Clock-Beziehungen die Tools kennen. Und falls dort das Timing nicht 
hinkommen sollte, wird ja eh gesagt, dass Metastabilität sowieso nur bei 
höheren Frequenzen auftreten kann.

Ist sowas sinnvoll, oder mache ich da einen Denkfehler?

Grüße,
Anguel

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Anguel S. schrieb:
> Das Domain-Crossing an sich von 100 MHz nach 8 MHz sollten Tools wie
> Xilinx ISE eigentlich automatisch machen,
Ja, das machen sie. Allerdings wirst du beim Umweg über die 100 
MHz-Domäne dann auf ein mal mit einer virtuellen Taktfrequenz von 200 
MHz (und damit einer Zeit von 5ns) am Übergang zwischen den 100 MHz und 
den 8 MHz konfrontiert werden. Denn 100/8 = 12,5. Und diese 0,5 
halbieren dir die Setupzeit für den Übergang des Triggersignals. Denn es 
könnte ja ein 100MHz-Takt genau zum Zeitpunkt t=120ns und der nächste 
zum Zeitpunkt t=130ns kommen. Der 8 MHz-Takt kommt aber zum Zeitpunkt 
t=125ns...  :-o

> Es ist bekanntlich nötig, externe asynchrone Signale erstmal über 2 FFs
> im FPGA einzusynchronisieren.
Mit diesen Faustformeln ist das so eine Sache...
Man sollte wissen, warum diese 2 FFs da sind. Wenn dieses externe 
Signal in einen Zähler oder eine FSM geht (was ja meist der Fall sein 
dürfte), dann muß das entsprechende signal rechtzeitig vor der nächsten 
steigenden Taktflanke stabil am FF-eingang anliegen. Die 2 FFs 
wurden/werden verwendet, wenn sich möglicherweise der metastabile 
Zustand des 1. FFs solange halten kann, dass bei der nächsten Taktflanke 
noch kein stabiler Zustand an den beteiligten FFs angekommen wäre. Dann 
sorgt das 2. FF für klare Verhältnisse.
Allerdings sind solche Betrachtungen zur Metastabilität bei aktuellen 
FPGAs eigentlich erst bei Taktfrequenzen >200MHz nötig. Deren FFs sind 
so schnell, dass sich bei kleineren Taktfrequenzen ein metastabiler 
Zustand gar nicht so lange halten kann, dass bei einer Taktfrequenz von 
8MHz beim nächsten Takt nicht schon alles klar wäre.
Fazit: bei dieser Taktfrequenz wird dir zum Einsynchronisieren 1 FF 
ausreichen.

> Ist sowas sinnvoll, oder mache ich da einen Denkfehler?
Dieser Umweg über die 100MHz Taktdomäne ist unnötig. Aber er könnte dir 
(wenn du die Sache mit den 5 ns in den Griff bekommst) die Latenz zum 
Erkennen des Triggersignals reduzieren. Denn wenn ein Triggersignal zum 
Zeitpunkt t=10ns kommt, dann bekommst du es auch mit nur 1 Sync-FF erst 
zum Zeitpunkt t=250ns in deiner FSM mit (also fast 2 Takte später). Wenn 
du es in der 100MHz-Domäne eintaktest und den 5 ns Pfad nach dem Sync-FF 
im Griff hast, dann könntest du schon bei t=125 darauf reagieren.

von Anguel S. (anguel)


Lesenswert?

Vielen Dank an Lothar für die sehr ausführliche Antwort!

Lothar Miller schrieb:
> Allerdings wirst du beim Umweg über die 100
> MHz-Domäne dann auf ein mal mit einer virtuellen Taktfrequenz von 200
> MHz (und damit einer Zeit von 5ns) am Übergang zwischen den 100 MHz und
> den 8 MHz konfrontiert werden.
Ja, das ist mir bekannt. Falls ich übrigens mit dem Timing an der Grenze 
zw. 100 MHz und 8 MHz nicht hinkommen sollte, würde (wenn ich das 
richtig verstehe) schlimmstenfalls folgendes passieren: Das 
einsynchronisierte Signal aus der 100 MHz Domäne wechselt von '0' auf 
'1' so, dass die Setup-Zeit des 8 MHz FFs missachtet wird. Somit wird 
dieses 8 MHz FF kurz metastabil, was aber keine schlimmen Konsequenzen 
für den Rest der Schaltung hätte da < 200 MHz. Schlimmstenfalls würde 
ich dann "nur" das Triggersignal zu diesem Zeitpunkt verpassen, da sich 
das FF aufgrund der Metastabilität auf '0' statt auf '1' einpendelt. In 
diesem Fall würde ich das Triggersignal aber beim nächsten 8 MHz clk 
rising edge mitbekommen, ich würde es also nicht ganz verpassen, da ich 
das Signal in der 100 MHz Domäne sowieso auf 13 * 10 ns = 130 ns 
stretche. Richtig?

> Fazit: bei dieser Taktfrequenz wird dir zum Einsynchronisieren 1 FF
> ausreichen.
2 FFs werden also nur empfohlen, falls man 100% sicher sein möchte, dass 
auch bei hohen Frequenzen nix schief geht.

> Dieser Umweg über die 100MHz Taktdomäne ist unnötig. Aber er könnte dir
> (wenn du die Sache mit den 5 ns in den Griff bekommst) die Latenz zum
> Erkennen des Triggersignals reduzieren.
Genau diese Latenz möchte ich reduzieren. Außerdem möchte ich in der 
Lage sein, Triggersignale, die kürzer als 125 ns sind, zu erkennen. 
Deshalb wollte ich über 100 MHz eintakten.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Anguel S. schrieb:
> Ja, das ist mir bekannt. Falls ich übrigens mit dem Timing an der Grenze
> zw. 100 MHz und 8 MHz nicht hinkommen sollte, würde (wenn ich das
> richtig verstehe) schlimmstenfalls folgendes passieren: ... Richtig?
Insgesamt richtig, aber weil du damit ja auch 1 FF zusätzlich hast, ist 
nichts gewonnen. Das könntest du mit 8 MHz Takt direkt auf dem 
Eingangssignal auch machen...

> Deshalb wollte ich über 100 MHz eintakten.
Du gewinnst wie gesagt nur was, wenn du dann das 100 MHz FF direkt 
weiterverwenden kannst, ohne nochmal auf 8 MHz "umzusynchronisieren"...

> 2 FFs werden also nur empfohlen, falls man 100% sicher sein möchte, dass
> auch bei hohen Frequenzen nix schief geht.
Es ist eine Frage von Wahrscheinlichkeiten. Dazu gibt es entsprechende 
Whitepaper und Dokumentationen von den FPGA-Herstellern...
Siehe auch im Beitrag "Re: Signal per Taktflanken setten/resetten"

von Anguel S. (anguel)


Lesenswert?

Lothar Miller schrieb:

> Insgesamt richtig, aber weil du damit ja auch 1 FF zusätzlich hast, ist
> nichts gewonnen. Das könntest du mit 8 MHz Takt direkt auf dem
> Eingangssignal auch machen...

Jetzt bin ich wieder etwas verwirrt und poste deshalb mal, was ich jetzt 
habe (clk_slow = 8 MHz, clk_fast = 100 MHz):
1
entity hw_trig_in_synchronizer is
2
    Port ( clk_slow : in  STD_LOGIC;
3
           clk_fast : in  STD_LOGIC;
4
           hw_trig_in_asynch : in  STD_LOGIC;
5
           hw_trig_in_synch : out  STD_LOGIC);
6
end hw_trig_in_synchronizer;
7
8
architecture Behavioral of hw_trig_in_synchronizer is
9
10
signal hw_trig_in_clk_fast : std_logic := '0';
11
signal hw_trig_in_stretched : std_logic := '0';
12
13
signal hw_trig_in_stretch_count : unsigned(3 downto 0) := to_unsigned(0, 4);
14
15
begin
16
17
  synch_hw_trig_in_to_clk_fast: process (clk_fast)
18
  begin
19
    if (rising_edge(clk_fast)) then
20
        -- da wir unter 200 MHz arbeiten, müsste ein FF zum einsynchronisieren ausreichen
21
        hw_trig_in_clk_fast <= hw_trig_in_asynch;
22
        -- priority used:
23
        if (hw_trig_in_clk_fast = '1') then
24
            hw_trig_in_stretch_count <= to_unsigned(1, 4);
25
        elsif (hw_trig_in_stretch_count < 12 AND hw_trig_in_stretch_count /= 0) then
26
          hw_trig_in_stretch_count <= hw_trig_in_stretch_count + 1;  -- increment
27
        elsif (hw_trig_in_stretch_count = 12) then -- and no hw_trig_in_clk_fast
28
            hw_trig_in_stretch_count <= to_unsigned(0, 4);
29
        end if;
30
        --- independent if:
31
        if (hw_trig_in_stretch_count > 0 OR hw_trig_in_clk_fast = '1') then
32
          hw_trig_in_stretched <= '1';
33
        else
34
          hw_trig_in_stretched <= '0';
35
        end if;
36
    end if;
37
  end process;
38
39
40
  synch_hw_trig_in_to_clk_slow : process (clk_slow)
41
  begin
42
    if (rising_edge(clk_slow)) then
43
        if (hw_trig_in_stretched = '1') then
44
          hw_trig_in_synch <= '1';
45
        else
46
          hw_trig_in_synch <= '0';
47
        end if;
48
    end if;
49
  end process;
50
51
end Behavioral;

Ich denke so müsste ich nun innerhalb von ca. 125 ns (etwas mehr wenn 
das Triggersignal ungünstig ankommt) immer in der clk_slow (8 MHz) 
Domäne angekommen sein, oder?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Anguel S. schrieb:
Nein, du streust dir sehr viel VHDLcode-Sand in die Augen. Das Problem 
liegt ja hier:
1
    if (rising_edge(clk_fast)) then
2
        :
3
        if (hw_trig_in_stretch_count > 0 OR hw_trig_in_clk_fast = '1') then
4
          hw_trig_in_stretched <= '1';
5
        else
6
          hw_trig_in_stretched <= '0';
7
        end if;
8
    end if;
9
 :
10
    if (rising_edge(clk_slow)) then
11
        if (hw_trig_in_stretched = '1') then
12
          :
13
        end if;
14
    end if;
Zwischen irgendeinem Zeitpunkt, zu dem hw_trig_in_stretched gesetzt wird 
und einem anderen Zeitpunkt, zu dem es abgefragt wird, vergehen u.U. nur 
5ns. Das ist das Problem, das auf diese Art nicht lösen kannst...

von Anguel S. (anguel)


Lesenswert?

Jetzt habe ich folgendes gefunden:
http://www.lothar-miller.de/s9y/categories/19-SpikePuls

Das würde wahrscheinlich mein Problem am besten lösen. Ich würde dann 
aber nur 1 Synchro-FF (statt 2 FFs) verwenden und den synchronen Teil 
mit den 8 MHz betreiben. Das einzige wo ich mir nicht ganz sicher bin, 
wie ich das abändere, damit nicht nur ein einzelnes Spikeout Signal 
ausgegeben wird, sondern dass Spikeout oben bleibt, während Spikein oben 
bleibt.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Anguel S. schrieb:
> Jetzt habe ich folgendes gefunden:
Das war auch nicht sonderlich gut versteckt... ;-)
> Das würde wahrscheinlich mein Problem am besten lösen.
Das glaube ich nicht, denn deine Aufgabe ist ein andere...

Es ist auch nicht schneller als das einfache Einsynchronisieren über 1 
FF. Denn wenn du das wegdiskutierst, bekommst du wieder ein asynchrones 
Signal mit den hinlänglich bekannten Effekten...

> ich würde dann aber nur 1 Synchro-FF (statt 2 FFs) verwenden und den
> synchronen Teil mit den 8 MHz betreiben.
Dann bist du wieder beim einfachen FF zum Eintakten. Fertig.
Das letzte FF ist nur dafür da, das erste zurückzusetzen...

von Anguel S. (anguel)


Lesenswert?

Also jetzt bin ich komplett verwirrt :-)

Meine Idee ist, dass ich möglichst kurze Signale erkennen kann und diese 
so schnell wie möglich in die 8 MHz Domäne einsynchronisiere.

Durch
ext. Signal --> Marker-FF --> Einzelnes Synchro-FF @ 8 MHz
hätte ich das doch erreicht, wenn ich nicht etwas komplett falsche 
verstehe.

Da ich vorher nicht wusste, dass man solche asynchronen Marker-FFs 
verwenden kann, wollte ich das ext. Signal ursprünglich mit einer 
höheren Taktrate (100 MHz) abtasten. Außerdem bin ich ganz am Anfang 
davon ausgegangen, dass ich 2 Synchro-FFs @ 8 MHz zum einsynchronisieren 
bräuchte, was sich nachher als überflüssig herausstellte, da nur 1 
Synchro-FF bei dieser Frequenz reicht.

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Anguel S. schrieb:
> Meine Idee ist, dass ich möglichst kurze Signale erkennen kann und diese
> so schnell wie möglich in die 8 MHz Domäne einsynchronisiere.
Ah, ok.
Wenn ein externes signal kürzer sein kann als das Abtastintervall, dann 
ist der Spike-Detektor das richtige...

Nur darfst du dann das Zurücksetzen eben nicht direkt vom externen 
Signal abhängig machen. Es könnten ja auch 2 (oder mehr) externe Impulse 
gekommen sein...

BTW:
Der Spike-Detector ist übrigens während des Rücksetzens für 1 Takt 
"blind"!

von Anguel S. (anguel)


Lesenswert?

Ok, vielen Dank nochmal für die Hilfe, ich werde etwas am Simulator 
herumprobieren, ob ich das mit dem Zurücksetzen hinbekomme ;)

von Peter (Gast)


Lesenswert?

Was spricht denn dagegen, einfach eine Flankendetektion zu machen?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Peter schrieb:
> Was spricht denn dagegen, einfach eine Flankendetektion zu machen?
Mit welchem Takt?

von Chris (Gast)


Lesenswert?

Geht das überhaupt intern, mit nur 8 MHz?

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Chris schrieb:
> Geht das überhaupt intern, mit nur 8 MHz?
Was geht?

Immerhin lief der erste IBM-PC auf X86 Basis mit 4,77MHz, und auch das 
hat die Entwicklung der Menschheit nicht aufgehalten...  ;-)

von Anguel S. (anguel)



Lesenswert?

Ich habe den "Spike zu Puls" Code von Lothar Miller 
(http://www.lothar-miller.de/s9y/categories/19-SpikePuls) nun etwas 
umgewandelt. Wie bei Lothar werden auch hier sehr kurze Spikes mit Hilfe 
eines asynchr. FFs, das auf deren rising_edge() reagiert, erkannt. 
Erkannte Spikes erscheinen dann beim nächsten rising_edge(clk) am 
synchronen Ausgang des Moduls. Ich nutze zum einsynchronisieren ein 
einziges synchr. FF, da ich davon ausgehe, dass mein Takt weit unter 200 
MHz liegt. Bei mir bleibt dann der synchrone Ausgang solange auf '1', 
bis an einem darauf folgenen rising_edge(clk) eine '0' als ext. Signal 
anliegt. Angehängt habe ich die Simulation und die RTL-Ansicht.

1
entity hw_trig_in_synchronizer is
2
    Port ( clk : in  STD_LOGIC;
3
           hw_trig_in_asynch : in  STD_LOGIC;
4
           hw_trig_in_synch : out  STD_LOGIC);
5
end hw_trig_in_synchronizer;
6
7
architecture Behavioral of hw_trig_in_synchronizer is
8
9
signal hw_trig_in_synch_int : std_logic := '0';
10
signal spike_mark : std_logic := '0';
11
12
begin
13
14
  -- Spike-Marking-FF
15
   process (hw_trig_in_synch_int, hw_trig_in_asynch)
16
  begin
17
      if (hw_trig_in_synch_int = '1' AND hw_trig_in_asynch = '0') then
18
         spike_mark <= '0';
19
    elsif rising_edge(hw_trig_in_asynch) then 
20
         spike_mark <= '1'; 
21
      end if;
22
   end process;
23
  
24
  -- Synch asynchronous Spike-Mark-FF
25
   process (clk)
26
  begin
27
    if (rising_edge(clk)) then
28
      -- da wir unter 200 MHz arbeiten, müsste ein FF zum einsynchronisieren ausreichen
29
         hw_trig_in_synch_int <= spike_mark;
30
      end if;
31
   end process;
32
  
33
  hw_trig_in_synch <= hw_trig_in_synch_int;
34
35
end Behavioral;

Weiß jemand, wie kurz eigentlich ein Spike beim Spartan3 sein darf???

Grüße,
Anguel

von Lothar M. (Firma: Titel) (lkmiller) (Moderator) Benutzerseite


Lesenswert?

Das UND-Gatter sieht irgendwie zwielichtig aus...  :-/

1. Das verletzt mein Postulat zum nicht-kombinatorischen Reset.

2. Ich bin mir nicht sicher, was passiert, wenn hw_trig_in_synch_int='1' 
ist und eine steigende Flanke am hw_trig_in_asynch kommt. Ich würde fast 
behaupten, dass der Reset langsamer weggeht, als der Takt ans FF kommt. 
Damit wäre die Taktflanke verpasst...

von Anguel S. (anguel)


Lesenswert?

Überzeugt :) Dann muss wohl eine HW-Lösung zum Abfangen kurzer Pulse 
her. Und einsynchronisiert wird dann über ein einziges synchrones FF. 
Ich hoffe trotzdem, dass diese Gedankenspielerei auch anderen hilft. 
Nochmals vielen Dank an Lothar!

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
Noch kein Account? Hier anmelden.