Warum sollte man keinen (wirklich) asynchronen Reset in einem FPGA-Design verwenden?
Was ist denn eigentlich am asynchronen Reset so schlimm?
Es gibt mehrere Argumente gegen einen solchen Reset:
****************************************
Die Flipflops im FPGA (hier Xilinx) können in zwei Modi betrieben werden:
1) als asynchrones FF mit asynchronen Clear- und Preset-Eingängen
2) als synchrones FF mit synchronen Set- und Reset-Eingägen
Wenn die übliche Lehrbuch-VHDL-Schreibweise eines getakteten Prozesses verwendet wird:
process (reset,clk)
begin
if reset = '0' then
:
elsif rising_edge(clk) then
:
end if;
end process;
dann muß die Synthese das FF asynchron mit Clear und Preset konfigurieren. Sie kann dann z.B. einen synchronen Zählerreset nicht auf den Reset-Eingang legen, weil ja gar kein solcher Eingang vorhanden ist. Der synchrone Zählerreset muß dann mit zusätzlicher Logik und mehr Verdrahtungsaufwand aufgebaut werden. Dies resultiert in einem erhöhten Ressourcenverbrauch und in einem langsameren Design, weil evtl. eine Logikebene mehr eingefügt werden muß. Hier finden sich ausführliche Erklärungen und Untersuchungen zum Thema:
http://www.mikrocontroller.net/topic/108606
http://www.mikrocontroller.net/topic/110266
****************************************
Wesentlich schlimmer als nur der zusätzliche Ressourcenverbrauch ist aber, dass ein asynchroner Reset zu undefiniertem Verhalten des Designs beim Verlassen des Resetzustands führen kann. Aufgrund unterschiedlicher Laufzeiten innerhalb des FPGAs sehen einzelne Register den Reset zeitverzögert weggehen. Wenn dann gerade ein Takt kommt, kann es sein, dass die eine Hälfte des FPGAs noch im Resetzustand ist, die andere Hälfte aber schon arbeitet.
Hier eine einfache Beschreibung mit einem Vektor (= ein paar Register), der im Reset auf einen definierten Wert gesetzt wird. Nachdem der Reset weggenommen ist, wird der Vektor mit jedem Takt getoggelt und gleichzeitig auf die beiden gültigen Werte verglichen. Ist der Wert des Vektors ungleich des Resetwerts oder seines Komplements, dann haben nicht alle FFs gleichzeitig den Resetzustand verlassen. Wären das jetzt keine Register eines simplen Vektors, sondern FFs einer State-Machine, dann hätte diese gleich nach dem Reset einen beliebigen Zustand eingenommen.
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;
entity AsyncReset is
Port ( clk : in STD_LOGIC;
rst : in STD_LOGIC;
leds : out STD_LOGIC_VECTOR(7 downto 0));
end AsyncReset;
architecture Behavioral of AsyncReset is
signal x : std_logic_vector(15 downto 0) := x"5555";
signal error : unsigned(7 downto 0) := x"00";
begin
process (clk,rst) begin
if rst='0' then -- asynchroner Reset low aktiv
x <= x"AAAA";
elsif rising_edge(clk) then
x <= not x; -- alle Register toggeln
if (x/=x"5555" and x/=x"AAAA") then -- wenn ungleich:
error <= error+1; -- Fehler mitzählen
x <= x"AAAA"; -- mit definierten Wert weitermachen
end if;
end if;
end process;
leds <= std_logic_vector(error); -- auf LEDs ausgeben
end Behavioral;
Mit diesem
Testaufbau kann mit ein paar LEDs und einem Taster oder Signalgenerator als Resetquelle das oben beschriebene Verhalten dargestellt werden: obwohl der Vektor
x nur "AAAA" oder "5555" sein kann, zählt der Zähler
error hoch.
Dies kann mit einer
Verhaltensimulation niemals nachvollzogen und herausgefunden werden. Bestenfalls eine aufwendige Post-Route-Simulation würde bei geeigneter und unabhängiger Wahl des Takts in der Testbench evtl. Setup- und Hold-Zeit-Verletzungen anzeigen.
****************************************
In die selbe Ecke kommt man, wenn der asynchrone
Reset dann noch
kombinatorisch erzeugt wird:
process (a,b,c,clk)
begin
if a='0' and b='1' and c='0' then
:
elsif rising_edge(clk) then
:
end if;
end process;
Hier ist wegen des Auftreten von
Glitches bei der kombinatorischen Verknüpfung der Reset sehr anfällig. Es kann sein, dass z.B. in einer
FSM nur einzelne FFs beeinflusst werden, und so die Zustandsmaschine
ungültige Übergänge macht oder in
undefinierten Zuständen landet.
Da hilft es auch nichts, sich selber Sand in die Augen zu streuen, und das Ganze zum Beispiel so zu schreiben:
process (reset,clk)
begin
if reset = '1' then -- sieht zwar schön aus...
:
elsif rising_edge(clk) then
:
end if;
end process;
reset <= not a and b and not c; -- ... ist aber trotzdem kombinatorisch