-- 
-- phase_shifter.vhd
-- 
-- implements interface between w5300_modul.vhd 
-- and clock_generator_variable_PS_struct.vhd 
-- 
--
--
library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use ieee.STD_LOGIC_UNSIGNED.all;

library FACT_FAD_lib;
use FACT_FAD_lib.fad_definitions.all;


ENTITY phase_shifter IS

	PORT(
		CLK : IN std_logic;
		
		-- interface to: clock_generator_variable_PS_struct.vhd 
		PSCLK : OUT std_logic;
		PSEN : OUT std_logic := '0'; 
		PSINCDEC : OUT std_logic := '1'; -- default is 'incrementing'
		PSDONE : IN std_logic; -- will pulse once, if phase shifting was done.
		LOCKED : IN std_logic;
		
		
		-- interface to: w5300_modul.vhd
		shift_phase : IN std_logic;
		direction : IN std_logic; -- corresponds to 'PSINCDEC'

		-- status:
		shifting : OUT std_logic := '0';
		ready : OUT std_logic := '0';
		offset : OUT std_logic_vector (7 DOWNTO 0) := (OTHERS => '0');
		DCM_locked : OUT std_logic
		
	);
END phase_shifter;

-- usage:
-- w5300_modul will set 'direction' to desired direction and pulse 'shift_phase' once
-- to initiate a phase shifting process.
-- while phase shifting, 'shifting' will show '1' and further pulses will be discarded.
-- 'offset' shows the number of phase_shift steps that have been performed. 
-- ready is high, when DCM is LOCKED and not phase_shifting.
-- DCM_status is a copy, of the STATUS input.
-- DCM_locked is a copy of LOCKED
-- 
-- how it works internally:
-- PSCLK is connected to clk, always.
--
-- main FSM goes from init to ready, when LOCKED is high.
-- main FSM goes from ready to shifting, when shift_phase goes high.
-- 		when in shifting: 
--			PSINCDEC is set to 'direction'
--			PSEN is set high
--			shifting is set high
--			next state waiting-for-done is entered
--
--		when in waiting-for-done:
--			PSEN is set low
--			if PSDONE is found to be high.
--			shifting is set low and state ready is entered.
--
--	whenever LOCKED goes low FSM enters 'init' state
--		when in init state:
--			'ready' is set low

architecture first_behave of phase_shifter is

	type states is (INIT, READY_STATE, SHIFTING_STATE, WAITINGFORDONE); 
	signal state,next_state : states := INIT;
	
	signal local_direction : std_logic;

begin

-- concurrent statements:

DCM_locked <= LOCKED;
PSCLK <= CLK;

  -- MAIN FSM: go to next state if rising edge, or to INIT if LOCKED not high.
  FSM_Registers: process(CLK, LOCKED)
  begin
    if LOCKED = '0' then
      state <= INIT;
    elsif Rising_edge(CLK) then
      state <= next_state;
    end if;
  end process;

  -- MAIN FSM
  FSM_logic: process(state, PSDONE, LOCKED, shift_phase)
  begin
    next_state <= state;
    case state is
	
	-- INIT state: here the FSM is idling, when LOCKED is not HIGH.
    when INIT =>
        ready <= '0';
		shifting <= '0';
		PSEN <= '0';
		if (LOCKED = '1') then
			next_state <= READY_STATE;
		else 
			next_state <= INIT;
		end if;
	
	-- READY_STATE state: here FSM is waiting for the 'shift_phase' to go high
    when READY_STATE =>
        ready <= '1';
		shifting <= '0';
		PSEN <= '0';
        if (shift_phase = '1') then
			next_state <= SHIFTING_STATE;
			local_direction <= direction; -- direction is sampled, once 'shift_phase' goes high
        else
			next_state <= READY_STATE;
        end if;
	
	-- SHIFTING_STATE state: PSENC is set HIGH here and set low in the next state.
	when SHIFTING_STATE =>
		ready <= '1';
		shifting <= '1';
		PSEN <= '1';
		PSINCDEC <= local_direction; -- this is the value of 'direction', when 'shift_phase' went up.
		next_state <= WAITINGFORDONE;
	
	-- WAITINGFORDONE state: PSENC is set LOW, ensuring that is was high only one clock cycle.
	when WAITINGFORDONE =>
		ready <= '1';
		shifting <= '1';
		PSEN <= '0';
        if (PSDONE = '1') then
			     next_state <= READY_STATE;
        else
			     next_state <= WAITINGFORDONE;
        end if;
	
    end case;
  end process;



end first_behave;