--============================================================================== -- © Copyright CERN for the benefit of the HPTD interest group 2018. All rights not -- expressly granted are reserved. -- -- This file is part of tx_phase_aligner. -- -- tx_phase_aligner is free VHDL code: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- tx_phase_aligner is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with tx_phase_aligner. If not, see . --============================================================================== --! @file tx_pi_ctrl.vhd --============================================================================== --! Standard library library ieee; --! Standard packages use ieee.std_logic_1164.all; use ieee.numeric_std.all; --! Specific packages ------------------------------------------------------------------------------- -- -- -- CERN, EP-ESE-BE, HPTD -- -- ------------------------------------------------------------------------------- -- -- unit name: Tx Phase Interpolator Controller (tx_pi_ctrl) -- --! @brief Transmitter phase interpolator controller for GTH/GTY (Ultrascale/Ultrascale plus - UG576 and UG578) and GTH (7-series - UG476) --! - This block provides a simple interface for controlling the phase interpolator of Xilinx devices --! - The control can be made via DRP or via PORT (selectable through attribute g_DRP_NPORT_CTRL) --! g_DRP_NPORT_CONTROL = true uses DRP control --! g_DRP_NPORT_CONTROL = false uses port control (a unexpected behaviour was observed in a GTH Ultrascale plus when using port control, this is the reason why the default is DRP control) --! - The address of DRP control is different for Ultrascale/Ultrascale plus (0x009A) and 7-series devices (0x0095) --! Default is the address of Ultrascale as this is what was tested --! --! @author Eduardo Brandao de Souza Mendes - eduardo.brandao.de.souza.mendes@cern.ch --! @date 02\05\2018 --! @version 1.0 --! @details --! --! Dependencies:\n --! --! --! References:\n --! \n --! --! --! Modified by:\n --! Author: Eduardo Brandao de Souza Mendes ------------------------------------------------------------------------------- --! \n\nLast changes:\n --! 02\05\2018 - EBSM - Created\n --! ------------------------------------------------------------------------------- --! @todo - \n --! \n -- ------------------------------------------------------------------------------- --============================================================================== --! Entity declaration for tx_pi_ctrl --============================================================================== entity tx_pi_ctrl is generic( -- User choice of DRP control or port control -- Recommended nowadays to use in DRP control as a strange behaviour was observed using the port in PI code stepping mode g_DRP_NPORT_CTRL : boolean := true; --! Uses DRP control of port control for the transmitter PI g_DRP_ADDR_TXPI_PPM_CFG : std_logic_vector(8 downto 0) := ("010011010") --! Check the transceiver user guide of your device for this address ); port ( -- User Interface clk_sys_i : in std_logic; --! system clock input reset_i : in std_logic; --! active high sync. reset strobe_i : in std_logic; --! pulse synchronous to clk_sys_i to activate a shift in the transmitter phase (only captured rising edge, so a signal larger than a pulse is also fine) inc_ndec_i : in std_logic; --! 1 increments tx phase by phase_step_i units, 0 decrements tx phase by phase_step_i units phase_step_i : in std_logic_vector(3 downto 0); --! number of units to shift the phase of the transmitter (see Xilinx transceiver User Guide to convert units in time) done_o : out std_logic; --! pulse synchronous to clk_sys_i to indicate a transmitter phase shift was performed phase_o : out std_logic_vector(6 downto 0); --! phase shift accumulated -- MGT interface -- Transmitter PI ports - see Xilinx transceiver User Guide for more information -- obs1: all txpi ports shall be connected to the transceiver even when using this block in DRP-mode clk_txusr_i : in std_logic; --! txusr2clk txpippmen_o : out std_logic; --! enable tx phase interpolator controller txpippmovrden_o : out std_logic; --! enable DRP control of tx phase interpolator txpippmsel_o : out std_logic; --! set to 1 when using tx pi ppm controler txpippmpd_o : out std_logic; --! power down transmitter phase interpolator txpippmstepsize_o : out std_logic_vector(4 downto 0); --! sets step size and direction of phase shift with port control PI code stepping mode -- DRP interface - see Xilinx transceiver User Guide for more information -- obs2: connect clk_sys_i to drpclk -- obs3: if using this block in port-mode, DRP output can be left floating and input connected to '0' drpaddr_o : out std_logic_vector(8 downto 0); --! For devices with a 10-bit DRP address interface, connect MSB to '0' drpen_o : out std_logic; --! DRP enable transaction drpdi_o : out std_logic_vector(15 downto 0); --! DRP data write drprdy_i : in std_logic; --! DRP finished transaction drpdo_i : in std_logic_vector(15 downto 0); --! DRP data read; not used nowadays, write only interface drpwe_o : out std_logic --! DRP write enable ); end tx_pi_ctrl; --============================================================================== -- architecture declaration --============================================================================== architecture rtl of tx_pi_ctrl is --! Attribute declaration attribute async_reg : string; --! Constant declaration --! Signal declaration -- ============================================================================== -- ======================== Common: PORT/DRP interface ========================== -- ============================================================================== -- phase accumulator signal phase_acc : unsigned(phase_o'range); signal strobe_r : std_logic; --rising edge detector for strobe -- ============================================================================== -- ======================== Interface 1: DRP interface ========================== -- ============================================================================== -- FSM to control Tx PI via DRP control -- obs: Two write a new phase value for the transmitter PI via DRP: -- The bits 6:0 of the corresponding DRP register have to be asserted -- The bit 7 has to be asserted high (REGISTER_1PHASE_DRP) and then low (REGISTER_0PHASE_DRP) type t_DRP_TX_PI_FSM is (IDLE, PHASE_ACCU, PRE_REGISTER_0PHASE_DRP, WAIT_PRE_REGISTER_0PHASE_DRP, REGISTER_1PHASE_DRP, WAIT_REGISTER_1PHASE_DRP, REGISTER_0PHASE_DRP, WAIT_REGISTER_0PHASE_DRP, DONE_DRP); signal drp_tx_pi_state : t_DRP_TX_PI_FSM; -- ============================================================================== -- ======================== Interface 2: PORT interface ========================= -- ============================================================================== -- Closed-loop strobe_toggle strategy -- sync to clk_sys_i signal strobe_toggle : std_logic := '0'; -- sync to clk_txusr_i signal strobe_toggle_txusr_meta : std_logic; signal strobe_toggle_txusr_r : std_logic; signal strobe_toggle_txusr_r2 : std_logic; attribute async_reg of strobe_toggle_txusr_meta, strobe_toggle_txusr_r, strobe_toggle_txusr_r2 : signal is "true"; signal strobe_txusr : std_logic; signal strobe_txusr_r : std_logic; signal strobe_txusr_extend : std_logic; signal done_toggle : std_logic := '0'; -- sync to clk_sys_i signal done_toggle_sys_meta : std_logic; signal done_toggle_sys_r : std_logic; signal done_toggle_sys_r2 : std_logic; attribute async_reg of done_toggle_sys_meta, done_toggle_sys_r, done_toggle_sys_r2 : signal is "true"; signal done : std_logic; begin -- ============================================================================== -- ======================== Interface 1: DRP interface ========================== -- ============================================================================== -- Only generated if user chooses to use port control gen_drp_interface : if g_DRP_NPORT_CTRL generate -- Tie Tx PI port signals txpippmen_o <= '0'; txpippmovrden_o <= '1'; txpippmsel_o <= '1'; txpippmpd_o <= '0'; txpippmstepsize_o(4 downto 0) <= (others => '0'); --============================================================================ -- Process p_strobe_r --! Delays strobe --! read: strobe_i\n --! write: strobe_r\n --! r/w: \n --============================================================================ p_strobe_r : process(clk_sys_i) begin if(rising_edge(clk_sys_i)) then strobe_r <= strobe_i; end if; end process p_strobe_r; --============================================================================ -- Process p_drp_tx_pi_fsm --! FSM for Tx PI control via DRP --! read: strobe_i, drprdy_i\n --! write: -\n --! r/w: drp_tx_pi_state\n --============================================================================ p_drp_tx_pi_fsm : process(clk_sys_i) begin if(rising_edge(clk_sys_i)) then if(reset_i = '1') then drp_tx_pi_state <= IDLE; else case drp_tx_pi_state is when IDLE => if(strobe_i = '1' and strobe_r = '0') then drp_tx_pi_state <= PHASE_ACCU; end if; when PHASE_ACCU => drp_tx_pi_state <= PRE_REGISTER_0PHASE_DRP; when PRE_REGISTER_0PHASE_DRP => drp_tx_pi_state <= WAIT_PRE_REGISTER_0PHASE_DRP; when WAIT_PRE_REGISTER_0PHASE_DRP => if(drprdy_i = '1') then drp_tx_pi_state <= REGISTER_1PHASE_DRP; end if; when REGISTER_1PHASE_DRP => drp_tx_pi_state <= WAIT_REGISTER_1PHASE_DRP; when WAIT_REGISTER_1PHASE_DRP => if(drprdy_i = '1') then drp_tx_pi_state <= REGISTER_0PHASE_DRP; end if; when REGISTER_0PHASE_DRP => drp_tx_pi_state <= WAIT_REGISTER_0PHASE_DRP; when WAIT_REGISTER_0PHASE_DRP => if(drprdy_i = '1') then drp_tx_pi_state <= DONE_DRP; end if; when DONE_DRP => drp_tx_pi_state <= IDLE; when others => drp_tx_pi_state <= IDLE; end case; end if; end if; end process p_drp_tx_pi_fsm; -- Tie static DRP signals drpaddr_o <= g_DRP_ADDR_TXPI_PPM_CFG; drpdi_o(15 downto 8) <= (others => '0'); -- DRP signals controlled via FSM drpdi_o(7) <= '1' when drp_tx_pi_state = REGISTER_1PHASE_DRP else '0'; drpdi_o(6 downto 0) <= std_logic_vector(phase_acc); drpen_o <= '1' when (drp_tx_pi_state = REGISTER_1PHASE_DRP or drp_tx_pi_state = REGISTER_0PHASE_DRP or drp_tx_pi_state = PRE_REGISTER_0PHASE_DRP) else '0'; drpwe_o <= '1' when (drp_tx_pi_state = REGISTER_1PHASE_DRP or drp_tx_pi_state = REGISTER_0PHASE_DRP or drp_tx_pi_state = PRE_REGISTER_0PHASE_DRP) else '0'; --============================================================================ -- Process p_phase_acc --! Increments or decrements phase accumulator --! read: drp_tx_pi_state, inc_ndec_i, phase_step_i\n --! write: \n --! r/w: phase_acc\n --============================================================================ p_phase_acc : process(clk_sys_i) begin if(rising_edge(clk_sys_i)) then if(reset_i = '1') then phase_acc <= to_unsigned(0, phase_acc'length); else if(drp_tx_pi_state = PHASE_ACCU) then if(inc_ndec_i = '1') then phase_acc <= phase_acc + unsigned(phase_step_i); else phase_acc <= phase_acc - unsigned(phase_step_i); end if; else phase_acc <= phase_acc; end if; end if; end if; end process p_phase_acc; phase_o <= std_logic_vector(phase_acc); done_o <= '1' when (drp_tx_pi_state = DONE_DRP) else '0'; end generate gen_drp_interface; -- ============================================================================== -- ======================== Interface 2: PORT interface ========================= -- ============================================================================== -- Only generated if user chooses to use port control gen_port_interface : if not g_DRP_NPORT_CTRL generate -- Tie to zero unused DRP signals drpaddr_o <= (others => '0'); drpen_o <= '0'; drpdi_o <= (others => '0'); drpwe_o <= '0'; -- Closed-loop clock-domain crossing strategy for strobe pulse and done as acknowledgment --============================================================================ -- Process p_strobe_toggle --! Creates strobe toggle register with rising edge of strobe --! read: strobe_i\n --! write: -\n --! r/w: strobe_r, strobe_toggle\n --============================================================================ p_strobe_toggle : process(clk_sys_i) begin if(rising_edge(clk_sys_i)) then strobe_r <= strobe_i; if(strobe_i = '1' and strobe_r = '0') then strobe_toggle <= not strobe_toggle; end if; end if; end process p_strobe_toggle; --============================================================================ -- Process p_strobe_toggle_txusrsync --! Creates a rising edge sync to clk_txusr_i when strobe_toggle changes level and generates acknowledgment --! read: strobe_toggle\n --! write: done_toggle\n --! r/w: strobe_txusr_r, strobe_txusr, strobe_toggle_txusr_r2, strobe_toggle_txusr_r, strobe_toggle_txusr_meta\n --============================================================================ p_strobe_toggle_txusrsync : process(clk_txusr_i) begin if(rising_edge(clk_txusr_i)) then -- capture strobe strobe_toggle_txusr_meta <= strobe_toggle; strobe_toggle_txusr_r <= strobe_toggle_txusr_meta; strobe_toggle_txusr_r2 <= strobe_toggle_txusr_r; strobe_txusr <= strobe_toggle_txusr_r2 xor strobe_toggle_txusr_r; strobe_txusr_r <= strobe_txusr; strobe_txusr_extend <= strobe_txusr or strobe_txusr_r; -- acknowledgment (done) if(strobe_txusr_r = '1') then done_toggle <= not done_toggle; end if; end if; end process p_strobe_toggle_txusrsync; -- Pulses txpippmen for two clock cycles - see Xilinx transceiver User Guide (reason for extension of strobe pulse in txusr domain) txpippmen_o <= strobe_txusr_extend; -- Tie other signals txpippmovrden_o <= '0'; txpippmsel_o <= '1'; txpippmpd_o <= '0'; txpippmstepsize_o(4) <= inc_ndec_i; -- obs: those signals should be stable between strobe->done, the latency of the closed-loop CDC ensures a proper capture txpippmstepsize_o(3 downto 0) <= phase_step_i; -- obs: those signals should be stable between strobe->done, the latency of the closed-loop CDC ensures a proper capture --============================================================================ -- Process p_done_toggle_syssync --! Creates a rising edge sync to clk_sys_i when done_toggle changes level --! read: done_toggle\n --! write: done_toggle_sys_r2\n --! r/w: done_toggle_sys_meta, done_toggle_sys_r\n --============================================================================ p_done_toggle_syssync : process(clk_sys_i) begin if(rising_edge(clk_sys_i)) then done_toggle_sys_meta <= done_toggle; done_toggle_sys_r <= done_toggle_sys_meta; done_toggle_sys_r2 <= done_toggle_sys_r; end if; end process p_done_toggle_syssync; done <= done_toggle_sys_r2 xor done_toggle_sys_r; --============================================================================ -- Process p_phase_acc --! Increments or decrements phase accumulator --! read: done, inc_ndec_i, phase_step_i\n --! write: done_o\n --! r/w: phase_acc\n --============================================================================ p_phase_acc : process(clk_sys_i) begin if(rising_edge(clk_sys_i)) then if(reset_i = '1') then phase_acc <= to_unsigned(0, phase_acc'length); done_o <= '0'; else if(done = '1') then if(inc_ndec_i = '1') then phase_acc <= phase_acc + unsigned(phase_step_i); else phase_acc <= phase_acc - unsigned(phase_step_i); end if; done_o <= '1'; else phase_acc <= phase_acc; done_o <= '0'; end if; end if; end if; end process p_phase_acc; phase_o <= std_logic_vector(phase_acc); end generate gen_port_interface; end architecture rtl; --============================================================================== -- architecture end --==============================================================================