-------------------------------------------------------------
-- MSS copyright 2012
--	Filename:  AD9747_DAC_DRIVER2.VHD
-- Author: 
--		Alain Zarembowitch / MSS, 
--	Version: 0
--	Date last modified: 5/15/12
-- Inheritance: 	none
--
-- description: 250 MHz dual 16-bit DAC driver.
-- Resampling at a fixed (and low-jitter) 160 MSamples/s. 
-- Includes 2 LPFs: one with 40 MHz passband, the other with 80 MHz passband 
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
library UNISIM;
use UNISIM.VComponents.all;

entity AD9747_DAC_DRIVER2 is
    port ( 
			--// Clocks Resets
		   CLK60G: in     std_logic;
			-- input reference clock global. 60 MHz (PLL valid input range 19 - 375 MHz)
			
			--// User-generated samples to DAC
			-- Format: 16-bit signed (2's complement)
			CLK: in std_logic;
				-- synchronous clock for input samples
			DAC1_DIN: in std_logic_vector(15 downto 0);
			DAC2_DIN: in std_logic_vector(15 downto 0);
			
			--// Controls
			-- Select filter (or none if no filering)
			FILA_EN: in std_logic;	
				-- enable the 40 MHz LPF
			FILB_EN: in std_logic;	
				-- enable the 80 MHz LPF
				

			--// AD9747 Dual 250 MS/s max D/A Converter  (connect directly to top-level port)
			-- FPGA - high-speed DAC interface
			DAC1_DOUT: out std_logic_vector(15 downto 0);
			DAC2_DOUT: out std_logic_vector(15 downto 0);
				-- 16-bit unsigned output samples (1 per DAC_CLKOUT_P period). 
			DAC_CLKOUT_P: out std_logic;
			DAC_CLKOUT_N: out std_logic
				-- differential (not LVDS!) reference clock (delayed 1/4 CLK period to 
				-- meet the DAC tDBS and tDBH setup and hold requirements
			
		
			);
end entity;

architecture Behavioral of AD9747_DAC_DRIVER2 is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
	COMPONENT CROSS_CLK_DOMAINS
	generic (
		NBITS: in integer
	);
	PORT(
		CLK_A : IN std_logic;
		CLK_B : IN std_logic;
		ASYNC_RESET : IN std_logic;
		DATA_IN: in std_logic_vector((NBITS-1) downto 0);
		DATA_OUT: out std_logic_vector((NBITS-1) downto 0)
		);
	END COMPONENT;

	COMPONENT RAISED_COS5E
	PORT(
		CLK : IN std_logic;
		SAMPLE_CLK_IN : IN std_logic;
		DATA_IN : IN std_logic_vector(13 downto 0);          
		SAMPLE_CLK_OUT : OUT std_logic;
		DATA_OUT : OUT std_logic_vector(15 downto 0)
		);
	END COMPONENT;

	COMPONENT FIRHALFBAND3
	PORT(
		CLK : IN std_logic;
		SAMPLE_CLK_IN : IN std_logic;
		DATA_IN : IN std_logic_vector(13 downto 0);          
		SAMPLE_CLK_OUT : OUT std_logic;
		DATA_OUT : OUT std_logic_vector(13 downto 0)
		);
	END COMPONENT;

	component SIM2OUTFILE is
		 Generic (
			FILENAME: string
		 );
		 Port ( 
			--// CLK, RESET
			CLK: in std_logic;
				-- Reference clock
			DATA1: in std_logic_vector(11 downto 0);
			DATA2: in std_logic_vector(11 downto 0);
			DATA3: in std_logic_vector(11 downto 0);
			SAMPLE_CLK_IN: in std_logic
				-- DATA_IN is read at the rising edge of CLK when SAMPLE_CLK_IN = '1'

	);
	END COMPONENT;

--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
-- Suffix _D indicates a one clock delay

signal CLKFB1: std_logic := '0';
signal PLL_CLKOUT0: std_logic := '0';
signal DAC_CLK: std_logic := '0';
signal DAC_CLK_N: std_logic := '0';
signal LOCKED_PLL: std_logic := '0';
signal DAC1_DATA1: std_logic_vector(15 downto 0) := (others => '0');
signal DAC2_DATA1: std_logic_vector(15 downto 0) := (others => '0');
signal DAC1_DATA2A: std_logic_vector(15 downto 0) := (others => '0');
signal DAC2_DATA2A: std_logic_vector(15 downto 0) := (others => '0');
signal DAC1_DATA2B: std_logic_vector(13 downto 0) := (others => '0');
signal DAC2_DATA2B: std_logic_vector(13 downto 0) := (others => '0');
signal DAC1_DATA2: std_logic_vector(15 downto 0) := (others => '0');
signal DAC2_DATA2: std_logic_vector(15 downto 0) := (others => '0');


constant SIMULATION: std_logic := '0';
--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
begin

-- first PLL generates a fixed 170 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        => 2,	
-- CLKFBOUT_MULT        => 24,	-- 720 MHz = (VCO / M) = (60 MHz / D) input reference freq.
-- CLKOUT0_DIVIDE       => 3,	-- 240 MH
--	DIVCLK_DIVIDE        => 2,	
--	CLKFBOUT_MULT        => 20,	-- 600 MHz = (VCO / M) = (60 MHz / D) input reference freq.
--	CLKOUT0_DIVIDE       => 3,	-- 200 MHz
 DIVCLK_DIVIDE        => 1,	
 CLKFBOUT_MULT        => 8,	-- 480 MHz = (VCO / M) = (60 MHz / D) input reference freq.
 CLKOUT0_DIVIDE       => 3,	-- 160 MHz
-- DIVCLK_DIVIDE        => 1,	
-- CLKFBOUT_MULT        => 9,	-- 540 MHz = (VCO / M) = (60 MHz / D) input reference freq.
-- CLKOUT0_DIVIDE       => 3,	-- 180 MHz
-- DIVCLK_DIVIDE        => 2,	
-- CLKFBOUT_MULT        => 17,	-- 510 MHz = (VCO / M) = (60 MHz / D) input reference freq.
-- CLKOUT0_DIVIDE       => 3,	-- 170 MHz
 CLKFBOUT_PHASE       => 0.000,
 CLKOUT0_PHASE        => 0.0,
 CLKOUT0_DUTY_CYCLE   => 0.500,
 CLKIN_PERIOD         => 16.666,
 REF_JITTER           => 0.010)
port map
 -- Output clocks
(CLKFBOUT            => CLKFB1,
 CLKOUT0             => PLL_CLKOUT0,
 -- Status and control signals
 LOCKED              => LOCKED_PLL,
 RST                 => '0',
 -- Input clock control
 CLKFBIN             => CLKFB1,
 CLKIN               => CLK60G
 );

-- make PLL_CLKOUT0 global
BUFG_001: BUFG port map(I => PLL_CLKOUT0, O => DAC_CLK);
DAC_CLK_N <= not DAC_CLK;

-- DAC clock output
-- It is recommended to only use a BUFG resource to drive clock loads.
-- It is recommended to use a Clock Forwarding technique to create a reliable and repeatable 
-- low skew solution, i.e. use ODDR2 to regenerate the output clock.
-- + invert DAC clock to create a delay between output data 
-- to meet the tDBS = 400ps setup, tDBH = 1200ps hold requirements.
DAC_CLKOUT_GEN_001P : ODDR2
generic map(
	DDR_ALIGNMENT => "NONE", -- Sets output alignment to "NONE", "C0", "C1" 
	INIT => '0', -- Sets initial state of the Q output to '0' or '1'
	SRTYPE => "SYNC") -- Specifies "SYNC" or "ASYNC" set/reset
port map (
	Q => DAC_CLKOUT_P, -- 1-bit output data
	C0 => DAC_CLK, -- 1-bit clock input
	C1 => DAC_CLK_N, -- 1-bit clock input
	CE => '1',  -- 1-bit clock enable input
	D0 => '0',   -- 1-bit data input (associated with C0)
	D1 => '1',   -- 1-bit data input (associated with C1)
	R => '0',    -- 1-bit reset input
	S => '0'     -- 1-bit set input
);

-- fake differential output (LVDS_33 is incompatible with LVCMOS33 in the same bank!)
DAC_CLKOUT_GEN_001N : ODDR2
generic map(
	DDR_ALIGNMENT => "NONE", -- Sets output alignment to "NONE", "C0", "C1" 
	INIT => '0', -- Sets initial state of the Q output to '0' or '1'
	SRTYPE => "SYNC") -- Specifies "SYNC" or "ASYNC" set/reset
port map (
	Q => DAC_CLKOUT_N, -- 1-bit output data
	C0 => DAC_CLK, 	 -- 1-bit clock input
	C1 => DAC_CLK_N, 	 -- 1-bit clock input
	CE => '1',  -- 1-bit clock enable input
	D0 => '1',   -- 1-bit data input (associated with C0)
	D1 => '0',   -- 1-bit data input (associated with C1)
	R => '0',    -- 1-bit reset input
	S => '0'     -- 1-bit set input
);
 
-- Note: use DAC_CLKOUT_N as REFERENCE_PIN to minimize the output skew between all output data pins. 
-- Google Xilinx output clock & data skew constraints
 
-- re-sample input at the faster DAC_CLK
CROSS_CLK_DOMAINS_001: CROSS_CLK_DOMAINS 
generic map(
	NBITS => 16
)
PORT MAP(
	CLK_A => CLK,
	CLK_B => DAC_CLK,
	ASYNC_RESET => '0',
	DATA_IN => DAC1_DIN,
	DATA_OUT => DAC1_DATA1
);

CROSS_CLK_DOMAINS_002: CROSS_CLK_DOMAINS 
generic map(
	NBITS => 16
)
PORT MAP(
	CLK_A => CLK,
	CLK_B => DAC_CLK,
	ASYNC_RESET => '0',
	DATA_IN => DAC2_DIN,
	DATA_OUT => DAC2_DATA1
);

-- anti-aliasing filterA. 40 MHz BW LPF
RAISED_COS5E_001: RAISED_COS5E PORT MAP(
	CLK => DAC_CLK,
	SAMPLE_CLK_IN => FILA_EN,
	DATA_IN => DAC1_DATA1(15 downto 2),
	SAMPLE_CLK_OUT => open,
	DATA_OUT => DAC1_DATA2A
);

RAISED_COS5E_002: RAISED_COS5E PORT MAP(
	CLK => DAC_CLK,
	SAMPLE_CLK_IN => FILA_EN,
	DATA_IN => DAC2_DATA1(15 downto 2),
	SAMPLE_CLK_OUT => open,
	DATA_OUT => DAC2_DATA2A
);

-- anti-aliasing filter2. 80 MHz BW LPF
-- latency: 5 CLKs
FIRHALFBAND3_001: FIRHALFBAND3 PORT MAP(
	CLK => DAC_CLK,
	SAMPLE_CLK_IN => FILB_EN,
	DATA_IN => DAC1_DATA1(15 downto 2),
	SAMPLE_CLK_OUT => open,
	DATA_OUT => DAC1_DATA2B
);

FIRHALFBAND3_002: FIRHALFBAND3 PORT MAP(
	CLK => DAC_CLK,
	SAMPLE_CLK_IN => FILB_EN,
	DATA_IN => DAC1_DATA1(15 downto 2),
	SAMPLE_CLK_OUT => open,
	DATA_OUT => DAC2_DATA2B
);

-- Filter selection
FIL_SEL: process(DAC_CLK)
begin
	if rising_edge(DAC_CLK) then
		if(FILA_EN = '1') then
			DAC1_DATA2 <= DAC1_DATA2A;
			DAC2_DATA2 <= DAC2_DATA2A;
		elsif(FILB_EN = '1') then
			DAC1_DATA2 <= DAC1_DATA2B & "00";
			DAC2_DATA2 <= DAC2_DATA2B & "00";
		else
			-- no filtering
			DAC1_DATA2 <= DAC1_DATA1;
			DAC2_DATA2 <= DAC2_DATA1;
		end if;
	end if;
end process;

-- reclock output for best timing (place FF at IO).
RECLOCK_OUTPUT_001: process(DAC_CLK)
begin
	if rising_edge(DAC_CLK) then
		DAC1_DOUT <= not DAC1_DATA2(15) & DAC1_DATA2(14 downto 0);
		DAC2_DOUT <= not DAC2_DATA2(15) & DAC2_DATA2(14 downto 0);
	end if;
end process;

--// simulation. send to a space-delimited text file for subsequent Matlab plot.
SIM2OUTFILE_X: if(SIMULATION = '1') generate
	SIM2OUTFILE_001: SIM2OUTFILE 
	generic map(
		FILENAME => "dac1.txt"
	)
	PORT MAP(
		CLK => CLK,
		DATA1 => DAC1_DIN(15 downto 4),
		DATA2 => DAC2_DIN(15 downto 4),
		DATA3 => DAC1_DATA2(15 downto 4),
		SAMPLE_CLK_IN => '1'
	);

	SIM2OUTFILE_002: SIM2OUTFILE 
	generic map(
		FILENAME => "dac2.txt"
	)
	PORT MAP(
		CLK => DAC_CLK,
		DATA1 => DAC1_DATA1(15 downto 4),
		DATA2 => DAC1_DATA2(15 downto 4),
		DATA3 => DAC1_DIN(15 downto 4),
		SAMPLE_CLK_IN => '1'
	);
end generate;

end Behavioral;

