Pulse Stretcher in Verilog: Extend a Short Pulse with Code
A pulse stretcher (also called a pulse extender or one-shot) takes a brief input pulse—sometimes only a single clock cycle wide—and produces an output that stays asserted for a fixed, programmable number of cycles. It is the circuit you reach for when a fast event must be made visible to a slower observer: driving an LED long enough for a human eye, holding a flag until a slow state machine can sample it, or widening a one-cycle strobe before it crosses into a slower clock domain.
This page gives you two pieces of pulse stretcher Verilog code—a retriggerable version and a non-retriggerable one-shot—each with a self-checking testbench. Both are written in portable Verilog-2005 and the simulation logs below are produced by Icarus Verilog, so you can see exactly how the output width tracks the parameter.
This retriggerable stretcher reloads a down-counter every time a pulse arrives. A single one-cycle pulse holds pulse_out high for exactly STRETCH cycles; a new pulse inside that window restarts the count, so a steady stream of pulses keeps the output continuously asserted.
// Retriggerable pulse stretcher: a pulse on pulse_in (re)loads a down-counter
// to STRETCH and holds pulse_out high until the count expires. Any new pulse
// during the window restarts the count (retriggerable).
module pulse_stretch_retrig #(parameter STRETCH = 4) (
input wire clk,
input wire rst_n,
input wire pulse_in,
output reg pulse_out
);
localparam CW = 16;
reg [CW-1:0] cnt;
always @(posedge clk or negedge rst_n) begin
if (!rst_n) begin
cnt <= {CW{1'b0}};
pulse_out <= 1'b0;
end else if (pulse_in) begin
// (Re)trigger: reload counter, assert output.
cnt <= STRETCH[CW-1:0];
pulse_out <= 1'b1;
end else if (cnt != {CW{1'b0}}) begin
cnt <= cnt - 1'b1;
pulse_out <= (cnt != 16'd1); // drop on the last tick
end else begin
pulse_out <= 1'b0;
end
end
endmodule
module tb;
localparam STRETCH = 4;
reg clk, rst_n, pulse_in;
wire pulse_out;
integer high_count;
integer errors;
pulse_stretch_retrig #(.STRETCH(STRETCH)) dut (
.clk(clk), .rst_n(rst_n), .pulse_in(pulse_in), .pulse_out(pulse_out)
);
initial clk = 1'b0;
always #5 clk = ~clk;
initial begin
errors = 0;
rst_n = 1'b0; pulse_in = 1'b0;
@(negedge clk);
@(negedge clk); rst_n = 1'b1;
// ---- Case 1: single 1-cycle pulse should hold output high exactly STRETCH cycles ----
@(negedge clk); pulse_in = 1'b1; // 1-cycle input pulse
@(negedge clk); pulse_in = 1'b0; // pulse_out is now asserted
high_count = 0;
while (pulse_out === 1'b1) begin
high_count = high_count + 1;
@(negedge clk);
end
if (high_count == STRETCH)
$display("PASS: single pulse held output high %0d cycles (== STRETCH)", high_count);
else begin
$display("FAIL: single pulse held output high %0d cycles, expected %0d", high_count, STRETCH);
errors = errors + 1;
end
// settle a few idle cycles
repeat (3) @(negedge clk);
// ---- Case 2: retrigger during window restarts the count ----
// Pulse, idle 1, pulse again -> last trigger is 2 cycles after the first,
// so total high window = 2 + STRETCH = 6 cycles (known-answer, hand-computed).
@(negedge clk); pulse_in = 1'b1;
@(negedge clk); pulse_in = 1'b0;
@(negedge clk); pulse_in = 1'b1; // retrigger
@(negedge clk); pulse_in = 1'b0;
// output has been high since the first trigger; count remaining high cycles + the 2 already elapsed
high_count = 2; // the two cycles already observed high before this point
while (pulse_out === 1'b1) begin
high_count = high_count + 1;
@(negedge clk);
end
if (high_count == 6 && high_count > STRETCH)
$display("PASS: retrigger extended output to %0d cycles (> STRETCH, restarted count)", high_count);
else begin
$display("FAIL: retrigger gave %0d high cycles, expected 6", high_count);
errors = errors + 1;
end
// ---- Case 3: asynchronous reset clears the output mid-window ----
@(negedge clk); pulse_in = 1'b1;
@(negedge clk); pulse_in = 1'b0;
if (pulse_out === 1'b1) $display("PASS: output asserted before reset");
else begin $display("FAIL: output not asserted before reset"); errors = errors + 1; end
rst_n = 1'b0; #1;
if (pulse_out === 1'b0) $display("PASS: async reset cleared output");
else begin $display("FAIL: async reset did not clear output"); errors = errors + 1; end
rst_n = 1'b1;
if (errors == 0) $display("ALL TESTS PASSED");
else $display("TESTS FAILED: %0d error(s)", errors);
$finish;
end
endmodule
PASS: single pulse held output high 4 cycles (== STRETCH) PASS: retrigger extended output to 6 cycles (> STRETCH, restarted count) PASS: output asserted before reset PASS: async reset cleared output ALL TESTS PASSEDThe log confirms the three properties that matter: a lone one-cycle pulse yields an output exactly
STRETCH(4) cycles wide, a retrigger two cycles into the window extends the total high time to 6 cycles (proving the count truly restarts), and the asynchronous active-low reset dropspulse_outimmediately mid-window.The non-retriggerable one-shot latches a
busyflag when it fires and ignores every further trigger until the current pulse finishes. The testbench sweeps two widths (STRETCH = 3 and 6) and then proves that a mid-window retrigger is discarded.// Non-retriggerable one-shot pulse stretcher: a trigger starts a fixed STRETCH- // cycle output pulse. While that pulse is active (busy), further triggers are // ignored until the one-shot completes. module one_shot_nonretrig #(parameter STRETCH = 4) ( input wire clk, input wire rst_n, input wire trig, output reg pulse_out ); localparam CW = 16; reg [CW-1:0] cnt; reg busy; always @(posedge clk or negedge rst_n) begin if (!rst_n) begin cnt <= {CW{1'b0}}; busy <= 1'b0; pulse_out <= 1'b0; end else if (!busy) begin if (trig) begin cnt <= STRETCH[CW-1:0] - 1'b1; // this cycle is the first high cycle busy <= 1'b1; pulse_out <= 1'b1; end else begin pulse_out <= 1'b0; end end else begin // busy: ignore trig until the pulse finishes if (cnt != {CW{1'b0}}) begin cnt <= cnt - 1'b1; pulse_out <= 1'b1; end else begin busy <= 1'b0; pulse_out <= 1'b0; end end end endmodule module tb; reg clk, rst_n, trig; // Two widths exercised to sweep the self-check. wire out3, out6; one_shot_nonretrig #(.STRETCH(3)) dut3 (.clk(clk), .rst_n(rst_n), .trig(trig), .pulse_out(out3)); one_shot_nonretrig #(.STRETCH(6)) dut6 (.clk(clk), .rst_n(rst_n), .trig(trig), .pulse_out(out6)); integer errors; integer c3, c6; initial clk = 1'b0; always #5 clk = ~clk; // measure consecutive high cycles of a signal starting now (sampled on negedges) task count_high; input sig_is_out3; // 1 -> watch out3, 0 -> watch out6 output integer n; begin n = 0; if (sig_is_out3) begin while (out3 === 1'b1) begin n = n + 1; @(negedge clk); end end else begin while (out6 === 1'b1) begin n = n + 1; @(negedge clk); end end end endtask initial begin errors = 0; rst_n = 1'b0; trig = 1'b0; @(negedge clk); @(negedge clk); rst_n = 1'b1; // ---- Case 1: single trigger -> output high exactly STRETCH cycles (both widths) ---- @(negedge clk); trig = 1'b1; @(negedge clk); trig = 1'b0; // outputs now asserted // out3 is the shorter one; capture it first, out6 keeps running in parallel. count_high(1'b1, c3); if (c3 == 3) $display("PASS: STRETCH=3 one-shot held %0d cycles", c3); else begin $display("FAIL: STRETCH=3 gave %0d cycles, expected 3", c3); errors = errors + 1; end // out6 may already be partway or done; re-run a fresh trigger for a clean count. repeat (4) @(negedge clk); // ensure both DUTs idle @(negedge clk); trig = 1'b1; @(negedge clk); trig = 1'b0; count_high(1'b0, c6); if (c6 == 6) $display("PASS: STRETCH=6 one-shot held %0d cycles", c6); else begin $display("FAIL: STRETCH=6 gave %0d cycles, expected 6", c6); errors = errors + 1; end // ---- Case 2: retrigger during the window is IGNORED (non-retriggerable) ---- repeat (4) @(negedge clk); @(negedge clk); trig = 1'b1; // first trigger (STRETCH=3) @(negedge clk); trig = 1'b0; @(negedge clk); trig = 1'b1; // mid-window retrigger -> must be ignored @(negedge clk); trig = 1'b0; // out3 has been high since the first trigger; 2 cycles already elapsed high. c3 = 2; while (out3 === 1'b1) begin c3 = c3 + 1; @(negedge clk); end if (c3 == 3) $display("PASS: mid-window retrigger ignored, output stayed exactly %0d cycles", c3); else begin $display("FAIL: retrigger not ignored, got %0d high cycles, expected 3", c3); errors = errors + 1; end if (errors == 0) $display("ALL TESTS PASSED"); else $display("TESTS FAILED: %0d error(s)", errors); $finish; end endmodulePASS: STRETCH=3 one-shot held 3 cycles PASS: STRETCH=6 one-shot held 6 cycles PASS: mid-window retrigger ignored, output stayed exactly 3 cycles ALL TESTS PASSEDThe log shows both parameterized widths producing output pulses of exactly 3 and 6 cycles, and—critically—a second trigger fired two cycles into an active 3-cycle pulse is ignored, so the output still stops after exactly 3 cycles instead of extending. That is the defining difference from the retriggerable design above.
Retriggerable vs non-retriggerable
The single most important design decision for a pulse stretcher is what happens when a new input event arrives while the output is still asserted.
- Retriggerable (Example 1): every incoming pulse reloads the counter. The output stays high until
STRETCHcycles after the last trigger. Use this when you want the output to represent “activity seen within the last N cycles”—for example a link-activity LED or a watchdog-style timeout that resets on each event.- Non-retriggerable (Example 2): once fired, the one-shot ignores triggers until it completes. The output width is always exactly
STRETCHcycles regardless of input activity. Use this when you need a bounded, deterministic pulse width—a fixed blanking interval, a debounce dead-time, or a single-shot command strobe.Edge-detect on the input
The examples above trigger on the input being high for a cycle. In a real system the “pulse” may actually be a level that stays high for many cycles, and you usually want to stretch only the rising edge. Add a one-flop edge detector in front of the stretcher:
reg in_d; wire rise = in & ~in_d; always @(posedge clk or negedge rst_n) if (!rst_n) in_d <= 1'b0; else in_d <= in;Feed
riseintopulse_in/triginstead of the raw level. This guarantees a single-cycle trigger per edge and keeps the stretch length independent of how long the source holds its level.Choosing the stretch length
The counter width must hold
STRETCH, so size it as$clog2(STRETCH+1)bits in real code (the examples use a generous fixed 16-bit counter for clarity). PickSTRETCHfrom the ratio of the two things you are bridging:
- Human-visible LED: stretch to roughly 20–50 ms. At a 50 MHz clock that is about 1–2.5 million cycles.
- Slow-clock sampling: stretch to at least two periods of the slower clock so the destination is guaranteed to sample the output at least once.
Always round up: a pulse that is one cycle too short in the destination domain is as good as lost.
Crossing to a slow clock domain
A pulse stretcher is often the first half of a pulse synchronizer. If a source-domain strobe is narrower than one destination-clock period, the destination's sampling flop can miss it entirely. Widening the pulse so it spans at least one—preferably two—destination edges makes the capture reliable.
Be careful, though: stretching alone does not make a signal safe to sample. The stretched output must still pass through a two-flop synchronizer in the destination domain to resolve metastability. For bursts of pulses that must each be counted, a proper toggle-based (closed-loop) pulse synchronizer is the correct structure; a simple stretcher only guarantees that at least one pulse is seen, not that every pulse is counted.
Reset behavior
Both designs use an asynchronous active-low reset that forces
pulse_out, the counter, and thebusyflag to their idle state, and Example 1's testbench checks that reset clears the output mid-window. In an FPGA flow you may prefer a synchronous reset; either way the key requirement is that reset leave the stretcher idle with the output deasserted, so a spurious pulse is never emitted at power-up.FAQ
Is a pulse stretcher the same as a monostable multivibrator?
Conceptually yes—a digital one-shot is the synchronous equivalent of a 555-style monostable. Instead of an RC time constant it uses a clock and a counter, which makes the output width exact and repeatable.
Why not just use a shift register?
For small, fixed stretch lengths a shift register works and needs no comparator. A counter scales far better: a 20-cycle stretch is a 5-bit counter versus a 20-flop shift register, and the length becomes a single parameter.
Can the output glitch?
No. In both designs
pulse_outis a registered flip-flop output, so it is glitch-free and changes only on a clock edge—exactly what you want when the stretched pulse feeds other synchronous logic.Have questions about this topic?
Share your question in comments or talk to our mentor team for batch guidance.▶ Try it in the ChipXpert LabRun it in the LabOpen this design in the free in-browser simulator — edit, compile and see the waveform/log instantly.
Learn Verilog with mentors, labs and placement support
This free series is maintained by the ChipXpert training team in Hyderabad & Bengaluru. Go deeper with instructor-led courses on real EDA tools.
Explore VLSI Courses Hi! Ask me about courses, fees, batches or discounts. ×ChipXpert AssistantAsk about courses, fees, batches and centres