---
title: 'Simple State Machine: Scanning a Keypad Grid'
...

# Reading from a Keypad

The Digilent Keypad PMOD is a simple grid of buttons:

![Keypad module for Basys3 board](figures/Pmod_KYPD.png){width=50%}

The keypad's physical circuit consists of a **switch matrix** organized into
four rows and four columns:

![Keypad switch matrix.](figures/keypad_schematic_1.png){width=75%}


The **`row`** signals are the keypad's **outputs**. A set of **pull-up resistors**
is used to hold all the `row` signals HIGH when no button is pressed. 

![Keypad switch matrix.](figures/keypad_schematic_2.png){width=75%}

The **`col`** signals are the keypad's **inputs**. At any time, a single
`col` is pulled LOW, so that it can overpower the pull-up resistors on the
`row` signals. Suppose `col[0]` is pulled LOW, and a key is pressed in that
column:

![Keypad with one key pressed.](figures/keypad_one_key_1.png){width=75%}

The keypad's **interface module** sets `col[0]` LOW and detects that `row[1]` is
HIGH, so that the keypress is uniquely detected at position (1,0).

## High-Impedance Signals

You may have noticed that `col[1]`, `col[2]` and `col[3]` are all indicated as `Z`,
which is not a logic value. Verilog supports **four-value logic** where any `wire` or
`reg` can have one of these values:

* `1` -- logic TRUE, electrical HIGH
* `0` -- logic FALSE, electrical LOW
* `X` -- logic "invalid", electrical unkown
* `Z` -- logic "undefined", electrical **high-impedance**, i.e. **disconnected**

In most situations, `X` and `Z` indicate design mistakes. But there are special
gates called **tri-state buffers** that support `Z` outputs, allowing us to
temporarily connect and disconnect wires within a circuit. The Xilinx FPGA uses 
tri-state buffers for its **top-level outputs**, so we can write `Z` values
when needed to control PMOD devices.



## Scanning Refresh

In order to detect a single keypress, we can pull down one `col` signal at a time.
The other `col` signals need to be `Z` so that a single column is isolated. We **scan**
rapidly across all the columns following this sequence:

0. `col = 0ZZZ`
1. `col = Z0ZZ`
2. `col = ZZ0Z`
3. `col = ZZZ0`

After scanning through each column, we repeat the sequence indefinitely to **refresh**
the key detection. This loop can be modeled as a simple **Finite State Machine (FSM).**
A Finite State Machine is visually represented using a state transition diagram like the one
below. In this diagram style, each circle is a *state*, and the label within the circle is 
the state's identifier (usually a name or integer value). The edges in the graph are
*transitions*, and each transition is labeled with two parts: a *condition*, then a `/`,
and then an *assignment*. The *condition* is the  logical test that must be TRUE in order
for the transition to occur. The *assignment* is the change in output signals due to the
transition. For clarity, conditions are indicated in blue color.


![State transition diagram for scanning refresh process.](figures/state_machine.svg){width=75%}

In this diagram, the process is initialized in state 0 (indicated by the `init` condition).
A state transition occurs whenever the `scan` signal is high. The frequency of `scan`
dictates the speed for the keypad refresh process.

## Active-Low Reset

Notice that the state diagram indicates `!rst_l` as a condition to enter state 0. In
digital systems there is usually a **global reset signal** that uses **active low logic**,
so the system is reset whenever `rst_l` is LOW. The suffix `_l` is used to indicate an 
active-low signal. It's also common to see other suffixes like `_b` (for "bar"), or
`_inv` (for "inverted"). **Whenever a signal is active-low, it should be named with a
`_l` suffix to clarify its behavior.**

**Why active-low reset?** When a system is first powered up, all signals are effectively
`0`, and at different times their electrical levels may appear to be `0` or `1`. By holding
`rst_l` at `0`, the system can be maintained in a reset condition until the supply voltage
is stable.

In the state transition diagram, there is an implied transition from every state back to `0`
whenever `rst_l` is `0`. These transitions are hidden in the diagram, but are indicated in
the `init | !rst_l` condition.


## Clock Divider Timing

The **refresh period** is the time it takes to repeat from `col` 0
back to `col` 0 again. The refresh period should be much faster than the time it takes
for a human finger to press and release a key. On the other hand, the refresh period should
be much slower than the system clock, since the Basys3 electrical signals are slower than
the internal FPGA clock.

A good refresh time might be around 12ms. To complete a scan of all four `col` wires in 12ms,
each individual `col` needs to be scanned for (12ms)/4 = 3ms. To create the appropriate timing,
we will use a **clock divider** module:

```
module clock_divider
#(
   parameter N=300_000
 )
 (
  input      clk,
  input      rst_l,
  output reg div_clk
  );
   
   integer   clk_count;
   initial begin
      clk_count = 0;
      div_clk   = 0;
   end
   
   always @(posedge clk, negedge rst_l) begin
      // RESET BEHAVIOR:
      if (!rst_l) begin
         clk_count <= 0;
         div_clk   <= 0;
      end
      // NORMAL BEHAVIOR:
      // Pulse div_clk every N clock cycles.
      else begin
         if (clk_count == N) begin
            clk_count <= 0;
            div_clk   <= 1;
         end 
         else begin
            clk_count <= clk_count + 1;
            div_clk   <= 0;
         end 
      end 
   end // always @ (posedge clk, negedge rst_l)
   
endmodule // clock_divider
```

The `clock_divider` module sets `clk_div` HIGH every `N` clock cycles. On the Basys3, the
system clock frequency is $f_c =$ 100MHz, with a period of $T_c =$ 10ns. To obtain a 3ms scanning period,
we need $T_c\times N=$(10ns)*`N` = 3ms, or `N=300000`. This is set as the default value for parameter `N`
in the module declaration.


# Assigned Tasks

In the `src` subdirectory, create a file named `clock_divider.v` and enter the code shown
above. **It is prefered for you to enter the code manually so that you fully understand
the module.**

## Prepare the `keypad` Module

Create a file named `src/keypad.v` to implement the `keypad` interface. Use this
declaration template:

```
module keypad
  #(
     parameter N=300_000
   )
   (
     input             clk,
     input             rst_l,
     input      [3:0]  row,
     output     [3:0]  col,
     output reg [15:0] keys
   );

   wire          scan;
   reg   [1:0]   state;

endmodule
```

The module has a `col` output to drive the keypad columns, and a `row` input to 
receive signals from the keypad rows. The detected keypress(es) are indicated in
an output named `keys`. There are 16 keys; if a key is pressed then the corresponding 
bit position in `keys` is set to `1`.

### Submodules and Initial Conditions

Declare a `clock_divider` submodule and initialize the `state` and `keys` signals:

```
   initial begin
      state = 0;
      keys  = 0;
   end
     
   clock_divider clkdiv(.clk(clk),.rst_l(rst_l),.div_clk(scan));

```

The global signals `clk` and `rst_l` are "passed through" from the top to submodules.
The `clock_divider` output `div_clk` is connected to the `scan` signal. Since `scan` is
defined in a submodule, it is considered a `wire` within the scope of `keypad`.


### Define the State Machine Behavior

Next, implement the state transition diagram using a **`case` statement** as shown below.
The behavior for state 0 is already completed, you should fill in the remaining states.
In the code below, the reset behavior is implemented using `if/else if` statements. Since
the state transitions only occur when `scan` is high, it is used as a condition for  
the entire `case` block:

```verilog
   always @(posedge clk, negedge rst_l) begin
      if (!rst_l) begin         
         state <= 0;
         keys  <= 0;
      end
      else if (scan) begin
         case (state)
           0:
             begin
                // Set the key values for this column:
                keys[4'h1] <= ~row[0];
                keys[4'h4] <= ~row[1];
                keys[4'h7] <= ~row[2];
                keys[4'h0] <= ~row[3];

                // Proceed to the next state:
                state    <= 1;                
             end
           1:
             begin
               // You do this
             end
           2: 
             begin
               // You do this
              end
           3:
             begin
               // You do this
             end
         endcase
      end
   end
```

### Define `col` assignments

To complete the `keypad` design, the `col` signals need to be appropriately
assigned. In each of the module's states, only one of the `col` signals is
assigned `0`, while the others are all `Z`. This is achieved using `assign`
statements with the conditional `?` operator:

```
   assign col[0] = (state == 0) ? 0 : 1'bz;
   assign col[1] = (state == 1) ? 0 : 1'bz;
   assign col[2] = (state == 2) ? 0 : 1'bz;
   assign col[3] = (state == 3) ? 0 : 1'bz;
```

**Since the `col` signals use `Z` values, they can only be assigned as top-level
outputs.** They can't be safely assigned as registers in the clocked `always` block.
The best practice is to use `assign` statements to define tri-state outputs
separately from the clocked logic, as is done here.

Enter the lines above to complete the `keypad` module definition.


## Testbench Design

A testbench is provided in the file `src/testbench.v`. Open the file and study its
contents. The testbench simulates a succession of keypress events in the order:
no key, then col 0: row 0, 1, 2, 3; then no key, followed by col 1: row 0, 1, 2, 3;
and so on.

### Simulating the Electrical Key Signals

To simulate these keypresses, we first define the depressed key position using
variables `row_pressed` and `col_pressed`. When the `keypad` module scans across
the columns, the electrical row signals should be `1111` **except when the pressed
column is being scanned.** To simulate this electrical activity, we make conditonal
assignments to `row_wire` as shown here:

```
   // Simulated row/col for keypress position:
   reg [3:0]  row_pressed;
   reg [3:0]  col_pressed;
   
   // Interface wires:
   wire [3:0]  row_wire;
   wire [3:0]  col_wire;
   wire [15:0] keys;
   wire        refresh;
   
   // The row wire can be pulled down only if the column wire matches
   // the column where the button is pressed:
   assign row_wire[0] = &(col_wire===col_pressed) ? row_pressed[0] : 1 ;
   assign row_wire[1] = &(col_wire===col_pressed) ? row_pressed[1] : 1 ;
   assign row_wire[2] = &(col_wire===col_pressed) ? row_pressed[2] : 1 ;
   assign row_wire[3] = &(col_wire===col_pressed) ? row_pressed[3] : 1 ;
```

Here, **the triple-equal operator `===` is used to compare 4-value logic vectors**.
If the `keypad` output `col_wire` matches `col_pressed` in all positions, then
`row_wire` is assigned to equal `row_pressed`. Otherwise `row_wire` is assigned
`1111`.

### Keypress Timing

For each simulated keypress, we need to give the `keypad` module enough time
to scan through all the columns and complete a full refresh. To achieve the
required timing, we use a `clock_divider` in the `testbench` to generate a
timing signal called `refresh`:

```
   // Timing to change key value every
   // refresh interval
   clock_divider #(.N(1_200_000)) clkdiv
       (.clk(clk),.rst_l(rst_l),.div_clk(refresh));
   
```

This clock divider's timing parameter is set 1,200,000, equal to four times
the `keypad` scan period. This means we'll change the keypress after the
`keypad` completes its scan of all four columns.

### Key Sequence

Further down in the testbench, we see a nested pair of `case` statements:

```
       if (refresh) begin
          case (row_pressed)
            4'b1111: row_pressed <= 4'b1110; // first row_pressed
            4'b1110: row_pressed <= 4'b1101; // second row_pressed
            4'b1101: row_pressed <= 4'b1011; // third row_pressed
            4'b1011: row_pressed <= 4'b0111; // fourth row_pressed
            4'b0111: // After the last row, we change column and 
                begin  // start going through the rows again:
                   row_pressed <= 4'b1111; 
               
                   case (col_pressed)
                     4'bZZZ0 : col_pressed <= 4'bZZ0Z;
                     4'bZZ0Z : col_pressed <= 4'bZ0ZZ;
                     4'bZ0ZZ : col_pressed <= 4'b0ZZZ;
                     4'b0ZZZ : col_pressed <= 4'bZZZ0;
                   endcase // case (col_pressed)
                end
          endcase
       end
```

Whenever the `refresh` signal is `1`, this code block is evaluated. It scans
through each of the four `row_pressed` values. On the final row, it reverts to
a no-keypress state, and advances `col_pressed` to the next column. Then the
sequence of `row_pressed` states is repeated.

Notice that this simple state-machine uses `Z` values in its registers. This was
not allowed in the `keypad` module design. Why? The answer is that **four-value
register assignments are allowed in simulation, but are not synthesizeable.** In
the `keypad` module, we *could have used* `Z` assignments within the state machine
in the `always` block, and it *may have simulated correctly*, but **in the end the
implemented design would not work properly on the Basys3 board**, all without any
clear error messages. 

### Simulate the Design

Once you have completed the `clock_divider` and `keypad` modules, and understand
the `testbench` design, run `make` to compile and simulate your design. In the
file `results.txt`, you should see an output like this:

```text
clk:       300002       col: zzz0       row: 1111       keys: 0000000000000000
clk:       600003       col: zz0z       row: 1111       keys: 0000000000000000
clk:       900004       col: z0zz       row: 1111       keys: 0000000000000000
clk:      1200005       col: 0zzz       row: 1111       keys: 0000000000000000
clk:      1500006       col: zzz0       row: 1110       keys: 0000000000000000
clk:      1800007       col: zz0z       row: 1111       keys: 0000000000000010
clk:      2100008       col: z0zz       row: 1111       keys: 0000000000000010
clk:      2400009       col: 0zzz       row: 1111       keys: 0000000000000010
clk:      2700010       col: zzz0       row: 1101       keys: 0000000000000010
clk:      3000011       col: zz0z       row: 1111       keys: 0000000000010000
clk:      3300012       col: z0zz       row: 1111       keys: 0000000000010000
clk:      3600013       col: 0zzz       row: 1111       keys: 0000000000010000
clk:      3900014       col: zzz0       row: 1011       keys: 0000000000010000
clk:      4200015       col: zz0z       row: 1111       keys: 0000000010000000
clk:      4500016       col: z0zz       row: 1111       keys: 0000000010000000
clk:      4800017       col: 0zzz       row: 1111       keys: 0000000010000000
clk:      5100018       col: zzz0       row: 0111       keys: 0000000010000000
clk:      5400019       col: zz0z       row: 1111       keys: 0000000000000001
clk:      5700020       col: z0zz       row: 1111       keys: 0000000000000001
clk:      6000021       col: 0zzz       row: 1111       keys: 0000000000000001
clk:      6300022       col: zzz0       row: 1111       keys: 0000000000000001
clk:      6600023       col: zz0z       row: 1111       keys: 0000000000000000
clk:      6900024       col: z0zz       row: 1111       keys: 0000000000000000
clk:      7200025       col: 0zzz       row: 1111       keys: 0000000000000000
clk:      7500026       col: zzz0       row: 1111       keys: 0000000000000000
clk:      7800027       col: zz0z       row: 1110       keys: 0000000000000000
```

(The console output is more detailed and could be useful for debugging if your design
has a problem). Each line of output represents one period of `scan`, and you can see the
`col` signal rotate through each of the four columns. After every four lines, the
`row` signal changes. In the output shown above, we see "no press" followed by keys
1, 4, 7, and 0, corresponding to the left-most column on the keypad.



## Synthesize, Implement and Program 

### Create a `top` Module

We're going to use the Basys3 board's **center button** as the **reset** for this design. 
Since the buttons are **active-high**, we need a top module to invert it and make an
active-low reset signal. This type of interface layer is sometimes called a "wrapper"
module, since it performs a minimal logic function.

```
module top
  (
   input         clk,
   input         rst,
   input  [3:0]  row,
   output [3:0]  col,
   output [15:0] keys
   );

   wire       rst_l;
   assign rst_l = ~rst;

   keypad #(.N(300_000)) kypd
     (
      .clk(clk),
      .rst_l(rst_l),
      .row(row),
      .col(col),
      .keys(keys)
      );
   

endmodule // top
```

### Inspect the `keypad.xdc` File

The top-level I/O assignments are specified in the XDC file. Open it and observe the usual
`clk` definition, and notice that the `keys` output vector is mapped to the 16 LEDs on the
Basys3 board. The `rst` signal is mapped to the center button.

To interface with the PMod hardware, the `row` and `col` signals are mapped to the **JA
Header** located on the upper left of the board. To test the keypad interface, you need
to plug the keypad into the JA header as shown:

![Header position for the keypad module.](figures/keypad_header_position.jpg){width=75%}


### Build and Test

Run `make implement` to synthesize, place-and-route, and generate a bitstream. Program
the bitstream onto your Basys3 board and verify that each key lights up the corresponding
LED on the board. 

## Turn in Your Work

### Make a Video

After verifying correct function, take a short video demonstrating keys
0 through F, and upload it in Canvas to indicate that you are done.

### Submit Design Files in Using `git`

Turn in your work using `git`:

```bash
git add src/*.v *.v *.rpt *.txt *.bit 
git commit . -m "Complete"
git push origin main
```

