DEV Community

Hedy
Hedy

Posted on

How to pause FPGA based digital clock through buttons on FPGA?

1) what “pause” means in an FPGA digital clock

A typical FPGA digital clock has at least two parts:

1. Time base (tick generator)

  • Creates a clean 1 Hz (or 100 Hz) “tick” from the FPGA system clock (e.g., 50 MHz / 100 MHz).
  • This tick drives seconds/minutes/hours counters.

2. Timekeeping counters + display driver

  • Counters update time on each tick.
  • Display driver (7-segment / LCD / VGA) continuously refreshes.

To “pause the clock” with a button, the safest approach is:

Best practice: freeze timekeeping, not the whole FPGA clock

  • Keep the FPGA system clock running normally (display scanning still works).
  • Pause only the time update by using a run enable (run=1) / pause (run=0) flag.
  • When paused, you stop consuming the 1 Hz tick (or stop incrementing counters).

Why?

  • You avoid glitchy clock gating.
  • Timing closure stays clean.
  • Display remains stable and readable.

2) Button theory: why you must condition button signals

A push button is:

  • Asynchronous to the FPGA clock → can cause metastability
  • Bouncy → a single press can generate many transitions

So the standard chain is:

  1. 2-FF synchronizer (bring button into clk domain)
  2. Debounce filter (counter or shift register)
  3. Edge detect (make a one-clock “pressed” pulse)
  4. Toggle run flag (run <= ~run on each press)

This ensures one press = one toggle, reliably.

3) Architecture for a pauseable FPGA digital clock

Signals

  • tick_1hz_pulse — one-cycle pulse each second (generated from sysclk)
  • run — 1 = running, 0 = paused
  • sec, min, hour counters

Update rule

  • If run==1 and tick_1hz_pulse==1 → increment time
  • Else → hold time steady

Display rule

  • Display driver runs continuously (always refresh)
  • It reads sec/min/hour and shows them

4) Real example (Artix-7 FPGA digital clock paused by a button)
Example target hardware

  • FPGA family: Xilinx Artix-7
  • Typical dev board: Basys 3 (XC7A35T) or Nexys A7 (XC7A100T)
  • System clock: 100 MHz (common on these boards)
  • Inputs: btn_pause
  • Outputs: 7-segment display (HH:MM) or LEDs

Below is a Verilog reference design showing:

  • Button conditioning
  • 1 Hz tick generator
  • Time counters (HH:MM:SS internally)
  • Pause via run enable

A) Button conditioner (sync + debounce + press pulse)

module button_conditioner #(
    parameter integer CLK_HZ = 100_000_000,
    parameter integer DEBOUNCE_MS = 20
)(
    input  wire clk,
    input  wire rst,
    input  wire btn_async,
    output wire pressed_pulse
);
    // 1) Synchronize
    reg [1:0] sync;
    always @(posedge clk) begin
        if (rst) sync <= 2'b00;
        else     sync <= {sync[0], btn_async};
    end
    wire btn_sync = sync[1];

    // 2) Debounce
    localparam integer COUNT_MAX = (CLK_HZ/1000)*DEBOUNCE_MS;
    reg [$clog2(COUNT_MAX+1)-1:0] cnt;
    reg btn_stable;

    always @(posedge clk) begin
        if (rst) begin
            cnt        <= 0;
            btn_stable <= 1'b0;
        end else begin
            if (btn_sync == btn_stable) begin
                cnt <= 0;
            end else begin
                if (cnt == COUNT_MAX[$clog2(COUNT_MAX+1)-1:0]) begin
                    btn_stable <= btn_sync;
                    cnt <= 0;
                end else begin
                    cnt <= cnt + 1'b1;
                end
            end
        end
    end

    // 3) Rising-edge detect
    reg btn_d;
    always @(posedge clk) begin
        if (rst) btn_d <= 1'b0;
        else     btn_d <= btn_stable;
    end
    assign pressed_pulse = btn_stable & ~btn_d;
endmodule
Enter fullscreen mode Exit fullscreen mode

B) 1 Hz tick generator (from 100 MHz)

module tick_1hz #(
    parameter integer CLK_HZ = 100_000_000
)(
    input  wire clk,
    input  wire rst,
    output reg  tick_pulse
);
    localparam integer DIV = CLK_HZ; // 100M cycles -> 1 second
    reg [$clog2(DIV)-1:0] c;

    always @(posedge clk) begin
        if (rst) begin
            c <= 0;
            tick_pulse <= 1'b0;
        end else begin
            tick_pulse <= 1'b0;
            if (c == DIV-1) begin
                c <= 0;
                tick_pulse <= 1'b1; // one-cycle tick
            end else begin
                c <= c + 1'b1;
            end
        end
    end
endmodule
Enter fullscreen mode Exit fullscreen mode

C) Digital clock core with pause (HH:MM:SS)

module digital_clock_pause (
    input  wire clk,          // 100 MHz
    input  wire rst,
    input  wire btn_pause_async,
    output reg  run_led,        // shows running/paused
    output reg [5:0] sec,
    output reg [5:0] min,
    output reg [4:0] hour
);
    wire pause_press;
    button_conditioner #(.CLK_HZ(100_000_000), .DEBOUNCE_MS(20))
    u_btn (.clk(clk), .rst(rst), .btn_async(btn_pause_async), .pressed_pulse(pause_press));

    // run flag toggles on each press
    reg run;
    always @(posedge clk) begin
        if (rst) run <= 1'b1;
        else if (pause_press) run <= ~run;
    end

    always @(posedge clk) begin
        if (rst) run_led <= 1'b1;
        else     run_led <= run;
    end

    wire tick_1s;
    tick_1hz #(.CLK_HZ(100_000_000)) u_tick (.clk(clk), .rst(rst), .tick_pulse(tick_1s));

    // Timekeeping: only updates when run==1 AND tick arrives
    always @(posedge clk) begin
        if (rst) begin
            sec  <= 0;
            min  <= 0;
            hour <= 0;
        end else if (run && tick_1s) begin
            if (sec == 59) begin
                sec <= 0;
                if (min == 59) begin
                    min <= 0;
                    if (hour == 23) hour <= 0;
                    else            hour <= hour + 1'b1;
                end else begin
                    min <= min + 1'b1;
                end
            end else begin
                sec <= sec + 1'b1;
            end
        end
        // else paused: hold sec/min/hour
    end
endmodule
Enter fullscreen mode Exit fullscreen mode

How “pause” works here

  • The 1 Hz tick keeps being generated.
  • When paused, the counters simply ignore the tick.
  • Your display logic can keep refreshing uninterrupted.

5) How to connect this to a 7-segment display (concept)

On boards like Basys 3, the 7-segment is multiplexed:

  • You run a scan timer (e.g., 1 kHz–10 kHz)
  • Cycle through digits and drive segment lines
  • Convert hour/min/sec to BCD digits

Important: don’t pause the scan timer, only pause timekeeping.

6) Practical model examples (real FPGA targets)
Xilinx examples (common)

  • Basys 3 (XC7A35T, 100 MHz clock) — ideal for button + 7-seg digital clock demos
  • Nexys A7 (XC7A100T, 100 MHz clock) — similar approach, more resources

Intel examples

DE10-Lite (MAX 10) — same RTL idea (sync/debounce + run enable), just different pin constraints.

7) Common mistakes (and quick fixes)

  • Mistake: Button causes multiple pause toggles
    Fix: Debounce + edge detect (as above)

  • Mistake: Trying to gate the clock with the button
    Fix: Use run enable on counters instead

  • Mistake: Display goes blank when paused
    Fix: Keep display scan logic always running

  • Mistake: Multi-clock designs pause only one domain
    Fix: Synchronize run into each clock domain (or centralize timekeeping to one domain)

Top comments (0)