Project

General

Profile

---------------------------------------------------------------------------
-- (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;


(2-2/9)