A previous article showed different examples of using an always block to implement combinational logic. An always block is also mainly used to implement sequential logic which has memory elements like flip flops that can hold values.

Here are some examples of using always to construct sequential logic.

JK Flip Flop

A JK flip flop is one of the many types of flops used to store values and has two data inputs j and k along with one for reset rstn and another for clock clk. The truth table for a JK flop is shown below and is typically implemented using NAND gates.

rstn j k q Comments
0 0 0 0 When reset is asserted, output is always zero
1 0 0 Hold value When both j and k are 0, output remains the same as before
1 0 1 1 When k=1, output becomes 1
1 1 0 0 When k=0, output becomes 0
1 1 1 Toggle value When j=1,k=1 output toggles current value

The behavioral Verilog code for a JK flip-flop can be written as shown below

 
module jk_ff ( input       j,         // Input J
               input       k,         // Input K
               input       rstn,     // Active-low async reset
               input       clk,       // Input clk
               output reg q);       // Output Q
 
  always @ (posedge clk or negedge rstn) begin
    if (!rstn) begin
      q <= 0;
    end else begin
      q <= (j & ~q) | (~k & q);
    end
  end
endmodule
 

Testbench

First declare all variables used in the testbench and start a clock using a simple always block that can be driven to the design. Then instantiate the design and connect its ports with corresponding testbench variables. Note that q is of type wire because it is connected to an output of the design which will be actively driving it. All other inputs to the design are of type reg so that they can be driven within a procedural block such as initial.

The stimulus first initializes all inputs to the design to zero and then de-asserts reset after some time. A for loop is used to drive different values to j and k which are driven at random times. Once the loop is done, wait for some more time and finish the simulation.

 
module tb;
  // Declare testbench variables
  reg j, k, rstn, clk;
  wire q;
  integer i;
  reg [2:0] dly;
 
  // Start the clock 
  always #10 clk = ~clk;
 
  // Instantiate the design
  jk_ff   u0 (  .j(j), .k(k), .clk(clk), .rstn(rstn), .q(q));
 
  // Write the stimulus
  initial begin
    {j, k, rstn, clk} <= 0;
    #10 rstn <= 1;
 
    for (i = 0; i < 10; i = i+1) begin
      dly = $random;
      #(dly) j <= $random;
      #(dly) k <= $random;
    end
 
    #20 $finish;
  end
endmodule
 

Note from the simulation wave that at the posedge of clock, output q changes value based on the state of inputs j and k as given in the truth table.

Modulo-10 counter

Modulus(MOD) counters simply count upto a certain number before rolling back to zero. A MOD-N counter will count from 0 to N-1 and then roll back to zero and start counting again. Such counters typically require log2N number of flops to hold the count value. Shown below is the Verilog code for a MOD-10 counter that keeps counting up at every clock clk as long as reset rstn is deasserted.

Verilog parameters can be used to make a MOD-N counter which is more scalable.

 
module mod10_counter (   input    clk,
                        input   rstn,
                        output  reg[3:0] out);
 
  always @ (posedge clk) begin
    if (!rstn) begin
      out <= 0;
    end else begin
      if (out == 10) 
        out <= 0;
      else
        out <= out + 1;
    end
  end
endmodule
 

Testbench

The testbench first declares some variables that can be assigned some values and driven to the design inputs. The counter module is then instantiated and connected with the testbench signals which are later driven with some values in the stimulus. Since the counter also requires a clock, the testbench clock is modeled with an always block. The stimulus simply sets default values at time 0ns, then deasserts reset after 10ns and the design is allowed to run for some time.

 
module tb;
  reg clk, rstn;
  reg [3:0] out;
 
  mod10_counter u0 ( .clk(clk), .rstn(rstn), .out(out));
 
  always #10 clk = ~clk;
 
  initial begin
    {clk, rstn} <= 0;
 
    #10 rstn <= 1;
    #450 $finish;
  end
endmodule
 

See that the counter module counts from zero to 9, rolls over to zero and starts counting again.

4bit Left Shift Register

Shown below is a 4-bit left shift register that accepts an input d into LSB and all other bits will be shifted left by 1. For example, if d equals zero and the initial value of the register is 0011, it will become 0110 at the next edge of the clock clk.

 
module lshift_4b_reg (  input d,                      
                        input clk,                    
                        input rstn,                   
                        output reg [3:0] out
                     );
 
   always @ (posedge clk) begin
      if (!rstn) begin
         out <= 0;
      end else begin
         out <= {out[2:0], d};
      end
   end
endmodule
 

Testbench

The testbench follows a similar template like the one shown before where some variables are declared, design module is instantiated and connected with the testbench signals. Then a clock is started and the stimulus is driven to the design using an initial block. In this testbench example, different values of d has to be exercised and hence a for loop is used to iterate 20 times and apply random values to the design.

 
module tb;
  reg clk, rstn, d;
  wire [3:0] out;
  integer i;
 
  lshift_4b_reg u0 (  .d(d), .clk(clk), .rstn(rstn), .out(out));
 
  always #10 clk = ~clk;
 
  initial begin
    {clk, rstn, d} <= 0;
 
    #10 rstn <= 1;
 
    for (i = 0; i < 20; i=i+1) begin
      @(posedge clk) d <= $random; 
    end
 
    #10 $finish;
  end  
endmodule
 

Note that each bit is shifted to the left by 1 and the new value of d is applied to LSB.

You may also like:

You consent to our cookies if you continue to use our website. To know more about cookies, see our privacy policy. I accept cookies from this site.

Agree