From fa02c3ac19a0b485af9f20ef97f44d419fe6b987 Mon Sep 17 00:00:00 2001 From: Ryan <> Date: Thu, 18 Sep 2025 14:54:35 -0500 Subject: [PATCH] Create copy of PS2 host for optimization Swap out the FIFOs, that's an easy win to start --- libraries/ps2/ps2_host_wb_opt.vhd | 317 ++++++++++++++++++++++++++++++ projects/cpu_0/nexys2_opt.vhd | 2 +- 2 files changed, 318 insertions(+), 1 deletion(-) create mode 100644 libraries/ps2/ps2_host_wb_opt.vhd diff --git a/libraries/ps2/ps2_host_wb_opt.vhd b/libraries/ps2/ps2_host_wb_opt.vhd new file mode 100644 index 0000000..2a824d9 --- /dev/null +++ b/libraries/ps2/ps2_host_wb_opt.vhd @@ -0,0 +1,317 @@ +-------------------------------------------------------------------------------- +-- ps2_host_wb - Wishbone interface to host PS2 (keyboard/mouse) interface +-------------------------------------------------------------------------------- +-- TODO: https://eecs.umich.edu/courses/doing_dsp/handout/SRL16E.pdf +-- Play with using shift-registers to make this more dense +-- +-- +---+---+---+---+---+---+---+---+ +-- | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | +-- +---+---+---+---+---+---+---+---+ +-- 0 |INH|AIH| |ENA| CTRL +-- +---+---+---+---+---+---+---+---+ +-- 1 |OER|FER|PER| |TXE|TXR|RXF|RXR| IMASK +-- +---+---+---+---+---+---+---+---+ +-- 2 |OER|FER|PER| |TXE|TXR|RXF|RXR| IFLAG (W1C) +-- +---+---+---+---+---+---+---+---+ +-- 3 | |TTO|RTO|NCK| ERROR (RO) +-- +-------------------+---+---+---+ +-- 4 | DATA | DATA +-- +-------------------------------+ +-- 5 | (RESERVED) | +-- +-------------------------------+ +-- 6 | (RESERVED) | +-- +-------------------------------+ +-- 7 | (RESERVED) | +-- +-------------------------------+ +-- +-- CTRL register bits: +-- ENA - Enable interface, interface and FIFOs are cleared and reset when 0 +-- AIH - Auto-inhibit device from sending if FIFO is full +-- INH - Inhibit device from sending +-- +-- IMASK/IFLAG register bits: +-- RXR - Receiver has at least one byte ready +-- Deasserts when all DATA read +-- RXF - Receiver FIFO is full +-- Deasserts on read from DATA +-- TXR - Transmitter ready to accept at least one byte +-- Deasserts on when TX FIFO is full +-- TXE - Transmitter FIFO is empty +-- Deasserts on write to DATA +-- PER - Receiver parity error +-- Write one to clear +-- FER - Receiver framing error, either start or stop bit was incorrect +-- Write one to clear +-- OER - Receiver FIFO overrun, at least one byte was lost due to lack of space +-- Write one to clear +-- +-- ERROR register bits, cleared by writing ENA to zero: +-- TTO - Transmitter timeout error, PS2 clock stopped unexpectedly +-- RTO - Receiver timeout error, PS2 clock stopped unexpectedly +-- NER - Transmitter NACK error, at least one byte was not acknowledged +-- +-- Notes: +-- +-- It's a good idea to always use the auto-inhibit feature, as it allows the +-- device to be informed of and prevent FIFO spillage, possibly retransmitting +-- or performing more queueing on its end. +-- +-- User must check flags before reading/writing DATA register to ensure data is +-- available in RX or space is available in TX, otherwise undefined data will be +-- returned or written data will be lost. +-- +-- TODO: Add timeout/start bit timer divisors +-- TODO: Use generics for FIFO capacities +-- TODO: Use constants for register addresses to allow easy rearranging later +-- +-- FIXME: Missing OER signal (probably framing and parity errors too) +-------------------------------------------------------------------------------- +-- WISHBONE DATASHEET +-- +-- Wishbone specification used: Rev B.3 +-- Interface type: device +-- Port size: 8-bit +-- Operand sizes: 8-bit +-- Endianness: undefined (port size same as granularity) +-- Data transfer sequence: undefined +-- Clock constraints: none +-- Signals: +-- * rst_i +-- * clk_i +-- * cyc_i +-- * stb_i +-- * we_i +-- * ack_o +-- * adr_i (3-bit) +-- * dat_i (8-bit) +-- * dat_o (8-bit) +-------------------------------------------------------------------------------- + +library ieee; +use ieee.std_logic_1164.all; + +library utility; +library work; + + +entity ps2_host_wb_opt is + port ( + rst_i: in std_logic; + clk_i: in std_logic; + + cyc_i: in std_logic; + stb_i: in std_logic; + we_i: in std_logic; + ack_o: out std_logic; + adr_i: in std_logic_vector(2 downto 0); + dat_i: in std_logic_vector(7 downto 0); + dat_o: out std_logic_vector(7 downto 0); + + rx_ready: out std_logic; + rx_full: out std_logic; + tx_ready: out std_logic; + tx_empty: out std_logic; + + err_nack: out std_logic; + err_txto: out std_logic; + err_rxto: out std_logic; + err_missed: out std_logic; + err_parity: out std_logic; + err_framing: out std_logic; + + ps2_clk: inout std_logic; + ps2_data: inout std_logic + ); +end ps2_host_wb_opt; + + +architecture behavioral of ps2_host_wb_opt is + + -- Bus-exposed registers + signal ctrl_reg: std_logic_vector(7 downto 0); + signal imask_reg: std_logic_vector(7 downto 0); + signal iflag_reg: std_logic_vector(7 downto 0); + signal error_vec: std_logic_vector(7 downto 0); + + -- Control register bit signals + signal ctrl_ena: std_logic; + signal ctrl_inhibit: std_logic; + signal ctrl_auto_inh: std_logic; + + -- Interface connections + signal iface_rst: std_logic; + signal tx_err_nack: std_logic; + signal tx_err_timeout: std_logic; + signal rx_err_timeout: std_logic; + signal rx_err_missed: std_logic; + signal rx_err_parity: std_logic; + signal rx_err_start: std_logic; + signal rx_err_stop: std_logic; + + -- Transmit FIFO connections + signal txqt_stb: std_logic; + signal txqt_ack: std_logic; + signal txqt_dat: std_logic_vector(7 downto 0); + signal txqh_stb: std_logic; + -- txqh_ack left unconnected to prevent wb bus stalls when full + -- txqh_dat comes from dat_i + signal txq_empty: std_logic; + signal txq_full: std_logic; + + -- Receive FIFO connections + signal rxqh_stb: std_logic; + signal rxqh_ack: std_logic; + signal rxqh_dat: std_logic_vector(7 downto 0); + signal rxqt_stb: std_logic; + signal rxqt_ack: std_logic; + signal rxqt_dat: std_logic_vector(7 downto 0); + signal rxq_empty: std_logic; + signal rxq_full: std_logic; + + signal data_stb: std_logic; + +begin + + -- Wishbone interface + process (rst_i, clk_i, cyc_i, stb_i, we_i, adr_i, dat_i) + begin + if rising_edge(clk_i) then + if rst_i = '1' then + ctrl_reg <= (others => '0'); + imask_reg <= (others => '0'); + iflag_reg <= (others => '0'); + else + -- Perform write cycle + if cyc_i = '1' and stb_i = '1' and we_i = '1' then + case adr_i is + when "000" => ctrl_reg <= dat_i; + when "001" => imask_reg <= dat_i; + when "010" => iflag_reg <= iflag_reg and (not dat_i); -- W1C + when "011" => null; -- RO + when "100" => null; -- Attached to FIFOs + when others => null; + end case; + end if; + + -- Latch interrupt flags + iflag_reg(0) <= rxqt_stb; -- using !rxq_empty will trigger interrupt before data makes it through the pipeline FIFO + iflag_reg(1) <= rxq_full; + iflag_reg(2) <= not txq_full; + iflag_reg(3) <= txq_empty; + + -- Catch receive error flags when the interface reports a new byte + if rxqh_stb = '1' then + iflag_reg(5) <= iflag_reg(5) or rx_err_parity; + iflag_reg(6) <= iflag_reg(6) or rx_err_start or rx_err_stop; + iflag_reg(7) <= iflag_reg(7) or rx_err_missed; + end if; + end if; + end if; + end process; + + error_vec <= "00000" & tx_err_timeout & rx_err_timeout & tx_err_nack; + + with adr_i select dat_o <= + ctrl_reg when "000", + imask_reg when "001", + iflag_reg when "010", + error_vec when "011", + rxqt_dat when "100", + (others => '1') when others; + + ack_o <= '1'; + + -- Interrupt output signals + rx_ready <= iflag_reg(0) and imask_reg(0); + rx_full <= iflag_reg(1) and imask_reg(1); + tx_ready <= iflag_reg(2) and imask_reg(2); + tx_empty <= iflag_reg(3) and imask_reg(3); + + err_missed <= iflag_reg(5) and imask_reg(5); + err_framing <= iflag_reg(6) and imask_reg(6); + err_parity <= iflag_reg(7) and imask_reg(7); + + err_nack <= tx_err_nack; + err_rxto <= rx_err_timeout; + err_txto <= tx_err_timeout; + + -- Control register bit signals + ctrl_inhibit <= ctrl_reg(7); + ctrl_auto_inh <= ctrl_reg(6); + ctrl_ena <= ctrl_reg(0); + + data_stb <= (cyc_i and stb_i) when adr_i = "100" else '0'; + txqh_stb <= data_stb when we_i = '1' else '0'; + rxqt_ack <= data_stb when we_i = '0' else '0'; + + iface_rst <= rst_i or (not ctrl_ena); + + e_txq: entity utility.fifo_16 + generic map ( + WIDTH => 8 + ) + port map ( + rst_i => iface_rst, + clk_i => clk_i, + + h_stb_i => txqh_stb, + h_ack_o => open, + h_dat_i => dat_i, + + t_stb_o => txqt_stb, + t_ack_i => txqt_ack, + t_dat_o => txqt_dat, + + is_empty => txq_empty, + is_full => txq_full + ); + + e_rxq: entity utility.fifo_16 + generic map ( + WIDTH => 8 + ) + port map ( + rst_i => iface_rst, + clk_i => clk_i, + + h_stb_i => rxqh_stb, + h_ack_o => rxqh_ack, + h_dat_i => rxqh_dat, + + t_stb_o => rxqt_stb, + t_ack_i => rxqt_ack, + t_dat_o => rxqt_dat, + + is_empty => rxq_empty, + is_full => rxq_full + ); + + e_ps2: entity work.ps2_host + port map ( + rst_i => iface_rst, + clk_i => clk_i, + + tx_stb_i => txqt_stb, + tx_ack_o => txqt_ack, + tx_dat_i => txqt_dat, + + rx_stb_o => rxqh_stb, + rx_ack_i => rxqh_ack, + rx_dat_o => rxqh_dat, + + inhibit => ctrl_inhibit, + auto_inhibit => ctrl_auto_inh, + + tx_err_nack => tx_err_nack, + tx_err_timeout => tx_err_timeout, + rx_err_timeout => rx_err_timeout, + rx_err_missed => rx_err_missed, + rx_err_parity => rx_err_parity, + rx_err_start => rx_err_start, + rx_err_stop => rx_err_stop, + + ps2_clk => ps2_clk, + ps2_data => ps2_data + ); + +end behavioral; diff --git a/projects/cpu_0/nexys2_opt.vhd b/projects/cpu_0/nexys2_opt.vhd index b363f22..e2980be 100644 --- a/projects/cpu_0/nexys2_opt.vhd +++ b/projects/cpu_0/nexys2_opt.vhd @@ -432,7 +432,7 @@ begin ps2_err_missed or ps2_err_parity or ps2_err_framing; - e_ps2: entity ps2.ps2_host_wb + e_ps2: entity ps2.ps2_host_wb_opt port map ( rst_i => d_rst, clk_i => d_clk, -- 2.43.0