Draft preview. Lessons are being written and reviewed.

Digital Design / Module 5: Counters and frequency dividers

31. Edge detectors and pulse generators

Turn levels into single-cycle events.

Lecture coming soon

See it move

The idea

Many blocks need to act once when a signal changes, not for as long as it stays high. An edge detector converts a level into a one-cycle pulse.

How it works

Keep a registered copy of the signal, which shows its value one cycle ago. Comparing past and present reveals the change.

always_ff @(posedge clk)
  s_d <= s;

assign rise = s & ~s_d;   // now 1, was 0
assign fall = ~s & s_d;   // now 0, was 1
assign any  = s ^ s_d;    // changed

Registered or combinational output

Those outputs are combinational, so they pulse in the same cycle as the change. A registered output instead pulses one cycle later, but it is -free and has cleaner timing into the next block. Both are valid. Know which one your downstream logic expects, because this is a frequent off-by-one source.

Pulse stretching

Sometimes a pulse needs to last longer, perhaps so a slower block can see it. A can OR several delayed copies, or a small counter can hold the output high for N cycles.

Inputs from elsewhere

If s comes from another or a push button, synchronize it first. Otherwise s might change right near the edge, and because the edge logic looks at both s and s_d, different parts of the logic may see different values. A mechanical button also bounces, so it needs debouncing with a timer as well.

always_ff @(posedge clk) begin
  sync1 <= async_in;
  sync2 <= sync1;
  sync3 <= sync2;
end
assign rise = sync2 & ~sync3;

Common mistake

Using an asynchronous input straight in the edge detector. The result often works on the bench, then fails occasionally in the field, which makes this among the hardest bugs to find.

Key ideas

  • Rising edge = signal AND NOT its registered copy.
  • Output timing depends on which registered stage you compare.
  • Inputs from another domain must be synchronized first.

Your exercise

Write rising, falling and any-edge detectors.

Find the bug

This code has one deliberate bug. Read the symptom, then click the line you think is wrong.

What goes wrong: rise should be a one-cycle pulse. It stays high for as long as s is high, apart from the first cycle.

Interview questions

coreDetect any edge?

basicHow do you turn a level signal s into a one-cycle pulse when s rises?

basicUsing s and its registered copy s_d, write the falling-edge and any-edge detectors.

coreThis rising-edge detector was written without the NOT. If s is high for 5 cycles, what does rise do?

always @(posedge clk) s_d <= s;
assign rise = s & s_d;

coreThis detector registers its output. How does its timing compare with the combinational version, and why might you choose it?

always @(posedge clk) begin
  s_d    <= s;
  rise_r <= s & ~s_d;
end

coreAn edge detector on an asynchronous input mostly works but occasionally counts one press twice or misses one. Why, and why does the fix compare sync2 with sync3?

always @(posedge clk) begin
  sync1 <= async_in;
  sync2 <= sync1;
  sync3 <= sync2;
end
assign rise = sync2 & ~sync3;

coreA push button bounces for up to 5 ms. With a 50 MHz clock, how many cycles must the debounce timer count, and how many bits does it need?

advancedA signal from another, faster domain produces a pulse that is high for less than one period of your clock. Will a synchroniser and edge detector on your side see it?

advancedThis circuit widens a one-cycle pulse p. How long is wide, and what happens if two pulses arrive two cycles apart?

always @(posedge clk)
  sr <= {sr[1:0], p};

assign wide = p | (|sr);

Practise these 9 questions with animations