A 4-bit Verilog counter is a digital device that counts from 0 to 15 in binary. It starts from 0000
and increments until it reaches 1111
. Once it hits 1111
, the counter rolls over to 0000
and continues counting. The counter works as long as the clock is running and the reset signal stays high.
How Rollover Works in a 4-Bit Counter
The counter’s rollover happens when it reaches its maximum value (1111
or 15 in decimal). When the counter receives one more clock pulse, it attempts to increment to 10000
(which is a 5-bit number). However, since the counter is only 4 bits wide, the most significant bit (MSB) is discarded, and the counter resets back to 0000
.
Verilog Code for 4-Bit Counter
The Verilog code for a simple 4-bit counter involves two main inputs: the clock signal and an active-low reset signal. The output is a 4-bit counter value.
module counter(
input clk, // Clock input to increment the counter
input rstn, // Active-low reset input to reset the counter to zero
output reg [3:0] out // 4-bit output to show the counter value
);
// Always block triggered at the rising edge of the clock
always @ (posedge clk) begin
if (!rstn) // If reset is active (low), set counter to zero
out <= 0;
else // Else, increment the counter
out <= out + 1;
end
endmodule
Key Features of the Verilog Counter
- Clock Signal: The counter increments on every rising edge of the clock signal (
posedge clk
). - Active-Low Reset: The counter resets to
0
when the reset signal is low (!rstn
). - 4-Bit Output: The counter value is represented as a 4-bit output.
How the Counter Operates
- When the clock signal transitions from low to high (rising edge), the counter either increments or resets, depending on the state of the reset signal.
- If the reset is low, the counter resets to
0000
. - If the reset is high, the counter increments by one on every clock pulse.
Verilog Testbench for 4-Bit Counter
To test the 4-bit counter design, we use a testbench module. A testbench simulates the behavior of the design by generating clock and reset signals. It also provides a way to monitor the counter’s output.
Testbench Code for 4-Bit Counter
module tb_counter;
reg clk; // Internal clock variable for testbench
reg rstn; // Internal reset variable for testbench
wire [3:0] out; // Wire to connect to the counter's output
// Instantiate the counter module and connect to testbench variables
counter c0 ( .clk(clk), .rstn(rstn), .out(out));
// Clock generation: toggles every 5 ns (100 MHz clock)
always #5 clk = ~clk;
// Stimulus generation in initial block
initial begin
clk = 0; // Initialize clock to 0
rstn = 0; // Initialize reset to 0
#20 rstn = 1; // Assert reset after 20ns
#80 rstn = 0; // Deassert reset after 80ns
#50 rstn = 1; // Assert reset again after 50ns
#20 $finish; // End simulation after 200ns
end
endmodule
Explanation of Testbench Behavior
- The clock signal toggles every 5ns, producing a frequency of 100 MHz.
- The initial block drives the stimulus to the reset signal, asserting and deasserting it at various intervals.
- The counter module is instantiated and connected to the testbench signals. The output is monitored to ensure the counter behaves as expected.
- The simulation ends after 200ns using the
$finish
command.
Types of Counters in Verilog
Verilog counters can take different forms based on their behavior. We can have up counters, down counters, up-down counters, and random counters that use Linear Feedback Shift Registers (LFSR). Let’s explore the Verilog code for each type.
1. Verilog Up Counter
An up counter counts from 0 to 15. It increments the count on every clock pulse and resets to 0
when the reset is asserted.
module up_counter(input clk, reset, output [3:0] counter);
reg [3:0] counter_up;
always @(posedge clk or posedge reset) begin
if (reset)
counter_up <= 4'd0;
else
counter_up <= counter_up + 4'd1;
end
assign counter = counter_up;
endmodule
Testbench Code for Up Counter
module upcounter_testbench();
reg clk, reset;
wire [3:0] counter;
up_counter dut(clk, reset, counter);
initial begin
clk = 0;
forever #5 clk = ~clk; // Toggle clock every 5ns
end
initial begin
reset = 1; // Assert reset at the start
#20; // Wait for 20ns
reset = 0; // Deassert reset
end
endmodule
2. Verilog Down Counter
A down counter counts backward from 15 to 0. When the reset signal is asserted, the counter resets to 15
.
module down_counter(input clk, reset, output [3:0] counter);
reg [3:0] counter_down;
always @(posedge clk or posedge reset) begin
if (reset)
counter_down <= 4'hF; // Reset to 15
else
counter_down <= counter_down - 4'd1;
end
assign counter = counter_down;
endmodule
Testbench Code for Down Counter
module downcounter_testbench();
reg clk, reset;
wire [3:0] counter;
down_counter dut(clk, reset, counter);
initial begin
clk = 0;
forever #5 clk = ~clk; // Toggle clock every 5ns
end
initial begin
reset = 1; // Assert reset at the start
#20; // Wait for 20ns
reset = 0; // Deassert reset
end
endmodule
3. Verilog Up-Down Counter
An up-down counter can count both upwards and downwards based on the value of the up_down
signal. If up_down
is low, the counter counts upwards; if it’s high, it counts downwards.
module up_down_counter(input clk, reset, up_down, output [3:0] counter);
reg [3:0] counter_up_down;
always @(posedge clk or posedge reset) begin
if (reset)
counter_up_down <= 4'h0; // Reset to 0
else if (~up_down)
counter_up_down <= counter_up_down + 4'd1; // Count up
else
counter_up_down <= counter_up_down - 4'd1; // Count down
end
assign counter = counter_up_down;
endmodule
Testbench Code for Up-Down Counter
module updowncounter_testbench();
reg clk, reset, up_down;
wire [3:0] counter;
up_down_counter dut(clk, reset, up_down, counter);
initial begin
clk = 0;
forever #5 clk = ~clk; // Toggle clock every 5ns
end
initial begin
reset = 1; // Assert reset at the start
up_down = 0; // Start counting up
#20; // Wait for 20ns
reset = 0; // Deassert reset
#200; // Wait for 200ns
up_down = 1; // Switch to count down
end
endmodule
4. Random Counter Using LFSR
A random counter can be created using a Linear Feedback Shift Register (LFSR). This generates pseudo-random numbers based on an initial seed value.
module random_counter_lfsr(input clk, rst_n, input [4:0] initialized_value, output [4:0] counter_random);
wire [4:0] counter_lfsr;
wire d_xor;
xor xor_u(d_xor, counter_lfsr[1], counter_lfsr[4]);
D_FF u0(.q(counter_lfsr[0]), .d(counter_lfsr[4]), .rst_n(rst_n), .clk(clk), .init_value(initialized_value[0]));
D_FF u1(.q(counter_lfsr[1]), .d(counter_lfsr[0]), .rst_n(rst_n), .clk(clk), .init_value(initialized_value[1])); D_FF u2(.q(counter_lfsr[2]), .d(d_xor), .rst_n(rst_n), .clk(clk), .init_value(initialized_value[2])); D_FF u3(.q(counter_lfsr[3]), .d(counter_lfsr[2]), .rst_n(rst_n), .clk(clk), .init_value(initialized_value[3])); D_FF u4(.q(counter_lfsr[4]), .d(counter_lfsr[3]), .rst_n(rst_n), .clk(clk), .init_value(initialized_value[4]));
assign counter_random = counter_lfsr;
endmodule
#### Testbench Code for Random Counter Using LFSR verilog
module lfsr_testbench();
reg clk, rst_n;
wire [4:0] counter;
random_counter_lfsr dut(clk, rst_n, 5'b10101, counter);
initial begin
clk = 0;
forever #5 clk = ~clk; // Toggle clock every 5ns
end
initial begin
rst_n = 0; // Assert reset at the start
#20; // Wait for 20ns
rst_n = 1; // Deassert reset
end
endmodule
Conclusion
In this article, we’ve covered various 4-bit counter designs in Verilog, including up counters, down counters, up-down counters, and random counters. Each counter design serves different needs based on counting direction and behavior, and their respective testbenches allow for simulation and validation. Verilog provides a flexible way to design and simulate these counters for various digital systems.