In digital design, resets are used to bring a circuit into a predefined state after power-up. This article focuses on how to design resets for synchronous digital circuits in VHDL. The concepts discussed in this article are equally valid in other design languages e.g. Verilog.
Resets are designed in synchronous (clocked) parts of the design. A reset is either asynchronous or synchronous. An asynchronous reset activates as soon as the reset signal is asserted. A synchronous reset activates on the active clock edge when the reset signal is asserted. The choice between a synchronous or asynchronous reset depends on the nature of the logic being reset and the project requirements.
Advantages and disadvantages of synchronous resets include:
- Synchronous resets are predictable (at the clock edge)
- Synchronous resets are robust a.o. against glitches
- In ASIC technology, smaller flip-flops may be used…
- … but the reset is implemented in (extra) logic, which may add latency
- Timing closure of a large reset net may be challenging
Advantages and disadvantages of asynchronous resets include:
- Reset can happen when the clock is not running, e.g. in early stages of circuit start-up or when using clock gating
- Lower latency is achievable because the reset circuitry is not part of the data path
- Glitches on the reset net can lead to spurious resets
- One must ensure that the de-assertion of the reset doesn’t happen at or near a clock edge
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. One place where asynchronous resets may be required is for inout and output pins of a chip, so these can be brought to a safe value before a clock is running. A more in-depth discussion of synchronous vs. asynchronous resets is presented in this, this and this article.
Asynchronous and synchronous reset
The following piece of code shows a standard implementation of a synchronous process with an asynchronous reset. As soon as the reset signal
rst is driven high, a reset is executed. If reset is low, the normal operation activates on every rising clock edge.
p_asynchronous_reset : process(clk, rst) is begin if rst = '1' then -- do reset q <= '0'; elsif rising_edge(clk) then -- normal operation q <= d; end if; end process p_asynchronous_reset;
The code snippet below shows a standard implementation of a synchronous process with a synchronous reset. The process is activated only on a rising clock edge, at which time either a reset or the normal operation is executed.
p_synchronous_reset : process (clk) is begin if rising_edge(clk) then if rst = '1' then -- do reset q <= '0'; else -- normal operation q <= d; end if; end if; end process p_synchronous_reset;
These ways of coding resets in VHDL are straightforward and efficient for simulation. Sigasi Studio can generate the code template for processes with synchronous or asynchronous reset. Simply type proc and Ctrl+Space and select process - define a synchronous process with synchronous reset or process - define a synchronous process with asynchronous reset as needed. Further information on the Content Assist and Auto-complete can be found in the manual.
An alternative way of coding a synchronous reset is shown below. At the clock edge, the normal operation executes. If reset is active, the result of the normal operation is overridden by the reset action.
p_synchronous_reset : process (clk) is begin if rising_edge(clk) then q <= d; -- normal operation if rst = '1' then q <= '0'; -- do reset end if; end if; end process p_synchronous_reset;
This coding style may be less intuitive and slightly slower in simulation, but it is equally valid and RTL synthesis will return the same result. Note that also an asynchronous reset can be coded as an override at the end of the process.
Resets: it’s all or nothing
One thing to remember is that one should either reset all signals written be a process, or none. This rule is useful for clarity in the first place. Not resetting a subset of signals written in a process may result in unintended circuit behavior or RTL synthesis may add unwanted additional circuitry.
p_partial_synchronous_reset_1 : process (clk) is -- BAD EXAMPLE!! begin if rising_edge(clk) then if rst = '1' then -- do reset q1 <= '0'; -- reset of q2 missing else q1 <= d1; -- normal operation q2 <= d2; end if; end if; end process p_synchronous_reset;
In this case, only
q1 is reset, while
q2 is left untouched during a reset operation. The behavior may be correct, because the value of
q2 may not be important after reset. RTL synthesis however will introduce circuitry to ensure that
q2 maintains the same state during reset, adding logic and possible delay to the circuit.
p_partial_asynchronous_reset : process(clk, rst) is -- BAD EXAMPLE!! begin if rst = '1' then -- do reset q1 <= '0'; elsif rising_edge(clk) then -- normal operation q1 <= d1; q2 <= d2; end if; end process p_asynchronous_reset;
The same applies when the reset is asynchronous. The logic to have
q2 keep its state during reset gets an asynchronous input, which will have a negative impact on RTL synthesis and circuit timing.
p_partial_synchronous_reset_2 : process (clk) is -- BAD EXAMPLE!! begin if rising_edge(clk) then q1 <= d1; -- normal operation q2 <= d2; if rst = '1' then -- do reset q1 <= '0'; -- reset of q2 missing end if; end if; end process p_synchronous_reset;
Using the alternative coding style, not resetting all signals leads to different behavior. In this case,
q1 will be reset while
q2 will continue operating normally, as far as normal goes during reset. If that is the intended behavior, it would be better for clarity to assign
q2 in separate processes.
So the best practice is: if a synchronous process has a reset, make sure to reset all signals written in the process.
The above examples all contain a test
if rst = '1' to check whether a reset has to be performed. This is called an active high reset. The alternative, an active low reset, would reset the circuit when the reset signal is low. Both active high and active low resets are valid.
The choice between active high and active low depends on the application and the implementation platform. For example, if your project targets an ASIC technology featuring flip-flops with an active low reset input, active low reset may be the best choice. Or, for an FPGA project, it depends on the specific FPGA technology whether active high (or low) resets can be implemented more efficiently than the other type. Especially on high-fanout nets, choosing the wrong reset type can lead to timing violations.
For clarity, it is good practice to add a
_n (not/negative) or
_b (bar) suffix to active low reset signal names.
For reusability, e.g. for IP blocks, you may want a configurable reset polarity. This can be achieved with a constant or a generic, as in the example below:
p_synchronous_reset : process (clk) is begin if rising_edge(clk) then if rst = reset_polarity then -- do reset q <= '0'; else -- normal operation q <= d; end if; end if; end process p_synchronous_reset;
In the above example, the reset polarity is determined by
reset_polarity could be either a constant or a generic of type
std_logic. Both have advantages and drawbacks.
- A constant can be defined globally for a project. This is convenient for smaller projects with a single reset.
- A generic needs to be propagated throughout the project. Complex projects with a mix of reset polarities would require this approach. Mixing reset polarities is not recommended, but IP cores from external suppliers may come with different reset polarities.
A universal reset
A further step towards a flexible reset is achieved by also making the choice between synchronous and asynchronous configurable. To this end, we add a boolean constant or generic
p_any_reset : process(clk, rst) is begin if not synchronous_reset and rst = reset_polarity then q <= '0'; -- asynchronous reset (if selected) elsif rising_edge(clk) then if synchronous_reset and rst = reset_polarity then q <= '0'; -- synchronous reset (if selected) else q <= d; -- normal operation end if; end if; end process p_any_reset;
Regardless of whether a synchronous or asynchronous reset is selected, the same registers need to be reset to the same values. In order to avoid code duplication, the reset action can be put in a procedure at the start of the process. The final code of a synchronous process with a universal reset could look like this:
entity my_entity is generic( reset_polarity : std_logic := '1'; synchronous_reset : boolean := true ); -- code omitted p_any_reset : process(clk, rst) is procedure perform_reset is -- this procedure contains the reset action begin -- reset **all** relevant signals here q <= '0'; end procedure; begin if not synchronous_reset and rst = reset_polarity then perform_reset; -- asynchronous reset (if selected) elsif rising_edge(clk) then if rst = reset_polarity and synchronous_reset then perform_reset; -- synchronous reset (if selected) else q <= d; -- normal operation end if; end if; end process p_any_reset;
This way, you can create truly reusable code that is easy to configure for various targets having different optimal reset strategies.
- Case statements in VHDL and (System)Verilog (blog post)
- Multi-dimensional array and record checks in VHDL (screencast)
- Actual? Formal? What do they mean? (blog post)
- Wildcards in sensitivity lists in VHDL and Verilog (blog post)
- VHDL 2019: Usability and APIs (blog post)