]> git.the-white-hart.net Git - vhdl/commitdiff
Create copy of PS2 host for optimization
authorRyan <>
Thu, 18 Sep 2025 19:54:35 +0000 (14:54 -0500)
committerRyan <>
Thu, 18 Sep 2025 19:54:35 +0000 (14:54 -0500)
Swap out the FIFOs, that's an easy win to start

libraries/ps2/ps2_host_wb_opt.vhd [new file with mode: 0644]
projects/cpu_0/nexys2_opt.vhd

diff --git a/libraries/ps2/ps2_host_wb_opt.vhd b/libraries/ps2/ps2_host_wb_opt.vhd
new file mode 100644 (file)
index 0000000..2a824d9
--- /dev/null
@@ -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;
index b363f226d01a81a45e971d18c740704da80670f8..e2980be3cd2e084cf213ef4361ce6e4d15bd1630 100644 (file)
@@ -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,