-- ----------------------------------------------------------------------------- -- Copyright (c) 2013 Benjamin Krill -- -- Permission is hereby granted, free of charge, to any person obtaining a copy -- of this software and associated documentation files (the "Software"), to deal -- in the Software without restriction, including without limitation the rights -- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -- copies of the Software, and to permit persons to whom the Software is -- furnished to do so, subject to the following conditions: -- -- The above copyright notice and this permission notice shall be included in -- all copies or substantial portions of the Software. -- -- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -- THE SOFTWARE. -- ----------------------------------------------------------------------------- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; library UNISIM; use UNISIM.Vcomponents.all; entity serdes_1_to_5_diff_data is generic ( DIFF_TERM : boolean := true; SIM_TAP_DELAY : integer := 49; BITSLIP_ENABLE : boolean := false ); port ( rst : in std_logic; -- Reset line gclk : in std_logic; -- Global clock rxioclk_i : in std_logic; -- IO Clock network datain_p : in std_logic; -- Input from LVDS receiver pin datain_n : in std_logic; -- Input from LVDS receiver pin use_phase_detector_i : in std_logic; -- '1' enables the phase detector logic rxserdesstrobe_i : in std_logic; -- Parallel data capture strobe bitslip_i : in std_logic; -- Bitslip control line data_o : out std_logic_vector(4 downto 0) -- Output data ); end serdes_1_to_5_diff_data; architecture serdes_1_to_5_diff_data of serdes_1_to_5_diff_data is type sm_cal_t is (IDLE, CALIBRATE, IODELAY_RST, IODELAY_WAIT, IODELAY_WAIT_OCCASIONAL_ENABLE, CAL_SLAVE, WAIT_COMMAND_ACCEPTED, IODELAY_WAIT_ALL); signal sm_cal : sm_cal_t; signal cal_cnt : unsigned(8 downto 0); signal cal_enable : std_logic; signal data_in : std_logic; signal data_cal_master : std_logic; signal data_cal_sint : std_logic; signal data_busy : std_logic; signal data_busy_q : std_logic; signal data_rst : std_logic; signal data_flag : std_logic; signal data_ce : std_logic; signal data_inc : std_logic; signal data_ce_int : std_logic; signal data_valid : std_logic; signal data_valid_q : std_logic; signal data_incdec : std_logic; signal data_incdec_q : std_logic; signal pdcounter : unsigned(4 downto 0); signal ddly_s : std_logic; signal ddly_m : std_logic; signal pd_edge : std_logic; signal cascade : std_logic; begin -- ---------------------------------------------------------- -- IDELAY Calibration FSM -- ---------------------------------------------------------- process (gclk, rst) begin if rst = '1' then sm_cal <= IDLE; cal_cnt <= (others => '0'); cal_enable <= '0'; data_cal_master <= '0'; data_cal_sint <= '0'; data_rst <= '0'; elsif rising_edge(gclk) then cal_cnt <= cal_cnt + 1; if cal_cnt(8) = '1' then cal_cnt <= (others => '0'); end if; cal_enable <= cal_enable; if cal_cnt(5) = '1' then cal_enable <= '1'; end if; case sm_cal is when IDLE => if cal_enable = '1' then data_cal_master <= '0'; data_cal_sint <= '0'; data_rst <= '0'; if data_busy_q = '0' then sm_cal <= CALIBRATE; end if; end if; when CALIBRATE => data_cal_master <= '1'; data_cal_sint <= '1'; if data_busy_q = '1' then sm_cal <= IODELAY_RST; end if; when IODELAY_RST => data_cal_master <= '0'; data_cal_sint <= '0'; if data_busy_q = '0' then data_rst <= '1'; sm_cal <= IODELAY_WAIT; end if; when IODELAY_WAIT => data_rst <= '0'; if data_busy_q = '0' then sm_cal <= IODELAY_WAIT_OCCASIONAL_ENABLE; end if; when IODELAY_WAIT_OCCASIONAL_ENABLE => if cal_cnt(8) = '1' then sm_cal <= CAL_SLAVE; end if; when CAL_SLAVE => if data_busy_q = '0' then data_cal_sint <= '1'; sm_cal <= WAIT_COMMAND_ACCEPTED; end if; when WAIT_COMMAND_ACCEPTED => data_cal_sint <= '0'; if data_busy_q = '1' then sm_cal <= IODELAY_WAIT_ALL; end if; when IODELAY_WAIT_ALL => data_cal_sint <= '0'; if data_busy_q = '0' then sm_cal <= IODELAY_WAIT_OCCASIONAL_ENABLE; end if; end case; end if; end process; -- Per-bit phase detection state machine process (gclk, rst) begin if rst = '1' then data_busy_q <= '0'; data_flag <= '0'; pdcounter <= "10000"; elsif rising_edge(gclk) then data_busy_q <= data_busy; if use_phase_detector_i = '1' then data_incdec_q <= data_incdec; data_valid_q <= data_valid; if data_ce_int = '1' then data_ce <= '1'; else data_ce <= '0'; end if; if sm_cal = IODELAY_WAIT_ALL then data_flag <= '0'; elsif sm_cal /= IODELAY_WAIT_OCCASIONAL_ENABLE or data_busy_q = '1' then -- Reset filter if state machine issues a cal command or unit is busy pdcounter <= "10000"; data_ce_int <= '0'; elsif pdcounter = "11111" and data_flag = '0' then -- Filter has reached positive max - increment the tap count data_ce_int <= '1'; data_inc <= '1'; data_flag <= '1'; pdcounter <= "10000"; elsif pdcounter = "00000" and data_flag = '0' then -- Filter has reached negative max - decrement the tap count data_ce_int <= '1'; data_inc <= '0'; data_flag <= '1'; pdcounter <= "10000"; elsif data_valid_q = '1' then -- increment filter data_ce_int <= '0'; if data_incdec_q = '1' and pdcounter /= "11111" then pdcounter <= pdcounter + "00001"; elsif data_incdec_q = '0' and pdcounter /= "00000" then -- decrement filter pdcounter <= pdcounter + "11111"; end if; else data_ce_int <= '0'; end if; else data_ce <= '0'; --data_inc <= '0'; end if; end if; end process; data_in_0: IBUFDS generic map ( DIFF_TERM => DIFF_TERM ) port map ( I => datain_p, IB => datain_n, O => data_in ); -- ---------------------------------------------------------- -- Master IDELAY -- ---------------------------------------------------------- iodelay_m: IODELAY2 generic map ( DATA_RATE => "SDR", IDELAY_VALUE => 0, IDELAY2_VALUE => 0, IDELAY_MODE => "NORMAL", ODELAY_VALUE => 0, IDELAY_TYPE => "DIFF_PHASE_DETECTOR", COUNTER_WRAPAROUND => "STAY_AT_LIMIT", --("WRAPAROUND"), DELAY_SRC => "IDATAIN", SERDES_MODE => "MASTER", SIM_TAPDELAY_VALUE => SIM_TAP_DELAY) port map ( IDATAIN => data_in, -- data from IBUFDS TOUT => open, -- tri-state signal to IOB DOUT => open, -- output data to IOB T => '1', -- tri-state control from OLOGIC/OSERDES2 ODATAIN => '0', -- data from OLOGIC/OSERDES2 DATAOUT => ddly_m, -- Output data 1 to ILOGIC/ISERDES2 DATAOUT2 => open, -- Output data 2 to ILOGIC/ISERDES2 IOCLK0 => rxioclk_i, -- High speed clock for calibration IOCLK1 => '0', -- High speed clock for calibration CLK => gclk, -- Fabric clock (GCLK) for control signals CAL => data_cal_master, -- Calibrate control signal INC => data_inc, -- Increment counter CE => data_ce, -- Clock Enable RST => data_rst, -- Reset delay line BUSY => open -- output signal indicating sync circuit has finished / calibration has finished ); -- ---------------------------------------------------------- -- Slave IDELAY -- ---------------------------------------------------------- iodelay_s: IODELAY2 generic map ( DATA_RATE => "SDR", IDELAY_VALUE => 0, IDELAY2_VALUE => 0, IDELAY_MODE => "NORMAL", ODELAY_VALUE => 0, IDELAY_TYPE => "DIFF_PHASE_DETECTOR", COUNTER_WRAPAROUND => "WRAPAROUND", DELAY_SRC => "IDATAIN", SERDES_MODE => "SLAVE", SIM_TAPDELAY_VALUE => SIM_TAP_DELAY) port map ( IDATAIN => data_in, -- data from IBUFDS TOUT => open, -- tri-state signal to IOB DOUT => open, -- output data to IOB T => '1', -- tri-state control from OLOGIC/OSERDES2 ODATAIN => '0', -- data from OLOGIC/OSERDES2 DATAOUT => ddly_s, -- Slave output data to ILOGIC/ISERDES2 DATAOUT2 => open, -- IOCLK0 => rxioclk_i, -- High speed IO clock for calibration IOCLK1 => '0', CLK => gclk, -- Fabric clock (GCLK) for control signals CAL => data_cal_sint, -- Calibrate control signal INC => data_inc, -- Increment counter CE => data_ce, -- Clock Enable RST => data_rst, -- Reset delay line BUSY => data_busy -- output signal indicating sync circuit has finished / calibration has finished ); -- ---------------------------------------------------------- -- Master ISERDES -- ---------------------------------------------------------- iserdes_m: ISERDES2 generic map ( DATA_WIDTH => 5, DATA_RATE => "SDR", BITSLIP_ENABLE => BITSLIP_ENABLE, SERDES_MODE => "MASTER", INTERFACE_TYPE => "RETIMED") port map ( D => ddly_m, CE0 => '1', CLK0 => rxioclk_i, CLK1 => '0', IOCE => rxserdesstrobe_i, RST => rst, CLKDIV => gclk, SHIFTIN => pd_edge, BITSLIP => bitslip_i, FABRICOUT => open, Q4 => data_o(4), Q3 => data_o(3), Q2 => data_o(2), Q1 => data_o(1), DFB => open, CFB0 => open, CFB1 => open, VALID => data_valid, INCDEC => data_incdec, SHIFTOUT => cascade ); -- ---------------------------------------------------------- -- Slave ISERDES -- ---------------------------------------------------------- iserdes_s: ISERDES2 generic map ( DATA_WIDTH => 5, DATA_RATE => "SDR", BITSLIP_ENABLE => BITSLIP_ENABLE, SERDES_MODE => "SLAVE", INTERFACE_TYPE => "RETIMED") port map ( D => ddly_s, CE0 => '1', CLK0 => rxioclk_i, CLK1 => '0', IOCE => rxserdesstrobe_i, RST => rst, CLKDIV => gclk, SHIFTIN => cascade, BITSLIP => bitslip_i, FABRICOUT => open, Q4 => data_o(0), Q3 => open, Q2 => open, Q1 => open, DFB => open, CFB0 => open, CFB1 => open, VALID => open, INCDEC => open, SHIFTOUT => pd_edge ); end serdes_1_to_5_diff_data;