This time I am going to create a configurable timer circuit with both an enable and a settable start value. You may recall that we’ve previously created counter circuits. For some examples see Creating a Configurable counter Circuit and Designing a Simple 4-bit Counter Circuit. Timers and counters are close related and very similar.
It is perhaps easiest to think of a timer as a specialized counter for counting clock pules. Counters on the other hand can count the occurrence of any kind of event. So far I’ve used the counters primarily as timers.
Unlike the counters we’ve created which counted up from zero, this timer circuit will be a countdown timer. On each clock tick it will count down from the start value. When the count reaches zero it will raise a tick signal. At that point it will begin counting down from the start value once again.
Designing the Timer
The number of bits in the timer is configurable through a parameter called BITS. For input ports I declare clk, rst, en and d_in. The timer updates on the rising edge of each clk transition and it resets when rst is raised. The timer is active only when en is raised. The final input port is d_in, it sets the initial value of the countdown timer. In many cases d_in is set to its full range of 2 ** BITS – 1. However, the timer interval be configured by setting d_in to a value between 0 and 2 ** BITS – 1.
For output ports I define q and tick. The current value of the countdown timer is available via q. The tick port asserts when the countdown timer reaches zero.
Let’s see how this looks in Verilog. By now the module declaration should be familiar. As should the declaration of a register rCounter to hold the internal counter. But there is at least one new concept to discuss. The full timer code follows.
// configurable timer module
module timer
#(parameter BITS = 8)
(
input clk, // clock input
input rst, // async reset
input en, // timer enable
input [BITS - 1 : 0] d_in, // timer start value
output [BITS - 1 : 0] q, // timer current value
output tick // tick event raised when counter reaches zero
);
// register for countdown timer
reg [BITS - 1 : 0] rCounter;
// holds the next value of internal counter
wire [BITS - 1 : 0] rNext;
// update or reset the timer on the clock tick
always @(posedge clk, posedge rst)
begin
if (rst)
rCounter <= d_in;
else
// only update counter when it is enabled
if (en) begin
rCounter <= rNext;
end
end
// reset timer when it reaches zero, otherwise
assign rNext = (rCounter == 0) ? d_in : rCounter - 1;
// raise tick when counter reaches 0
assign tick = (rCounter == 0) ? 1'b1 : 1'b0;
// make current count available on q
assign q = rCounter;
endmodule
Design Details
You may have already spotted it, but if not notice on line 17 where I define a new internal signal called rNext. I’ll talk about rNext in detail later. For now it is important to know that rNext contains the value to assign to rCounter on the next clk tick. This style is a different from how I coded the earlier Counter circuits.
Separating combinational and sequential logic is an important aspect to digital design. So while it may not be required for this circuit it is worth introducing the concept now. As our circuits become increasingly complex this will become a more common pattern.
In the always block the counter I handle the reset by reloading rCounter with the value of d_in. Otherwise, when en is logic high, I set rCounter to the value of rNext.
On line 32 you can see where I compute the value of rNext. I am using a continuous assignment since this is purely combinational logic. If the current value of rCounter is zero, I set rNext to the value of d_in. Otherwise, rNext is given the value of rCounter – 1.
Next on line 35 you see where I compute the value of tick similarly. If rCounter is zero then tick is logic high, otherwise it is logic low. And finally on line 38 I assign the current value of rCounter to the output port q.
Testing the Design
Below you can see the test bench code that I used to simulate the timer circuit. The test bench creates an 8-bit timer. At the start I reset the timer. Next, it sets d_in to 4 and raises en to start the timer. After counting for a few cycles, I disable the timer for a few more cycles. Next, we reset the timer once again, set d_in to 2 and re-enable the timer. The full Verilog code for the test circuit follows.
// timer test bench
module timer_test();
localparam T=20;
reg clk, rst, en;
reg [7:0] d_in;
wire tick;
wire [7:0] q;
timer t0(
.clk(clk),
.rst(rst),
.en(en),
.d_in(d_in),
.q(q),
.tick(tick)
);
// generate clock signal
always
begin
clk = 1'b1;
#(T/2);
clk = 1'b0;
#(T/2);
end
// reset the circuit for first half clock cycle
initial
begin
rst = 1'b1;
#(T/2);
rst = 1'b0;
end
initial
begin
d_in = 4;
en = 1;
// wait for reset to end
@(negedge rst);
repeat (10) @(negedge clk);
en = 0;
repeat (3) @(negedge clk);
rst = 1'b1;
#(T/2);
rst = 1'b0;
en = 1;
d_in = 2;
repeat (10) @(negedge clk);
$stop;
end
endmodule
Simulation Results
I ran a behavioral simulation and it produces the waveform below. You will see starting at 0ns that the value of q counts down from 4 to 0 at which point tick transitions from 0 to 1 at 80ns. This repeats until tick raises again at 180ns. Next, the timer enable goes to logic low from 210ns through 280ns. Note that while en is low the value of q does not change. And finally you see the timer counting down from 2 to 0 for the remainder of the simulation.
Very good. So our timer works! And that’s it for now. The source code for this project is on github.
Please like and share these posts. And remember if you have feedback, questions or suggestions regarding this post, please leave a comment!