--------------------------------------------------------------------------------
-- CERN BE-CO-HT
-- WR2RF_VME core
-- https://ohwr.org/project/vme-rf-wr-bobr
--------------------------------------------------------------------------------
--
-- unit name:   vtu_blk
--
-- description: vtu block (vtu_core + serdes + delays)
--
--------------------------------------------------------------------------------
-- Copyright (c) 2020 CERN (home.cern)
--------------------------------------------------------------------------------
-- Copyright and related rights are licensed under the Solderpad Hardware
-- License, Version 2.0 (the "License"); you may not use this file except
-- in compliance with the License. You may obtain a copy of the License at
-- http://solderpad.org/licenses/SHL-2.0.
-- Unless required by applicable law or agreed to in writing, software,
-- hardware and materials distributed under this License is distributed on an
-- "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
-- or implied. See the License for the specific language governing permissions
-- and limitations under the License.
--------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

library unisim;
use unisim.vcomponents.all;

use work.wishbone_pkg.all;

entity vtu_blk is
   generic (
      trigger_unit_t1 : boolean := true;
      g_output_stage  : string  := "serdes"; -- "ddr"
      g_hc_string     : string  := "DDR" );
    port (
      --  System clock (125Mhz or 62.5Mhz)
      clk_sys_i  : in  std_logic;
      --  System reset clock (> 50ns)
      rst_sys_i  : in  std_logic;
      -- RF reset due to nco_reset
      rst_rf_n_i : in  std_logic;
      rst_serdes_i : in std_logic;

      --  Wishbone interface, uses clk_sys.
      wb_i         : in    t_wishbone_slave_in;
      wb_o         : out   t_wishbone_slave_out;

      -- TAI
      tm_tai_i      : in  std_logic_vector(39 downto 0);
      tm_cycles_i   : in  std_logic_vector(27 downto 0);

      -- RF clock (~200Mhz) for serdes
      clk_rf_io_i  : in  std_logic;

      -- RF clock (~200Mhz) for fabric
      clk_rf_i     : in  std_logic;

      --  RF clock / 8 (for vtu)
      clk_vtu_i    : in std_logic;
      clk_vtu_x2_i : in std_logic;

      -- Sync input for the trigger unit
      sync_i       : in  std_logic;
      sync_x8_fb_i : in  std_logic_vector(7 downto 0);
      sync_x8_fb_o : out std_logic_vector(7 downto 0);

      -- Generated trigger pulse (after delay, before pad).
      trig_o   : out std_logic;

      --  dff reset (pad)
      trig_rst_i   : in  std_logic;
      trig_rst_n_o : out std_logic;
      trig_rst_p_o : out std_logic;

      --  Asynchronous inputs.
      start_i  : in  std_logic;
      stop_i   : in  std_logic;

      softstop_o  : out std_logic;
      softstart_o : out std_logic;

      --  Pulse when the vtu is started.
      vtu_start_p_o : out std_logic;

      ila_dbg_o : out std_logic_vector(15 downto 0) );

   attribute keep_hierarchy            : STRING;
   attribute keep_hierarchy of vtu_blk : entity is "yes";

end vtu_blk;

architecture rtl of vtu_blk is

    type t_vtu_params is record
        mode : std_logic_vector (2 downto 0);
        htswitchen : std_logic;
        bvalue : std_logic_vector (63 downto 0);
        htvalue : std_logic_vector (63 downto 0);
        wvalue : std_logic_vector (63 downto 0);
    end record;

    constant c_no_vtu_params : t_vtu_params :=
       (mode => (others => '0'),
        htswitchen => '0',
        bvalue => (others => '0'),
        htvalue => (others => '0'),
        wvalue => (others => '0'));

    --  VTU offline parameters.
    signal sys_params_offline : t_vtu_params;
    signal params_offline     : t_vtu_params;
    signal sys_valid_offline  : std_logic;
    signal valid_offline      : std_logic;
    signal valid_data         : std_logic;
    signal valid_wr           : std_logic;

    --  VTU inline parameters (for clk_sys and clk_vtu domains)
    signal params_vtu : t_vtu_params;
    signal params_online : t_vtu_params;

    signal force_valid_offline : std_logic;
    signal force_valid_offline_vtu : std_logic; -- In clk_vtu domain

    signal start_as_stop : std_logic;
    signal start_as_stop_vtu : std_logic;

    signal vtu_use_sync_as_start : std_logic := '0';

    --  Status from VTUcore and their synchronized version.
    signal vtu_wrongb           : std_logic;
    signal vtu_wrongb_sys       : std_logic;
    signal vtu_wronght          : std_logic;
    signal vtu_wronght_sys      : std_logic;
    signal vtu_wrongw           : std_logic;
    signal vtu_wrongw_sys       : std_logic;

    signal start_sys       : std_logic;
    signal start_pulse_sys : std_logic;
    signal start_pulse_2   : std_logic;
    signal start_ready     : std_logic;
    signal start_ack       : std_logic;

    signal vtu_start       : std_logic;
    signal vtu_start_d     : std_logic;
    signal vtu_start_d2     : std_logic;

    signal miss_ready      : std_logic;
    signal miss_valid      : std_logic;
    signal miss_idle      : std_logic;

    signal soft_stop_ready : std_logic;
    signal soft_stop_vtu   : std_logic;
    signal stop_vtu        : std_logic;
    signal stop_vtu_pulse  : std_logic;
    signal vtu_stop        : std_logic;
    signal vtu_stop_start  : std_logic;

    signal soft_start      : std_logic;
    signal soft_stop       : std_logic;
    signal sys_rst_vtu     : std_logic;
    signal sys_rst_vtu_n   : std_logic;
    signal rst_vtu         : std_logic;
    signal rst_vtu_n       : std_logic;

    --  Status bit (rf and sys clock domains)
    signal vtu_running, vtu_running_sys : std_logic;
    signal vtu_started, vtu_started_sys : std_logic;
    signal vtu_idle,    vtu_idle_sys    : std_logic;

    signal sync_data_in        : std_logic_vector(7 downto 0);
    signal sync_data_in_clean  : std_logic_vector(7 downto 0);
    signal sync_data_out       : std_logic_vector(7 downto 0);

    --  Trigger out, before delay.
    signal trig_out       : std_logic;

    --  Trig delay current value.
    signal sys_trig_odelay : std_logic_vector (4 downto 0);
    signal trig_odelay     : std_logic_vector (4 downto 0);

    -- Trig delay by half cycle
    signal sys_trig_delay_hc : std_logic;
    signal trig_delay_hc     : std_logic;

    signal diag_enable : std_logic;
    signal diag_window : std_logic_vector (2 downto 0);
    signal diag_generation : std_logic_vector (15 downto 0);
    signal diag_freq : std_logic_vector (31 downto 0);
    signal diag_counter : std_logic_vector (31 downto 0);
    signal rst_sys_n : std_logic;

    signal clk_rf_io_n : std_logic;

    signal vtu_trig_redge              : std_logic;
    signal vtu_trig_redge_r            : std_logic;    
    signal vtu_trig_redge_sys          : std_logic;
    signal last_vtu_trig_tai_sec_r     : std_logic_vector(63 downto 0);
    signal last_vtu_trig_tai_cycles_r  : std_logic_vector(31 downto 0);    
    signal first_vtu_trig_tai_sec_r    : std_logic_vector(63 downto 0);
    signal first_vtu_trig_tai_cycles_r : std_logic_vector(31 downto 0);
    signal first_vtu_trig_active       : std_logic;
    signal first_vtu_trig_active_r     : std_logic;
    
    signal sync_data_out_clean   : std_logic_vector(7 downto 0);
    signal sync_data_out_clean_r : std_logic_vector(7 downto 0);
    signal sync_data_x16_nohc    : std_logic_vector(15 downto 0);
    signal sync_data_x16_hc      : std_logic_vector(15 downto 0);
    signal sync_data_x16         : std_logic_vector(15 downto 0);
    signal vtu_clk_r             : std_logic;
    signal sync_data_oserdes     : std_logic_vector(7 downto 0);
    signal sync_data_oserdes_r   : std_logic_vector(7 downto 0);
    signal clk_vtu_hold_delay_n  : std_logic;
    signal clk_vtu_hold_delay    : std_logic;
    attribute dont_touch : string;
    attribute dont_touch of clk_vtu_hold_delay_n : signal is "true";
    attribute dont_touch of clk_vtu_hold_delay   : signal is "true";

    -- IOB the trigger unit flip-flop reset driver.
    signal trig_rst_r        : std_logic;

    attribute IOB  : string;
    attribute keep : string;
    attribute IOB  of trig_rst_r : signal is "true";
    attribute keep of trig_rst_r : signal is "true";

    attribute keep_hierarchy of inst_vtu    : label is "yes";

    signal dbg               : std_logic_vector(15 downto 0);

begin
    rst_sys_n   <= not rst_sys_i;
    rst_vtu_n   <= not rst_vtu;
    clk_rf_io_n <= not clk_rf_io_i;

    --  Synchronize start_i
    --  Path: start_i (async)
    --     --> start_pulse_sys (clk_sys)
    --     --> start_pulse_2  (clk_sys, if ready)
    --     --> vtu_start (clk_vtu)
    --     --> vtu_start_d (clk_vtu, after loading the config)
    --         [read by the vtu]
    inst_start_isync: entity work.gc_sync
        port map (
            clk_i     => clk_sys_i,
            rst_n_a_i => rst_sys_n,
            d_i       => start_i,
            q_o       => start_sys );
    inst_start_edge: entity work.gc_edge_detect
      generic map (
        g_async_rst => False,
        g_pulse_edge => "positive",
        g_clock_edge => "positive" )
      port map (
        clk_i   => clk_sys_i,
        rst_n_i => rst_sys_n,
        data_i  => start_sys,
        pulse_o => start_pulse_sys );
    --  Synchronizer from the external/soft start to the start for VTU.
    inst_start_psync: entity work.gc_pulse_synchronizer2
        port map (
            clk_in_i    => clk_sys_i,
            rst_in_n_i  => rst_sys_n,
            clk_out_i   => clk_vtu_i,
            rst_out_n_i => rst_vtu_n,
            d_ready_o   => start_ready,
            d_ack_p_o   => start_ack,
            d_p_i       => start_pulse_2,
            q_p_o       => vtu_start );

    --  FIXME: add a miss flag for soft_stop ?
    inst_softstop_psync: entity work.gc_pulse_synchronizer2
        port map (
            clk_in_i    => clk_sys_i,
            rst_in_n_i  => rst_sys_n,
            clk_out_i   => clk_vtu_i,
            rst_out_n_i => rst_vtu_n,
            d_ready_o   => soft_stop_ready,
            d_ack_p_o   => open,
            d_p_i       => soft_stop,
            q_p_o       => soft_stop_vtu
        );

    inst_stop_psync: entity work.gc_sync
        port map (
            clk_i     => clk_vtu_i,
            rst_n_a_i => rst_vtu_n,
            d_i       => stop_i,
            q_o       => stop_vtu
        );

    inst_stop_edge: entity work.gc_edge_detect
      generic map (
        g_async_rst => False,
        g_pulse_edge => "positive",
        g_clock_edge => "positive"
      )
      port map (
        clk_i   => clk_vtu_i,
        rst_n_i => rst_vtu_n,
        data_i  => stop_vtu,
        pulse_o => stop_vtu_pulse
      );


    vtu_stop <= stop_vtu_pulse or soft_stop_vtu or vtu_stop_start;

    inst_force_valid_psync: entity work.gc_sync
        port map (
            clk_i     => clk_vtu_i,
            rst_n_a_i => rst_vtu_n,
            d_i       => force_valid_offline,
            q_o       => force_valid_offline_vtu
        );

    inst_start_as_stop_psync: entity work.gc_sync
        port map (
          clk_i     => clk_vtu_i,
          rst_n_a_i => rst_vtu_n,
          d_i       => start_as_stop,
          q_o       => start_as_stop_vtu
          );

    --  Handle start pulse.
    process (clk_sys_i)
    begin
        if rising_edge (clk_sys_i) then
            if rst_sys_n = '0' or sys_rst_vtu_n = '0' then
                start_pulse_2 <= '0';
                miss_ready <= '0';
                miss_valid <= '0';
                miss_idle <= '0';
                params_online <= c_no_vtu_params;
            else
                start_pulse_2 <= '0';
                if start_pulse_sys = '1' or soft_start = '1' then
                    --  Start.
                    if (sys_valid_offline = '1' or force_valid_offline = '1')
                       and (start_ready = '1' or start_as_stop = '1')
                    then
                        --  Start vtu, copy params.
                        start_pulse_2 <= '1';
                        params_online <= sys_params_offline;
                    end if;
                    --  Update flags (after a start).
                    miss_ready <= not start_ready;
                    miss_valid <= not sys_valid_offline;
                    miss_idle <= not vtu_idle_sys;
                end if;
            end if;
        end if;
    end process;

    vtu_start_p_o <= start_pulse_2;

    -- Resynchronise sys_params_offline in the vtu clock domain.
    -- We are not concerned with coherency across the bits
    -- these signals rarely change and there's an external
    -- triggering method to sample the bits that ensures the
    -- sys_param_offline values are settled when we synchronise
    g_params_mode : for i in 0 to 2 generate
    begin
       inst_params_mode : entity work.gc_sync
          port map (
             clk_i     => clk_vtu_i,
             rst_n_a_i => rst_vtu_n,
             d_i       => sys_params_offline.mode(i),
             q_o       => params_offline.mode(i) );
    end generate;
    inst_params_htsw : entity work.gc_sync
       port map (
          clk_i     => clk_vtu_i,
          rst_n_a_i => rst_vtu_n,
          d_i       => sys_params_offline.htswitchen,
          q_o       => params_offline.htswitchen );
    g_params_bvalue : for i in 0 to 63 generate
    begin
       inst_params_bvalue : entity work.gc_sync
          port map (
             clk_i     => clk_vtu_i,
             rst_n_a_i => rst_vtu_n,
             d_i       => sys_params_offline.bvalue(i),
             q_o       => params_offline.bvalue(i) );
    end generate;
    g_params_htvalue : for i in 0 to 63 generate
    begin
       inst_params_htvalue : entity work.gc_sync
          port map (
             clk_i     => clk_vtu_i,
             rst_n_a_i => rst_vtu_n,
             d_i       => sys_params_offline.htvalue(i),
             q_o       => params_offline.htvalue(i) );
    end generate;
    g_params_wvalue : for i in 0 to 63 generate
    begin
       inst_params_wvalue : entity work.gc_sync
          port map (
             clk_i     => clk_vtu_i,
             rst_n_a_i => rst_vtu_n,
             d_i       => sys_params_offline.wvalue(i),
             q_o       => params_offline.wvalue(i) );
    end generate;

    --  Handle start pulse to vtu
    process (clk_vtu_i)
    begin
        if rising_edge (clk_vtu_i) then
            vtu_stop_start <= '0';
            if rst_vtu_n = '0' then
                params_vtu <= c_no_vtu_params;
                vtu_start_d <= '0';
                vtu_start_d2 <= '0';
            else
                vtu_start_d <= '0';
                --  Delayed 2 is used by the vtu so that it has enough time to check the values.
                vtu_start_d2 <= vtu_start_d;
                if vtu_start = '1' then
                    if valid_offline = '1' or force_valid_offline_vtu = '1' then
                        --  CDC
                        --  Registers can be read because they aren't modified
                        --  while the start pulse is transfered.
                        params_vtu <= params_offline;
                        --  Generate a pulse to the vtu.
                        vtu_start_d <= '1';
                        --  If start acts as stop, stop the vtu now.
                        --  It will be started in 2 cycles.
                        if start_as_stop_vtu = '1' then
                            vtu_stop_start <= '1';
                        end if;
                    else
                        --  Parameters are not valid -> set to 0.
                        params_vtu <= c_no_vtu_params;
                    end if;
                end if;
            end if;
        end if;
    end process;

    --  Handle offline valid bit
    process (clk_sys_i)
    begin
        if rising_edge (clk_sys_i) then
            if rst_sys_n = '0' then
                sys_valid_offline <= '0';
            else
                if start_ack = '1' and not sys_rst_vtu_n = '0' then
                    --  Copy for online registers.
                    --  Clear valid
                    sys_valid_offline <= '0';
                elsif valid_wr = '1' then
                    --  Write to VALID register from WB.
                    --  TODO: do not allow change of offline data if the valid bit is set.
                    --  TODO: detect ignored writes to offline data if the valid bit is set.
                    sys_valid_offline <= valid_data;
                end if;
            end if;
        end if;
    end process;

    inst_valid_offline_isync: entity work.gc_sync
       port map (
          clk_i     => clk_vtu_i,
          rst_n_a_i => rst_vtu_n,
          d_i       => sys_valid_offline,
          q_o       => valid_offline );

    --  VTU status synchronizers
    inst_running_sync: entity work.gc_sync
        port map (
            clk_i => clk_sys_i,
            rst_n_a_i => rst_sys_n,
            d_i => vtu_running,
            q_o => vtu_running_sys);

    inst_started_sync: entity work.gc_sync
        port map (
            clk_i => clk_sys_i,
            rst_n_a_i => rst_sys_n,
            d_i => vtu_started,
            q_o => vtu_started_sys);

    inst_idle_sync: entity work.gc_sync
        port map (
            clk_i => clk_sys_i,
            rst_n_a_i => rst_sys_n,
            d_i => vtu_idle,
            q_o => vtu_idle_sys);

    inst_wrongb_sync: entity work.gc_sync
        port map (
            clk_i => clk_sys_i,
            rst_n_a_i => rst_sys_n,
            d_i => vtu_wrongb,
            q_o => vtu_wrongb_sys);

    inst_wronght_sync: entity work.gc_sync
        port map (
            clk_i => clk_sys_i,
            rst_n_a_i => rst_sys_n,
            d_i => vtu_wronght,
            q_o => vtu_wronght_sys);

    inst_wrongw_sync: entity work.gc_sync
        port map (
            clk_i => clk_sys_i,
            rst_n_a_i => rst_sys_n,
            d_i => vtu_wrongw,
            q_o => vtu_wrongw_sys);

    inst_trig_diag: entity work.vtu_diag
      port map (
        clk_i => clk_sys_i,
        rst_n_i => rst_sys_n,
        enable_i => diag_enable,
        windows_i => diag_window,
        sig_i => clk_vtu_i,
        dat_i  => sync_data_out,
        freq_o => diag_freq,
        generation_o => diag_generation,
        counter_o => diag_counter
      );

    inst_trigunit_regs: entity work.trigunit_regs
        port map (
            rst_n_i => rst_sys_n,
            clk_i   => clk_sys_i,
            wb_i    => wb_i,
            wb_o    => wb_o,

            status_started_i      => vtu_started_sys,
            status_idle_i         => vtu_idle_sys,
            status_running_i      => vtu_running_sys,
            status_wrongbvalue_i  => vtu_wrongb_sys,
            status_wrongwvalue_i  => vtu_wrongw_sys,
            status_wronghtvalue_i => vtu_wronght_sys,
            status_startReady_i   => start_ready,
            status_missValid_i    => miss_valid,
            status_missReady_i    => miss_ready,
            status_missIdle_i     => miss_idle,

            control_vtuReset_o         => sys_rst_vtu,
            control_startSoftTrigger_o => soft_start,
            control_stopSoftTrigger_o  => soft_stop,

            flags_forceValidOffline_o => force_valid_offline,
            flags_startasstop_o       => start_as_stop,

            configoffline_mode_o  => sys_params_offline.mode,
            configOffline_htSwitchingEnable_o => sys_params_offline.htswitchen,
            configOffline_valid_i => sys_valid_offline,
            configOffline_valid_o => valid_data,
            configOffline_wr_o    => valid_wr,

            bvalueOffline_o  => sys_params_offline.bvalue,
            htValueOffline_o => sys_params_offline.htvalue,
            wValueOffline_o  => sys_params_offline.wvalue,

            configOnline_mode_i => params_online.mode,
            configOnline_htSwitchingEnable_i => params_online.htswitchen,
            bvalueOnline_i      => params_online.bvalue,
            htValueOnline_i     => params_online.htvalue,
            wValueOnline_i      => params_online.wvalue,

            trigOHCDelay_set_o => sys_trig_delay_hc,
            trigODelay_delay_o => sys_trig_odelay,

            TS_FirstTrigTAIsec_i    => first_vtu_trig_tai_sec_r,
            TS_FirstTrigTAIcycles_i => first_vtu_trig_tai_cycles_r,
            TS_LastTrigTAIsec_i     => last_vtu_trig_tai_sec_r,
            TS_LastTrigTAIcycles_i  => last_vtu_trig_tai_cycles_r,

            trigdiag_control_window_o => diag_window,
            trigdiag_control_enable_o => diag_enable,
            trigdiag_generation_i     => diag_generation,
            trigdiag_freq_i           => diag_freq,
            trigdiag_counter_i        => diag_counter );

    softstop_o  <= soft_stop;
    softstart_o <= soft_start;

    sys_rst_vtu_n <= not sys_rst_vtu;

    -- re-sync sys_rst_vtu into rf clock domain
    inst_rstvtu_sync: entity work.gc_sync
       port map (
          clk_i     => clk_vtu_i,
          rst_n_a_i => '1',
          d_i       => sys_rst_vtu,
          q_o       => rst_vtu );

    --ila_dbg_o(4 downto 0)   <= sys_trig_odelay_out;
    --ila_dbg_o(9 downto 5)   <= sys_trig_odelay_in;
    --ila_dbg_o(10)           <= sys_trig_odelay_wr;
    --ila_dbg_o(15 downto 11) <= (others => '0');

    --ila_dbg_o(7 downto 0) <= sync_data_out;
    --ila_dbg_o(8)          <= rst_rf_n_i;
    --ila_dbg_o(9)          <= rst_vtu_n;
    --ila_dbg_o(10)         <= sys_trig_odelay_hc;
    --ila_dbg_o(15 downto 11) <= (others => '0');

    ila_dbg_o(7 downto 0) <= sync_data_in;
    ila_dbg_o(8)  <= rst_serdes_i;
    ila_dbg_o(9)  <= '0';
    ila_dbg_o(10) <= clk_vtu_i;
    ila_dbg_o(11) <= clk_vtu_x2_i;
    ila_dbg_o(12) <= rst_rf_n_i;
    ila_dbg_o(15 downto 13) <= (others => '0');


    --ila_dbg_o <= dbg;


    g_t1_true : if trigger_unit_t1 generate
    begin
       inst_ISERDESE2 : ISERDESE2
          generic map (
             DATA_RATE         => "SDR",        -- DDR, SDR
             DATA_WIDTH        => 8,            -- Parallel data width (2-8,10,14)
             DYN_CLKDIV_INV_EN => "FALSE",      -- Enable DYNCLKDIVINVSEL inversion (FALSE, TRUE)
             DYN_CLK_INV_EN    => "FALSE",      -- Enable DYNCLKINVSEL inversion (FALSE, TRUE)
             INIT_Q1           => '0',          -- INIT_Q1 - INIT_Q4: Initial value on the Q outputs (0/1)
             INIT_Q2           => '0',
             INIT_Q3           => '0',
             INIT_Q4           => '0',
             INTERFACE_TYPE    => "NETWORKING", -- MEMORY, MEMORY_DDR3, MEMORY_QDR, NETWORKING, OVERSAMPLE
             IOBDELAY          => "NONE",       -- NONE, BOTH, IBUF, IFD
             NUM_CE            => 1,            -- Number of clock enables (1,2)
             OFB_USED          => "FALSE",      -- Select OFB path (FALSE, TRUE)
             SERDES_MODE       => "MASTER",     -- MASTER, SLAVE
             SRVAL_Q1          => '0',          -- SRVAL_Q1 - SRVAL_Q4: Q output values when SR is used (0/1)
             SRVAL_Q2          => '0',
             SRVAL_Q3          => '0',
             SRVAL_Q4          => '0' )
          port map (
             O            => open,              -- 1-bit output: Combinatorial output
             Q1           => sync_data_in(0),   -- Q1 - Q8: 1-bit (each) output: Registered data outputs
             Q2           => sync_data_in(1),   -- FIXME: check order
             Q3           => sync_data_in(2),
             Q4           => sync_data_in(3),
             Q5           => sync_data_in(4),
             Q6           => sync_data_in(5),
             Q7           => sync_data_in(6),
             Q8           => sync_data_in(7),
             SHIFTOUT1    => open,              -- SHIFTOUT1, SHIFTOUT2: 1-bit (each) output: Data width expansion output ports
             SHIFTOUT2    => open,
             BITSLIP      => '0',               -- The BITSLIP pin performs a Bitslip operation synchronous
             CE1          => '1',               -- CE1, CE2: 1-bit (each) input: Data register clock enable inputs
             CE2          => '1',
             CLKDIVP      => '0',               -- 1-bit input: TBD
             CLK          => clk_rf_io_i,       -- Clocks: 1-bit (each) input: ISERDESE2 clock input ports
             CLKB         => clk_rf_io_n,
             CLKDIV       => clk_vtu_i,         -- 1-bit input: Divided clock
             OCLK         => '0',               -- 1-bit input: High speed output clock used when INTERFACE_TYPE="MEMORY"
             DYNCLKDIVSEL => '0',               -- 1-bit input: Dynamic CLKDIV inversion
             DYNCLKSEL    => '0',               -- 1-bit input: Dynamic CLK/CLKB inversion
             D            => sync_i,            -- 1-bit input: Data input
             DDLY         => '0',               -- 1-bit input: Serial data from IDELAYE2
             OFB          => '0',               -- 1-bit input: Data feedback from OSERDESE2
             OCLKB        => '0',               -- 1-bit input: High speed negative edge output clock
             RST          => rst_serdes_i, --rst_vtu,           -- 1-bit input: Active high asynchronous reset
             SHIFTIN1     => '0',               -- SHIFTIN1, SHIFTIN2: 1-bit (each) input: Data width expansion input ports
             SHIFTIN2     => '0' );
    end generate;

    g_t1_false : if not trigger_unit_t1 generate
    begin
       sync_data_in <= sync_x8_fb_i;
    end generate;

    sync_data_in_clean <= (others => '0') when rst_rf_n_i = '0' else sync_data_in;

    inst_vtu : entity work.vtuCore
        port map (
            --  Interface to the memory for the play memory mode (not used).
            Mem_RdDone => '1',
            Mem_Addr => open,
            Mem_RdData => x"00",
            Mem_RdEna => open,

            Mode => params_vtu.mode,

            bValue => params_vtu.bvalue,
            wrongB => vtu_wrongb,
            UseSyncAsStart => vtu_use_sync_as_start,

            htValue => params_vtu.htvalue,
            htSwitching => params_vtu.htswitchen,
            wrongHT => vtu_wronght,

            wValue => params_vtu.wvalue,
            wrongW => vtu_wrongw,

            Start => vtu_start_d2,
            Stop => vtu_stop,

            DataIn => sync_data_in_clean,
            DataOut => sync_data_out,

            Mem_LastBit => (others => '0'),
            Mem_LastElem => (others => '0'),

            SyncPulse => open,

            State_Running => vtu_running,
            State_Idle    => vtu_idle,
            State_Started => vtu_started,

            Rst => rst_vtu,
            Clk => clk_vtu_i);

    sync_x8_fb_o        <= sync_data_out;

    inst_trig_hcdelay : entity work.gc_sync
          port map (
             clk_i     => clk_vtu_i,
             rst_n_a_i => rst_vtu_n,
             d_i       => sys_trig_delay_hc,
             q_o       => trig_delay_hc );

    g_ddr : if g_output_stage = "ddr" generate
       inst_trig_x8oddr : entity work.x8oddr
          port map (
             clk_rf_io_i => clk_rf_io_i,
             clk_rf_i    => clk_rf_i,
             rst_rf_n_i  => rst_rf_n_i,
             clk_vtu_i   => clk_vtu_i,
             rst_vtu_n_i => rst_vtu_n,
             data_out_i  => sync_data_out,
             hc_delay_i  => trig_delay_hc,
             ddr_data_o  => trig_out,
             dbg_o       => dbg );
    end generate;

    g_serdes : if g_output_stage = "serdes" generate
    begin
       sync_data_out_clean <= (others => '0') when rst_rf_n_i = '0' else sync_data_out;
       sync_data_x16_nohc <= sync_data_out_clean(7) & sync_data_out_clean(7) &
                             sync_data_out_clean(6) & sync_data_out_clean(6) &
                             sync_data_out_clean(5) & sync_data_out_clean(5) &
                             sync_data_out_clean(4) & sync_data_out_clean(4) &
                             sync_data_out_clean(3) & sync_data_out_clean(3) &
                             sync_data_out_clean(2) & sync_data_out_clean(2) &
                             sync_data_out_clean(1) & sync_data_out_clean(1) &
                             sync_data_out_clean(0) & sync_data_out_clean(0);
       sync_data_x16_hc   <= sync_data_out_clean_r(0) & sync_data_out_clean(7) &
                             sync_data_out_clean(7)   & sync_data_out_clean(6) &
                             sync_data_out_clean(6)   & sync_data_out_clean(5) &
                             sync_data_out_clean(5)   & sync_data_out_clean(4) &
                             sync_data_out_clean(4)   & sync_data_out_clean(3) &
                             sync_data_out_clean(3)   & sync_data_out_clean(2) &
                             sync_data_out_clean(2)   & sync_data_out_clean(1) &
                             sync_data_out_clean(1)   & sync_data_out_clean(0);
       sync_data_x16      <= sync_data_x16_nohc when trig_delay_hc = '0' else sync_data_x16_hc;

       clk_vtu_hold_delay_n <= (not clk_vtu_i);
       clk_vtu_hold_delay   <= (not clk_vtu_hold_delay_n);

       process (clk_vtu_i) is
       begin
          if rising_edge(clk_vtu_i) then
             sync_data_out_clean_r <= sync_data_out_clean;
          end if;
       end process;

       process (clk_vtu_x2_i) is
       begin
          if rising_edge(clk_vtu_x2_i) then
             vtu_clk_r <= clk_vtu_hold_delay;
          end if;
       end process;

       g_hc_true : if g_hc_string = "DDR" generate
       begin
          sync_data_oserdes <= sync_data_x16(15 downto 8) when vtu_clk_r = '0' else sync_data_x16(7 downto 0);
       end generate;
       g_hc_false : if g_hc_string = "SDR" generate
       begin
          sync_data_oserdes <= sync_data_out_clean;
       end generate;

       -- find a rising edge in the VTU output
       vtu_trig_redge <= '1' when ((sync_data_oserdes_r(0) = '0' and sync_data_oserdes(7) = '1') or
                               (sync_data_oserdes(7)   = '0' and sync_data_oserdes(6) = '1') or
                               (sync_data_oserdes(6)   = '0' and sync_data_oserdes(5) = '1') or
                               (sync_data_oserdes(5)   = '0' and sync_data_oserdes(4) = '1') or
                               (sync_data_oserdes(4)   = '0' and sync_data_oserdes(3) = '1') or
                               (sync_data_oserdes(3)   = '0' and sync_data_oserdes(2) = '1') or
                               (sync_data_oserdes(2)   = '0' and sync_data_oserdes(1) = '1') or
                               (sync_data_oserdes(1)   = '0' and sync_data_oserdes(0) = '1')) else '0';
       
       process (clk_vtu_x2_i) is
       begin
          if (rising_edge(clk_vtu_x2_i)) then
             sync_data_oserdes_r <= sync_data_oserdes;
             vtu_trig_redge_r    <= vtu_trig_redge;
          end if;
       end process;

       inst_redge_sync: entity work.gc_sync
          port map ( 
             clk_i     => clk_sys_i,
             rst_n_a_i => rst_sys_n,
             d_i       => vtu_trig_redge_r,
             q_o       => vtu_trig_redge_sys);

       first_vtu_trig_active <= '1' when start_pulse_2      = '1' else
                                '0' when vtu_trig_redge_sys = '1' else first_vtu_trig_active_r;
       
       process (clk_sys_i) is
       begin
          if (rising_edge(clk_sys_i)) then
             first_vtu_trig_active_r <= first_vtu_trig_active;             
             if vtu_trig_redge_sys = '1' then
                last_vtu_trig_tai_sec_r    <= X"000000" & tm_tai_i;
                last_vtu_trig_tai_cycles_r <= X"0"      & tm_cycles_i;
                if (first_vtu_trig_active_r = '1') then
                   first_vtu_trig_tai_sec_r    <= X"000000" & tm_tai_i;
                   first_vtu_trig_tai_cycles_r <= X"0"      & tm_cycles_i;
                end if;
             end if; 
          end if;
       end process;

       -- determine if there was a rising edge in the trigger output...

       inst_trig_OSERDESE2 : OSERDESE2
          generic map (
             DATA_RATE_OQ => g_hc_string,    -- DDR, SDR
             DATA_RATE_TQ => "SDR",          -- DDR, BUF, SDR
             DATA_WIDTH => 8,                -- Parallel data width (2-8,10,14)
             INIT_OQ => '0',                 -- Initial value of OQ output (1'b0,1'b1)
             INIT_TQ => '0',                 -- Initial value of TQ output (1'b0,1'b1)
             SERDES_MODE => "MASTER",        -- MASTER, SLAVE
             SRVAL_OQ => '0',                -- OQ output value when SR is used (1'b0,1'b1)
             SRVAL_TQ => '0',                -- TQ output value when SR is used (1'b0,1'b1)
             TBYTE_CTL => "FALSE",           -- Enable tristate byte operation (FALSE, TRUE)
             TBYTE_SRC => "FALSE",           -- Tristate byte source (FALSE, TRUE)
             TRISTATE_WIDTH => 1             -- 3-state converter width (1,4)
             )
          port map (
             OFB => trig_out,                -- 1-bit output: Feedback path for data
             OQ => open,                     -- 1-bit output: Data path output
             SHIFTOUT1 => open,              -- SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each)
             SHIFTOUT2 => open,
             TBYTEOUT => open,               -- 1-bit output: Byte group tristate
             TFB => open,                    -- 1-bit output: 3-state control
             TQ => open,                     -- 1-bit output: 3-state control
             CLK => clk_rf_io_i,             -- 1-bit input: High speed clock
             CLKDIV => clk_vtu_x2_i,         -- 1-bit input: Divided clock
             D1 => sync_data_oserdes(7),     -- D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each)
             D2 => sync_data_oserdes(6),
             D3 => sync_data_oserdes(5),
             D4 => sync_data_oserdes(4),
             D5 => sync_data_oserdes(3),
             D6 => sync_data_oserdes(2),
             D7 => sync_data_oserdes(1),
             D8 => sync_data_oserdes(0),
             OCE => '1',                     -- 1-bit input: Output data clock enable
             RST => rst_serdes_i,            -- 1-bit input: Reset
             SHIFTIN1 => '0',                -- SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each)
             SHIFTIN2 => '0',
             T1 => '0',                      -- T1 - T4: 1-bit (each) input: Parallel 3-state inputs
             T2 => '0',
             T3 => '0',
             T4 => '0',
             TBYTEIN => '0',                 -- 1-bit input: Byte group tristate
             TCE => '0'                      -- 1-bit input: 3-state clock enable
             );
    end generate;

    g_odelay_out : for i in 0 to 4 generate
    begin
       inst_odelay_trig: entity work.gc_sync
          port map (
             clk_i     => clk_vtu_i,       -- clock from the destination clock domain
             rst_n_a_i => rst_vtu_n,       -- async reset
             d_i       => sys_trig_odelay(i), -- async input
             q_o       => trig_odelay(i) );   -- positive edge detect output
    end generate;

    inst_trig_ODELAYE2 : ODELAYE2
        generic map (
            CINVCTRL_SEL          => "FALSE",    -- Enable dynamic clock inversion (FALSE, TRUE)
            DELAY_SRC             => "ODATAIN",  -- Delay input (ODATAIN, CLKIN)
            HIGH_PERFORMANCE_MODE => "TRUE",     -- Reduced jitter ("TRUE"), Reduced power ("FALSE")
            ODELAY_TYPE           => "VAR_LOAD", -- FIXED, VARIABLE, VAR_LOAD, VAR_LOAD_PIPE
            ODELAY_VALUE          => 0,          -- Output delay tap setting (0-31)
            PIPE_SEL              => "FALSE",    -- Select pipelined mode, FALSE, TRUE
            REFCLK_FREQUENCY      => 200.0,      -- IDELAYCTRL clock input frequency in MHz (190.0-210.0, 290.0-310.0).
            SIGNAL_PATTERN        => "DATA"      -- DATA, CLOCK input signal
            )
        port map (
            CNTVALUEOUT => open,               -- output: Counter value output
            DATAOUT     => trig_o,             -- output: Delayed data/clock output
            C           => clk_vtu_i,          -- input: Clock input
            CE          => '0',                -- input: Active high enable increment/decrement input
            CINVCTRL    => '0',                -- input: Dynamic clock inversion input
            CLKIN       => '0',                -- input: Clock delay input
            CNTVALUEIN  => trig_odelay,        -- input: Counter value input
            INC         => '0',                -- input: Increment / Decrement tap delay input
            LD          => '1',                -- input: Loads ODELAY_VALUE tap delay in VARIABLE mode
            LDPIPEEN    => '0',                -- input: Enables the pipeline register to load data
            ODATAIN     => trig_out,           -- input: Output delay data input
            REGRST      => rst_sys_i           -- input: Active-high reset tap-delay input
        );

    process (clk_sys_i)
    begin
       if (rising_edge(clk_sys_i)) then
          trig_rst_r <= trig_rst_i;
       end if;
    end process;

    inst_trig_rst_OBUFDS: OBUFDS
        generic map (
            IOSTANDARD => "LVDS" )
        port map (
            I  => trig_rst_r,
            O  => trig_rst_p_o,
            OB => trig_rst_n_o );
end rtl;
