--------------------------------------------------------------------------------
-- CERN BE-CO-HT
--
-- https://www.ohwr.org/projects/wr2rf-vme
--------------------------------------------------------------------------------
--
-- unit name:   wr2rf_sysclks
--
-- description: Provides clean clocks for further distribution and usage
--
--------------------------------------------------------------------------------
-- Copyright CERN 2020
--------------------------------------------------------------------------------
-- 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;

library unisim;
use unisim.vcomponents.all;

entity wr2rf_sysclks is

    port (
        clk_sys_i       : in  std_logic;
        reset_i         : in  std_logic;

        mmcm_locked_o       : out std_logic;        
        mmcm_shift_incdec_i : in  std_logic;
        mmcm_shift_en_i     : in  std_logic;
        mmcm_shift_busy_o   : out std_logic;

        clk62m5_o       : out std_logic;
        rst_clk62m5_n_o : out std_logic;
        rst_clk62m5_o   : out std_logic;        

        clk125m_o       : out std_logic;
        rst_clk125m_n_o : out std_logic;
        rst_clk125m_o   : out std_logic;
        
        clk250m_o       : out std_logic;
        rst_clk250m_n_o : out std_logic;
        rst_clk250m_o   : out std_logic;

        clk500m_o       : out std_logic;
        rst_clk500m_n_o : out std_logic;
        rst_clk500m_o   : out std_logic;                

        clk200m_o       : out std_logic;
        rst_clk200m_n_o : out std_logic;
        rst_clk200m_o   : out std_logic );    

end entity;

architecture rtl of wr2rf_sysclks is

    -- Input clock buffering / unused connectors
    signal clkin1      : std_logic;
    -- Output clock buffering / unused connectors
    signal clkfbout         : std_logic;
    signal clkfbout_buf     : std_logic;
    signal clkfboutb_unused : std_logic;
    signal clkout0          : std_logic;
    signal clkout0b_unused  : std_logic;
    signal clkout1          : std_logic;
    signal clkout1b_unused  : std_logic;
    signal clkout2          : std_logic;
    signal clkout2b_unused  : std_logic;
    signal clkout3          : std_logic;
    signal clkout3b_unused  : std_logic;
    signal clkout4          : std_logic;
    signal clkout5_unused   : std_logic;
    signal clkout6_unused   : std_logic;
    -- Dynamic programming unused signals
    signal do_unused        : std_logic_vector(15 downto 0);
    signal drdy_unused      : std_logic;
    -- Dynamic phase shift unused signals
    signal psdone_unused    : std_logic;
    -- Unused status signals
    signal clkfbstopped_unused : std_logic;
    signal clkinstopped_unused : std_logic;

    signal arst     : std_logic;
    signal clks     : std_logic_vector(4 downto 0);
    signal rsts_n   : std_logic_vector(4 downto 0);
    signal locked   : std_logic;

    signal mmcm_shift_done : std_logic;

    attribute keep_hierarchy                    : STRING;
    attribute keep_hierarchy of inst_aasd       : label is "yes";

begin

    mmcm_adv_inst : MMCME2_ADV
        generic map(
            BANDWIDTH            => "OPTIMIZED",
            CLKOUT4_CASCADE      => FALSE,
            COMPENSATION         => "ZHOLD",
            STARTUP_WAIT         => FALSE,
            DIVCLK_DIVIDE        => 1,
            CLKFBOUT_MULT_F      => 16.000,     --  1Ghz
            CLKFBOUT_PHASE       => 0.000,
            CLKFBOUT_USE_FINE_PS => FALSE,
            CLKOUT0_DIVIDE_F     => 16.000,  -- 62.5 MHz
            CLKOUT0_PHASE        => 0.000,
            CLKOUT0_DUTY_CYCLE   => 0.500,
            CLKOUT0_USE_FINE_PS  => FALSE,
            CLKOUT1_DIVIDE       => 8,  -- 125 MHz
            CLKOUT1_PHASE        => 0.000,
            CLKOUT1_DUTY_CYCLE   => 0.500,
            CLKOUT1_USE_FINE_PS  => FALSE,
            CLKOUT2_DIVIDE       => 4,  -- 250 MHz
            CLKOUT2_PHASE        => 0.000,
            CLKOUT2_DUTY_CYCLE   => 0.500,
            CLKOUT2_USE_FINE_PS  => FALSE,
            CLKOUT3_DIVIDE       => 5,  -- 200 MHz
            CLKOUT3_PHASE        => 0.000,
            CLKOUT3_DUTY_CYCLE   => 0.500,
            CLKOUT3_USE_FINE_PS  => FALSE,
            CLKOUT4_DIVIDE       => 2,  -- 500 MHz
            CLKOUT4_PHASE        => 0.000,
            CLKOUT4_DUTY_CYCLE   => 0.500,
            CLKOUT4_USE_FINE_PS  => FALSE,
            CLKOUT5_DIVIDE       => 0, 
            CLKOUT5_PHASE        => 0.000,
            CLKOUT5_DUTY_CYCLE   => 0.500,
            CLKOUT5_USE_FINE_PS  => FALSE,            
            CLKIN1_PERIOD        => 16.000 )
        port map ( -- Output clocks
            CLKFBOUT            => clkfbout,
            CLKFBOUTB           => clkfboutb_unused,
            CLKOUT0             => clkout0,
            CLKOUT0B            => clkout0b_unused,
            CLKOUT1             => clkout1,
            CLKOUT1B            => clkout1b_unused,
            CLKOUT2             => clkout2,
            CLKOUT2B            => clkout2b_unused,
            CLKOUT3             => clkout3,
            CLKOUT3B            => clkout3b_unused,
            CLKOUT4             => clkout4,
            CLKOUT5             => clkout5_unused,
            CLKOUT6             => clkout6_unused,
            -- Input clock control
            CLKFBIN             => clkfbout_buf,
            CLKIN1              => clk_sys_i,
            CLKIN2              => '0',
            -- Tied to always select the primary input clock
            CLKINSEL            => '1',
            -- Ports for dynamic reconfiguration
            DADDR               => (others => '0'),
            DCLK                => '0',
            DEN                 => '0',
            DI                  => (others => '0'),
            DO                  => do_unused,
            DRDY                => drdy_unused,
            DWE                 => '0',
            -- Ports for dynamic phase shift
            PSCLK               => clk_sys_i,
            PSEN                => mmcm_shift_en_i,
            PSINCDEC            => mmcm_shift_incdec_i,
            PSDONE              => mmcm_shift_done,
            -- Other control and status signals
            LOCKED              => locked,
            CLKINSTOPPED        => clkinstopped_unused,
            CLKFBSTOPPED        => clkfbstopped_unused,
            PWRDWN              => '0',
            RST                 => reset_i );

    process (clk_sys_i)
    begin
      if rising_edge (clk_sys_i) then
        if reset_i = '1' then
          mmcm_shift_busy_o <= '0';
        else
          if mmcm_shift_en_i = '1' then
            mmcm_shift_busy_o <= '1';
          elsif mmcm_shift_done = '1' then
            mmcm_shift_busy_o <= '0';
          end if;
        end if;
      end if;
    end process;

    clkf_buf : BUFG
        port map (
            I => clkfbout,
            O => clkfbout_buf );

    clks(0) <= clkout0; -- this is clock buffered later by a bufgmux

    clk125m_buf : BUFG
        port map (
            I => clkout1,
            O => clks(1) );

    clk250m_buf : BUFG
        port map (
            I => clkout2,
            O => clks(2) );

    clk200m_buf : BUFG
        port map (
            I => clkout3,
            O => clks(3) );

    clk500m_buf : BUFG
        port map (
            I => clkout4,
            O => clks(4) );    
    

    mmcm_locked_o <= locked;
    arst          <= '1' when locked = '0' else '0';

    inst_aasd : entity work.gc_reset_multi_aasd
        generic map (
            g_CLOCKS   => 5,
            g_RST_LEN  => 5 )
        port map (
            arst_i     => arst,
            clks_i     => clks,
            rst_n_o    => rsts_n );

    clk62m5_o   <= clks(0);
    clk125m_o   <= clks(1);
    clk250m_o   <= clks(2);
    clk200m_o   <= clks(3);
    clk500m_o   <= clks(4);    

    rst_clk62m5_n_o <= rsts_n(0);
    rst_clk125m_n_o <= rsts_n(1);
    rst_clk250m_n_o <= rsts_n(2);
    rst_clk200m_n_o <= rsts_n(3);
    rst_clk500m_n_o <= rsts_n(4);    

    rst_clk62m5_o   <= not rsts_n(0);
    rst_clk125m_o   <= not rsts_n(1);
    rst_clk250m_o   <= not rsts_n(2);
    rst_clk200m_o   <= not rsts_n(3);
    rst_clk500m_o   <= not rsts_n(4);          
    
end architecture;
