--==============================================================================
-- © Copyright CERN for the benefit of the HPTD interest group 2019. All rights not
-- expressly granted are reserved.
--
-- This file is part of TClink.
--
-- TClink 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.
--
-- TClink 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 TClink. If not, see .
--==============================================================================
--! @file cdc_rx.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: Rx mesochronous fixed-phase clock-domain crossing
--
--! @brief Mesochronous fixed-phase CDC for Receiver
--! - This Clock-domain crossing requires that the clk_a_i and clk_b_i are an integer related of each other
--! @author Eduardo Brandao de Souza Mendes - eduardo.brandao.de.souza.mendes@cern.ch
--! @date 30\06\2020
--! @version 1.0
--! @details
--!
--! Dependencies:\n
--!
--!
--! References:\n
--! \n
--!
--!
--! Modified by:\n
--! Author: Eduardo Brandao de Souza Mendes
-------------------------------------------------------------------------------
--! \n\nLast changes:\n
--! 30\06\2020 - EBSM - Created\n
--! 15\07\2020 - EBSM - Support for fixed-latency operation\n
--!
-------------------------------------------------------------------------------
--! @todo - \n
-------------------------------------------------------------------------------
--==============================================================================
--! Entity declaration for cdc_rx
--==============================================================================
entity cdc_rx is
generic (
g_CLOCK_A_RATIO : integer := 8; --! Frequency ratio between slow and fast frequencies (>4)
g_PHASE_SIZE : integer := 3 --! log2(g_CLOCK_A_RATIO)
);
port (
-- Interface A (latch - from where data comes)
reset_a_i : in std_logic; --! reset (only de-assert when all clocks and strobes are stable)
clk_a_i : in std_logic; --! clock A
data_a_i : in std_logic_vector; --! data A
strobe_a_i : in std_logic; --! strobe A
-- Interface B (capture_a - to where data goes)
clk_b_i : in std_logic; --! clock B
data_b_o : out std_logic_vector; --! data B (connected to vector of same size as data_a_i)
strobe_b_i : in std_logic; --! strobe B
ready_b_o : out std_logic; --! Inteface is ready
-- Only relevant for fixed-phase operation
phase_o : out std_logic_vector(g_PHASE_SIZE-1 downto 0); --! Phase to check fixed-phase
phase_calib_i : in std_logic_vector(g_PHASE_SIZE-1 downto 0); --! Phase measured in first reset
phase_force_i : in std_logic --! Force the phase to be the calibrated one
);
end cdc_rx;
--==============================================================================
-- architecture declaration
--==============================================================================
architecture rtl of cdc_rx is
--! Function declaration
--! Constant declaration
--! Signal declaration
-- CLK_A domain
signal data_a_reg : std_logic_vector(data_a_i'range);
signal ce_cntr_a : integer range 0 to g_CLOCK_A_RATIO-1;
signal capture_a : std_logic;
signal strobe_a_r : std_logic;
signal phase_calib_a : std_logic_vector(g_PHASE_SIZE-1 downto 0);
signal phase_force_a : std_logic;
-- CLK_B -> CLK_A domain
signal strobe_b_toggle : std_logic := '0';
signal strobe_b_toggle_meta : std_logic;
signal strobe_b_toggle_sync : std_logic;
signal strobe_b_toggle_sync_r : std_logic;
signal reset_a_strobe_sync : std_logic;
-- CLK_B domain
signal data_b_reg : std_logic_vector(data_a_i'range);
attribute ASYNC_REG : string;
attribute ASYNC_REG of strobe_b_toggle_meta : signal is "TRUE";
attribute ASYNC_REG of phase_calib_a : signal is "TRUE";
attribute ASYNC_REG of phase_force_a : signal is "TRUE";
attribute ASYNC_REG of phase_o : signal is "TRUE";
begin
--=== CLK_B -> CLK_A
strobe_b_toggle <= not strobe_b_toggle when rising_edge(clk_b_i);
strobe_b_toggle_meta <= strobe_b_toggle when rising_edge(clk_a_i);
strobe_b_toggle_sync <= strobe_b_toggle_meta when rising_edge(clk_a_i);
strobe_b_toggle_sync_r <= strobe_b_toggle_sync when rising_edge(clk_a_i);
-- This process is intended to ensure reset_a is de-asserted with a fixed-phase w.r.t. strobe B
p_reset_a_strobe_sync : process(clk_a_i)
begin
if (rising_edge(clk_a_i)) then
if (reset_a_i='1') then
reset_a_strobe_sync <= '1';
elsif(strobe_b_toggle_sync_r/=strobe_b_toggle_sync) then
reset_a_strobe_sync <= '0';
end if;
end if;
end process p_reset_a_strobe_sync;
--=== CLK_A
phase_calib_a <= phase_calib_i when rising_edge(clk_a_i);
phase_force_a <= phase_force_i when rising_edge(clk_a_i);
p_clock_enable : process(clk_a_i)
begin
if(rising_edge(clk_a_i)) then
if(reset_a_strobe_sync='1') then
ce_cntr_a <= 0;
capture_a <= '0';
else
if(phase_force_a='1' and strobe_a_i='1') then
ce_cntr_a <= to_integer(unsigned(phase_calib_a));
elsif(ce_cntr_a = g_CLOCK_A_RATIO-1) then
ce_cntr_a <= 0;
else
ce_cntr_a <= ce_cntr_a + 1;
end if;
if (ce_cntr_a = g_CLOCK_A_RATIO-1) then
capture_a <= '1';
else
capture_a <= '0';
end if;
end if;
end if;
end process p_clock_enable;
--============================================================================
-- Phase measurement
--============================================================================
strobe_a_r <= strobe_a_i when rising_edge(clk_a_i);
p_phase : process(clk_a_i)
begin
if(rising_edge(clk_a_i)) then
if(reset_a_strobe_sync = '1') then
phase_o <= (others => '0');
ready_b_o <= '0';
else
if(strobe_a_r = '1') then
phase_o <= std_logic_vector(to_unsigned(ce_cntr_a, phase_o'length));
ready_b_o <= '1';
end if;
end if;
end if;
end process p_phase;
--============================================================================
-- Data intermediary
--============================================================================
p_data_int : process(clk_a_i) -- do not reset to avoid unecessarily large fan-out of reset_a_strobe_sync signal
begin
if(rising_edge(clk_a_i)) then
if (capture_a = '1') then
data_a_reg <= data_a_i;
else
data_a_reg <= data_a_reg;
end if;
end if;
end process p_data_int;
--============================================================================
-- Data out
--============================================================================
p_data_out : process(clk_b_i)
begin
if(rising_edge(clk_b_i)) then
if(strobe_b_i='1') then
data_b_reg <= data_a_reg;
end if;
end if;
end process p_data_out;
data_b_o <= data_b_reg;
end architecture rtl;
--==============================================================================
-- architecture end
--==============================================================================