----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    15:08:55 04/04/2011 
-- Design Name: 
-- Module Name:    deser - deser_a 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
library UNISIM;
use UNISIM.VComponents.all;

library WORK;
use WORK.common.ALL;



entity deser is
   Port (
		i 		: in t_deser_i;
		o 		: out t_deser_o;
		reset		: in std_logic;
		clk40 	: in std_logic
	);
end deser;




architecture deser_a of deser is

	-- PLL
	signal CLKFBIN : std_logic;
	signal CLKFBOUT : std_logic;
	signal pll_locked : std_logic;
	signal clk800i : std_logic;
	signal clk800 : std_logic;
	signal clk200 : std_logic;
	signal clk200i : std_logic;
	signal bufpll_locked : std_logic;
	signal SERDESSTROBE : std_logic;
	signal reset_pll : std_logic;

--	signal clk20s :std_logic;
--	signal clk100s:std_logic;
--	signal outstatus : integer range 5 downto 0 := 0;



	-- After the buffer
	signal data_in     : std_logic;
	signal data_delayed_m     : std_logic;
	signal data_delayed_s     : std_logic;
	signal data_dly_fabric     : std_logic;
	signal DLY_BUSY : std_logic;
	signal DLY_BUSY_M : std_logic;
	signal DLY_BUSY_S : std_logic;
	signal DLY_CAL : std_logic;
	signal DLY_RST : std_logic;
	signal DLY_CE : std_logic;
	signal DLY_INC : std_logic;
	signal PD_INCDEC : std_logic;
	signal PD_VALID	: std_logic;
	signal PD_EDGE	: std_logic;
	signal ISD_CASCADE	: std_logic;
	
	signal reset_isd : std_logic;
	signal iserdes_q                : std_logic_vector(3 downto 0);

	signal word_i : std_logic_vector(19 downto 0);
	signal lock_i : std_logic;
begin


reset_pll <= reset;
reset_isd <= reset_pll or not bufpll_locked;


   PLL_BASE_inst : PLL_BASE
   generic map (
      BANDWIDTH => "OPTIMIZED",             -- "HIGH", "LOW" or "OPTIMIZED" 
      CLKFBOUT_MULT => 20,                   -- Multiply value for all CLKOUT clock outputs (1-64)
      CLKFBOUT_PHASE => 0.0,                -- Phase offset in degrees of the clock feedback output
                                            -- (0.0-360.0).
      CLKIN_PERIOD => 25.000,                  -- Input clock period in ns to ps resolution (i.e. 33.333 is 30
                                            -- MHz).
      -- CLKOUT0_DIVIDE - CLKOUT5_DIVIDE: Divide amount for CLKOUT# clock output (1-128)
      CLKOUT0_DIVIDE => 1,
      CLKOUT1_DIVIDE => 4,
      CLKOUT2_DIVIDE => 1,
      CLKOUT3_DIVIDE => 1,
      CLKOUT4_DIVIDE => 1,
      CLKOUT5_DIVIDE => 1,
      -- CLKOUT0_DUTY_CYCLE - CLKOUT5_DUTY_CYCLE: Duty cycle for CLKOUT# clock output (0.01-0.99).
      CLKOUT0_DUTY_CYCLE => 0.5,
      CLKOUT1_DUTY_CYCLE => 0.5,
      CLKOUT2_DUTY_CYCLE => 0.5,
      CLKOUT3_DUTY_CYCLE => 0.5,
      CLKOUT4_DUTY_CYCLE => 0.5,
      CLKOUT5_DUTY_CYCLE => 0.5,
      -- CLKOUT0_PHASE - CLKOUT5_PHASE: Output phase relationship for CLKOUT# clock output (-360.0-360.0).
      CLKOUT0_PHASE => 0.0,
      CLKOUT1_PHASE => 0.0,
      CLKOUT2_PHASE => 0.0,
      CLKOUT3_PHASE => 0.0,
      CLKOUT4_PHASE => 0.0,
      CLKOUT5_PHASE => 0.0,
      CLK_FEEDBACK => "CLKFBOUT",           -- Clock source to drive CLKFBIN ("CLKFBOUT" or "CLKOUT0")
      COMPENSATION => "SYSTEM_SYNCHRONOUS", -- "SYSTEM_SYNCHRONOUS", "SOURCE_SYNCHRONOUS", "EXTERNAL" 
      DIVCLK_DIVIDE => 1,                   -- Division value for all output clocks (1-52)
      REF_JITTER => 0.1,                    -- Reference Clock Jitter in UI (0.000-0.999).
      RESET_ON_LOSS_OF_LOCK => FALSE        -- Must be set to FALSE
   )
   port map (
      CLKFBOUT => CLKFBOUT, -- 1-bit output: PLL_BASE feedback output
      CLKOUT0 => clk800i,
      CLKOUT1 => clk200i,
      CLKOUT2 => open,
      CLKOUT3 => open,
      CLKOUT4 => open,
      CLKOUT5 => open,
      LOCKED => pll_locked,     -- 1-bit output: PLL_BASE lock status output
      CLKFBIN => CLKFBIN,   -- 1-bit input: Feedback clock input
      CLKIN => clk40,       -- 1-bit input: Clock input
      RST => reset_pll            -- 1-bit input: Reset input
   );

   BUFG_clk200 : BUFG
   port map (O => clk200, I => clk200i );

   BUFG_FB : BUFG
   port map (O => CLKFBIN, I => CLKFBOUT );


   BUFPLL_inst : BUFPLL
   generic map (
      DIVIDE => 4,         -- DIVCLK divider (1-8)
      ENABLE_SYNC => TRUE  -- Enable synchrnonization between PLL and GCLK (TRUE/FALSE)
   )
   port map (
      IOCLK => clk800,               -- 1-bit output: Output I/O clock
      LOCK => bufpll_locked,                 -- 1-bit output: Synchronized LOCK output
      SERDESSTROBE => SERDESSTROBE, -- 1-bit output: Output SERDES strobe (connect to ISERDES2/OSERDES2)
      GCLK => clk200,                 -- 1-bit input: BUFG clock input
      LOCKED => pll_locked,             -- 1-bit input: LOCKED input from PLL
      PLLIN => clk800i                -- 1-bit input: Clock input from PLL
   );


	-- INPUT BUFFER (IBUFDS)
	ibufds_inst : IBUFDS
		generic map (DIFF_TERM  => TRUE, IOSTANDARD => "LVDS_33")
		port map (I => i.serialin_p, IB => i.serialin_n, O => data_in);
	
	
   IODELAY2_inst_M : IODELAY2
   generic map (
      COUNTER_WRAPAROUND => "STAY_AT_LIMIT", -- "STAY_AT_LIMIT" or "WRAPAROUND" 
      DATA_RATE => "SDR",                 -- "SDR" or "DDR" 
      DELAY_SRC => "IDATAIN",                  -- "IO", "ODATAIN" or "IDATAIN" 
      IDELAY2_VALUE => 0,                 -- Delay value when IDELAY_MODE="PCI" (0-255)
      IDELAY_TYPE => "DIFF_PHASE_DETECTOR",           -- "FIXED", "DEFAULT", "VARIABLE_FROM_ZERO", "VARIABLE_FROM_HALF_MAX" 
                                          -- or "DIFF_PHASE_DETECTOR" 
      IDELAY_VALUE => 0,                  -- Amount of taps for fixed input delay (0-255)
      SERDES_MODE => "MASTER",              -- "NONE", "MASTER" or "SLAVE" 
      SIM_TAPDELAY_VALUE => 75            -- Per tap delay used for simulation in ps
   )
   port map (
      BUSY => DLY_BUSY_M,         -- 1-bit output: Busy output after CAL
      DATAOUT => data_delayed_m,   -- 1-bit output: Delayed data output to ISERDES/input register
      DATAOUT2 => data_dly_fabric, -- 1-bit output: Delayed data output to general FPGA fabric
      DOUT => open,         -- 1-bit output: Delayed data output
      TOUT => open,         -- 1-bit output: Delayed 3-state output
      CAL => DLY_CAL,           -- 1-bit input: Initiate calibration input
      CE => DLY_CE,             -- 1-bit input: Enable INC input
      CLK => clk200,           -- 1-bit input: Clock input
      IDATAIN => data_in,   -- 1-bit input: Data input (connect to top-level port or I/O buffer)
      INC => DLY_INC,           -- 1-bit input: Increment / decrement input
      IOCLK0 => clk800,     -- 1-bit input: Input from the I/O clock network
      IOCLK1 => '0',     -- 1-bit input: Input from the I/O clock network
      ODATAIN => '0',   -- 1-bit input: Output data input from output register or OSERDES2.
      RST => DLY_RST,           -- 1-bit input: Reset to zero or 1/2 of total delay period
      T => '1'                -- 1-bit input: 3-state input signal
   );

--DLY_BUSY_M <= '0';
--DLY_BUSY_S <= '0';
--data_dly_fabric <= data_in;
--data_delayed <= '0';


   IODELAY2_inst_S : IODELAY2
   generic map (
      COUNTER_WRAPAROUND => "STAY_AT_LIMIT", -- "STAY_AT_LIMIT" or "WRAPAROUND" 
      DATA_RATE => "SDR",                 -- "SDR" or "DDR" 
      DELAY_SRC => "IDATAIN",                  -- "IO", "ODATAIN" or "IDATAIN" 
      IDELAY_MODE => "NORMAL",            -- "NORMAL" or "PCI" 
      IDELAY_TYPE => "DIFF_PHASE_DETECTOR",           -- "FIXED", "DEFAULT", "VARIABLE_FROM_ZERO", "VARIABLE_FROM_HALF_MAX" 
                                          -- or "DIFF_PHASE_DETECTOR" 
      IDELAY_VALUE => 0,                  -- Amount of taps for fixed input delay (0-255)
      SERDES_MODE => "SLAVE",              -- "NONE", "MASTER" or "SLAVE" 
      SIM_TAPDELAY_VALUE => 75            -- Per tap delay used for simulation in ps
   )
   port map (
      BUSY => DLY_BUSY_S,         -- 1-bit output: Busy output after CAL
      DATAOUT => data_delayed_s,   -- 1-bit output: Delayed data output to ISERDES/input register
      DATAOUT2 => open, -- 1-bit output: Delayed data output to general FPGA fabric
      DOUT => open,         -- 1-bit output: Delayed data output
      TOUT => open,         -- 1-bit output: Delayed 3-state output
      CAL => DLY_CAL,           -- 1-bit input: Initiate calibration input
      CE => DLY_CE,             -- 1-bit input: Enable INC input
      CLK => clk200,           -- 1-bit input: Clock input
      IDATAIN => data_in,   -- 1-bit input: Data input (connect to top-level port or I/O buffer)
      INC => DLY_INC,           -- 1-bit input: Increment / decrement input
      IOCLK0 => clk800,     -- 1-bit input: Input from the I/O clock network
      IOCLK1 => '0',     -- 1-bit input: Input from the I/O clock network
      ODATAIN => '0',   -- 1-bit input: Output data input from output register or OSERDES2.
      RST => DLY_RST,           -- 1-bit input: Reset to zero or 1/2 of total delay period
      T => '0'                -- 1-bit input: 3-state input signal
   );

DLY_BUSY <= DLY_BUSY_M or DLY_BUSY_S;

--PD_INCDEC <= '0';
--PD_VALID <= '0';
--iserdes_q <= (others => '0');

   ISERDES2_inst_master : ISERDES2
   generic map (
      BITSLIP_ENABLE => FALSE,        -- Enable Bitslip Functionality (TRUE/FALSE)
      DATA_RATE => "SDR",             -- Data-rate ("SDR" or "DDR")
      DATA_WIDTH => 4,                -- Parallel data width selection (2-8)
      INTERFACE_TYPE => "RETIMED", -- "NETWORKING", "NETWORKING_PIPELINED" or "RETIMED" 
      SERDES_MODE => "MASTER"           -- "NONE", "MASTER" or "SLAVE" 
   )
   port map (
      CFB0 => open,           -- 1-bit output: Clock feed-through route output
      CFB1 => open,           -- 1-bit output: Clock feed-through route output
      DFB => open,             -- 1-bit output: Feed-through clock output
      FABRICOUT => open, -- 1-bit output: Unsynchrnonized data output
      INCDEC => PD_INCDEC,       -- 1-bit output: Phase detector output
      -- Q1 - Q4: 1-bit (each) output: Registered outputs to FPGA logic
      Q1 => iserdes_q(0),
      Q2 => iserdes_q(1),
      Q3 => iserdes_q(2),
      Q4 => iserdes_q(3),
      SHIFTOUT => ISD_CASCADE,   -- 1-bit output: Cascade output signal for master/slave I/O
      VALID => PD_VALID,         -- 1-bit output: Output status of the phase detector
      BITSLIP => '0',     -- 1-bit input: Bitslip enable input
      CE0 => '1',             -- 1-bit input: Clock enable input
      CLK0 => clk800,           -- 1-bit input: I/O clock network input
      CLK1 => '0',           -- 1-bit input: Secondary I/O clock network input
      CLKDIV => clk200,       -- 1-bit input: FPGA logic domain clock input
      D => data_delayed_m,                 -- 1-bit input: Input data
      IOCE => SERDESSTROBE,           -- 1-bit input: Data strobe input
      RST => reset_isd,             -- 1-bit input: Asynchronous reset input
      SHIFTIN => PD_EDGE      -- 1-bit input: Cascade input signal for master/slave I/O
   );

   ISERDES2_inst : ISERDES2
   generic map (
      BITSLIP_ENABLE => FALSE,        -- Enable Bitslip Functionality (TRUE/FALSE)
      DATA_RATE => "SDR",             -- Data-rate ("SDR" or "DDR")
      DATA_WIDTH => 4,                -- Parallel data width selection (2-8)
      INTERFACE_TYPE => "RETIMED", -- "NETWORKING", "NETWORKING_PIPELINED" or "RETIMED" 
      SERDES_MODE => "SLAVE"           -- "NONE", "MASTER" or "SLAVE" 
   )
   port map (
      CFB0 => open,           -- 1-bit output: Clock feed-through route output
      CFB1 => open,           -- 1-bit output: Clock feed-through route output
      DFB => open,             -- 1-bit output: Feed-through clock output
      FABRICOUT => open, -- 1-bit output: Unsynchrnonized data output
      INCDEC => open,       -- 1-bit output: Phase detector output
      -- Q1 - Q4: 1-bit (each) output: Registered outputs to FPGA logic
      Q1 => open,
      Q2 => open,
      Q3 => open,
      Q4 => open,
      SHIFTOUT => PD_EDGE,   -- 1-bit output: Cascade output signal for master/slave I/O
      VALID => OPEN,         -- 1-bit output: Output status of the phase detector
      BITSLIP => '0',     -- 1-bit input: Bitslip enable input
      CE0 => '1',             -- 1-bit input: Clock enable input
      CLK0 => clk800,           -- 1-bit input: I/O clock network input
      CLK1 => '0',           -- 1-bit input: Secondary I/O clock network input
      CLKDIV => clk200,       -- 1-bit input: FPGA logic domain clock input
      D => data_delayed_s,                 -- 1-bit input: Input data
      IOCE => SERDESSTROBE,           -- 1-bit input: Data strobe input
      RST => reset_isd,             -- 1-bit input: Asynchronous reset input
      SHIFTIN => ISD_CASCADE      -- 1-bit input: Cascade input signal for master/slave I/O
   );



-- State machine that controls the phase detector and issues idelay commands

process (clk200)
	constant integrator_bits : integer := 8;
	variable integrator: integer range 2**integrator_bits-1 downto 0 := 2**(integrator_bits-1);
	variable status : integer range 5 downto 0 := 0;
	variable timer : integer range 2**30-1 downto 0 := 2**30-1;
	variable substatus : integer range 7 downto 0 := 0;
	constant default_substatus : integer := 1;
begin
	if rising_edge(clk200) then
--		outstatus <= status;
		if bufpll_locked = '0' then status := 0;
		else
			case status is
				when 0 =>
					DLY_CAL <= '1';
					DLY_INC <= '0';
					DLY_CE  <= '0';
					DLY_RST <= '0';
					status  := 1;
					substatus := default_substatus;
					integrator := 2**(integrator_bits-1);
				when 1 =>
					if substatus > 0 then substatus := substatus -1;
					else DLY_CAL <= '0';
					end if;
					if DLY_CAL = '0' and DLY_BUSY = '0' then
						DLY_RST <= '1';
						status  := 2;
						substatus := default_substatus;
					end if;
				when 2 =>
					if substatus > 0 then substatus := substatus -1;
					else DLY_RST <= '0';
					end if;
					if DLY_RST = '0' and DLY_BUSY = '0' then status := 3;
					end if;
				when 3 =>
					if timer >0 then timer := timer -1;
					else 
						timer := 2**30-1;
						DLY_CAL <= '1';
						status := 5;
						substatus := default_substatus;
					end if;
					if integrator = 0 then
						integrator := 2**(integrator_bits-1);
						DLY_CE <= '1';
						DLY_INC <= '0';
						status := 4;
					elsif integrator = 2**integrator_bits-1 then
						integrator := 2**(integrator_bits-1);
						DLY_CE <= '1';
						DLY_INC <= '1';
						status := 4;
					elsif PD_VALID = '1' and PD_INCDEC = '0' then
						integrator := integrator - 1;
					elsif PD_VALID ='1' AND PD_INCDEC = '1' then
						integrator := integrator + 1;
					end if;
				when 4 =>
					DLY_CE <= '0';
					DLY_INC <= '0';
					if DLY_CE = '0' and DLY_BUSY = '0' then status := 3;
					end if;
				when others =>
					if substatus > 0 then substatus := substatus -1;
					else DLY_CAL <= '0';
					end if;
					if DLY_CAL = '0' and DLY_BUSY = '0' then
						status  := 3;
					end if;
			end case;
		end if;
	end if;
end process;


-- Build the 20-bit words for output

process (clk200)
	variable nextbit : integer range 19 downto 0;
	variable nextword : std_logic_vector(19 downto 0);
--	variable thirdword : std_logic_vector(2 downto 0);
	variable relock : integer range 100 downto 0;
	variable wrap : std_logic := '0';
	variable shift : std_logic := '0';
begin
	if rising_edge(clk200) then
		if wrap = '1' then
			wrap := '0';
			if nextword(19) = '1' or nextword(0) = '0' then -- mala cosa
				relock := 100;
				lock_i <= '0';
				shift := '1';
			
			else
				if relock > 0 then relock := relock - 1;
				else lock_i <= '1';
				end if;
			end if;
		end if;

		if nextbit < 16 then
			if shift = '1' then
				shift := '0';
				nextword(3 downto 1) := nextword (2 downto 0);
				nextword(0) := nextword(19);
				nextbit := nextbit + 1;
			end if;
		
			nextword(nextbit+3 downto nextbit) := iserdes_q(3 downto 0);
			nextbit := nextbit + 4;
		else
			nextword(19 downto nextbit) := iserdes_q(19-nextbit downto 0);
			
			word_i <= nextword;

			if nextbit > 16 then nextword(nextbit-17 downto 0) := iserdes_q(3 downto 20-nextbit);
			end if;
			nextbit := nextbit + 4 - 20;
			
			wrap := '1';
		end if;
		

		
	end if;
end process;

process (clk40)
begin
	if rising_edge(clk40) then
		o.word <= word_i;
		o.lock <= lock_i;
	end if;
end process;


o.bufpll_locked <= bufpll_locked;
o.pll_locked <= pll_locked;

end deser_a;

