Skip to content

Commit

Permalink
UART: Use same keyboard mapping as XEMU
Browse files Browse the repository at this point in the history
This is part 1 of issue #41

To enable the UART, set the constant ENABLE_UART to true in
CORE/vhdl/globals.vhd
  • Loading branch information
MJoergen committed Nov 15, 2024
1 parent 965072f commit e21f3e5
Show file tree
Hide file tree
Showing 4 changed files with 113 additions and 54 deletions.
23 changes: 16 additions & 7 deletions M2M/vhdl/ascii_to_mega65.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,15 @@ library ieee;
use ieee.numeric_std_unsigned.all;

-- Translates key codes from ASCII to MEGA65.
--
-- I'm using the same key mapping as the XEMU:
-- https://github.com/MEGA65/mega65-user-guide/blob/master/images/xemu-extended-keyboard.png
--
-- In particular:
-- CLR/HOME <-> Home
-- RUN/STOP <-> End
-- HELP <-> Page Up
-- RESTORE <-> Page Down

entity ascii_to_mega65 is
port (
Expand Down Expand Up @@ -130,7 +139,7 @@ architecture synthesis of ascii_to_mega65 is

begin

uart_rx_ready_o <= '1';
uart_rx_ready_o <= key_ready_i or not key_valid_o;

uart_buf_proc : process (clk_i)
variable uart_buf_hex_v : std_logic_vector(C_UART_BUF_SIZE * 16 - 1 downto 0);
Expand All @@ -140,7 +149,7 @@ begin
key_valid_o <= '0';
end if;

if uart_rx_valid_i = '1' then
if uart_rx_valid_i = '1' and uart_rx_ready_o = '1' then
-- A byte is received, just shift it in.
uart_buf <= uart_buf(uart_buf'left-8 downto 0) & uart_rx_data_i;
if uart_buf_len < C_UART_BUF_SIZE then
Expand All @@ -157,16 +166,17 @@ begin
when C_F7_5 => key_data_o <= C_M65_F7;
when C_F9_5 => key_data_o <= C_M65_F9;
when C_F11_5 => key_data_o <= C_M65_F11;
when C_F12_5 => key_data_o <= C_M65_HELP;
when others =>
key_valid_o <= key_valid_o; -- Leave unchanged
end case;
elsif uart_buf_len >= 4 then
uart_buf_len <= 0;
key_valid_o <= '1';
case uart_buf(4*8-1 downto 0) is
when C_HOME_4 => key_data_o <= C_M65_CLR_HOME;
when C_END_4 => key_data_o <= C_M65_RESTORE;
when C_PAGE_UP_4 => key_data_o <= C_M65_HELP;
when C_PAGE_DOWN_4 => key_data_o <= C_M65_RESTORE;
when C_HOME_4 => key_data_o <= C_M65_CLR_HOME;
when C_END_4 => key_data_o <= C_M65_RUN_STOP;

when others =>
key_valid_o <= key_valid_o; -- Leave unchanged
Expand All @@ -188,7 +198,7 @@ begin
when C_LEFT_3 => key_data_o <= C_M65_LEFT_CRSR;
when C_RIGHT_3 => key_data_o <= C_M65_HORZ_CRSR;
when C_HOME_3 => key_data_o <= C_M65_CLR_HOME;
when C_END_3 => key_data_o <= C_M65_RESTORE;
when C_END_3 => key_data_o <= C_M65_RUN_STOP;
when others =>
key_valid_o <= key_valid_o; -- Leave unchanged
if uart_buf(uart_buf_len*8-1 downto uart_buf_len*8-8) = X"1b" and uart_buf(7 downto 0) /= X"7e" then
Expand All @@ -202,7 +212,6 @@ begin
case character'val(to_integer(uart_buf(7 downto 0))) is
when character'val( 9) => key_data_o <= C_M65_TAB;
when character'val(13) => key_data_o <= C_M65_RETURN;
when character'val(26) => key_data_o <= C_M65_RUN_STOP; -- Pause/Break key
when ' ' => key_data_o <= C_M65_SPACE;
when '*' => key_data_o <= C_M65_ASTERISK;
when '+' => key_data_o <= C_M65_PLUS;
Expand Down
1 change: 1 addition & 0 deletions M2M/vhdl/framework.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ begin
)
port map (
clk_main_i => main_clk_i,
rst_main_i => main_rst_i,
clk_main_speed_i => CORE_CLK_SPEED,

-- interface to the MEGA65 keyboard controller
Expand Down
135 changes: 93 additions & 42 deletions M2M/vhdl/m2m_keyb.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ entity m2m_keyb is
);
port (
clk_main_i : in std_logic; -- core clock
rst_main_i : in std_logic;
clk_main_speed_i : in natural; -- speed of core clock in Hz

-- interface to the MEGA65 keyboard controller
Expand Down Expand Up @@ -68,70 +69,120 @@ architecture synthesis of m2m_keyb is
signal uart_rx_valid : std_logic;
signal uart_rx_data : std_logic_vector(7 downto 0);

signal fifo_ready : std_logic;
signal fifo_valid : std_logic;
signal fifo_data : std_logic_vector(7 downto 0);

signal key_ready : std_logic;
signal key_valid : std_logic;
signal key_data : natural range 0 to 79;
signal key_data_r : natural range 0 to 79;
signal key_timer : natural range 0 to 100_000_000; -- Counts clock cycles
signal key_pressed_n : std_logic;

type key_state_type is (IDLE_ST, KEY_PRESS_ST, KEY_RELEASE_ST);
signal key_state : key_state_type := IDLE_ST;

begin

-- Read characters from UART (ASCII format)
uart_inst : entity work.uart
port map (
clk_speed_i => clk_main_speed_i,
clk_i => clk_main_i,
rst_i => '0',
tx_valid_i => '0',
tx_ready_o => open,
tx_data_i => X"00",
rx_valid_o => uart_rx_valid,
rx_ready_i => uart_rx_ready,
rx_data_o => uart_rx_data,
uart_tx_o => open,
uart_rx_i => uart_rx_i
); -- uart_inst

-- Convert ASCII codes to MEGA65 keyboard codes
ascii_to_mega65_inst : entity work.ascii_to_mega65
port map (
clk_i => clk_main_i,
rst_i => '0',
uart_rx_valid_i => uart_rx_valid,
uart_rx_ready_o => uart_rx_ready,
uart_rx_data_i => uart_rx_data,
key_valid_o => key_valid,
key_ready_i => key_ready,
key_data_o => key_data
); -- ascii_to_mega65_inst
use_uart_gen : if G_USE_UART generate

-- Read characters from UART (ASCII format)
uart_inst : entity work.uart
port map (
clk_speed_i => clk_main_speed_i,
clk_i => clk_main_i,
rst_i => rst_main_i,
tx_valid_i => '0',
tx_ready_o => open,
tx_data_i => X"00",
rx_valid_o => uart_rx_valid,
rx_ready_i => uart_rx_ready,
rx_data_o => uart_rx_data,
uart_tx_o => open,
uart_rx_i => uart_rx_i
); -- uart_inst

axi_fifo_small_inst : entity work.axi_fifo_small
generic map (
G_RAM_WIDTH => 8,
G_RAM_DEPTH => 256
)
port map (
clk_i => clk_main_i,
rst_i => rst_main_i,
s_ready_o => uart_rx_ready,
s_valid_i => uart_rx_valid,
s_data_i => uart_rx_data,
m_ready_i => fifo_ready,
m_valid_o => fifo_valid,
m_data_o => fifo_data
); -- axi_fifo_small_inst

-- Convert ASCII codes to MEGA65 keyboard codes
ascii_to_mega65_inst : entity work.ascii_to_mega65
port map (
clk_i => clk_main_i,
rst_i => rst_main_i,
uart_rx_valid_i => fifo_valid,
uart_rx_ready_o => fifo_ready,
uart_rx_data_i => fifo_data,
key_valid_o => key_valid,
key_ready_i => key_ready,
key_data_o => key_data
); -- ascii_to_mega65_inst

end generate use_uart_gen;

key_pressed_n_o <= key_pressed_n or not enable_core_i;

-- output the keyboard interface for the core
output_proc : process (clk_main_i)
key_ready <= '1' when key_state = IDLE_ST else '0';

uart_fsm_proc : process (clk_main_i)
begin
if rising_edge(clk_main_i) then
key_num_o <= key_num;
key_pressed_n <= key_status_n;

if G_USE_UART then
key_ready <= '0';
if key_timer > 0 then
key_timer <= key_timer - 1;
if key_num = key_data then
case key_state is
when IDLE_ST =>
if key_valid = '1' then
-- Store key
key_data_r <= key_data;
-- Simulate key pressed for a short while
key_timer <= clk_main_speed_i / 32; -- 32 ms
key_state <= KEY_PRESS_ST;
end if;

when KEY_PRESS_ST =>
if key_num = key_data_r then
-- Override with key from UART
key_pressed_n <= '0';
end if;
if key_timer = 2 then
key_ready <= '1';
end if;
elsif key_valid = '1' then
-- Simulate key pressed for a short while
key_timer <= clk_main_speed_i / 8; -- 125 ms
if key_timer > 0 then
key_timer <= key_timer - 1;
else
-- Simulate key released for a short while
key_timer <= clk_main_speed_i / 32; -- 32 ms
key_state <= KEY_RELEASE_ST;
end if;

when KEY_RELEASE_ST =>
if key_timer > 0 then
key_timer <= key_timer - 1;
else
key_state <= IDLE_ST;
end if;

end case;

if rst_main_i = '1' then
key_state <= IDLE_ST;
end if;
end if;
end process output_proc;
end if;
end process uart_fsm_proc;

-- output the keyboard interface for QNICE
qnice_keys_n_o <= keys_n;
Expand Down Expand Up @@ -167,7 +218,7 @@ begin
port map (
clk => clk_main_i,
clock_frequency => clk_main_speed_i,
reset_in => '0',
reset_in => rst_main_i,

matrix_col => matrix_col,
matrix_col_idx => matrix_col_idx,
Expand Down
8 changes: 3 additions & 5 deletions M2M/vhdl/uart.vhd
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,15 @@ begin
-- Start bit detected
if uart_rx_d = '0' then
-- Make sure we sample in the "middle" of each bit
rx_counter <= clk_speed_i / 2;
rx_counter <= clk_speed_i / 2 + G_BAUD_RATE;
rx_state <= CHECK_START_ST;
end if;

when CHECK_START_ST =>
if rx_counter < clk_speed_i then
rx_counter <= rx_counter + G_BAUD_RATE;
else
rx_counter <= 0;
rx_counter <= (rx_counter - clk_speed_i) + G_BAUD_RATE;
rx_data <= uart_rx_d & rx_data(9 downto 1);
rx_state <= BUSY_ST;
end if;
Expand All @@ -127,7 +127,7 @@ begin
if rx_counter < clk_speed_i then
rx_counter <= rx_counter + G_BAUD_RATE;
else
rx_counter <= 0;
rx_counter <= (rx_counter - clk_speed_i) + G_BAUD_RATE;
rx_data <= uart_rx_d & rx_data(9 downto 1);
end if;

Expand All @@ -143,14 +143,12 @@ begin
end if;
rx_data <= (others => '1');
rx_state <= IDLE_ST;
rx_counter <= 0;
end if;

if rst_i = '1' then
rx_valid_o <= '0';
rx_data <= (others => '1');
rx_state <= IDLE_ST;
rx_counter <= 0;
end if;
end if;
end process rx_proc;
Expand Down

0 comments on commit e21f3e5

Please sign in to comment.