-------------------------------------------------------------------------------
--
-- File: RadioClocking.vhd
-- Author: Daniel Jepson
-- Original Project: N310
-- Date: 22 February 2016
--
-------------------------------------------------------------------------------
-- Copyright 2016-2018 Ettus Research, A National Instruments Company
-- SPDX-License-Identifier: LGPL-3.0
-------------------------------------------------------------------------------
--
-- Purpose:
--
-- Instantiates a MMCM to produce 1x, 2x, and 3x versions of the Radio Clock
-- coming from the FPGA input pin. Handles all the buffering for the input clock.
-- Additionally allows the clocks to be turned on and off, and phase shifted.
--
-- NOTE: This module hard-codes the MMCM settings for a SPECIFIC clock rate!
--
-------------------------------------------------------------------------------

library ieee;
  use ieee.std_logic_1164.all;

library unisim;
  use unisim.vcomponents.all;


entity RadioClocking is
  port (
    -- Async reset. Can be tied low if desired.
    aReset                 : in  boolean;
    -- Sync reset... used in the same places as the async one.
    bReset                 : in  boolean;

    -- Should be a always-on clock
    BusClk                 : in  std_logic;

    -- Sync reset to the RadioClkMmcm.
    bRadioClkMmcmReset     : in  std_logic;

    -- Locked indication from the RadioClkMmcm in BusClk and aReset domains.
    bRadioClksValid        : out std_logic;

    bRadioClk1xEnabled     : in  std_logic;
    bRadioClk2xEnabled     : in  std_logic;
    bRadioClk3xEnabled     : in  std_logic;

    -- Phase shift interface for the RadioClkMmcm. PsClk must be <= 200 MHz.
    pPsInc                 : in  std_logic;
    pPsEn                  : in  std_logic;
    PsClk                  : in  std_logic;
    pPsDone                : out std_logic;

    -- Straight from pins. Buffer included in here.
    FpgaClk_n              : in  std_logic;
    FpgaClk_p              : in  std_logic;

    RadioClk1x             : out std_logic;
    RadioClk2x             : out std_logic;
    RadioClk3x             : out std_logic

  );
end RadioClocking;


architecture rtl of RadioClocking is

  --vhook_sigstart
  signal RadioClk1xLcl: std_logic;
  signal RadioClk1xPll: std_logic;
  signal RadioClk2xLcl: std_logic;
  signal RadioClk2xPll: std_logic;
  signal RadioClk3xLcl: std_logic;
  signal RadioClk3xPll: std_logic;
  --vhook_sigend

  signal RadioClkMmcmFeedbackIn,
         RadioClkMmcmFeedbackOut,
         FpgaClkSE,
         aRadioClkMmcmLocked : std_logic;

  signal bRadioClkMmcmLocked_ms,
         bRadioClkMmcmLocked,
         bEnableRadioClkBufgOutputs,
         bEnableRadioClk1xBufgOutput,
         bEnableRadioClk2xBufgOutput,
         bEnableRadioClk3xBufgOutput : std_logic := '0';

  signal aRadioClkMmcmResetInternal  : std_logic := '1';

  attribute ASYNC_REG : string;
  attribute ASYNC_REG of bRadioClkMmcmLocked_ms : signal is "true";
  attribute ASYNC_REG of bRadioClkMmcmLocked    : signal is "true";

begin

  -- Radio Clock Buffering : ------------------------------------------------------------
  --
  -- ------------------------------------------------------------------------------------
  --vhook_i IBUFDS FpgaClkIbufg hidegeneric=true
  --vhook_a I  FpgaClk_p
  --vhook_a IB FpgaClk_n
  --vhook_a O  FpgaClkSE
  FpgaClkIbufg: IBUFDS
    port map (
      O  => FpgaClkSE,  --out std_ulogic
      I  => FpgaClk_p,  --in  std_ulogic
      IB => FpgaClk_n); --in  std_ulogic
 

  ResetDelay : process(aReset, BusClk)
  begin
    if aReset then
      aRadioClkMmcmResetInternal   <= '1';
    elsif rising_edge(BusClk) then
      if bReset then
        aRadioClkMmcmResetInternal   <= '1';
      else
        -- Delay by 1 to allow the BUFGs to turn off before the MMCM is reset.
        aRadioClkMmcmResetInternal <= bRadioClkMmcmReset;
      end if;
    end if;
  end process ResetDelay;


  RadioClkMmcm: MMCME2_ADV
    generic map(
      COMPENSATION         => "ZHOLD",
      BANDWIDTH            => "OPTIMIZED",
      CLKFBOUT_MULT_F      => 4.000,    -- Feedback
      CLKOUT0_DIVIDE_F     => 4.000,    -- Data Clock 1x, RadioClk1xPll
      CLKOUT1_DIVIDE       => 2,        -- Data Clock 2x, RadioClk2xPll
      CLKOUT2_DIVIDE       => 2,        -- Data Clock 3x, RadioClk3xPll
      CLKOUT3_DIVIDE       => 1,        -- unused
      CLKOUT4_DIVIDE       => 1,        -- unused
      CLKOUT5_DIVIDE       => 1,        -- unused
      CLKOUT6_DIVIDE       => 1,        -- unused
      CLKFBOUT_PHASE       => 0.000,    -- Feedback
      CLKOUT0_PHASE        => 0.000,    -- Data Clock 1x
      CLKOUT1_PHASE        => 0.000,    -- Data Clock 2x
      CLKOUT2_PHASE        => 0.000,    -- Data Clock 3x
      CLKOUT3_PHASE        => 0.000,    -- unused
      CLKOUT4_PHASE        => 0.000,    -- unused
      CLKOUT5_PHASE        => 0.000,    -- unused
      CLKOUT6_PHASE        => 0.000,    -- unused
      CLKOUT0_DUTY_CYCLE   => 0.500,
      CLKOUT1_DUTY_CYCLE   => 0.500,
      CLKOUT2_DUTY_CYCLE   => 0.500,
      CLKOUT3_DUTY_CYCLE   => 0.500,
      CLKOUT4_DUTY_CYCLE   => 0.500,
      CLKOUT5_DUTY_CYCLE   => 0.500,
      CLKOUT6_DUTY_CYCLE   => 0.500,
      DIVCLK_DIVIDE        => 1,
      REF_JITTER1          => 0.010,
      CLKIN1_PERIOD        => 4.069,    -- 245.76 MHz max
      CLKFBOUT_USE_FINE_PS => true,
      CLKOUT0_USE_FINE_PS  => false,
      CLKOUT1_USE_FINE_PS  => false,
      CLKOUT2_USE_FINE_PS  => false,
      CLKOUT3_USE_FINE_PS  => false,
      CLKOUT4_USE_FINE_PS  => false,
      CLKOUT5_USE_FINE_PS  => false,
      CLKOUT6_USE_FINE_PS  => false,
      STARTUP_WAIT         => false,
      CLKOUT4_CASCADE      => false)
    port map (
      CLKINSEL     => '1',
      CLKIN1       => FpgaClkSE,
      CLKIN2       => '0',
      CLKFBIN      => RadioClkMmcmFeedbackIn,
      RST          => aRadioClkMmcmResetInternal,
      PWRDWN       => '0',
      DADDR        => (others => '0'),
      DI           => (others => '0'),
      DWE          => '0',
      DEN          => '0',
      DCLK         => '0',
      DO           => open,
      DRDY         => open,
      PSINCDEC     => pPsInc,
      PSEN         => pPsEn,
      PSCLK        => PsClk,
      PSDONE       => pPsDone,
      CLKOUT0      => RadioClk1xPll,
      CLKOUT0B     => open,
      CLKOUT1      => RadioClk2xPll,
      CLKOUT1B     => open,
      CLKOUT2      => RadioClk3xPll,
      CLKOUT2B     => open,
      CLKOUT3      => open,
      CLKOUT3B     => open,
      CLKOUT4      => open,
      CLKOUT5      => open,
      CLKOUT6      => open,
      CLKFBOUT     => RadioClkMmcmFeedbackOut,
      CLKFBOUTB    => open,
      LOCKED       => aRadioClkMmcmLocked,
      CLKINSTOPPED => open,
      CLKFBSTOPPED => open);

  RadioClkMmcmFeedbackBufg: BUFG
    port map (
      I => RadioClkMmcmFeedbackOut,
      O => RadioClkMmcmFeedbackIn
    );


  -- Only enable the WRAPBUFGs when the MMCM is locked. If the MMCM is ever placed in
  -- reset, we turn off the clocks one cycle before the asynchronous version
  -- (aRadioClkMmcmResetInternal) reaches the MMCM inputs in order to prevent
  -- output glitches.
  CombineEnablesForBuffers : process(aReset, BusClk)
  begin
    if aReset then
      bRadioClkMmcmLocked_ms <= '0';
      bRadioClkMmcmLocked    <= '0';
      bEnableRadioClk1xBufgOutput <= '0';
      bEnableRadioClk2xBufgOutput <= '0';
      bEnableRadioClk3xBufgOutput <= '0';
      bEnableRadioClkBufgOutputs  <= '0';
    elsif rising_edge(BusClk) then
      if bReset then
        bRadioClkMmcmLocked_ms <= '0';
        bRadioClkMmcmLocked    <= '0';
        bEnableRadioClk1xBufgOutput <= '0';
        bEnableRadioClk2xBufgOutput <= '0';
        bEnableRadioClk3xBufgOutput <= '0';
        bEnableRadioClkBufgOutputs  <= '0';
      else
        bRadioClkMmcmLocked_ms <= aRadioClkMmcmLocked;
        bRadioClkMmcmLocked    <= bRadioClkMmcmLocked_ms;

        bEnableRadioClkBufgOutputs <= bRadioClkMmcmLocked and
                                      not bRadioClkMmcmReset;
        bEnableRadioClk1xBufgOutput <= bRadioClk1xEnabled and bEnableRadioClkBufgOutputs;
        bEnableRadioClk2xBufgOutput <= bRadioClk2xEnabled and bEnableRadioClkBufgOutputs;
        bEnableRadioClk3xBufgOutput <= bRadioClk3xEnabled and bEnableRadioClkBufgOutputs;
      end if;
    end if;
  end process CombineEnablesForBuffers;

  bRadioClksValid <= bEnableRadioClkBufgOutputs;

  --vhook_e WrapBufg         RadioClk1xBuf
  --vhook_a kEnableByDefault false
  --vhook_a kIgnore          false
  --vhook_a kEnableIsAsync   true
  --vhook_a ClkIn            RadioClk1xPll
  --vhook_a aCe              bEnableRadioClk1xBufgOutput
  --vhook_a ClkOut           RadioClk1xLcl
  RadioClk1xBuf: entity work.WrapBufg (rtl)
    generic map (
      kEnableByDefault => false,  --boolean:=false
      kIgnore          => false,  --boolean:=false
      kEnableIsAsync   => true)   --boolean:=false
    port map (
      ClkIn  => RadioClk1xPll,                --in  std_logic
      aCe    => bEnableRadioClk1xBufgOutput,  --in  std_logic
      ClkOut => RadioClk1xLcl);               --out std_logic

  --vhook_e WrapBufg         RadioClk2xBuf
  --vhook_a kEnableByDefault false
  --vhook_a kIgnore          false
  --vhook_a kEnableIsAsync   true
  --vhook_a ClkIn            RadioClk2xPll
  --vhook_a aCe              bEnableRadioClk2xBufgOutput
  --vhook_a ClkOut           RadioClk2xLcl
  RadioClk2xBuf: entity work.WrapBufg (rtl)
    generic map (
      kEnableByDefault => false,  --boolean:=false
      kIgnore          => false,  --boolean:=false
      kEnableIsAsync   => true)   --boolean:=false
    port map (
      ClkIn  => RadioClk2xPll,                --in  std_logic
      aCe    => bEnableRadioClk2xBufgOutput,  --in  std_logic
      ClkOut => RadioClk2xLcl);               --out std_logic

  --vhook_e WrapBufg         RadioClk3xBuf
  --vhook_a kEnableByDefault false
  --vhook_a kIgnore          false
  --vhook_a kEnableIsAsync   true
  --vhook_a ClkIn            RadioClk3xPll
  --vhook_a aCe              bEnableRadioClk3xBufgOutput
  --vhook_a ClkOut           RadioClk3xLcl
  RadioClk3xBuf: entity work.WrapBufg (rtl)
    generic map (
      kEnableByDefault => false,  --boolean:=false
      kIgnore          => false,  --boolean:=false
      kEnableIsAsync   => true)   --boolean:=false
    port map (
      ClkIn  => RadioClk3xPll,                --in  std_logic
      aCe    => bEnableRadioClk3xBufgOutput,  --in  std_logic
      ClkOut => RadioClk3xLcl);               --out std_logic


  -- Assign outputs from locals.
  RadioClk1x   <= RadioClk1xLcl;
  RadioClk2x   <= RadioClk2xLcl;
  RadioClk3x   <= RadioClk3xLcl;



end rtl;
