--- /dev/null
+--------------------------------------------------------------------------------
+-- 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;