內容目錄

Lab 6: Combination Lock & Keypad, Part 2

You must work on this lab with a partner. V1.

Objective

The purpose of this lab is to learn to design more complicated FSMs to drive an external logic circuit. You will use behavioural SystemVerilog design with Quartus. Similar to Lab 5, design a circuit that locks and unlocks a safe that you might find in a hotel room. In this lab, however, you will use a numeric keypad to enter the combination.

  • To lock the safe, enter any combination as a 6-digit password and press #
  • To unlock the safe, enter the same password and press #
  • When entering the password, press * to cancel and start over
  • When ready to start accepting a password, display OPEn or LOCHED, as appropriate
  • For visual confirmation, incrementally display the password as each key is pressed

1. Equipment

You will need:

  • Two FPGA boards (DE10-Lite and DE1-SoC) and two computers
  • One 16-key (4×4) numeric keypad with keys labelled 0 to 9, A to D, # (aka E) and * (aka F)
  • Prebuilt jumper wires (the rainbow cable) with male (M) and female (F) ends:
    • 8+7 MM (two DE10), 8 MM + 7 MF (mixed boards), 7 FF + 8 MF (two DE1)
    • Note: two DE1-SoC boards require FF cables that are NOT included in your Parts kit.

This lab will be implemented as two separate designs on two connected FPGA boards:

  • keyboard accepts user input, debounces the pressed key, and both displays/transmits it
  • combo receives a key from keyboard and displays the password entry, OPEn or LOCHED
  • students without a partner will design the keyboard module to use only one FPGA board

A precompiled golden reference solution for each module is available as a bitstream (.SOF file) for both DE10-Lite and DE1-SoC boards, allowing you to test against a known working solution for each design.

Start early – this lab will be more challenging than previous labs. If you fail to get one module working, you may use the reference solution to demonstrate that your other module works properly.

The most difficult part of this lab is metastability and keypad switch bounce, so it is provided to you on Canvas as a SystemVerilog module key_db.

2. Overall Design

Begin by testing your keypad. Connect it to your FPGA board according to the figure below and use the golden reference solution keyboard.sof on Canvas. All 16 keys should work reliably with the golden reference solution for either FPGA board (even the DE1-SoC).

If there is a problem with your keypad, get a replacement from the instructor. Note: when writing your own keyboard module, it will probably be easier to get reliable operation with the DE10-Lite. Reliable performance with the DE1-SoC is tricky; tips to improve reliability will be provided below.

Figure 1. Wiring diagram.
Figure 2. Block diagram and information flow.

Build and test your solution incrementally:

  1. Test your equipment
    • connect your boards and keypad together
    • using the two golden reference solutions
    • test the keypad and wiring to the golden keyboard module
    • test the wiring to the golden combo module
  2. Build and test your SystemVerilog modules independently
    • combo module tips
      • start using simulation and a testbench: you can also print Verilog debug messages with statement $display(“OPEn”); or $display(“LOCHED”);
      • single-board testing: use KEY[0] to manually clock your FSM, SW[3:0] to provide keycode[3:0] input values and ~KEY[1] to simulate valid
      • multi-board testing: connect to the golden reference keyboard solution
    • keyboard module tips
      • start using simulation and a testbench
      • use the 7-segment display to display the last 6 key presses
      • single-board testing: use KEY[0] to manually clock your FSM, SW[7:0] to provide {row, col} input values, and ~KEY[1] to simulate a keypress
      • multi-board testing: connect to the golden reference combo solution
  3. Finally, connect your two modules together!

3. Inout Wires and GPIO Connections

The connections between FPGA boards, and from FPGA board to keypad, will be using some general purpose input/output (I/O) pins on the PCB. Each pin can be configured to be either an input pin, or an output pin, or both (bidirectional).

On the DE10-Lite, we will be using the two Arduino connectors labelled J2 and J3 on the PCB.

  • These female connectors accept a male (M) wire end.
  • In SystemVerilog, these signals are named ARDUINO_IO[15:0]. See Figure 1.

On the DE1-SoC, we will be using the GPIO_1 connector
also labelled JP2 on the PCB.

  • These are called ‘header connectors’ with male pins, so they accept a female (F) wire end.
  • In SystemVerilog, these signals are named GPIO_1[35:0]. See Figure 1.

For both DE10-Lite and DE1-SoC connectors, the location of connector pin 1 can be confirmed on the bottom side of the circuit board by the square solder joint. The DE1-SoC also has a cutout in the plastic shroud on the top side next to pins 19 and 21.

The keypad accepts male (M) wire ends. Connecting the keypad to a DE10-Lite requires MM cables, while connecting to a DE1-SoC requires MF cables. Both of these cable types are in your electronics kit.

Connecting DE10-Lite to DE10-Lite requires MM cables, while connecting DE10-Lite to DE1-SoC requires MF cables. Again, these are the same cables in your electronics kit.
Connecting two DE1-SoCs requires FF cables, but these are not included in your electronics kit. While not ideal, you can use your breadboard to connect two male ends of two MF cables.
In SystemVerilog, connections to these general-purpose I/O signals should be declared as type ‘inout wire’.
Using ‘input logic’ or ‘output logic’ also works when all of the pins are the same direction (ie, all inputs or all outputs). However, the ‘inout wire’ declaration allows you to set the direction of each pin separately, including using the pin as a bidirectional pin (using a tri-state gate); inside your logic module, use a continuous assignment statement to clearly indicate your intent, e.g.:

logic [3:0] row, col;  
assign ARDUINO_IO[7:4] = row;  
assign col = ARDUINO_IO[3:0];

4. Combo Module

The combo module implements the FSM that allows the user to operate the safe as described in the Objective section.
It uses the 7-segment display to show the current state of the FSM, including open, unlocked, or a partially entered password.
The inputs to the FSM come from the keyboard module implemented on a separate FPGA board, giving rise to a few important technical issues:

  • You must connect the Ground (0V) between boards so they have a common reference voltage. Without this, the receiving board will not know the proper voltage being supplied by the other board, so it may consider the input to be in the forbidden zone or misinterpret the 0 or 1 value.
  • Do NOT connect +5V or +3.3V or any other power signals. The two boards have independent power supplies; any small differences in their voltage would cause the two power supplies to draw a large current from one with the higher voltage.
  • Although both boards have highly accurate 50MHz clock, these clocks operate independently and will not perfectly matched; one is likely to be a bit faster than the others, and the edges will never be perfectly coincident (called phase alignment). For reliable communications, we will practice some simple rules:
    • To avoid metastability, the receiver must use a synchronizer. This means each of the 5 signals (valid and keycode[3:0]) must pass through two flip-flops in series, all clocked by the receiver’s clock. Call these synchronized signals valid_ff and keycode_ff[3:0].
    • The transmitter must hold the valid and keycode[3:0] signals constant for at least 3 cycles so the receiver has time to recognize the inputs even if its clock is faster.
    • After the synchronizer, the receiver must sample keycode_ff[3:0] one cycle after it sees valid_ff go high. Sampling a set of parallel signals on the same cycle its valid signal is asserted could lead to incorrect results; if any of the signals (including the valid signal) is delayed (or advanced) by a small amount and/or fails to meet the setup time of its flip-flop, its value may appear one cycle early, or it may one cycle later, than the other signals. Ensuring all signals are constant (stable) for 3 cycles and trying to sample in the middle of that time period is a simple and safe way to ensure success.
    • There are many other ways this could be done in practice, including: source-synchronous transmission, phase-locking clocks with PLLs, distributing globally phase-aligned clocks, sending data and clock and recovering them on the other end through clock-data recovery, and asynchronous transmission over a serial wire (see UART in Wikipedia). Don’t attempt any of these!

5. Keyboard Module

The keyboard module continuously scans the keypad and transmits each key press to another module.
It is governed by an FSM that must take into account a few technical issues:

  • The user can press a key at any time relative to the clock, so metastability is a concern.
  • The key may bounce.
  • Holding a key down should result in a single keypress, not constant keypresses.
  • The # and * keys will transmit keycodes 4’hE and 4’hF, respectively. Other keys (0-9, A-D) will transmit their regular 4-bit hexadecimal encoding.
  • The combo module protocol requires keycode and valid to be held constant for at least 3 cycles for each keypress.

The first two issues are quite serious, so we have written a SystemVerilog module called kb_db to perform debouncing and resolve metastability.
However, your FSM must resolve all of the other issues.
Below is a PRELIMINARY VERSION of kb_db – note how <= is used inside always_ff and = is used inside always_comb. The pressed output is set as soon as a keypress is detected, allowing you to sample col_F1 and row_F1, but wait until debounceOK is also set before checking for the key to be released. You should also wait for debounceOK to be set after you detect a released key.

module kb_db #( DELAY=20 ) ( 
 input logic  clk, 
 input logic  rst, 
 inout wire   [7:0] ARDUINO_IO, 
 input logic  [3:0] fsm_row_F0, 
 output logic [3:0] col_F1, 
 output logic [3:0] row_F1, 
 output logic  pressed, 
 output logic  debounceOK 
); 

 logic [DELAY:0]  counter; 
 logic [3:0] kb_col_L0; 
 logic press_L1, press_F2, edge_L1; 

 assign ARDUINO_IO[7:4] = fsm_row_F0; 
 assign kb_col_L0   = ARDUINO_IO[3:0]; 

 assign pressed   = press_L1; 
 assign debounceOK  = counter[DELAY]; 

 // edge detection 
 always_comb begin 
  press_L1   = ~&( col_F1[3:0] ); 
  edge_L1   = press_L1 ^ press_F2; 
 end 
 // edge detection and synchronizers 
 always_ff @( posedge clk ) begin 
  col_F1 <= kb_col_L0; 
  row_F1 <= fsm_row_F0; 
  press_F2 <= press_L1; 
 end 

 // debounce counter 
 initial counter = 0; 
 always_ff @( posedge clk ) begin 
  if( rst | edge_L1 ) begin 
   counter <= 0; 
  end else if( !debounceOK ) begin 
   counter <= counter+1; 
  end 
 end 

endmodule 

To decode which key is pressed on the keypad, note:

  • A keypad is incredibly simple: pressing a specific key results in a simple connection between one specific row wire and one specific column wire. For the 4×4 keypad, there are 4 row wires and 4 column wires; the FSM must determine which key is pressed and transmit the corresponding 4-bit keycode.
  • When no key is pressed, the 4 column wires are floating. To make them appear as a logic 1 instead, connect all 4 column wires to Vdd through a pull-up resistor.
    • For this, enable a weak pullup resistor on FPGA inputs, reportedly 50-500 kOhm.
  • When a key is pressed, it connects the key’s column wire to its row wire.
    • To determine which column, look for the 0 on the four column wires.
    • To determine which row, you need to scan row-by-row with the FSM:
    • Output a 1 to all row wires except the row being scanned which should have a 0.
    • Pressing any key on that row will send the respective column wire to 0; all other column wires remain 1 due to the pull-up resistor.

In addition to the keypad scanning just explained, there are some issues of timing to note:

  • Releasing a key requires its column wire to be restored to Vdd through a weak pull-up resistor in the FPGA. How long does this take? With a 500kOhm resistor, it could take several (tens? hundreds?) of clock cycles. You should wait long enough for this to occur (e.g., one debounce period after the key is released.
  • They keypad itself has an internal resistance of 50-500 Ohms (measure it!) when a key is pressed. This limits how rapidly the FPGA row output pin can pull down the column wire. On the DE10 Lite it appears that 2 clock cycles is long enough. On the DE1-SoC, this appears to take 3 clock cycles. I do not recommend attempting to speed this up.
  • Because of the synchronizer flip-flops and debounce detection in kb_db, you cannot sample the column inputs immediately the row outputs are set. The 2 or 3 cycles of waiting needed (as explained in the previous bullet) also covers this delay.

Add 4 weak pull-up resistors to the column GPIO pins:

  • Quartus à Assignments à Assignment Editor à scroll to bottom à in the “To” column, enter a pin name (e.g., ARDUINO_IO[0]) where it says <> à in the “Assignment Name” column, double-click and select “Weak Pull-Up Resistor” near the bottom àin the “Value” column, double-click and select “On” à in the “Enabled” column, make sure it says “Yes”.
  • Repeat for each of the other 3 column inputs (e.g., ARDUINO_IO[1], ARDUINO_IO[2], and ARDUINO_IO[3]).

6. Putting it All Together

When you think your combo and keyboard modules are both 100% correct (and each works with the corresponding golden reference solution), you are ready to try the whole thing. Good luck!

7. What to Turn In and Demonstrate

TBD.

最後修改日期: 2025 年 10 月 10 日

作者

留言

撰寫回覆或留言

發佈留言必須填寫的電子郵件地址不會公開。