--------------------------------------------------------------------------------
-- SPI Interface for c't-Lab FPGA und c't-Lab UNIC
-- AVR SPI-Mode CPOL/CPHA 1.1 (SCK normally high)
-- by C. Meyer cm@ctmagazin.de
-- Creative Commons by-nc-sa, use by your own risk
--------------------------------------------------------------------------------

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity SPI_inout is
port (
    SYSCLK: in STD_LOGIC;
    SCK   : in STD_LOGIC;
    MOSI  : in STD_LOGIC;
	 DS_N  : in STD_LOGIC;
	 RS_N  : in STD_LOGIC;
    ADDR  : in STD_LOGIC_VECTOR(3 downto 0);
	 STR0  : out STD_LOGIC;
	 STR1  : out STD_LOGIC;
	 STR2  : out STD_LOGIC;
	 STR3  : out STD_LOGIC;
	 MISO  : out STD_LOGIC;
    D0   : in STD_LOGIC_VECTOR(31 downto 0);
    D1   : in STD_LOGIC_VECTOR(31 downto 0);
    D2   : in STD_LOGIC_VECTOR(31 downto 0);
    D3   : in STD_LOGIC_VECTOR(31 downto 0);
	 
--    D4   : in STD_LOGIC_VECTOR(15 downto 0);
--    D5   : in STD_LOGIC_VECTOR(15 downto 0);
--    D6   : in STD_LOGIC_VECTOR(15 downto 0);
--    D7   : in STD_LOGIC_VECTOR(15 downto 0);
	 
    Q0   : out STD_LOGIC_VECTOR(31 downto 0);
    Q1   : out STD_LOGIC_VECTOR(31 downto 0);
    Q2   : out STD_LOGIC_VECTOR(31 downto 0);
    Q3   : out STD_LOGIC_VECTOR(31 downto 0)
	 
--    Q8   : out STD_LOGIC_VECTOR(15 downto 0);
--    Q9   : out STD_LOGIC_VECTOR(15 downto 0);
--    Q10  : out STD_LOGIC_VECTOR(15 downto 0);
--    Q11  : out STD_LOGIC_VECTOR(15 downto 0);
--    Q12  : out STD_LOGIC_VECTOR(15 downto 0);
--    Q13  : out STD_LOGIC_VECTOR(15 downto 0);
--    Q14  : out STD_LOGIC_VECTOR(15 downto 0);
--    Q15  : out STD_LOGIC_VECTOR(15 downto 0)
    );
end SPI_inout;

architecture behave of SPI_inout is

signal q_int, q_sro, q_sro_tmp, q_sri : std_logic_vector(31 downto 0);
signal r_int, r_sri : std_logic_vector(7 downto 0);
signal rs_tick, rs_tmp, ds_tick, ds_tmp, miso_int : std_logic;

begin

process(SCK, DS_N)
-- Schieberegister für Eingangsdaten
begin
	if rising_edge(SCK) then
		if (DS_N='0') then 
			q_sri <= ( q_sri(30 downto 0) & MOSI );
		end if;
	end if;
end process;

process(SCK, RS_N)
-- Schieberegister für Select-Adresse
begin
	if rising_edge(SCK) then
		if (RS_N='0') then 
			r_sri <= ( r_sri(6 downto 0) & MOSI );
		end if;
	end if;
end process;

process(RS_N)
-- Eingangsregister Adresse buffered
begin
	if rising_edge(RS_N) then
		r_int <= r_sri;
	end if;
end process;

process(DS_N)
-- Eingangsregister Daten buffered
begin
	if rising_edge(DS_N) then
		q_int <= q_sri;
	end if;
end process;

process(SCK, DS_N)
-- Schieberegister für Ausgangsdaten
begin
   if DS_N = '1' then	-- Latch für Ausgangsdaten an MISO
		q_sro_tmp <= q_sro;
	elsif rising_edge(SCK) then
		q_sro_tmp <= ( q_sro_tmp(30 downto 0) & '0' );	-- MSB first
	end if;
end process;

miso_int <= q_sro_tmp(31);
MISO <= miso_int when DS_N = '0' else 'Z';

process(SYSCLK)
-- SPI-Registerwerte zwischenspeichern
begin
	if rising_edge(SYSCLK) then
		if ds_tick = '1' then	-- Daten 
			STR0 <= '0';
			STR1 <= '0';
			STR2 <= '0';
			STR3 <= '0';
			if r_int(7 downto 4) = ADDR then
				case r_int(3 downto 0) is 
					when x"0" =>
						STR0 <= '1';
						Q0 <= q_int;
					when x"1" =>
						STR1 <= '1';
						Q1 <= q_int;
					when x"2" =>
						STR2 <= '1';
						Q2 <= q_int;
					when x"3" =>
						STR3 <= '1';
						Q3 <= q_int;
						
--					when x"9" =>
--						Q9 <= q_int(15 downto 0);
--					when x"A" =>
--						Q10 <= q_int(15 downto 0);
--					when x"B" =>
--						Q11 <= q_int(15 downto 0);
--					when x"C" =>
--						Q12 <= q_int(15 downto 0);
--					when x"D" =>
--						Q13 <= q_int(15 downto 0);
--					when x"E" =>
--						Q14 <= q_int(15 downto 0);
--					when x"F" =>
--						Q15 <= q_int(15 downto 0);
					when others =>
				end case;
			end if;
		end if;
	end if;
end process;

process(SYSCLK)
-- Synchrone Ticks erzeugen bei SlaveSelect-Ende (steigende Flanke)
begin
	if rising_edge(SYSCLK) then
		ds_tmp <= DS_N;
		ds_tick <= (not ds_tmp) and DS_N;
		rs_tmp <= RS_N;
		rs_tick <= (not rs_tmp) and RS_N;
		if rs_tick = '1' then	-- Register-Select geschrieben?
-- Eingangsmultiplexer, aktualisiert mit Schreiben von RS_N
			if r_int(7 downto 4) = ADDR then
				case r_int(3 downto 0) is -- aktuelle Registeradresse
					when x"0" =>
						q_sro <= D0;
					when x"1" =>
						q_sro <= D1;
					when x"2" =>
						q_sro <= D2;
					when x"3" =>
						q_sro <= D3;
--					when x"4" =>
--						q_sro <= x"0000" & D4;	-- bei 16-Bit-Werten MSBs
--					when x"5" =>
--						q_sro <= x"0000" & D5;
--					when x"6" =>
--						q_sro <= x"0000" & D6;
--					when x"7" =>
--						q_sro <= x"0000" & D7;
					when others =>
						q_sro <= (others => 'X');
				end case;
			end if;
		end if;
	end if;
end process;


end behave;

