Table of Contents
Understanding Asynchronous Reset in Digital Design
Implementing asynchronous reset logic in VHDL is a fundamental aspect of creating robust and reliable digital designs. Whether you're developing FPGA-based systems or ASIC implementations, understanding how to properly implement reset mechanisms is crucial for ensuring your circuits can be reliably initialized to a known state. In digital design, resets are used to bring a circuit into a predefined state after power-up. This capability is essential for system stability, error recovery, and predictable behavior across various operating conditions.
An asynchronous reset is a control signal that operates independently of the clock signal, allowing flip-flops and other sequential elements to be reset immediately upon assertion. An asynchronous reset activates as soon as the reset signal is asserted. This immediate response characteristic distinguishes asynchronous resets from their synchronous counterparts and makes them particularly valuable in specific design scenarios.
What Makes Asynchronous Reset Different
Asynchronous Reset circuit is independent of free running clock. Which means Reset circuit got no knowledge of Clock input. This independence from the clock domain provides several unique characteristics that designers must understand and account for in their implementations.
The key distinction between asynchronous and synchronous resets lies in their timing relationship with the system clock. A synchronous reset activates on the active clock edge when the reset signal is asserted. In contrast, asynchronous resets take effect immediately, regardless of clock state or timing. This fundamental difference has significant implications for design methodology, timing analysis, and overall system behavior.
When to Use Asynchronous Reset
One of the key benefits is their ability to provide immediate and independent reset functionality, as the reset signal can be asserted at any time, independent of the clock signal. This can be particularly useful in situations where a system needs to be reset immediately, without waiting for the next clock cycle. This makes asynchronous resets especially valuable during power-up sequences and critical fault conditions.
Reset can happen when the clock is not running, e.g. during power-on initialization or when clock sources are unstable. Asynchronous resets, by definition, don't need a clock to be present and it might be necessary to use this kind of reset in certain situations – for example, the Xilinx MMCM and PLL primitives have an asynchronous reset to make sure they go to a known state even if the input clock is not present.
Implementing Asynchronous Reset in VHDL
Proper implementation of asynchronous reset logic in VHDL requires careful attention to coding style and process sensitivity lists. The standard approach involves creating a process that is sensitive to both the clock signal and the reset signal, ensuring that the reset can take effect immediately when asserted.
Basic Asynchronous Reset Structure
The fundamental structure for implementing asynchronous reset in VHDL follows a well-established pattern. The code snippet below shows a standard implementation of a synchronous process with a synchronous reset. For asynchronous reset, the process sensitivity list must include both the clock and reset signals.
Here's a basic example of asynchronous reset implementation:
library IEEE;
use IEEE.std_logic_1164.all;
entity dff_async_reset is
port(
clk : in std_logic;
reset : in std_logic;
d : in std_logic;
q : out std_logic
);
end dff_async_reset;
architecture behavioral of dff_async_reset is
begin
process(clk, reset)
begin
if reset = '1' then
q <= '0';
elsif rising_edge(clk) then
q <= d;
end if;
end process;
end behavioral;
In this implementation, the process is sensitive to both clk and reset. When the reset signal is asserted (logic '1' in this case), the output q is immediately set to '0', regardless of the clock state. Only when reset is not asserted does the flip-flop respond to the rising edge of the clock and capture the input data d.
Multi-Bit Register with Asynchronous Reset
For more complex designs involving multi-bit registers or state machines, the same principle applies but with additional signals to manage. Here's an example of an 8-bit register with asynchronous reset:
library IEEE;
use IEEE.std_logic_1164.all;
entity register_async is
port(
clk : in std_logic;
reset : in std_logic;
d_in : in std_logic_vector(7 downto 0);
q_out : out std_logic_vector(7 downto 0)
);
end register_async;
architecture behavioral of register_async is
begin
process(clk, reset)
begin
if reset = '1' then
q_out <= (others => '0');
elsif rising_edge(clk) then
q_out <= d_in;
end if;
end process;
end behavioral;
The (others => '0') construct provides a convenient way to initialize all bits of the vector to zero, ensuring complete reset coverage across the entire register width.
Counter Implementation with Asynchronous Reset
Counters are common building blocks in digital designs and benefit significantly from proper reset implementation. Here's a comprehensive example of a counter with asynchronous reset:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity counter_async is
port(
clk : in std_logic;
reset : in std_logic;
enable : in std_logic;
count : out std_logic_vector(7 downto 0)
);
end counter_async;
architecture behavioral of counter_async is
signal count_reg : unsigned(7 downto 0);
begin
process(clk, reset)
begin
if reset = '1' then
count_reg <= (others => '0');
elsif rising_edge(clk) then
if enable = '1' then
count_reg <= count_reg + 1;
end if;
end if;
end process;
count <= std_logic_vector(count_reg);
end behavioral;
This counter demonstrates the hierarchical structure of conditional logic in asynchronous reset implementations. The reset check occurs first and takes highest priority, followed by the clock edge detection, and finally the enable condition for normal operation.
State Machine with Asynchronous Reset
Finite state machines (FSMs) are critical components in digital systems, and proper reset implementation ensures they always start in a known, safe state. Here's an example of a simple FSM with asynchronous reset:
library IEEE;
use IEEE.std_logic_1164.all;
entity fsm_async is
port(
clk : in std_logic;
reset : in std_logic;
input : in std_logic;
output: out std_logic
);
end fsm_async;
architecture behavioral of fsm_async is
type state_type is (IDLE, ACTIVE, DONE);
signal current_state, next_state : state_type;
begin
-- State register with asynchronous reset
process(clk, reset)
begin
if reset = '1' then
current_state <= IDLE;
elsif rising_edge(clk) then
current_state <= next_state;
end if;
end process;
-- Next state logic
process(current_state, input)
begin
case current_state is
when IDLE =>
if input = '1' then
next_state <= ACTIVE;
else
next_state <= IDLE;
end if;
when ACTIVE =>
next_state <= DONE;
when DONE =>
next_state <= IDLE;
end case;
end process;
-- Output logic
output <= '1' when current_state = ACTIVE else '0';
end behavioral;
This FSM implementation separates the state register (with asynchronous reset) from the combinational next-state logic, following best practices for state machine design. The reset ensures the FSM always starts in the IDLE state, providing predictable initialization behavior.
Critical Challenges with Asynchronous Reset
While asynchronous resets offer immediate response and clock-independent operation, they introduce several challenges that designers must carefully address to ensure reliable operation.
Metastability and Reset Deassertion
The most significant challenge with asynchronous resets occurs during reset deassertion (release). Asynchronous resets however have one major problem – the reset deassertion is not guaranteed to happen at the same clock edge for all synchronous primitives in the design. This means that different parts of the design may exit from reset at different times, there is no control of the reset sequencing.
However, when the reset is deasserted and does not pass the recovery (µtSU) or removal (µtH) time check (the Timing Analyzer recovery and removal analysis checks both times), the edge is said to have fallen into the metastability zone. Additional time is required to determine the correct state, and the delay can cause the setup time to fail to register downstream, leading to system failure. This metastability issue can cause intermittent failures that are difficult to debug and reproduce.
Reset Distribution and Timing
The problem exacerbates when large, multiple-clock domain designs are considered. In addition to the synchronization issues, the distribution of an asynchronous reset to millions of flip-flops is challenging, calling for techniques similar to CTS (Clock Tree Synthesis) and requiring similar area and routing resources. This makes reset distribution a critical concern in modern, complex FPGA and ASIC designs.
Asynchronous reset release operation must be coordinated with the synchronous logic clock signal to eliminate synchronization failures due to possible contention between the reset and the clock. A lack of such coordination leads to intermittent failures on power up. These failures can be particularly problematic because they may not appear during initial testing but manifest in production environments.
Glitch Sensitivity
Asynchronous reset signals are inherently sensitive to glitches and noise on the reset line. Unlike synchronous resets, which are sampled only at clock edges and therefore have some natural filtering, asynchronous resets respond to any transition on the reset signal. This sensitivity means that proper reset signal conditioning and routing become critical design considerations.
Reset Synchronization Techniques
To address the challenges associated with asynchronous reset deassertion, designers commonly employ reset synchronization techniques that combine the benefits of asynchronous assertion with synchronous deassertion.
Asynchronous Assert, Synchronous Deassert
We can assert the reset synchronously and de-assert it asynchronously. Such a circuit is called a reset synchronizer. This approach, often called "async assert, sync deassert," provides the best of both worlds: immediate reset capability when needed, with controlled, synchronized release to avoid metastability issues.
Here's a VHDL implementation of a reset synchronizer:
library IEEE;
use IEEE.std_logic_1164.all;
entity reset_synchronizer is
port(
clk : in std_logic;
async_reset: in std_logic;
sync_reset : out std_logic
);
end reset_synchronizer;
architecture behavioral of reset_synchronizer is
signal reset_sync_reg : std_logic_vector(1 downto 0);
attribute ASYNC_REG : string;
attribute ASYNC_REG of reset_sync_reg : signal is "TRUE";
begin
process(clk, async_reset)
begin
if async_reset = '1' then
reset_sync_reg <= (others => '1');
elsif rising_edge(clk) then
reset_sync_reg <= reset_sync_reg(0) & '0';
end if;
end process;
sync_reset <= reset_sync_reg(1);
end behavioral;
This synchronizer uses a two-stage shift register to synchronize the reset deassertion. When the asynchronous reset is asserted, both stages immediately go to '1'. When the reset is released, zeros are shifted through the register synchronously with the clock, ensuring that the final synchronized reset signal is deasserted cleanly at a clock edge.
This will guarantee that the synchronous elements within each single clock domain exit from reset at the same time (i.e. at the same clock edge). The ASYNC_REG attribute helps synthesis and place-and-route tools understand that these registers form a synchronization chain and should be placed close together to minimize metastability risks.
Multi-Stage Synchronization
To avoid this, add a few follower registers after the register with the asynchronous reset and use the output of these registers in the design. The number of synchronization stages depends on the specific requirements and the MTBF (Mean Time Between Failures) targets for your design.
For critical applications, a three-stage synchronizer may be appropriate:
library IEEE;
use IEEE.std_logic_1164.all;
entity reset_sync_3stage is
port(
clk : in std_logic;
async_reset: in std_logic;
sync_reset : out std_logic
);
end reset_sync_3stage;
architecture behavioral of reset_sync_3stage is
signal sync_chain : std_logic_vector(2 downto 0);
attribute ASYNC_REG : string;
attribute ASYNC_REG of sync_chain : signal is "TRUE";
begin
process(clk, async_reset)
begin
if async_reset = '1' then
sync_chain <= (others => '1');
elsif rising_edge(clk) then
sync_chain <= sync_chain(1 downto 0) & '0';
end if;
end process;
sync_reset <= sync_chain(2);
end behavioral;
Each additional stage in the synchronization chain reduces the probability of metastability propagating through to the design logic, at the cost of additional latency in reset deassertion.
Per-Clock-Domain Reset Synchronization
In general, one of these synchronizing circuits will be required for each asynchronous clock domain. In multi-clock designs, each clock domain should have its own reset synchronizer to ensure proper reset sequencing within that domain.
Here's an example architecture for a dual-clock domain system:
library IEEE;
use IEEE.std_logic_1164.all;
entity multi_clock_reset is
port(
clk_a : in std_logic;
clk_b : in std_logic;
async_reset : in std_logic;
reset_a : out std_logic;
reset_b : out std_logic
);
end multi_clock_reset;
architecture behavioral of multi_clock_reset is
component reset_synchronizer is
port(
clk : in std_logic;
async_reset: in std_logic;
sync_reset : out std_logic
);
end component;
begin
-- Reset synchronizer for clock domain A
sync_a: reset_synchronizer
port map(
clk => clk_a,
async_reset => async_reset,
sync_reset => reset_a
);
-- Reset synchronizer for clock domain B
sync_b: reset_synchronizer
port map(
clk => clk_b,
async_reset => async_reset,
sync_reset => reset_b
);
end behavioral;
This architecture ensures that each clock domain has a properly synchronized reset signal, preventing timing violations and metastability issues that could arise from using a single reset across multiple clock domains.
Best Practices for Asynchronous Reset Implementation
Successful implementation of asynchronous reset logic requires adherence to established best practices that have been refined through years of industry experience and lessons learned from design failures.
Consistent Reset Polarity
Maintain consistent reset polarity throughout your design. Choose either active-high or active-low reset and stick with it across all modules. While the choice between active-high and active-low is often a matter of convention or target technology requirements, consistency is crucial for maintainability and reducing errors.
For FPGA designs, consider the native reset polarity of the target device's flip-flops. Some FPGA families have dedicated active-high reset resources, while others use active-low. Matching your design to the hardware can improve resource utilization and timing.
Complete Signal Reset Coverage
So the best practice is: if a synchronous process has a reset, make sure to reset all signals written in the process. This principle applies equally to asynchronous reset implementations. Incomplete reset coverage can lead to unpredictable behavior and difficult-to-debug initialization issues.
Here's an example showing proper complete reset coverage:
-- GOOD: All signals reset
process(clk, reset)
begin
if reset = '1' then
signal_a <= '0';
signal_b <= '0';
signal_c <= (others => '0');
elsif rising_edge(clk) then
signal_a <= input_a;
signal_b <= input_b;
signal_c <= input_c;
end if;
end process;
-- BAD: Incomplete reset
process(clk, reset)
begin
if reset = '1' then
signal_a <= '0';
-- signal_b and signal_c not reset!
elsif rising_edge(clk) then
signal_a <= input_a;
signal_b <= input_b;
signal_c <= input_c;
end if;
end process;
Reset Synchronization is Mandatory
Always use reset synchronizers for asynchronous reset deassertion. The caveat is that you need to synchronize the reset sources to each clock domain in your FPGA, i.e., use the reset synchronizer PietervanStar posted. This is not optional for reliable designs—it's a fundamental requirement.
The synchronized reset approach provides several benefits:
- Eliminates metastability risks during reset deassertion
- Ensures all flip-flops in a clock domain exit reset simultaneously
- Provides predictable state machine initialization
- Simplifies timing analysis and closure
- Reduces the likelihood of intermittent failures
Proper Sensitivity List Management
For asynchronous reset processes, the sensitivity list must include both the clock and reset signals. Omitting the reset from the sensitivity list will result in synthesis-simulation mismatch, where the simulation behaves differently from the synthesized hardware.
-- CORRECT: Both clk and reset in sensitivity list
process(clk, reset)
begin
if reset = '1' then
q <= '0';
elsif rising_edge(clk) then
q <= d;
end if;
end process;
-- INCORRECT: Missing reset in sensitivity list
process(clk) -- WRONG!
begin
if reset = '1' then
q <= '0';
elsif rising_edge(clk) then
q <= d;
end if;
end process;
Reset Signal Routing and Distribution
Pay careful attention to reset signal routing, especially in large designs. Use dedicated global reset resources when available in your target FPGA. These resources are specifically designed for low-skew distribution of control signals like reset.
For very large designs, consider implementing a hierarchical reset distribution network where a primary reset synchronizer feeds secondary synchronizers for different regions or modules of the design. This approach can help manage fan-out and improve timing closure.
Avoid Mixing Reset Types
The big problem that many designers make is that they mix their synchronous and asynchronous resets together to drive the async reset port on the FF. This practice creates complex timing scenarios and can lead to difficult-to-diagnose issues.
If you need both power-on reset (asynchronous) and functional reset (synchronous) capabilities, implement them separately and clearly document their purposes and interactions.
Testbench Verification
Include comprehensive reset testing in your testbenches. Verify that:
- Reset assertion properly initializes all state elements
- Reset can be asserted at any time during operation
- The design recovers correctly from reset
- Reset deassertion doesn't cause metastability or timing violations
- Multiple reset/release cycles work correctly
Here's a testbench template that includes thorough reset testing:
library IEEE;
use IEEE.std_logic_1164.all;
entity tb_reset_test is
end tb_reset_test;
architecture testbench of tb_reset_test is
signal clk : std_logic := '0';
signal reset : std_logic := '1';
signal data : std_logic := '0';
signal q : std_logic;
constant CLK_PERIOD : time := 10 ns;
begin
-- Clock generation
clk <= not clk after CLK_PERIOD/2;
-- DUT instantiation
dut: entity work.dff_async_reset
port map(
clk => clk,
reset => reset,
d => data,
q => q
);
-- Test process
process
begin
-- Test 1: Initial reset
reset <= '1';
wait for 50 ns;
assert q = '0' report "Reset failed" severity error;
-- Test 2: Release reset and verify operation
reset <= '0';
wait for 20 ns;
data <= '1';
wait until rising_edge(clk);
wait for 1 ns;
assert q = '1' report "Normal operation failed" severity error;
-- Test 3: Asynchronous reset during operation
wait for 30 ns;
reset <= '1';
wait for 1 ns;
assert q = '0' report "Async reset failed" severity error;
-- Test 4: Reset release at various clock phases
reset <= '0';
wait for 3 ns; -- Release at arbitrary time
wait until rising_edge(clk);
wait for 50 ns;
-- Test 5: Multiple reset cycles
for i in 1 to 5 loop
reset <= '1';
wait for 15 ns;
reset <= '0';
wait for 25 ns;
end loop;
report "All tests passed" severity note;
wait;
end process;
end testbench;
Asynchronous vs Synchronous Reset: Making the Choice
The choice between a synchronous or asynchronous reset depends on the nature of the logic being reset and the project requirements. Understanding the trade-offs between these approaches is essential for making informed design decisions.
Advantages of Asynchronous Reset
Asynchronous resets offer several compelling advantages:
- Clock-independent operation: The circuit can be reset even when the clock is not running or is unstable
- Immediate response: Reset takes effect instantly without waiting for a clock edge
- Power-on initialization: Enables reliable initialization during power-up sequences before clocks stabilize
- Simpler datapath: Unlike the synchronous reset, the asynchronous reset is not inserted in the datapath, and does not negatively impact the data arrival times between registers.
- Hardware efficiency: Uses dedicated reset pins on flip-flops rather than consuming logic resources
Advantages of Synchronous Reset
Synchronous resets also provide significant benefits:
- Predictable timing: Synchronous resets are predictable (at the clock edge) Synchronous resets are robust a.o. against glitches
- No metastability issues: By synchronizing the reset signal to the clock, designers can ensure that the reset operation occurs at a known and stable point in the system's timing, reducing the risk of unpredictable behavior.
- Better for FPGA synthesis: The synthesis tools can merge a synchronous reset signal into the logic for the datapath (i.e the LUTs that drive the flip-flop D input). This reduces the fanout on the reset signal and also the number of control sets which in turn improves device packing.
- Primitive compatibility: If you are relying on the tools to infer certain primitives such as DPS48s or BRAMs, this is only possible if you have coded a synchronous reset – these primitives do not support asynchronous resets.
Industry Practices and Recommendations
In general, synchronous resets are recommended unless the particular circuit requires an asynchronous reset. The choice may depend on the technology used, e.g. some FPGA blocks may only support a synchronous reset. However, industry practice varies significantly between ASIC and FPGA design communities.
For ASIC designs, asynchronous resets remain common, particularly for power-on reset scenarios. For FPGA designs, vendor recommendations increasingly favor synchronous resets or the hybrid approach of asynchronous assertion with synchronous deassertion.
If not, prefer a synchronous reset. Use asynchronous resets only with logic elements that explicitly require that (in particular complex FPGA primitives and IP cores, e.g. transceivers and bus controllers), and even so, try to use the synchronous reset signal if possible.
Advanced Reset Techniques and Patterns
Beyond basic reset implementation, several advanced techniques can enhance the robustness and functionality of reset logic in complex designs.
Power-On Reset Generation
Many FPGA designs require a power-on reset that automatically asserts during device configuration and releases after the clocks stabilize. Here's a pattern for generating a reliable power-on reset:
library IEEE;
use IEEE.std_logic_1164.all;
entity power_on_reset is
generic(
RESET_CYCLES : integer := 16 -- Number of clock cycles to hold reset
);
port(
clk : in std_logic;
por_reset : out std_logic
);
end power_on_reset;
architecture behavioral of power_on_reset is
signal reset_counter : integer range 0 to RESET_CYCLES := RESET_CYCLES;
signal reset_reg : std_logic := '1';
begin
process(clk)
begin
if rising_edge(clk) then
if reset_counter > 0 then
reset_counter <= reset_counter - 1;
reset_reg <= '1';
else
reset_reg <= '0';
end if;
end if;
end process;
por_reset <= reset_reg;
end behavioral;
This power-on reset generator uses the FPGA's initialization capabilities to start the counter at its maximum value, ensuring reset is asserted immediately after configuration. The reset remains asserted for a programmable number of clock cycles, providing time for PLLs and other circuits to stabilize.
Conditional Reset Implementation
In some designs, not all registers need to be reset. Data path registers that are guaranteed to be loaded with valid data before use can often omit reset logic, saving resources and improving timing. However, control logic and state machines should always include reset.
architecture behavioral of mixed_reset is
signal control_state : state_type;
signal data_pipeline : std_logic_vector(31 downto 0);
begin
-- Control logic: MUST have reset
control_proc: process(clk, reset)
begin
if reset = '1' then
control_state <= IDLE;
elsif rising_edge(clk) then
-- state machine logic
end if;
end process;
-- Data pipeline: No reset needed if always loaded before use
data_proc: process(clk)
begin
if rising_edge(clk) then
if data_valid = '1' then
data_pipeline <= input_data;
end if;
end if;
end process;
end behavioral;
This selective approach to reset can significantly reduce resource usage in large designs, but requires careful analysis to ensure unreset registers cannot cause problems during initialization or after reset release.
Reset Priority and Hierarchy
In designs with multiple reset sources (power-on reset, external reset button, watchdog timer reset, etc.), establish a clear priority hierarchy:
library IEEE;
use IEEE.std_logic_1164.all;
entity reset_manager is
port(
clk : in std_logic;
por_reset : in std_logic; -- Power-on reset (highest priority)
external_reset: in std_logic; -- External reset button
watchdog_reset: in std_logic; -- Watchdog timer reset
system_reset : out std_logic -- Combined system reset
);
end reset_manager;
architecture behavioral of reset_manager is
signal combined_reset : std_logic;
signal sync_reset : std_logic;
begin
-- Combine all reset sources (OR logic)
combined_reset <= por_reset or external_reset or watchdog_reset;
-- Synchronize the combined reset
sync_proc: process(clk, combined_reset)
variable sync_chain : std_logic_vector(1 downto 0) := (others => '1');
begin
if combined_reset = '1' then
sync_chain := (others => '1');
elsif rising_edge(clk) then
sync_chain := sync_chain(0) & '0';
end if;
sync_reset <= sync_chain(1);
end process;
system_reset <= sync_reset;
end behavioral;
This reset manager combines multiple reset sources and provides a single, synchronized reset output for the rest of the design, simplifying reset distribution and ensuring consistent behavior.
Timing Constraints and Analysis for Asynchronous Reset
Proper timing constraints are essential for ensuring asynchronous reset circuits meet their timing requirements and operate reliably.
Recovery and Removal Timing
Asynchronous reset signals must meet recovery and removal timing requirements relative to the clock. TimeQuest will analyze your synchronized reset paths via the reset recovery and removal timing. These timing checks ensure that when reset is deasserted, it doesn't violate setup and hold time requirements.
Recovery time is analogous to setup time—the minimum time the reset must be deasserted before the active clock edge. Removal time is analogous to hold time—the minimum time the reset must remain deasserted after the active clock edge.
SDC Constraints for Reset Paths
For proper timing analysis, constrain your reset paths appropriately. Here are example SDC constraints for asynchronous reset:
# Set false path for asynchronous reset assertion
# (Reset assertion is asynchronous and doesn't need timing analysis)
set_false_path -from [get_ports async_reset] -to [all_registers] -setup
# Constrain reset recovery/removal timing
# (Reset deassertion must meet timing)
set_max_delay -from [get_ports async_reset] -to [all_registers] 5.0
# For reset synchronizer chains, preserve registers
set_preserve_register [get_cells reset_sync_reg*]
# Mark synchronizer registers with ASYNC_REG property
set_property ASYNC_REG TRUE [get_cells reset_sync_reg*]
These constraints tell the timing analyzer to ignore the asynchronous assertion of reset (since it's meant to be asynchronous) while still checking that reset deassertion meets timing requirements through the synchronizer chain.
Reset Distribution Timing
In large designs, reset signal distribution can become a timing bottleneck. Consider these strategies:
- Use dedicated global reset networks provided by the FPGA
- Implement regional reset synchronizers to reduce fan-out
- Pipeline reset signals for very large designs
- Use timing-driven placement for reset synchronizer chains
Common Pitfalls and How to Avoid Them
Understanding common mistakes in asynchronous reset implementation helps designers avoid costly debugging sessions and potential field failures.
Pitfall 1: Forgetting Reset Synchronization
The most common and dangerous mistake is using asynchronous reset without proper synchronization of the deassertion. This can lead to intermittent failures that are extremely difficult to debug because they depend on the precise timing relationship between reset release and clock edges.
Solution: Always use a reset synchronizer for asynchronous reset deassertion. Make this a standard practice in your design methodology.
Pitfall 2: Incomplete Sensitivity Lists
Omitting the reset signal from the process sensitivity list creates a synthesis-simulation mismatch. The simulation will treat the reset as synchronous (only checked at clock edges), while synthesis will correctly implement asynchronous reset.
Solution: Always include both clock and reset in the sensitivity list for asynchronous reset processes. Use VHDL-2008's process(all) if your tools support it, or be meticulous about sensitivity lists.
Pitfall 3: Mixing Reset Styles
Combining synchronous and asynchronous reset logic, or using different reset polarities in different parts of the design, creates confusion and increases the likelihood of errors.
Solution: Establish and document a consistent reset strategy for your entire project. Use coding templates and design reviews to enforce consistency.
Pitfall 4: Insufficient Reset Pulse Width
If the reset pulse is too short, some flip-flops may not reset properly, especially in large designs with significant reset distribution delay.
Solution: Ensure reset pulses are wide enough to guarantee all flip-flops receive adequate reset duration. For power-on reset, hold reset for multiple clock cycles after clocks stabilize.
Pitfall 5: Ignoring Reset in Testbenches
Many testbenches inadequately test reset functionality, missing potential issues that only manifest during reset sequences.
Solution: Include comprehensive reset testing: initial reset, reset during operation, multiple reset cycles, and reset at various clock phases.
FPGA-Specific Considerations
Different FPGA vendors and families have specific characteristics and recommendations regarding reset implementation that designers should understand.
Xilinx FPGAs
Xilinx FPGAs have built-in initialization capabilities that set all flip-flops to a known state after configuration. This means that for many designs, explicit reset logic may not be necessary for initialization. However, runtime reset capability is still often required.
Xilinx generally recommends synchronous resets for most applications, as they integrate better with the FPGA fabric and don't consume the dedicated asynchronous set/reset resources that could be used for other purposes.
Intel (Altera) FPGAs
The registers in Altera devices have asynchronous reset ports, so you should write your code such that it uses them. The caveat is that you need to synchronize the reset sources to each clock domain in your FPGA, i.e., use the reset synchronizer PietervanStar posted.
If you write your code for a synchronous reset, then Quartus will create logic to implement your synchronous reset, i.e., you will needlessly use up LUT inputs. So the "cost" of using an incorrect style, is a larger design, and a potential to increase the combinatorial path in your design.
FPGA Initialization vs. Runtime Reset
FPGA vendors do not recommend using asynchronous resets for FPGA designs. Instead, many modern FPGA designs leverage the device's built-in initialization for power-on state and use synchronous resets for runtime reset requirements.
This approach can significantly reduce resource usage while maintaining robust reset capability. However, it requires careful consideration of which registers truly need runtime reset capability versus those that only need initialization.
Design Examples and Case Studies
Examining complete design examples helps solidify understanding of asynchronous reset implementation in realistic contexts.
Example 1: UART Receiver with Asynchronous Reset
A UART receiver demonstrates practical asynchronous reset usage in a communication peripheral:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity uart_rx is
generic(
CLKS_PER_BIT : integer := 87 -- For 115200 baud at 10MHz clock
);
port(
clk : in std_logic;
reset : in std_logic;
rx_serial : in std_logic;
rx_data : out std_logic_vector(7 downto 0);
rx_valid : out std_logic
);
end uart_rx;
architecture behavioral of uart_rx is
type state_type is (IDLE, START_BIT, DATA_BITS, STOP_BIT);
signal state : state_type;
signal bit_counter : integer range 0 to 7;
signal clk_counter : integer range 0 to CLKS_PER_BIT-1;
signal rx_data_reg : std_logic_vector(7 downto 0);
begin
process(clk, reset)
begin
if reset = '1' then
state <= IDLE;
bit_counter <= 0;
clk_counter <= 0;
rx_data_reg <= (others => '0');
rx_valid <= '0';
elsif rising_edge(clk) then
rx_valid <= '0'; -- Default, pulse for one cycle
case state is
when IDLE =>
if rx_serial = '0' then -- Start bit detected
state <= START_BIT;
clk_counter <= 0;
end if;
when START_BIT =>
if clk_counter = CLKS_PER_BIT/2 then
if rx_serial = '0' then -- Verify start bit
state <= DATA_BITS;
clk_counter <= 0;
bit_counter <= 0;
else
state <= IDLE; -- False start
end if;
else
clk_counter <= clk_counter + 1;
end if;
when DATA_BITS =>
if clk_counter = CLKS_PER_BIT-1 then
clk_counter <= 0;
rx_data_reg(bit_counter) <= rx_serial;
if bit_counter = 7 then
state <= STOP_BIT;
else
bit_counter <= bit_counter + 1;
end if;
else
clk_counter <= clk_counter + 1;
end if;
when STOP_BIT =>
if clk_counter = CLKS_PER_BIT-1 then
if rx_serial = '1' then -- Valid stop bit
rx_valid <= '1';
rx_data <= rx_data_reg;
end if;
state <= IDLE;
else
clk_counter <= clk_counter + 1;
end if;
end case;
end if;
end process;
end behavioral;
This UART receiver uses asynchronous reset to ensure the state machine can be reliably initialized even if the clock is not yet stable. All state variables are explicitly reset to known values, ensuring predictable behavior after reset release.
Example 2: Multi-Clock FIFO with Reset Synchronization
A dual-clock FIFO demonstrates reset synchronization across clock domains:
library IEEE;
use IEEE.std_logic_1164.all;
use IEEE.numeric_std.all;
entity async_fifo is
generic(
DATA_WIDTH : integer := 8;
ADDR_WIDTH : integer := 4
);
port(
-- Write clock domain
wr_clk : in std_logic;
wr_reset : in std_logic;
wr_en : in std_logic;
wr_data : in std_logic_vector(DATA_WIDTH-1 downto 0);
wr_full : out std_logic;
-- Read clock domain
rd_clk : in std_logic;
rd_reset : in std_logic;
rd_en : in std_logic;
rd_data : out std_logic_vector(DATA_WIDTH-1 downto 0);
rd_empty : out std_logic;
-- Asynchronous reset input
async_reset : in std_logic
);
end async_fifo;
architecture behavioral of async_fifo is
-- Synchronized resets for each domain
signal wr_reset_sync : std_logic;
signal rd_reset_sync : std_logic;
-- FIFO memory and pointers
type memory_type is array (0 to 2**ADDR_WIDTH-1) of
std_logic_vector(DATA_WIDTH-1 downto 0);
signal memory : memory_type;
signal wr_ptr : unsigned(ADDR_WIDTH downto 0);
signal rd_ptr : unsigned(ADDR_WIDTH downto 0);
begin
-- Reset synchronizer for write clock domain
wr_sync: entity work.reset_synchronizer
port map(
clk => wr_clk,
async_reset => async_reset,
sync_reset => wr_reset_sync
);
-- Reset synchronizer for read clock domain
rd_sync: entity work.reset_synchronizer
port map(
clk => rd_clk,
async_reset => async_reset,
sync_reset => rd_reset_sync
);
-- Write process
wr_proc: process(wr_clk, wr_reset_sync)
begin
if wr_reset_sync = '1' then
wr_ptr <= (others => '0');
elsif rising_edge(wr_clk) then
if wr_en = '1' and wr_full = '0' then
memory(to_integer(wr_ptr(ADDR_WIDTH-1 downto 0))) <= wr_data;
wr_ptr <= wr_ptr + 1;
end if;
end if;
end process;
-- Read process
rd_proc: process(rd_clk, rd_reset_sync)
begin
if rd_reset_sync = '1' then
rd_ptr <= (others => '0');
elsif rising_edge(rd_clk) then
if rd_en = '1' and rd_empty = '0' then
rd_data <= memory(to_integer(rd_ptr(ADDR_WIDTH-1 downto 0)));
rd_ptr <= rd_ptr + 1;
end if;
end if;
end process;
-- Status flags (simplified - full implementation needs Gray code)
wr_full <= '1' when (wr_ptr + 1) = rd_ptr else '0';
rd_empty <= '1' when wr_ptr = rd_ptr else '0';
end behavioral;
This FIFO demonstrates proper reset synchronization for each clock domain, ensuring that both the write and read sides exit reset cleanly without metastability issues.
Debugging and Verification Strategies
Effective debugging and verification of asynchronous reset logic requires specific strategies and tools.
Simulation Techniques
When simulating designs with asynchronous reset, pay special attention to:
- Reset timing: Test reset assertion and deassertion at various points in the clock cycle
- Multiple domains: Verify that each clock domain properly handles reset
- Reset duration: Ensure reset pulses are long enough for all logic to reset
- Post-reset behavior: Verify the design operates correctly after reset release
Static Timing Analysis
Use your timing analysis tools to verify:
- Recovery and removal timing for all asynchronous reset paths
- Proper synchronization of reset deassertion
- Reset distribution delays in large designs
- Clock-to-reset timing relationships
Hardware Testing
When testing in hardware:
- Test power-on reset behavior across multiple power cycles
- Verify reset works at different operating frequencies
- Test reset under various temperature and voltage conditions
- Perform extended stress testing with frequent reset cycles
- Monitor for any intermittent failures that might indicate metastability issues
Industry Standards and Guidelines
Several industry resources provide additional guidance on reset implementation. The Sigasi VHDL reset guidelines offer comprehensive coverage of reset coding practices. For FPGA-specific guidance, consult your vendor's design methodology guides, which provide device-specific recommendations and constraints.
Clifford Cummings' papers on reset synchronization are widely regarded as authoritative references in the field. The Embedded.com website hosts numerous articles on advanced reset techniques for both ASIC and FPGA designs.
For those working with specific FPGA families, vendor documentation provides essential device-specific information. Intel's FPGA documentation and AMD Xilinx documentation include detailed reset implementation guidelines tailored to their respective architectures.
Summary and Key Takeaways
Implementing asynchronous reset logic in VHDL is a critical skill for digital designers working on both FPGA and ASIC projects. While asynchronous resets provide immediate response and clock-independent operation, they require careful implementation to avoid metastability and timing issues.
The key principles for robust asynchronous reset implementation include:
- Always synchronize reset deassertion using a multi-stage synchronizer
- Include both clock and reset in the process sensitivity list
- Reset all signals written in a process to ensure complete initialization
- Implement per-clock-domain reset synchronization in multi-clock designs
- Use consistent reset polarity throughout your design
- Apply proper timing constraints for recovery and removal analysis
- Thoroughly test reset functionality in simulation and hardware
- Consider the trade-offs between asynchronous and synchronous reset for your specific application
By following these best practices and understanding the underlying principles, engineers can create robust, reliable digital systems that initialize predictably and recover gracefully from reset conditions. Whether you're designing a simple state machine or a complex multi-clock system, proper reset implementation forms the foundation for dependable hardware operation.
Remember that the choice between asynchronous and synchronous reset depends on your specific requirements, target technology, and design constraints. In many modern FPGA designs, the hybrid approach of asynchronous assertion with synchronous deassertion provides an optimal balance of immediate reset capability and reliable, metastability-free operation.