When it's necessary to time something or generate a delay, the common solution is to create a loadable counter, decrement it, and detect zero. This leads to a very poor resource utilization. For example, let's say you want to generate a pulse every 1 second with a 27MHz clock:
* 25 registers to store the count;
* 25 adders to increment;
* 25 muxes to select preset value or incremented value into the registers;
* a wide lut to detect zero.
There is a better way. All FPGAs allow registers to be settable or resettable. Instead of using an array of plain old registers, we can use the verilog 'generate' statement to create a mix of registers that can be set or reset based on the binary preset value. A single input wire can now preset the register array to the desired value, eliminating a wide mux.
An adder is then constructed in the logic preceding the register, using the carry chain for incrementing. Carry-in triggers the counter, while carry-out indicates overflow (no zero detect or limit-detect needed). If overflow is connected to preset, the counter will run continuously, generating pulses at desired rate.
A side benefit of this is that the logic in front of the register is used exclusively for the carry chain, and may be used as a full adder. For instance, a Program Counter can normally increment, but may jump by adding an offset, and can be preset to a fixed value to handle reset or interrupt.
The following recipe is for Gowin FPGAs (on Xilinx there is a lot more flexibility with phasing mutually-prime SRL16 shift registers, which I will leave for another time).
Code:
//===========================================================================
// A specialized pre/resettable down-counter. It is compiled with the
// reset count baked in, using flip-flops which clear or set upon restart.
// All I/O logic is positive (1=asserted).
// * Asserting restart input will preset the counter to RESETVAL.
// * The bit WIDTH of the counter will be computed automagically.
// * EN must be asserted for the counter to sense and count pulses. EN may
// be used anytime to freeze the operation at the next clock, and
// continue when de-asserted
// * The device may operate as an event counter countdown/up mode sensing
// input pulses on SENSE (gate/shape them to be one clock wide!).
// * Alternatively, the device can operate in free-running timer mode by
// pulling EN and SENSE up.
// * DONE output will pulse high when the counter reaches zero. The device
// will continue counting (and will trigger again every full bitwidth count).
// * If DONE is connected to RESTART the device will restart every n clocks,
// generating pulses at programmed interfvals.
// * If DONE is connected to EN with a simple latch-like circuit, the device
// will work in one-shot mode.
module SpecialCounter #(parameter RESETVAL=27000000)
(
input clk,
input en,
input sense,
input restart,
output done
);
localparam WIDTH = $clog2(RESETVAL + 1);
initial begin
$display("Building a counter %d bits wide",WIDTH);
end
wire [WIDTH-1:0] X; wire [WIDTH-1:0]sum; wire [WIDTH:0] ca;
wire [WIDTH-1:0] B; wire [WIDTH-1:0] C; // not connected
genvar i; generate
for (i = 0; i < WIDTH; i = i + 1) begin : spclctr
if(~RESETVAL[i]) // zero means set, 1 means reset
DFFSE flopOne (.Q(X[i]),.D(sum[i]),.CLK(clk),.CE(en),.SET(restart));
else
DFFRE flopZero(.Q(X[i]),.D(sum[i]),.CLK(clk),.CE(en),.RESET(restart));
ALU alut(.I0(X[i]), .I1(B[i]), .I3(C[i]), //I0 connects to FF output
.CIN(ca[i]), .COUT(ca[i+1]),.SUM(sum[i])); //sum goes to FF input
defparam alut.ALU_MODE=0;
end
endgenerate
assign ca[0] = sense;
assign done = ca[WIDTH];
endmodule
Note that this counter counts up to zero, and its preset value is inverted, which makes it equivalent to a down counter. I just like counting up, because counting down sense and done are inverted.
I think Ken Chapman of KCPSM/Picoblaze fame did something like that.