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:
- 2-FF synchronizer (bring button into clk domain)
- Debounce filter (counter or shift register)
- Edge detect (make a one-clock “pressed” pulse)
- 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
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
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
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 insteadMistake: Display goes blank when paused
Fix: Keep display scan logic always runningMistake: Multi-clock designs pause only one domain
Fix: Synchronize run into each clock domain (or centralize timekeeping to one domain)

Top comments (0)