--==============================================================================
-- © 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_tx.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 mesochronous fixed-phase clock-domain crossing
--
--! @brief Mesochronous fixed-phase CDC for Transmitter
--! - 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_tx
--==============================================================================
entity cdc_tx is
generic (
g_CLOCK_A_RATIO : integer := 1 ; --! Ratio between strobe period and clock A period
g_CLOCK_B_RATIO : integer := 8 ; --! Ratio between strobe period and clock B period (>=4)
g_ACC_PHASE : integer := 125*8; --! Phase accumulator number - only relevant for fixed phase operation
g_PHASE_SIZE : integer := 10 --! ceil(log2(g_ACC_PHASE))
);
port (
-- Interface A (latch - from where data comes)
reset_a_i : in std_logic; --! reset (only de-assert when all clocks and strobe A 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 - 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_o : out std_logic; --! strobe B
ready_b_o : out std_logic; --! ready B (CDC is operating)
-- Only relevant for fixed-phase operation
clk_freerun_i : in std_logic; --! Free-running clock (125MHz)
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_tx;
--==============================================================================
-- architecture declaration
--==============================================================================
architecture rtl of cdc_tx is
attribute keep : string;
--! Function declaration
--! Constant declaration
--! Signal declaration
--===== Ensuring a safe data-transfer =====
-- CLK_A domain
signal data_a_reg : std_logic_vector(data_a_i'range);
signal strobe_a_reg : std_logic := '0';
-- CLK_A -> CLK_B domain
signal reset_a_strobe_sync : std_logic; -- reset_a falling-edge re-sampled with strobe_a to ensure a fixed-phase deactivation
signal reset_b_meta : std_logic; -- reset re-sampled to clock B domain (2-FF sync)
signal reset_b_sync : std_logic;
-- CLK_B domain
signal ce_cntr_b : integer range 0 to g_CLOCK_B_RATIO;
signal strobe_b_reg : std_logic;
signal data_b_reg : std_logic_vector(data_b_o'range);
signal ready_b_meta : std_logic;
signal ready_b_sync : std_logic;
--=========================================
--===== Deterministic phase operation =====
-- reset sync (2-FF sync)
signal reset_freerun_meta : std_logic;
signal reset_freerun_sync : std_logic;
-- phase-measurement
signal strobeclk_a : std_logic_vector(g_CLOCK_A_RATIO-1 downto 0);
signal strobeclk_b : std_logic_vector(g_CLOCK_B_RATIO-1 downto 0);
signal xormeas_meta : std_logic; -- strobeclk_b('left) XOR strobeclk_a('left) (2-FF sync)
signal xormeas_sync : std_logic;
-- phase control
signal advance_toggle : std_logic := '0';
signal retard_toggle : std_logic := '0';
signal advance_toggle_meta : std_logic;
signal retard_toggle_meta : std_logic;
signal advance_toggle_sync : std_logic;
signal retard_toggle_sync : std_logic;
signal advance_toggle_sync_r : std_logic;
signal retard_toggle_sync_r : std_logic;
-- phase alignment FSM
signal phase_acc : integer range 0 to g_ACC_PHASE; -- Phase accumulator
signal events_acc : integer range 0 to g_ACC_PHASE; -- Total events accumulator
type t_ALIGNMENT_FSM is (IDLE, RESET_CNTR, PHASE_SACC, COMPARE, ADVANCE, RETARD, ALIGNED);
signal alignment_state : t_ALIGNMENT_FSM;
signal cdc_ready : std_logic;
--=========================================
attribute ASYNC_REG : string;
attribute ASYNC_REG of xormeas_meta, reset_freerun_meta, advance_toggle_meta, retard_toggle_meta: signal is "TRUE";
begin
--===== Ensuring a safe data-transfer =====
--=== CLK_A
strobe_a_reg <= strobe_a_i when rising_edge(clk_a_i);
data_a_reg <= data_a_i when rising_edge(clk_a_i);
--=== CLK_A -> CLK_B
-- This process is intended to ensure reset_a is de-asserted with a fixed-phase w.r.t. strobe A
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_a_i='1') then
reset_a_strobe_sync <= '0';
end if;
end if;
end process p_reset_a_strobe_sync;
-- 2-FF sync.
reset_b_meta <= reset_a_strobe_sync when rising_edge(clk_b_i);
reset_b_sync <= reset_b_meta when rising_edge(clk_b_i);
ready_b_meta <= cdc_ready when rising_edge(clk_b_i);
ready_b_sync <= ready_b_meta when rising_edge(clk_b_i);
ready_b_o <= ready_b_sync ;
--=== CLK_B
-- counter with advance/retard capability
p_ce_cntr_b : process(clk_b_i)
begin
if(rising_edge(clk_b_i)) then
if (reset_b_sync = '1') then
ce_cntr_b <= 0;
else
if (advance_toggle_sync_r/=advance_toggle_sync) then -- advance counter
if (ce_cntr_b = g_CLOCK_B_RATIO-1) then
ce_cntr_b <= 1;
elsif (ce_cntr_b = g_CLOCK_B_RATIO-2) then
ce_cntr_b <= 0;
else
ce_cntr_b <= ce_cntr_b + 2;
end if;
elsif (retard_toggle_sync_r = retard_toggle_sync) then -- normal counter
if (ce_cntr_b = g_CLOCK_B_RATIO-1) then
ce_cntr_b <= 0;
else
ce_cntr_b <= ce_cntr_b + 1;
end if;
else -- retard counter
ce_cntr_b <= ce_cntr_b;
end if;
end if;
end if;
end process p_ce_cntr_b;
p_strobe_b : process(clk_b_i)
begin
if(rising_edge(clk_b_i)) then
if (reset_b_sync = '1') then
strobe_b_reg <= '0';
else
if ((ce_cntr_b = 0 and (retard_toggle_sync_r = retard_toggle_sync) ) or (ce_cntr_b=g_CLOCK_B_RATIO-1 and advance_toggle_sync_r/=advance_toggle_sync)) then
strobe_b_reg <= '1';
else
strobe_b_reg <= '0';
end if;
end if;
end if;
end process p_strobe_b;
p_data_b : process(clk_b_i)
begin
if(rising_edge(clk_b_i)) then
if (ce_cntr_b = 0) then
data_b_reg <= data_a_reg;
else
data_b_reg <= data_b_reg;
end if;
end if;
end process p_data_b;
strobe_b_o <= strobe_b_reg;
data_b_o <= data_b_reg ;
--=========================================
--===== Deterministic phase operation =====
--=== CLK_A
p_strobeclk_a : process(clk_a_i)
begin
if(rising_edge(clk_a_i)) then
if (reset_a_strobe_sync = '1') then
strobeclk_a <= (others => '0');
else
if (strobe_a_reg = '1') then
strobeclk_a(0) <= not strobeclk_a(0);
end if;
strobeclk_a(strobeclk_a'left downto 1) <= strobeclk_a(strobeclk_a'left-1 downto 0);
end if;
end if;
end process p_strobeclk_a;
--=== CLK_B
p_strobeclk_b : process(clk_b_i)
begin
if(rising_edge(clk_b_i)) then
if (reset_b_sync='1') then
strobeclk_b <= (others => '0');
else
if(strobe_b_reg = '1') then
strobeclk_b(0) <= not strobeclk_b(0);
end if;
strobeclk_b(strobeclk_b'left downto 1) <= strobeclk_b(strobeclk_b'left-1 downto 0);
end if;
end if;
end process p_strobeclk_b;
--=== Phase-measurement and adjustment (CLK_FREE-RUN)
-- synchronization
reset_freerun_meta <= reset_a_strobe_sync when rising_edge(clk_freerun_i);
reset_freerun_sync <= reset_freerun_meta when rising_edge(clk_freerun_i);
advance_toggle_meta <= advance_toggle when rising_edge(clk_b_i);
advance_toggle_sync <= advance_toggle_meta when rising_edge(clk_b_i);
advance_toggle_sync_r <= advance_toggle_sync when rising_edge(clk_b_i);
retard_toggle_meta <= retard_toggle when rising_edge(clk_b_i);
retard_toggle_sync <= retard_toggle_meta when rising_edge(clk_b_i);
retard_toggle_sync_r <= retard_toggle_sync when rising_edge(clk_b_i);
-- Phase measurement and adjustment
xormeas_meta <= strobeclk_a(strobeclk_a'left) xor strobeclk_b(strobeclk_b'left) when rising_edge(clk_freerun_i);
xormeas_sync <= xormeas_meta when rising_edge(clk_freerun_i);
p_phase_measurement : process(clk_freerun_i)
begin
if(rising_edge(clk_freerun_i)) then
if(alignment_state = IDLE) then
phase_acc <= 0;
events_acc <= 0;
phase_o <= (others => '0');
else
if(alignment_state = RESET_CNTR)then
phase_acc <= 0;
events_acc <= 0;
elsif(alignment_state = PHASE_SACC) then
if(events_acc < g_ACC_PHASE) then
if(xormeas_sync = '1') then
phase_acc <= phase_acc + 1;
end if;
events_acc <= events_acc + 1;
end if;
end if;
if(alignment_state = ALIGNED) then
phase_o <= std_logic_vector(to_unsigned(phase_acc,phase_o'length));
end if;
end if;
end if;
end process p_phase_measurement;
p_phase_control : process(clk_freerun_i)
begin
if(rising_edge(clk_freerun_i)) then
if(alignment_state = IDLE) then
advance_toggle <= '0';
retard_toggle <= '0';
else
if(alignment_state = ADVANCE) then
advance_toggle <= not advance_toggle;
end if;
if(alignment_state = RETARD) then
retard_toggle <= not retard_toggle;
end if;
end if;
end if;
end process p_phase_control;
p_alignment_fsm : process(clk_freerun_i)
begin
if(rising_edge(clk_freerun_i)) then
if(reset_freerun_sync = '1') then
alignment_state <= IDLE;
else
case alignment_state is
when IDLE =>
alignment_state <= RESET_CNTR;
when RESET_CNTR =>
alignment_state <= PHASE_SACC;
when PHASE_SACC =>
if(events_acc = g_ACC_PHASE) then
alignment_state <= COMPARE;
end if;
when COMPARE =>
if(phase_force_i='0' or cdc_ready='1') then
alignment_state <= ALIGNED;
else
if (phase_acc > (to_integer(unsigned(phase_calib_i)) + (g_ACC_PHASE/g_CLOCK_B_RATIO)/2)) then
alignment_state <= ADVANCE;
elsif(phase_acc < (to_integer(unsigned(phase_calib_i)) - (g_ACC_PHASE/g_CLOCK_B_RATIO)/2)) then
alignment_state <= RETARD;
else
alignment_state <= ALIGNED;
end if;
end if;
when ADVANCE =>
alignment_state <= RESET_CNTR;
when RETARD =>
alignment_state <= RESET_CNTR;
when ALIGNED =>
alignment_state <= RESET_CNTR;
when others =>
alignment_state <= IDLE;
end case;
end if;
end if;
end process p_alignment_fsm;
p_cdc_ready : process(clk_freerun_i)
begin
if(rising_edge(clk_freerun_i)) then
if(reset_freerun_sync = '1') then
cdc_ready <= '0';
else
if(alignment_state = ALIGNED) then
cdc_ready <= '1';
end if;
end if;
end if;
end process p_cdc_ready;
--=========================================
end architecture rtl;
--==============================================================================
-- architecture end
--==============================================================================