------------------------------------------------------------------------------- --! @file --! @author Hipolito Guzman-Miranda --! @brief Emulates a Digilent PMOD keypad ------------------------------------------------------------------------------- --! Use IEEE standard definitions library library ieee; --! Use std_logic* signal types use ieee.std_logic_1164.all; --! Use the slv2string function use work.vhdl_verification.all; --! @brief Emulates PMOD keypad --! --! @details Propagates inputs from \c keypad_columns into \c keypad_rows, --! according to the bits of \c pressed_keys, which is a vector that has 1 bit --! for each key in the keypad. Please read the detailed description of the --! architecture to understand which bit of the vector maps to each key. entity keypad_emu is generic ( VERBOSE : boolean := false --! If true, log changes in the \c pressed_keys vector, apart from the changes in the individual keys. Also log which row is connected to which column when a key is pressed. ); port ( keypad_rows : out std_logic_vector(3 downto 0); --! This is the signal that must be read from the keypad keypad_columns : in std_logic_vector(3 downto 0); --! Connect here the signal that drives the keypad pressed_keys : in std_logic_vector(15 downto 0) --! One bit for each key. Set to '1' to emulate key press, set to '0' to emulate key release ); end keypad_emu; --! @brief Architecture is based on multiple concurrent assignments to the same --! signal, but some of the drivers are weak (pullups), just like the real --! hardware --! --! @detailed --! Each pressed key connects a row to a column, so we have a second driver --! for rows here. If multiple keys are pressed, we may have --! multiple strong drivers with different values for the same column, --! depending on what values the user puts in the rows, which --! could result in an 'X' in simulation. In the hardware, the internal --! resistances avoid a short-circuit, but the resulting value is not --! defined. --! --! The keys in the physical keyboard are: --! --! | | Col0 | Col1 | Col2 | Col3 | --! |------|------|------|------|------| --! | Row0 | 1 | 2 | 3 | A | --! | Row1 | 4 | 5 | 6 | B | --! | Row2 | 7 | 8 | 9 | C | --! | Row3 | 0 | F | E | D | --! --! (Please note that Digilent's schematics are slightly outdated and do not --! reflect the actual key layout. The correct key layout is in the table --! above) --! --! The correspondence, for this simulation model, between the keys and the --! bits in the \c pressed_keys input is: --! --! pressed_keys(i) to Row/Col/Key --! --! | i | Row | Col | Key | --! |-----|-------|-------|-------| --! | 0 | 0 | 0 | 1 | --! | 1 | 0 | 1 | 2 | --! | 2 | 0 | 2 | 3 | --! | 3 | 0 | 3 | A | --! | 4 | 1 | 0 | 4 | --! | 5 | 1 | 1 | 5 | --! | 6 | 1 | 2 | 6 | --! | 7 | 1 | 3 | B | --! | 8 | 2 | 0 | 7 | --! | 9 | 2 | 1 | 8 | --! | 10 | 2 | 2 | 9 | --! | 11 | 2 | 3 | C | --! | 12 | 3 | 0 | 0 | --! | 13 | 3 | 1 | F | --! | 14 | 3 | 2 | E | --! | 15 | 3 | 3 | D | --! architecture keypad_emu_arch of keypad_emu is signal last_pressed_keys : std_logic_vector(15 downto 0); -- This array allows converting a key position to the char representation -- of the pressed button. This way, the simulation logs can show which -- button is pressed or released. type char_vector is array (0 to 15) of character; constant keypad_table : char_vector := ('1', '2', '3', 'A', '4', '5', '6', 'B', '7', '8', '9', 'C', '0', 'F', 'E', 'D'); begin -- The keypad connects all rows to a pullup, so emulate this keypad_rows <= (others => 'H'); -- To implement that each key press connects a specific column to a specific -- row, we just have to take into account that, according the to previous -- table, Row is i/4 and Col is i mod 4 process(pressed_keys, keypad_columns) begin -- Store last values so we know if a key press is new or not last_pressed_keys <= pressed_keys; -- Avoid 'U' in outputs keypad_rows <= (others => 'H'); if VERBOSE then if pressed_keys'event then report "pressed_keys: " & slv2string(pressed_keys) & " last_pressed_keys: " & slv2string(last_pressed_keys); end if; end if; -- Implement key presses for i in 0 to 15 loop if pressed_keys(i) = '1' then if VERBOSE then if pressed_keys'event then report "Key in position " & integer'image(i) & " (" & keypad_table(i) & ")" & " pressed, connecting column " & integer'image(i mod 4) & " to row " & integer'image(i/4); end if; end if; keypad_rows(i/4) <= keypad_columns(i mod 4); end if; -- Report key presses in the simulation log if pressed_keys(i) /= last_pressed_keys(i) then if pressed_keys(i) = '1' then report "Key in position " & integer'image(i) & " (" & keypad_table(i) & ")" & " pressed. Current value: " & std_logic'image(pressed_keys(i)) & ", last value: " & std_logic'image(last_pressed_keys(i)); elsif pressed_keys(i) = '0' then report "Key in position " & integer'image(i) & " (" & keypad_table(i) & ")" & " released. Current value: " & std_logic'image(pressed_keys(i)) & ", last value: " & std_logic'image(last_pressed_keys(i)); else report "Key in position " & integer'image(i) & " (" & keypad_table(i) & ")" & " set to a value different than '0' or '1', check testbench. Current value: " & std_logic'image(pressed_keys(i)) & ", last value: " & std_logic'image(last_pressed_keys(i)); end if; end if; end loop; end process; end keypad_emu_arch;