--------------------------------------------------------------------------- -- (c) 2017 mark watson -- I am happy for anyone to use this for non-commercial use. -- If my vhdl files are used commercially or otherwise sold, -- please contact me for explicit permission at scrameta (gmail). -- This applies for source and binary form and derived works. --------------------------------------------------------------------------- LIBRARY ieee; USE ieee.std_logic_1164.all; use ieee.numeric_std.all; use IEEE.STD_LOGIC_MISC.all; -- Now USB is handled by ZPU its dropping SIO commands on the floor while polling USB -- Capture the SIO command here, so it can poll the latest command instead -- All other processing is direct to pokey... -- memory map -- 0 = transmit (w) -- 1 = tx fifo status (r) -- 2 = fetch/receive (r) - requests next data - i.e. first read trash -- 3 = rx fifo status (r) -- 4 = divisor (w) - applied after transmit done -- 5 = framing error (auto clear) (r) (command&serial) ENTITY sio_handler IS PORT ( CLK : IN STD_LOGIC; ADDR : IN STD_LOGIC_VECTOR(4 DOWNTO 0); CPU_DATA_IN : IN STD_LOGIC_VECTOR(7 DOWNTO 0); EN : IN STD_LOGIC; WR_EN : IN STD_LOGIC; RESET_N : IN STD_LOGIC; -- clock for pokey POKEY_ENABLE : in std_logic; -- ATARI interface (in future we can also turbo load by directly hitting memory...) SIO_DATA_IN : out std_logic; SIO_COMMAND : in std_logic; SIO_DATA_OUT : in std_logic; SIO_CLK_OUT : in std_logic; -- CPU interface DATA_OUT : OUT STD_LOGIC_VECTOR(15 DOWNTO 0) ); END sio_handler; ARCHITECTURE vhdl OF sio_handler IS signal sio_data_out_reg : std_logic; signal sio_data_out_next : std_logic; signal sio_command_next : std_logic; signal sio_command_reg : std_logic; signal sio_command_count_next : std_logic_vector(6 downto 0); signal sio_command_count_reg : std_logic_vector(6 downto 0); signal sio_command_framing_error_reg : std_logic; signal sio_command_framing_error_next : std_logic; signal sio_command_rising : std_logic; signal receive_write : std_logic; signal addr_decoded : std_logic_vector(15 downto 0); signal receive_enable : std_logic; signal receive_detect : std_logic; signal transmit_enable : std_logic; signal fifo_tx_write : std_logic; signal fifo_tx_full : std_logic; signal fifo_tx_empty : std_logic; signal fifo_tx_advance : std_logic; signal fifo_tx_data : std_logic_vector(7 downto 0); signal fifo_tx_count : std_logic_vector(7 downto 0); signal fifo_rx_data : std_logic_vector(14 downto 0); signal fifo_rx_full : std_logic; signal fifo_rx_empty : std_logic; signal fifo_rx_advance : std_logic; signal fifo_rx_count : std_logic_vector(7 downto 0); signal divisor_next : std_logic_vector(7 downto 0); signal divisor_reg : std_logic_vector(7 downto 0); signal pending_divisor_next : std_logic_vector(7 downto 0); signal pending_divisor_reg : std_logic_vector(7 downto 0); signal transmit_divisor_count_next : std_logic_vector(7 downto 0); signal transmit_divisor_count_reg : std_logic_vector(7 downto 0); signal receive_divisor_count_next : std_logic_vector(7 downto 0); signal receive_divisor_count_reg : std_logic_vector(7 downto 0); signal receive_divisor_next : std_logic_vector(7 downto 0); signal receive_divisor_reg : std_logic_vector(7 downto 0); signal sio_clk_out_reg : std_logic; signal data_out_next : std_logic_vector(15 downto 0); signal data_out_reg : std_logic_vector(15 downto 0); -- have to return results NEXT cycle! constant P2S_STATE_WAIT : std_logic_vector(3 downto 0) := "0000"; constant P2S_STATE_STOP : std_logic_vector(3 downto 0) := "0001"; constant P2S_STATE_SHIFT_0 : std_logic_vector(3 downto 0) := "1000"; constant P2S_STATE_SHIFT_1 : std_logic_vector(3 downto 0) := "1001"; constant P2S_STATE_SHIFT_2 : std_logic_vector(3 downto 0) := "1010"; constant P2S_STATE_SHIFT_3 : std_logic_vector(3 downto 0) := "1011"; constant P2S_STATE_SHIFT_4 : std_logic_vector(3 downto 0) := "1100"; constant P2S_STATE_SHIFT_5 : std_logic_vector(3 downto 0) := "1101"; constant P2S_STATE_SHIFT_6 : std_logic_vector(3 downto 0) := "1110"; constant P2S_STATE_SHIFT_7 : std_logic_vector(3 downto 0) := "1111"; signal p2s_state_next : std_logic_vector(3 downto 0); signal p2s_state_reg : std_logic_vector(3 downto 0); signal p2s_shift_next : std_logic_vector(7 downto 0); signal p2s_shift_reg : std_logic_vector(7 downto 0); signal p2s_transmit_next : std_logic; signal p2s_transmit_reg : std_logic; signal p2s_idle : std_logic; constant S2P_STATE_WAIT : std_logic_vector(3 downto 0) := "0000"; constant S2P_STATE_STOP : std_logic_vector(3 downto 0) := "0001"; constant S2P_STATE_SHIFT_0 : std_logic_vector(3 downto 0) := "1000"; constant S2P_STATE_SHIFT_1 : std_logic_vector(3 downto 0) := "1001"; constant S2P_STATE_SHIFT_2 : std_logic_vector(3 downto 0) := "1010"; constant S2P_STATE_SHIFT_3 : std_logic_vector(3 downto 0) := "1011"; constant S2P_STATE_SHIFT_4 : std_logic_vector(3 downto 0) := "1100"; constant S2P_STATE_SHIFT_5 : std_logic_vector(3 downto 0) := "1101"; constant S2P_STATE_SHIFT_6 : std_logic_vector(3 downto 0) := "1110"; constant S2P_STATE_SHIFT_7 : std_logic_vector(3 downto 0) := "1111"; signal s2p_state_next : std_logic_vector(3 downto 0); signal s2p_state_reg : std_logic_vector(3 downto 0); signal s2p_shift_next : std_logic_vector(6 downto 0); signal s2p_shift_reg : std_logic_vector(6 downto 0); signal s2p_write : std_logic; signal s2p_framing_error_reg : std_logic; signal s2p_framing_error_next : std_logic; signal s2p_start : std_logic; signal framing_error_clear : std_logic; begin -- register process(clk,reset_n) begin if (reset_n = '0') then sio_data_out_reg <= '1'; sio_command_reg <= '1'; sio_command_count_reg <= (others=>'0'); sio_command_framing_error_reg <= '0'; divisor_reg <= (others=>'0'); pending_divisor_reg <= (others=>'0'); transmit_divisor_count_reg <= (others=>'0'); receive_divisor_count_reg <= (others=>'0'); receive_divisor_reg <= (others=>'0'); s2p_state_reg <= S2P_STATE_WAIT; s2p_shift_reg <= (others=>'1'); s2p_framing_error_reg <= '0'; p2s_state_reg <= P2S_STATE_WAIT; p2s_shift_reg <= (others=>'1'); p2s_transmit_reg <= '1'; data_out_reg <= (others=>'0'); sio_clk_out_reg <='0'; elsif (clk'event and clk='1') then sio_data_out_reg <= sio_data_out_next; sio_command_reg <= sio_command_next; sio_command_count_reg <= sio_command_count_next; sio_command_framing_error_reg <= sio_command_framing_error_next; divisor_reg <= divisor_next; pending_divisor_reg <= pending_divisor_next; transmit_divisor_count_reg <= transmit_divisor_count_next; receive_divisor_count_reg <= receive_divisor_count_next; receive_divisor_reg <= receive_divisor_next; s2p_state_reg <= s2p_state_next; s2p_shift_reg <= s2p_shift_next; s2p_framing_error_reg <= s2p_framing_error_next; p2s_state_reg <= p2s_state_next; p2s_shift_reg <= p2s_shift_next; p2s_transmit_reg <= p2s_transmit_next; data_out_reg <= data_out_next; sio_clk_out_reg <= sio_clk_out; end if; end process; -- decode address decode_addr1 : entity work.complete_address_decoder generic map(width=>4) port map (addr_in=>addr(3 downto 0), addr_decoded=>addr_decoded); -- Writes to registers process(cpu_data_in,wr_en,addr_decoded, addr, pending_divisor_reg) begin fifo_tx_write <= '0'; pending_divisor_next <= pending_divisor_reg; if (wr_en = '1') then if (addr_decoded(0) = '1') then fifo_tx_write <= '1'; end if; if (addr_decoded(4) = '1') then pending_divisor_next <= cpu_data_in(7 downto 0); end if; end if; end process; -- Only change divisor once we are done transmitting process(divisor_reg,pending_divisor_reg,p2s_idle) begin divisor_next<=divisor_reg; if (not(pending_divisor_reg = divisor_reg) and p2s_idle='1') then divisor_next <= pending_divisor_reg; end if; end process; -- Count command packets rather than storing command position in fifo process(s2p_start,sio_command_reg,sio_command,sio_command_count_reg,sio_command_framing_error_reg,framing_error_clear) begin sio_command_next <= sio_command; sio_command_count_next <= sio_command_count_reg; sio_command_framing_error_next <= sio_command_framing_error_reg; sio_command_rising <= '0'; if (sio_command_reg='1') then -- not a command sio_command_count_next <= (others=>'0'); end if; if (s2p_start='1' and sio_command_reg='0') then sio_command_count_next <= std_logic_vector(unsigned(sio_command_count_reg)+1); end if; if (sio_command_reg='0' and sio_command='1') then -- rising edge sio_command_rising <= '1'; if (sio_command_count_reg /= "0000101") then sio_command_framing_error_next <= '1'; end if; end if; if (framing_error_clear='1') then sio_command_framing_error_next <= '0'; end if; end process; -- Read from registers process(en,addr_decoded, fifo_rx_data, fifo_tx_full, fifo_tx_empty, fifo_tx_count, fifo_rx_full, fifo_rx_empty, fifo_rx_count, s2p_framing_error_reg, sio_command_framing_error_reg, receive_divisor_reg) begin data_out_next <= X"0000"; fifo_rx_advance <= '0'; framing_error_clear <= '0'; if (en = '1') then if (addr_decoded(1) = '1') then data_out_next(9 downto 0) <= fifo_tx_full&fifo_tx_empty&fifo_tx_count; end if; if (addr_decoded(2) = '1') then if fifo_rx_empty = '0' then data_out_next(14 downto 0) <= fifo_rx_data; -- assumed to be already valid fifo_rx_advance <= '1'; -- data read, next byte please end if; end if; if (addr_decoded(3) = '1') then data_out_next(9 downto 0) <= fifo_rx_full&fifo_rx_empty&fifo_rx_count; end if; if (addr_decoded(4) = '1') then data_out_next(7 downto 0) <= receive_divisor_reg; end if; if (addr_decoded(5) = '1') then data_out_next(1 downto 0) <= sio_command_framing_error_reg&s2p_framing_error_reg; framing_error_clear <= '1'; end if; end if; end process; -- serial enable generation process(divisor_reg,pokey_enable,transmit_divisor_count_reg,sio_data_out_reg, sio_data_out_next, s2p_state_reg) begin transmit_divisor_count_next <= transmit_divisor_count_reg; transmit_enable <= '0'; if (pokey_enable='1') then transmit_divisor_count_next <= std_logic_vector(unsigned(transmit_divisor_count_reg)-to_unsigned(1,1)); if or_reduce(transmit_divisor_count_reg)='0' then transmit_divisor_count_next <= divisor_reg; transmit_enable <= '1'; end if; end if; end process; process(sio_clk_out, sio_clk_out_reg) begin -- 0->1 pokey transmit -- 1->0 receive... receive_enable <= '0'; if (sio_clk_out_reg='1' and sio_clk_out='0') then receive_enable <= '1'; end if; end process; process(pokey_enable,receive_enable,receive_detect,receive_divisor_reg,receive_divisor_count_reg) begin receive_divisor_next <= receive_divisor_reg; receive_divisor_count_next <= receive_divisor_count_reg; if (pokey_enable='1') then receive_divisor_count_next <= std_logic_vector(unsigned(receive_divisor_count_reg)+1); end if; if (receive_enable='1') then receive_divisor_count_next <= (others=>'0'); end if; if (receive_enable='1' and receive_detect='1') then receive_divisor_next <= receive_divisor_count_reg; end if; end process; -- Transmit fifo (7-0= data) --transmit_fifo : work.std_fifo -- generic_map ( -- DATA_WIDTH => 8, -- FIFO_DEPTH => 256 -- ) -- port_map ( -- CLK => CLK, -- RST => RESET, -- WriteEn => fifo_tx_write, -- DataIn => cpu_data_in(7 downto 0), -- ReadEn => fifo_tx_advance, -- DataOut => fifo_tx_data, -- Empty => fifo_tx_empty, -- Full => fifo_tx_full -- ); transmit_fifo : work.fifo_transmit PORT MAP ( clock => clk, data => cpu_data_in(7 downto 0), rdreq => fifo_tx_advance, wrreq => fifo_tx_write, empty => fifo_tx_empty, full => fifo_tx_full, q => fifo_tx_data, usedw => fifo_tx_count ); -- parallel to serial converter process(p2s_state_reg, p2s_transmit_reg,p2s_shift_reg,fifo_tx_data,fifo_tx_empty,transmit_enable) begin p2s_state_next <= p2s_state_reg; p2s_transmit_next <= p2s_transmit_reg; p2s_shift_next <= p2s_shift_reg; fifo_tx_advance <= '0'; p2s_idle <= '0'; if transmit_enable='1' then p2s_shift_next <= '1'&p2s_shift_reg(7 downto 1); case p2s_state_reg is when P2S_STATE_WAIT => p2s_idle <= fifo_tx_empty; if fifo_tx_empty='0' then p2s_state_next <= P2S_STATE_SHIFT_0; fifo_tx_advance <= '1'; p2s_shift_next <= fifo_tx_data; -- already valid (depends on fifo type: todo) p2s_transmit_next <= '0'; --start end if; when P2S_STATE_SHIFT_0 => p2s_transmit_next <= p2s_shift_reg(0); p2s_state_next <= P2S_STATE_SHIFT_1; when P2S_STATE_SHIFT_1 => p2s_transmit_next <= p2s_shift_reg(0); p2s_state_next <= P2S_STATE_SHIFT_2; when P2S_STATE_SHIFT_2 => p2s_transmit_next <= p2s_shift_reg(0); p2s_state_next <= P2S_STATE_SHIFT_3; when P2S_STATE_SHIFT_3 => p2s_transmit_next <= p2s_shift_reg(0); p2s_state_next <= P2S_STATE_SHIFT_4; when P2S_STATE_SHIFT_4 => p2s_transmit_next <= p2s_shift_reg(0); p2s_state_next <= P2S_STATE_SHIFT_5; when P2S_STATE_SHIFT_5 => p2s_transmit_next <= p2s_shift_reg(0); p2s_state_next <= P2S_STATE_SHIFT_6; when P2S_STATE_SHIFT_6 => p2s_transmit_next <= p2s_shift_reg(0); p2s_state_next <= P2S_STATE_SHIFT_7; when P2S_STATE_SHIFT_7 => p2s_transmit_next <= p2s_shift_reg(0); p2s_state_next <= P2S_STATE_STOP; when P2S_STATE_STOP => p2s_transmit_next <= '1'; --stop p2s_state_next <= P2S_STATE_WAIT; -- send stop bit, check fifo when others => p2s_transmit_next <= '1'; --stop p2s_state_next <= P2S_STATE_WAIT; -- send stop bit, check fifo end case; end if; end process; -- Receive fifo (8=command, 7-0=data) --receive_fifo : work.std_fifo -- generic_map ( -- DATA_WIDTH => 9, -- FIFO_DEPTH => 256 -- ) -- port_map ( -- CLK => CLK, -- RST => RESET, -- WriteEn => s2p_write, -- DataIn => sio_command&s2p_data, -- ReadEn => fifo_rx_advance, -- DataOut => fifo_rx_data, -- Empty => fifo_rx_empty, -- Full => fifo_rx_full -- ); sio_data_out_next <= sio_data_out; receive_write <= s2p_write or sio_command_rising; receive_fifo : entity work.fifo_receive PORT MAP ( clock => clk, data => sio_command_count_reg&sio_data_out_reg&s2p_shift_reg(6 downto 0), rdreq => fifo_rx_advance, wrreq => receive_write, empty => fifo_rx_empty, full => fifo_rx_full, q => fifo_rx_data, usedw => fifo_rx_count ); -- serial to parallel converter -- 0 = start bit (space) -- 8 data bits -- 1 = stop bit (mask) -- Note:sio_data_out_reg = computer out, zpu in... process(s2p_state_reg,s2p_shift_reg,receive_enable,sio_data_out_reg,framing_error_clear, s2p_framing_error_reg) begin s2p_state_next <= s2p_state_reg; s2p_shift_next <= s2p_shift_reg; s2p_framing_error_next <= s2p_framing_error_reg; s2p_start <= '0'; s2p_write <= '0'; receive_detect <= '0'; if (framing_error_clear='1') then s2p_framing_error_next <= '0'; end if; if (receive_enable='1') then s2p_shift_next <= sio_data_out_reg&s2p_shift_reg(6 downto 1); case s2p_state_reg is when S2P_STATE_WAIT => if (sio_data_out_reg='0') then -- start bit -- TODO - sync clock?? s2p_state_next <= S2P_STATE_SHIFT_0; s2p_start <= '1'; end if; when S2P_STATE_SHIFT_0 => receive_detect <= '1'; s2p_state_next <= S2P_STATE_SHIFT_1; when S2P_STATE_SHIFT_1 => receive_detect <= '1'; s2p_state_next <= S2P_STATE_SHIFT_2; when S2P_STATE_SHIFT_2 => receive_detect <= '1'; s2p_state_next <= S2P_STATE_SHIFT_3; when S2P_STATE_SHIFT_3 => s2p_state_next <= S2P_STATE_SHIFT_4; when S2P_STATE_SHIFT_4 => s2p_state_next <= S2P_STATE_SHIFT_5; when S2P_STATE_SHIFT_5 => s2p_state_next <= S2P_STATE_SHIFT_6; when S2P_STATE_SHIFT_6 => s2p_state_next <= S2P_STATE_SHIFT_7; when S2P_STATE_SHIFT_7 => s2p_write <= '1'; s2p_state_next <= S2P_STATE_STOP; when S2P_STATE_STOP => s2p_framing_error_next <= s2p_framing_error_reg or not(sio_data_out_reg); s2p_state_next <= S2P_STATE_WAIT; when others => s2p_state_next <= S2P_STATE_WAIT; end case; end if; end process; -- output sio_data_in <= p2s_transmit_reg; data_out <= data_out_reg; end vhdl;