--------------------------------------------------------------------------------
-- Company: Klaus Philipp
-- Engineer: Klaus Philipp
--
-- Create Date: 23.12.2007
-- Design Name: CT-Lab Frequency Counter
-- Component Name: SPI Interface
-- Target Device: XC3S400 144 TQFP
-- Tool versions: ISE 8.2
-- Description:
--    spi receiver
-- Dependencies:
--    none
-- Revision:
--    0.1
-- Additional Comments:
--    <Additional_comments>
--------------------------------------------------------------------------------
			
--LIBRARY IEEE, converters;
LIBRARY IEEE;
USE IEEE.std_logic_1164.ALL;
USE IEEE.std_logic_arith.ALL;
USE IEEE.std_logic_unsigned.ALL;
--use converters.converters.all;

ENTITY receive_spi IS

  PORT(       clk : IN std_logic ;
            reset : IN std_logic ;
 spi_data_reg_out : out std_logic_vector (15 downto 0);
   spi_data_valid : out std_logic;
	         sdata : in std_logic;
	         fsync : in std_logic;
	          sclk : in std_logic);
				 
END receive_spi;

ARCHITECTURE receive_spi OF receive_spi IS

CONSTANT sys_clk_freq: integer := 50000000;
CONSTANT sclk_freq: integer := 1000000;

CONSTANT sclk_div: integer := sys_clk_freq/sclk_freq-1;

TYPE state_type IS (idle, d0, d1, d2, d3, d4, d5, d6, d7, d8, d9, d10, d11, d12, d13, d14, d15);
SIGNAL next_state: state_type;

SIGNAL spi_reg: std_logic_vector (15 DOWNTO 0);
SIGNAL spi_shift_enable : std_logic;
SIGNAL sync_sdata : std_logic;
signal sync_sclk : std_logic;
signal sync_fsync : std_logic;
SIGNAL sclk1 : std_logic;
SIGNAL sclk2 : std_logic;
signal spi_data_v: std_logic;
signal spi_data_v1: std_logic;
--signal spi_data_clk : std_logic;

BEGIN

spi_sync: PROCESS (clk, reset, sdata, sclk, fsync)

begin
  IF reset = '1'
  THEN
     sync_sdata <= '0';
	  sync_sclk <= '0';
	  sync_fsync <= '1';
  ELSIF rising_edge (clk)
     THEN
	     sync_sdata <= sdata;
		  sync_sclk <= sclk;
		  sync_fsync <= fsync;
  END IF;
END PROCESS spi_sync;

spi_rx: PROCESS (clk, reset, sync_sclk, sync_fsync, sync_sdata, spi_shift_enable)

begin
   if reset = '1'
	THEN
	  next_state <= idle;
	  spi_reg <= "0000000000000000";
	  spi_data_reg_out <= "0000000000000000";
	  spi_data_v <= '0';
	elsif rising_edge (clk)
	  then
	    spi_data_v <= '0';
	    if spi_shift_enable = '1'
		 then
		 
		   case next_state IS
		 
		     WHEN idle =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
			      spi_reg(15) <= sync_sdata;
				   next_state <= d15;
				 END IF;
			  WHEN d15 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
			      spi_reg(14) <= sync_sdata;
               next_state <= d14;
 				 END IF;
			  WHEN d14 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(13) <= sync_sdata;
				   next_state <= d13;
				 END IF;
			  WHEN d13 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(12) <= sync_sdata;
				   next_state <= d12;
				 END IF;
			  WHEN d12 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(11) <= sync_sdata;
				   next_state <= d11;
				 END IF;
			  WHEN d11 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(10) <= sync_sdata;
				   next_state <= d10;
				 END IF;
			  WHEN d10 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(9) <= sync_sdata;
				   next_state <= d9;
				 END IF;
			  WHEN d9 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(8) <= sync_sdata;
				   next_state <= d8;
				 END IF;
			  WHEN d8 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(7) <= sync_sdata;
				   next_state <= d7;
				 END IF;
			  WHEN d7 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(6) <= sync_sdata;
				   next_state <= d6;
				 END IF;
			  WHEN d6 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(5) <= sync_sdata;
				   next_state <= d5;
				 END IF;
			  WHEN d5 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(4) <= sync_sdata;
				   next_state <= d4;
				 END IF;
			  WHEN d4 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(3) <= sync_sdata;
				   next_state <= d3;
				 END IF;
			  WHEN d3 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(2) <= sync_sdata;
				   next_state <= d2;
				 END IF;
			  WHEN d2 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(1) <= sync_sdata;
				   next_state <= d1;
				 END IF;
			  WHEN d1 =>
			    IF sync_fsync = '1'
				 THEN
				   next_state <= idle;
				 ELSE
					spi_reg(0) <= sync_sdata;
				   next_state <= d0;
				 END IF;
			  WHEN d0 =>
			    IF sync_fsync = '1'
				 THEN
				   spi_data_reg_out <= spi_reg;
					spi_data_v <= '1';
				   next_state <= idle;
				 ELSE
				   spi_data_reg_out <= spi_reg;
					spi_data_v <= '1';
					spi_reg(15) <= sync_sdata;
				   next_state <= d15;
			    END IF;
			END CASE;
		END IF;
	END IF;


end PROCESS spi_rx;

-- spi_shift_enable at the falling edge of sclk
-- enables the spi state machine to step to the next state

spi_shift_enable <= sclk2 and not sclk1;

spi_shift: PROCESS (clk, reset, sync_sclk)

begin

  IF reset = '1'
  THEN
	  sclk1 <= '0';
	  sclk2 <= '0';
  ELSIF rising_edge (clk)
  THEN
	  sclk1 <= sync_sclk;
	  sclk2 <= sclk1;
   END IF;
 
END PROCESS spi_shift;

spi_data_valid <= spi_data_v1 and not spi_data_v;

gen_spi_data_valid: process (clk, reset, spi_data_v)

begin

  if reset = '1'
  then
	  spi_data_v1 <= '0';
  elsif rising_edge (clk)
  then
     spi_data_v1 <= spi_data_v;
  end if;
end process gen_spi_data_valid;

END ARCHITECTURE receive_spi ; -- of receive_spi
