VHDL Flankenerkennung

Aus der Mikrocontroller.net Artikelsammlung, mit Beiträgen verschiedener Autoren (siehe Versionsgeschichte)
Wechseln zu: Navigation, Suche

Fragestellung

Ich habe an einem Eingang ein Signal. Wechselt dieses von 0 auf 1, dann möchte ich mit einem anderen Signal darauf reagieren.


Die Falle

Die intuitive Lösung

 if rising_edge(signal) then

ist hier in der Regel falsch. Dieser Konstrukt wird ausschließlich für Clock-Signale verwendet und führt dazu, daß das externe Signal an den clk-Eingang eines eigenen FFs geht. Eine solche Vorgehensweise ist praktisch nur bei Hochgeschwindigkeitszählern und Schaltungsteilen nötig, die aus anderen Gründen in einer eigenen CLK-Domain laufen müssen oder sollen. Deren Ausgänge müssen dann nochmals gesondert behandelt und ggf. einsynchronisiert werden.

Die Lösung

1. Das eingehende Signal wird mittels eines Flipflops synchronisiert und durch ein weiteres um einen Takt verzögert. Die Synchronisation ist in der Regel nötig, um das externe Signal stabil betrachten zu können. Die folgende FF-Stufe ist wiederum nötig, um einen "alten" und einen "neuen" Zustand zu haben und so einmalig erkennen zu können, wenn der Wechsel des Signals vom ersten in das zweite FF transportiert wird.

 process(CLK)
 begin
   if rising_edge(CLK) then
    if RESET = '1' then
     signal_syn <= '0';
     signal_last <= '0';
    else
     signal_syn <= signal;
     signal_last <= signal_syn;
    end if;
   end if;
 end process;


2. Erkennen der Flanke. Die steigende Flanke befindet sich genau dort, wo das verzögerte Signal noch 0 und das aktuelle Signal 1 ist. Wir können also zu obigem Prozess noch folgende Zeile hinzufügen:

 signal_rising <= signal_syn and not signal_last;

Somit haben wir ein zu CLK synchrones Signal, das für genau einen Takt 1 ist, wenn das Eingangssignal ansteigt. Dieses Signal kann jetzt als Clockenable an jedem beliebigen Flipflop benutzt werden.

Nehmen wir an, wir brauchen ein Signal, das bei der ersten steigenden Flanke am Eingang auf 1 geht und dort bleibt:

 clk_en <= signal_rising;
 
 process(CLK)
  begin
  if rising_edge(CLK) then
   if RESET = '1' then
    output <= '0';
   elsif clk_en = '1' then
    output <= '1';
   end if;
  end if;
  end process;

Der obige Konstrukt toggelt allerdings jedesmal, wenn die Flanke wechselt, also auch dann, wenn das Eingangssignal von 1 auf 0 geht. Um nur auf die steigende Flanke sensitiv zu sein, müsste ausdrücklich auf "old = 0 and new = 1" geprüft werden.

Das Problem hoher Frequenzen

Der obige Konstrukt eignet sich nur für Signale, die genügend langsamer sind, als die Taktfrequenz. Um kürzere und schneller aufeinander folgende Flanken unterhalb der Taktperiode noch zuverlässig erkennen zu können, ist es möglich, mehrere Takte gegeneinander zu verschieben, z. B. um jeweils 45 Grad (8 Takte aus PLLs) und das Signal mit diesen Takten in parallelen FFs einzusynchronisieren. Durch 2-faches Synchronisieren und Weitergabe über jeweils asynchrone FIFOs können die Werte der FFs in einer Zieltaktdomain addiert werden. Beim Übergang von Ergebnis>3 wäre ebenfalls eine Flanke sicher zu erkennen. Mit den 8 Takten sind z. B. Flanken von bis zu dem 4fachen der Systemfrequenz zu erkennen.