TABLE OF CONTENTS (HIDE)

Verilog HDL and Sequential Circuit Design

Verilog HDL module invocation

We use an example to show how a module to invoke other modules in Verilog HDL. Suppose we want to design a simple ALU as shown in the following figure.

myalu_sym.svg

Following is the schematic of the ALU. The top ALU module will invoke two mux2x32 (32-bit 2-to-1 multiplexer) modules and one addsub32 (32-bit adder/subtracter) module.

my_alu.svg

First, we give the Verilog HDL codes of the mux2x32 and addsub32 modules as follows.

1
2
3
4
5
6
module mux2x32 (a0, a1, s, y);
  input  [31:0] a0, a1;   // a0, a1: 32-bit
  input         s;        // s: 1-bit
  output [31:0] y;        // y: 32-bit
  assign y = s ? a1 : a0; // like C
endmodule

1
2
3
4
5
6
module addsub32 (a, b, sub, s);
  input  [31:0] a, b;
  input         sub;
  output [31:0] s;
  assign s = sub ? a - b : a + b;
endmodule

Then, we give the Verilog HDL code of the top ALU module in structural style, as follows. It invokes mux2x32 and addsub32 modules with its own signals as the parameters for the invoked modules. Note that the order of the paramemters is important in such invocation. There is another method to pass the parameters to the invoked modules which does not care about the order. Please investigate it by yourself.

1
2
3
4
5
6
7
8
9
10
11
12
module alu1 (a, b, aluc, s);
  input  [31:0] a, b;
  input   [1:0] aluc;
  output [31:0] s;
  wire [31:0] d_and = a & b; // AND
  wire [31:0] d_or  = a | b; // OR
  wire [31:0] d_ao, d_as;    // 32-bit wires
  // use other modules:
  mux2x32 mx_ao (d_and, d_or, aluc[0], d_ao);
  addsub32 as   (a, b, aluc[0], d_as);
  mux2x32 mx_la (d_ao, d_as, aluc[1], s);
endmodule

The test bench codes are shown below.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
`timescale 1ns/1ns
module alu1_tb;
  reg  [31:0] a, b;
  reg   [1:0] aluc;
  wire [31:0] s;
  alu1 alu (a, b, aluc, s);
  initial begin
    #0 aluc = 2'd0;
    #0 a = 32'haaaaaaaa;
    #0 b = 32'hcccccccc;
    #2 b = 32'h55555555;
    #2 $stop;
  end
  always #1 aluc = aluc + 2'd1;
endmodule

The simulation waveform is shown below. In 0 - 1ns, aluc = 00, a (1010) & c (1100) = 8 (1000); in 1 - 2ns, aluc = 01, a (1010) | c (1100) = e (1110); in 2 - 3ns, aluc = 10, a (1010) + 5 (0101) = f (1111); and in 3 - 4ns, aluc = 11, a (1010) - 5 (0101) = 5 (0101);

alu1_sim.png

The following code also implements the ALU above.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
module alu2 (a, b, aluc, s);
  input  [31:0] a, b;
  input   [1:0] aluc;
  output [31:0] s;

  assign s = operate(a, b, aluc);

  function [31:0] operate;
    input  [31:0] x, y;
    input   [1:0] c;
    case (c)
      2'b00: operate = x & y;
      2'b01: operate = x | y;
      2'b10: operate = x + y;
      2'b11: operate = x - y;
    endcase
  endfunction
endmodule

Verilog HDL sequential circuit design

Two sequential circuit models are shown below. The key point of the sequential circuits is that there are components that can record the current state, d flip flop (dff) in the figure, for instance. The other two modules, "Next state" and "Output function" are combinatorial circuits.

fsm.svg

D flip flop

A D flip flop (DFF) can store one bit information. In the raising edge (the transition from 0 to 1) of a clock, the input information d is stored. We also need a clear (reset) signal to reset the state of the DFF. According to the relationship between clear and clock, we have two types DFFs: synchronous clear DFF and asynchronous clear DFF. The following figure shows a 32-bit DFF (dff32).

dff32.svg
Synchronous clear DFF

1
2
3
4
5
6
7
8
9
10
11
12
13
  module dff32_sync (clk, clrn, d, q);
  input  [31:0] d;
  input         clk, clrn;
  output [31:0] q;
  reg    [31:0] q;
  always @ (posedge clk) begin
    if (clrn == 0) begin
      q <= 0;
    end else begin
      q <= d;
    end
  end
endmodule
Asynchronous clear DFF

1
2
3
4
5
6
7
8
9
10
11
12
13
module dff32_async (clk, clrn, d, q);
  input  [31:0] d;
  input         clk, clrn;
  output [31:0] q;
  reg    [31:0] q;
  always @ (negedge clrn or posedge clk) begin
    if (clrn == 0) begin
      q <= 0;
    end else begin
      q <= d;
    end
  end
endmodule

D flip flop with enable

DFF stores d on every raising edge of the clock. DFFE adds an enable signal to control the updating.

dffe32.svg
Synchronous clear DFFE

1
2
3
4
5
6
7
8
9
10
11
12
13
module dffe32_sync (clk, clrn, e, d, q);
  input  [31:0] d;
  input         clk, clrn, e;
  output [31:0] q;
  reg    [31:0] q;
  always @ (posedge clk) begin
    if (clrn == 0) begin
      q <= 0;
    end else begin
      if (e) q <= d;
    end
  end
endmodule
Asynchronous clear DFFE

1
2
3
4
5
6
7
8
9
10
11
12
13
module dffe32_async (clk, clrn, e, d, q);
  input  [31:0] d;
  input         clk, clrn, e;
  output [31:0] q;
  reg    [31:0] q;
  always @ (negedge clrn or posedge clk) begin
    if (clrn == 0) begin
      q <= 0;
    end else begin
      if (e) q <= d;
    end
  end
endmodule

A sequential circuit example

A state transition diagram:

fsm_example.svg

Implementation in Verilog HDL:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
module fsm (x,clk,clrn,state);                          // a finite state example
    input        x,clk,clrn;                            // x==1: s0,s1,s3,s0,s1,s3,...
    output [1:0] state;                                 // else: s0,s1,s2,s0,s1,s2,...

    parameter [1:0] s0 = 2'b00,                         // define parameters
                    s1 = 2'b01,
                    s2 = 2'b10,
                    s3 = 2'b11;

    reg [1:0] next_state;                               // will be nets
    always @* begin                                     // combinational circuit here
        case (state)
            s0: begin
                next_state = s1;
            end
            s1: begin
                next_state = x? s3 : s2;
            end
            default: begin
                next_state = s0;                        // default
            end
        endcase
    end

    reg [1:0] state;                                    // registers for state
    always @ (posedge clk or negedge clrn) begin
        if (clrn == 0) begin
            state <= s0;
        end else begin
            state <= next_state;                        // state transition
        end
    end
endmodule

Testbench:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
`timescale 1ns/1ps
module fsm_tb;
    reg        x,clk,clrn;
    wire [1:0] state;
    fsm inst (x,clk,clrn,state);
    initial begin
        #0 clk = 1; clrn = 0; x = 0;
        #1 clrn = 1;
        #6 x = 1;
        #6 x = 0;
        #7 $stop;
    end
    always #1 clk = ~clk;
endmodule

Simulation waveform:

fsm_wave.png

x = 0: state: 0,1,2,0
x = 1: state: 0,1,3,0

Counter16 design in Verilog HDL

A 4-bit counter16:

1
2
3
4
5
6
7
8
9
10
11
12
module counter16 (clk, clrn, q);
  input        clk, clrn;
  output [3:0] q;
  reg    [3:0] q;
  always @ (negedge clrn or posedge clk) begin
    if (clrn == 0) begin
      q <= 0;
    end else begin
      q <= q + 4'd1;
    end
  end
endmodule

Test bench for the counter:

1
2
3
4
5
6
7
8
9
10
11
12
13
`timescale 1ns/1ns
module counter16_tb;
  reg        clk, clrn;
  wire [3:0] q;
  counter16 cnt (clk, clrn, q);
  initial begin
    #0 clrn = 0;
    #0 clk  = 1;
    #1 clrn = 1;
    #41 $stop;
  end
  always #1 clk = ~clk;
endmodule

Waveform:

counter16_sim.png

Counter10 design in Verilog HDL

A 4-bit counter10:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
module counter10 (clk, clrn, q);
  input        clk, clrn;
  output [3:0] q;
  reg    [3:0] q;
  always @ (negedge clrn or posedge clk) begin
    if (clrn == 0) begin
      q <= 0;
    end else begin
      if (q == 4'd9)
        q <= 4'd0;
      else q <= q + 4'd1;
    end
  end
endmodule

Test bench for the counter:

1
2
3
4
5
6
7
8
9
10
11
12
13
`timescale 1ns/1ns
module counter10_tb;
  reg        clk, clrn;
  wire [3:0] q;
  counter10 cnt (clk, clrn, q);
  initial begin
    #0 clrn = 0;
    #0 clk  = 1;
    #1 clrn = 1;
    #41 $stop;
  end
  always #1 clk = ~clk;
endmodule

Waveform:

counter10_sim.png

Clock divider

Clock divider generates a new clock whose frequency f1 = f0 / n where f0 is the original clock frequency and n is a constant. The following example shows the case of n = 10. The idea is to prepare a counter with initial value of 0. The counter increments at the raising edge of the input clock. When the counter value reach n / 2 - 1, the output clock inverts and the counter is reset to 0. In the following example, because n = 10 and n / 2 = 5, we have to use a 3-bit counter to count from 0 to 4. That is, $\lceil\log_25\rceil=3$ and 3-bit counter can count 0, 1, 2, 3, 4, 5, 6, and 7.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module clock10 (clk0, clk1);
  input  clk0;
  output clk1;

  reg    clk1 = 1; // start state
  reg [2:0] counter = 0;

  always @ (posedge clk0) begin
    if (counter == 3'd4) begin
      counter <= 0;
      clk1 = ~clk1; // reverse clk1
    end else begin
      counter <= counter + 3'd1;
    end
  end

endmodule

Test bench:

1
2
3
4
5
6
7
8
9
10
11
`timescale 1ns/1ns
module clock10_tb;
  reg  clk0;
  wire clk1;
  clock10 div10 (clk0, clk1);
  initial begin
    #0 clk0 = 1;
    #50 $stop;
  end
  always #1 clk0 = ~clk0;
endmodule

Waveform (the frequency of clk1 is 1/10 of the clk0's frequency):

clock10_sim.png

Exercise

Design a 4-bit counter and display the counter value with a 7-segment LED. The frequency of the counter is 1Hz which can be generated from the 50MHz system clock. Project name: counter_7s_led.

four_bit_counter.svg

The top module counter_7s_led.v is given below. You need to write the codes of two modules: gen_sclk and four_cnt.

1
2
3
4
5
6
7
8
9
10
11
12
module counter_7s_led (sys_clk, seg7);
  input        sys_clk; // system clock, 50MHz
  output [6:0] seg7;    // 7-segment LED 0-F

  wire         sec_clk; // second clock, 1Hz

  // generate sec_clk (1Hz) from sys_clk (50MHz)
  gen_sclk s1 (sys_clk, sec_clk); // module gen_sclk

  // display 4-bit counter with 7-segment LED
  four_cnt s7 (sec_clk, seg7);    // module four_cnt
endmodule
50MHz_1Hz.svg

gen_sclk.v:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
module gen_sclk (clk, sec_clk);
    input      clk;               // 50MHz
    output reg sec_clk = 0;       // second clock, 1Hz. 50MHz / 50M = 1Hz; 50M / 2 = 25M; counter: 0 - 25M-1

    reg   [24:0] clk_cnt = 25'd4; // 25-bit counter counts 0, 1, 2, ... 24999999

    always @ (posedge clk) begin
        //if (clk_cnt == 25'd4) begin // for simulation
        if (clk_cnt == 25'd24999999) begin
            clk_cnt <= 0;
            sec_clk <= ~sec_clk;
        end else begin
            clk_cnt <= clk_cnt + 25'd1;
        end

    end
endmodule

four_cnt.v:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
module four_cnt (sec_clk, seg7);
   input sec_clk;
   output [6:0] seg7;

   reg [3:0] 	cnt = 4'hf;

   always @ (posedge sec_clk)
     cnt <= cnt + 1;

   assign seg7 = func (cnt);

   function [6:0] func;
      input [3:0] cnt;
      case(cnt)
        4'h0 : func = 7'b1000000;
        4'h1 : func = 7'b1111001;
        4'h2 : func = 7'b0100100;
        4'h3 : func = 7'b0110000;
        4'h4 : func = 7'b0011001;
        4'h5 : func = 7'b0010010;
        // ... ... for 6, 7, 8, 9, A, b, c, d, E, and F
      endcase
   endfunction
endmodule

Write a test bench counter_7s_led_tb.v to simulate counter_7s_led.v with ModelSim. You can use add wave -r /* to show the waveforms of internal signals.

counter_7s_led_wave1.png
counter_7s_led_wave2.png
counter_7s_led_wave3.png
counter_7s_led_wave4.png

Implement it on FPGA.