-------------------------------------------------------------
-- MSS copyright 2011-2012
--	Filename:  SYMBOL_CLKGEN.VHD
-- Authors: 
--		Alain Zarembowitch / MSS, 
--	Version: 2
--	Date last modified: 2/20/12
-- Inheritance: 	none
--
-- description: Generate a fully programmable clocks for tx and rx symbols. 
-- A fixed PLL generates a fixed frequency 300 MHz clock from a 60 MHz frequency reference.
-- A follow-on DDS generates a clock with fine-programmable frequency 
-- For maximum speed, the DDS is split into two parts: a 10-bit DDS with a step size alternating
-- between N and N+1 (N represents the upper 10 frequency bits), statistically reflecting 
-- the lower 10 frequency bits. 
-- This code is optimized for speed.
--
-- Rev 2 2/20/12 AZ
-- reduced PLL clock from 360 to 300 MHz (too difficult to meet timing)
----------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
use ieee.numeric_std.all;
library unisim;
use unisim.vcomponents.all;

entity SYMBOL_CLKGEN is
port
 (
  USB_CLK60G           : in     std_logic;
	-- input reference clock global. 60 MHz (PLL valid input range 19 - 375 MHz)
  CLK_PLLG: out std_logic;
	-- fixed high frequency clock from PLL. Global. 
	-- Here 300 MHz
  NCO_FREQ_TX: in std_logic_vector(19 downto 0);
	-- DDS frequencies. Expressed as f(in MHz)/300*2^20
	-- The frequencies can be changed on the fly.
  TXCLKG          : out    std_logic;
	-- Clock out ports (after BUFGs)
  LOCKED_PLL            : out    std_logic
	-- Status and control signals
 );
end entity;

architecture Behavioral of SYMBOL_CLKGEN is
--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
signal CLKFB1: std_logic;
signal CLKOUT300: std_logic;
signal CLKOUT300G: std_logic;
signal NCO_ACC: std_logic_vector(9 downto 0) := (others => '0');
signal CLK_FREQA: std_logic_vector(9 downto 0) := (others => '0');
signal CLK_FREQB: std_logic_vector(9 downto 0) := (others => '0');
signal CNTR: std_logic_vector(9 downto 0) := (others => '0');
signal CNTR_SHUFFLED: std_logic_vector(9 downto 0) := (others => '0');
signal NPLUSONESEL: std_logic := '0';
signal NCO_ACC_MSB_D: std_logic := '0';
signal TXCLK: std_logic := '0';

--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
begin


-- first PLL generates a fixed 300 MHz from the 60 MHz reference
PLL_BASE_001 : PLL_BASE
generic map
(BANDWIDTH            => "LOW",	-- fixed PLL, prefer low jitter to fast lock
 CLK_FEEDBACK         => "CLKFBOUT",
 COMPENSATION         => "INTERNAL",
 DIVCLK_DIVIDE        => 1,	
 CLKFBOUT_MULT        => 15,	-- 900 MHz VCO / 15 = 60 MHz input reference freq.
 CLKFBOUT_PHASE       => 0.000,
 CLKOUT0_DIVIDE       => 3,	-- 300 MHz output
 CLKOUT0_PHASE        => 0.0,
 CLKOUT0_DUTY_CYCLE   => 0.500,
 CLKIN_PERIOD         => 16.666,
 REF_JITTER           => 0.010)
port map
 -- Output clocks
(CLKFBOUT            => CLKFB1,
 CLKOUT0             => CLKOUT300,
 -- Status and control signals
 LOCKED              => LOCKED_PLL,
 RST                 => '0',
 -- Input clock control
 CLKFBIN             => CLKFB1,
 CLKIN               => USB_CLK60G
 );


-- make 300 MHz CLK0 global
BUFG_001: BUFG port map(I => CLKOUT300, O => CLKOUT300G);
CLK_PLLG <= CLKOUT300G;

-- TX DDS to generate the tx symbol rate with fine tuning
NCO_ACC_001: process(CLKOUT300G)
begin
	if rising_edge(CLKOUT300G) then
		NCO_ACC <= NCO_ACC + CLK_FREQB;
		
		NCO_ACC_MSB_D <= NCO_ACC(NCO_ACC'left);
		if(NCO_ACC(NCO_ACC'left) /= NCO_ACC_MSB_D) then
			TXCLK <= not TXCLK;
		end if;
	end if;
end process;

NCO_ACC_002: process(CLKOUT300G)
begin
	if rising_edge(CLKOUT300G) then
		-- fractional-N DDS: statistically alternate DDS step value between N and N+1 
		if(NPLUSONESEL = '0') then
			CLK_FREQB <= CLK_FREQA;
		else
			CLK_FREQB <= CLK_FREQA + 1;
		end if;
	end if;
end process;

-- make it a fractional-N DDS (it would be easier to do a 20-bit accumulator, but slower)
FRACT_N_001: process(CLKOUT300G)
begin
	if rising_edge(CLKOUT300G) then
		CNTR <= CNTR + 1;
		if(CNTR_SHUFFLED > NCO_FREQ_TX(9 downto 0)) then -- lower 10 frequency bits
			NPLUSONESEL <= '0';
		else
			NPLUSONESEL <= '1';
		end if;
	end if;
end process;

-- shuffle the counter bits to randomize the fractional-N DDS
CNTR_SHUFFLED(9) <= CNTR(0);
CNTR_SHUFFLED(8) <= CNTR(1);
CNTR_SHUFFLED(7) <= CNTR(2);
CNTR_SHUFFLED(6) <= CNTR(3);
CNTR_SHUFFLED(5) <= CNTR(4);
CNTR_SHUFFLED(4) <= CNTR(5);
CNTR_SHUFFLED(3) <= CNTR(6);
CNTR_SHUFFLED(2) <= CNTR(7);
CNTR_SHUFFLED(1) <= CNTR(8);
CNTR_SHUFFLED(0) <= CNTR(9);

-- just reclock for better timing
NCO_ACC_GEN_003: process(CLKOUT300G)
begin
	if rising_edge(CLKOUT300G) then
		-- no phase correction. just the nominal phase increment.
		CLK_FREQA <= NCO_FREQ_TX(19 downto 10);
	end if;
end process;

-- make output global
BUFG_002: BUFG port map(I => TXCLK, O => TXCLKG);


end Behavioral;
