Simple resets don't work

From Hamsterworks Wiki!

Jump to: navigation, search

This FPGA Project was completed in November 2014.

If you've been using FPGAs you will have seen code with an asynchronous reset - something like:

c: process(clk)
  begin
     if reset = '1' then
        counter <= (others => '0');
     elsif rising_edge(clk) then
        counter <= counter + 1;
     end if;
  end process;

and you have also seen synchronous resets, something like this:

c: process(clk)
  begin
     if rising_edge(clk) then
        if reset = '1' then
           counter <= (others => '0');
        else
           counter <= counter + 1;
        end if;
     end if;
  end process;

However you have to be really careful with the nature of the reset signal - it can't be "too asynchronous" or it won't work reliably, as different parts of the design may come out of reset during different clock cycles.

Contents

Example of error

Here is a simple design to prove that a simple reset signal fails.

As written, if all the switches set to the same value, then on reset occurs (when btnC is pushed) the value in 'toggle' will flip between all ones and all zeros. The LEDs will glow dimly. If all the switches are not the same, then the LEDs will show the settings of the switches, which is also being held in 'toggle'.

However when the reset fails to work correctly different parts of the design come out of reset during different cycles some of 'toggle' will flip, and the LEDs will show unexpected (incorrect) results.

reset_test.vhd - synchronous version

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity reset_test is
    Port ( clk  : in STD_LOGIC;
           sw   : in STD_LOGIC_VECTOR (15 downto 0);
           led  : out STD_LOGIC_VECTOR (15 downto 0);
           btnC : in STD_LOGIC);
end reset_test;

architecture Behavioral of reset_test is
    signal toggle : STD_LOGIC_VECTOR (15 downto 0) := (others => '0');
    signal reset  : STD_LOGIC;
begin
    reset <= btnC;
    led   <= toggle;
    
p: process(clk)
    begin
        if rising_edge(clk) then
            if reset = '1' then
                toggle <= sw;
            else
                if toggle = x"0000" or toggle = x"FFFF" then
                    toggle <= not toggle;
                end if;
            end if;      
        end if;
    end process;
end Behavioral;

reset_test.vhd - asynchronous version

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity reset_test is
    Port ( clk  : in STD_LOGIC;
           sw   : in STD_LOGIC_VECTOR (15 downto 0);
           led  : out STD_LOGIC_VECTOR (15 downto 0);
           btnC : in STD_LOGIC);
end reset_test;

architecture Behavioral of reset_test is
    signal toggle : STD_LOGIC_VECTOR (15 downto 0) := (others => '0');
    signal reset  : STD_LOGIC;
begin
    reset <= btnC;
    led   <= toggle;
    
p: process(clk, reset)
    begin
        if reset = '1' then
            toggle <= sw;
        else
            if rising_edge(clk) then
                if toggle = x"0000" or toggle = x"FFFF" then
                    toggle <= not toggle;
                end if;
            end if;      
        end if;
    end process;
end Behavioral;

What is going wrong?

It is pretty simple - the reset signal takes time to propagate across the FPGA, and so when the reset signal is released the logic closer to the source of the reset signal will see the transition a little earlier than logic that is further away, and once in a while they will see the signal different of the rising edge of the design's clock signal.

Many of the possible workarounds (such as using a dedicated reset distribution network) don't completely resolve this problem.

So how can this be fixed?

The fix is pretty easy - the reset signal has be synchronized with the FPGA's clock domain. Here is a design that almost works correctly - it works well enough that you won't able to to make it reset incorrectly. It is most probably "good enough".

There is a very, very, very small chance that it will fail, if the btnC signal violates the setup or hold time of the flip-flop that holds the "reset" register, but the likelihood of that happening is so tiny that you can be pretty sure that the button will wear out first!

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity reset_test is
    Port ( clk  : in STD_LOGIC;
           sw   : in STD_LOGIC_VECTOR (15 downto 0);
           led  : out STD_LOGIC_VECTOR (15 downto 0);
           btnC : in STD_LOGIC);
end reset_test;

architecture Behavioral of reset_test is
    signal toggle : STD_LOGIC_VECTOR (15 downto 0) := (others => '0');
    signal reset  : STD_LOGIC;
begin
    led   <= toggle;

reset_p: process(clk)
    begin
        if rising_edge(clk) then
           reset <= btnC;
        end if;
    end process;
    
p: process(clk, reset)
    begin
        if reset = '1' then
            toggle <= sw;
        else
            if rising_edge(clk) then
                if toggle = x"0000" or toggle = x"FFFF" then
                    toggle <= not toggle;
                end if;
            end if;      
        end if;
    end process;
end Behavioral;

So how can this be fixed properly?

Strangely enough, for most SRAM based FPGAs the solution is to not have a reset signal at all!

Why? A lot of the failure modes where you want to have a reset are to recover from events that cause an inconsistent internal state within your design. Things like soft single-bit memory errors, power brown-outs, comsic rays and so on. These can cause your design to fail, and the reset signal will allow you to get things back into a consistent state.

But here's the problem - as your FPGA holds the design in SRAM any event that corrupts your design's state could also corrupt the design logic within the FPGA - maybe a cosmic ray flips a bit in a block RAM that is acting as a ROM. A reset signal in your design will not recover from this sort of error!

The best reset occurs during FPGA configuration, as almost everything is set to a known state. There are a few exceptions like the internal registers of the DSP blocks where no reset is implemented, but apart from them forcing the FPGA to reconfigure is the ultimate reset. On the Basys3 this can be done by pushing the "PROG" button in the top right of the board.

This Xilinx whitepaper provides a lot more information on implementing a reset in an Xilinx FPGA - http://www.xilinx.com/support/documentation/white_papers/wp272.pdf

Constraints for Basys3 board

I've been running my tests on a Basys3 board - here are the required constraints:

## Clock signal
set_property PACKAGE_PIN W5 [get_ports clk]							
	set_property IOSTANDARD LVCMOS33 [get_ports clk]
	create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk]
 
## Switches
set_property PACKAGE_PIN V17 [get_ports {sw[0]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[0]}]
set_property PACKAGE_PIN V16 [get_ports {sw[1]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[1]}]
set_property PACKAGE_PIN W16 [get_ports {sw[2]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[2]}]
set_property PACKAGE_PIN W17 [get_ports {sw[3]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[3]}]
set_property PACKAGE_PIN W15 [get_ports {sw[4]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[4]}]
set_property PACKAGE_PIN V15 [get_ports {sw[5]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[5]}]
set_property PACKAGE_PIN W14 [get_ports {sw[6]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[6]}]
set_property PACKAGE_PIN W13 [get_ports {sw[7]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[7]}]
set_property PACKAGE_PIN V2 [get_ports {sw[8]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[8]}]
set_property PACKAGE_PIN T3 [get_ports {sw[9]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[9]}]
set_property PACKAGE_PIN T2 [get_ports {sw[10]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[10]}]
set_property PACKAGE_PIN R3 [get_ports {sw[11]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[11]}]
set_property PACKAGE_PIN W2 [get_ports {sw[12]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[12]}]
set_property PACKAGE_PIN U1 [get_ports {sw[13]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[13]}]
set_property PACKAGE_PIN T1 [get_ports {sw[14]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[14]}]
set_property PACKAGE_PIN R2 [get_ports {sw[15]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {sw[15]}]
 

## LEDs
set_property PACKAGE_PIN U16 [get_ports {led[0]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[0]}]
set_property PACKAGE_PIN E19 [get_ports {led[1]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[1]}]
set_property PACKAGE_PIN U19 [get_ports {led[2]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[2]}]
set_property PACKAGE_PIN V19 [get_ports {led[3]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[3]}]
set_property PACKAGE_PIN W18 [get_ports {led[4]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[4]}]
set_property PACKAGE_PIN U15 [get_ports {led[5]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[5]}]
set_property PACKAGE_PIN U14 [get_ports {led[6]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[6]}]
set_property PACKAGE_PIN V14 [get_ports {led[7]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[7]}]
set_property PACKAGE_PIN V13 [get_ports {led[8]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[8]}]
set_property PACKAGE_PIN V3 [get_ports {led[9]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[9]}]
set_property PACKAGE_PIN W3 [get_ports {led[10]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[10]}]
set_property PACKAGE_PIN U3 [get_ports {led[11]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[11]}]
set_property PACKAGE_PIN P3 [get_ports {led[12]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[12]}]
set_property PACKAGE_PIN N3 [get_ports {led[13]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[13]}]
set_property PACKAGE_PIN P1 [get_ports {led[14]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[14]}]
set_property PACKAGE_PIN L1 [get_ports {led[15]}]					
	set_property IOSTANDARD LVCMOS33 [get_ports {led[15]}]
		
##Buttons
set_property PACKAGE_PIN U18 [get_ports btnC]						
	set_property IOSTANDARD LVCMOS33 [get_ports btnC]

Personal tools