How to design a good Edge Detector

Level vs. Edge Signals: Why You Need Edge Detection

In digital synchronous design, sometimes we need to detect the transition ‘0’ -> ‘1’ or ‘1’ -> ‘0’ of a signal.

As a simple example, suppose you have a counter with an enable input port connected to an external push button. You need to count +1 every time you push the button.

Figure1 – example of human generated pulse used to enable a counter

Figure1 – example of human generated pulse used to enable a counter

Let the counter clock be, for example, 50 MHz. The clock period is 20 ns. Even if you are very fast at pushing the button, it will be difficult to generate a pulse of 20 ns to enable the counter for only one clock cycle.

For example, if you push the button even for a few milliseconds—let’s say 200 ms—your counter will be enabled for 200 ms / 20 ns = 10,000,000 clock cycles!

As you can see, we need a solution other than trying to push the button extremely fast.

The Standard VHDL Architecture for a Rising Edge Detector

The solution is to implement something able to detect the transition of our input signal: an edge detector. No matter how long your control signal remains high, you detect the transition from ‘0’ to ‘1’.

In this case, we have the opposite problem: the control signal must remain high for at least 2 clock cycles (remember the Nyquist theorem?) to be sure to sample the signal correctly.

Figure2 – typical implementation architecture of a rising edge detector

Figure2 – typical implementation architecture of a rising edge detector

Using the architecture in Figure2, we can generate a pulse of one clock cycle, no matter how long the input control signal is. So, every time we push the button, we will count by +1.

The VHDL Code Implementation

A possible VHDL implementation is:

library ieee;
use ieee.std_logic_1164.all;

entity edge_detector is
port (
  i_clk   : in  std_logic;
  i_rstb  : in  std_logic;
  i_input : in  std_logic;
  o_pulse : out std_logic);
end edge_detector;

architecture rtl of edge_detector is
  signal r0_input : std_logic;
  signal r1_input : std_logic;
begin

  p_rising_edge_detector : process(i_clk,i_rstb)
  begin
    if(i_rstb='0') then
      r0_input <= '0';
      r1_input <= '0';
    elsif(rising_edge(i_clk)) then
      r0_input <= i_input;
      r1_input <= r0_input;
    end if;
  end process p_rising_edge_detector;

  o_pulse <= not r1_input and r0_input;

end rtl;

Falling Edge Detector Variant

In case you need to detect the falling edge, the solution is very simple:

o_pulse <= not r0_input and r1_input;


The simulation in Figure3 shows that the circuit generates a pulse of only one clock cycle, no matter how long the control signal is.

Figure3 – VHDL code simulation of rising edge detector

Figure3 – VHDL code simulation of rising edge detector


Edge Detection vs. Button Debouncing: A Crucial Difference

Since we used a push button as an example, a quick warning is mandatory: an edge detector is not a debouncer.

Physical buttons generate mechanical bounces when pressed. If you connect a raw mechanical button directly to an edge detector, the circuit will detect multiple ‘0’ -> ‘1’ transitions in a matter of milliseconds, registering multiple false clicks.

When dealing with physical inputs, you must always filter the signal using a debouncer circuit before feeding it into your edge detector.


The “Asynchronous Trap”: Common Mistakes in Edge Detection

Surfing the web, you can find many examples of rising edge detectors, as reported in Figure4.

Figure4 – typical mistake of rising edge detector implementation

Figure4 – typical mistake of rising edge detector implementation

This implementation contains a big mistake.

Let’s see a simulation of this circuit. As you can see, if the edge of the input signal is very close to the internal clock edge, the generated pulse is very short, as shown in Figure5 and Figure6. Moreover, it is possible to lose the detection due to the internal physical delay of the circuit.

Figure5 – simulation detail of a wrong implementation of an edge detector

Figure5 – simulation detail of a wrong implementation of an edge detector

Figure6 – simulation detail of a wrong implementation of an edge detector

Figure6 – simulation detail of a wrong implementation of an edge detector

So, when you have to interface an asynchronous domain with a synchronous one, you should put a first sampling stage before implementing any combinatorial operation.

Advanced: Handling Metastability with a 2-Register Synchronizer

As a general rule, to minimize metastability issues caused by asynchronous signals (like a user pressing a button relative to the system clock), a standard solution is to put 2 register stages before any combinatorial operation.

If the first flip-flop goes metastable because the input changed exactly during the setup/hold time window, the second flip-flop will have an entire clock cycle to resolve to a stable state before the signal is used by the rest of your logic.

Here is how you upgrade the basic edge detector to include a proper synchronizer chain:

architecture rtl of sync_edge_detector is
  signal r_sync1 : std_logic;
  signal r_sync2 : std_logic;
  signal r_sync3 : std_logic;
begin

  p_sync_and_edge : process(i_clk,i_rstb)
  begin
    if(i_rstb='0') then
      r_sync1 <= '0';
      r_sync2 <= '0';
      r_sync3 <= '0';
    elsif(rising_edge(i_clk)) then
      r_sync1 <= i_input;
      r_sync2 <= r_sync1;
      r_sync3 <= r_sync2;
    end if;
  end process p_sync_and_edge;

  o_pulse <= not r_sync3 and r_sync2;

end rtl;

As a drawback, this robust solution will introduce a delay of an extra clock cycle in the output of the circuit, but it guarantees a safe synchronous design.


If you appreciated this post, please help us to share it with your friends.
If you need to contact us, please write to: surf.vhdl@gmail.com