The 4-bit counter starts incrementing from 4'b0000 to 4'h1111 and then rolls over back to 4'b0000. It will keep counting as long as it is provided with a running clock and reset is held high.
The rollover happens when the most significant bit of the final addition gets discarded. When counter is at a maximum value of 4'b1111 and gets one more count request, the counter tries to reach 5'b10000 but since it can support only 4-bits, the MSB will be discarded resulting in 0.
0000 0001 0010 ... 1110 1111 rolls over 0000 0001 ...
The design contains two inputs one for the clock and another for an active-low reset. An active-low reset is one where the design is reset when the value of the reset pin is 0. There is a 4-bit output called out which essentially provides the counter values.
Electronic Counter Design
module counter ( input clk, // Declare input port for clock to allow counter to count up input rstn, // Declare input port for reset to allow the counter to be reset to 0 when required output reg[3:0] out); // Declare 4-bit output port to get the counter values // This always block will be triggered at the rising edge of clk (0->1) // Once inside this block, it checks if the reset is 0, if yes then change out to zero // If reset is 1, then design should be allowed to count up, so increment counter always @ (posedge clk) begin if (! rstn) out <= 0; else out <= out + 1; end endmodule
module counter has a clock and active-low reset (denoted by n) as inputs and the counter value as a 4-bit output. The
always block is always executed whenever the clock transitions from 0 to 1 which signifies a rising edge or a positive edge. The output is incremented only if reset is held high or 1, achieved by the
if-else block. If reset is found to be low at the positive edge of clock, then output is reset to a default value of 4'b0000.
We can instantiate the design into our testbench module to verify that the counter is counting as expected.
The testbench module is named tb_counter and ports are not required since this is the top-module in simulation. However we do need to have internal variables to generate, store and drive clock and reset. For that purpose, we have declared two variables of type
reg for clock and reset. We also need a
wire type net to make the connection with the design's output, else it will default to a 1-bit scalar net.
Clock is generated via
always block which will give a period of 10 time units. The
initial block is used to set initial values to our internal variables and drive the reset value to the design. The design is instantiated in the testbench and connected to our internal variables, so that it will get the values when we drive them from the testbench. We don't have any
$display statements in our testbench and hence we will not see any message in the console.
module tb_counter; reg clk; // Declare an internal TB variable called clk to drive clock to the design reg rstn; // Declare an internal TB variable called rstn to drive active low reset to design wire [3:0] out; // Declare a wire to connect to design output // Instantiate counter design and connect with Testbench variables counter c0 ( .clk (clk), .rstn (rstn), .out (out)); // Generate a clock that should be driven to design // This clock will flip its value every 5ns -> time period = 10ns -> freq = 100 MHz always #5 clk = ~clk; // This initial block forms the stimulus of the testbench initial begin // 1. Initialize testbench variables to 0 at start of simulation clk <= 0; rstn <= 0; // 2. Drive rest of the stimulus, reset is asserted in between #20 rstn <= 1; #80 rstn <= 0; #50 rstn <= 1; // 3. Finish the stimulus after 200ns #20 $finish; end endmoduleSimulation Log
ncsim> run [0ns] clk=0 rstn=0 out=0xx [5ns] clk=1 rstn=0 out=0x0 [10ns] clk=0 rstn=0 out=0x0 [15ns] clk=1 rstn=0 out=0x0 [20ns] clk=0 rstn=1 out=0x0 [25ns] clk=1 rstn=1 out=0x1 [30ns] clk=0 rstn=1 out=0x1 [35ns] clk=1 rstn=1 out=0x2 [40ns] clk=0 rstn=1 out=0x2 [45ns] clk=1 rstn=1 out=0x3 [50ns] clk=0 rstn=1 out=0x3 [55ns] clk=1 rstn=1 out=0x4 [60ns] clk=0 rstn=1 out=0x4 [65ns] clk=1 rstn=1 out=0x5 [70ns] clk=0 rstn=1 out=0x5 [75ns] clk=1 rstn=1 out=0x6 [80ns] clk=0 rstn=1 out=0x6 [85ns] clk=1 rstn=1 out=0x7 [90ns] clk=0 rstn=1 out=0x7 [95ns] clk=1 rstn=1 out=0x8 [100ns] clk=0 rstn=0 out=0x8 [105ns] clk=1 rstn=0 out=0x0 [110ns] clk=0 rstn=0 out=0x0 [115ns] clk=1 rstn=0 out=0x0 [120ns] clk=0 rstn=0 out=0x0 [125ns] clk=1 rstn=0 out=0x0 [130ns] clk=0 rstn=0 out=0x0 [135ns] clk=1 rstn=0 out=0x0 [140ns] clk=0 rstn=0 out=0x0 [145ns] clk=1 rstn=0 out=0x0 [150ns] clk=0 rstn=1 out=0x0 [155ns] clk=1 rstn=1 out=0x1 [160ns] clk=0 rstn=1 out=0x1 [165ns] clk=1 rstn=1 out=0x2 Simulation complete via $finish(1) at time 170 NS + 0
Note that the counter resets to 0 when the active-low reset becomes 0, and when reset is de-asserted at around 150ns, the counter starts counting from the next occurence of the positive edge of clock.
Click to try this example in a simulator!