-------------------------------------------------------------
-- MSS copyright 2011
--	Filename:  AD9747_DAC_DRIVER.VHD
-- Author: 
--		Alain Zarembowitch / MSS, 
--	Version: 0
--	Date last modified: 10-1-10
-- Inheritance: 	none
--
-- description:  Hardware verification test for the COM-1600 (or COM-1500) + COM-3504 assembly.
-- The COM-3504 plug-in module includes dual 250 MSamples/s 16-bit DACs
-- and dual 150 MSamples/s 12-bit ADCs.
-- Two auxiliary 12-bit DACs and one auxiliary 12-bit ADC are also available for
-- gain control and power measurement.
-- See inside this test component for the actual ADCs and DACs drivers.

----------------------------------------------------------------------------------
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_DRIVER is
	generic (
		DAC_SPEEDx: in integer := 2
			-- 1 = same sampling speed for high-speed ADCs and DACs. For example 150/150 MHz
			-- 2 = DAC sampling rate = 2 x ADC sampling rate. For example DAC 250/ ADC 125 MHz
			-- The ADCs sampling rate is set by the reference clock CLK.
	);
    port ( 
			--// Clocks Resets
			CLK: in std_logic;
				-- reference clock for high-speed ADCs (x1) and DACs (x1 or x2 depending on DAC_SPEEDx parameter)
			ASYNC_RESET: in std_logic;
			
			--// User-generated samples to DAC
			-- Format: 16-bit unsigned (offset binary)
			DAC1A_DIN: in std_logic_vector(15 downto 0);
			DAC1B_DIN: in std_logic_vector(15 downto 0);
			DAC2A_DIN: in std_logic_vector(15 downto 0);
			DAC2B_DIN: in std_logic_vector(15 downto 0);
			DAC_EN: in std_logic;
			

			--// AD9747 Dual 250 MS/s 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 output samples (1 per DAC_CLKOUT_P period) .
				-- A indices are sent before B
				-- B indices are ignored if DAC_SPEEDx = 1
			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_DRIVER is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
	COMPONENT CLKDOUBLER_DCM
	PORT(
		CLK_REF_IN : IN std_logic;
		ASYNC_RESET : IN std_logic;          
		CLKx2G : OUT std_logic;
		CLKx2_180G : OUT std_logic;
		LOCKED_DCM : OUT std_logic
		);
	END COMPONENT;

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

signal DAC_CLK: std_logic;
signal DAC_CLK_N: std_logic;
signal CLK_N: std_logic;

--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------
begin
CLK_N <= not CLK;
DAC_CLK_N <= not DAC_CLK;

-- double clock frequency for high-speed DACs
DAC_CLKx2_GEN: if(DAC_SPEEDx = 2) generate
	CLKDOUBLER_DCM_001: CLKDOUBLER_DCM PORT MAP(
		CLK_REF_IN => CLK,
		ASYNC_RESET => ASYNC_RESET,
		CLKx2G => DAC_CLK,
		CLKx2_180G => open,
		LOCKED_DCM => open
	);
end generate;

DAC_CLKx1_GEN: if(DAC_SPEEDx = 1) generate
	DAC_CLK <= CLK;
end generate;

-- 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
);
 
--// DAC data output
DACxDOUT_GEN2a: if(DAC_SPEEDx = 2) generate
	DACxDOUT_GEN2b: for I in 0 to 15 generate   
		DAC1DOUT_GEN : ODDR2
		generic map(
			DDR_ALIGNMENT => "C0", -- Sets output alignment to "NONE", "C0", "C1" 
			INIT => '0', -- Sets initial state of the Q output to '0' or '1'
			SRTYPE => "ASYNC") -- must be ASYNC in DDR mode with DDR_ALIGNMENT mode of C0 or C1
		port map (
			Q => DAC1_DOUT(I), 	-- 1-bit output data
			C0 => CLK, 				-- 1-bit clock input
			C1 => CLK_N, 			-- 1-bit clock input
			CE => DAC_EN,  		-- 1-bit clock enable input (don't waste power when not writing) 
			D0 => DAC1A_DIN(I),   -- 1-bit data input (associated with C0)
			D1 => DAC1B_DIN(I),   -- 1-bit data input (associated with C1)
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);

		DAC2DOUT_GEN : ODDR2
		generic map(
			DDR_ALIGNMENT => "C0", -- Sets output alignment to "NONE", "C0", "C1" 
			INIT => '0', -- Sets initial state of the Q output to '0' or '1'
			SRTYPE => "ASYNC") -- must be ASYNC in DDR mode with DDR_ALIGNMENT mode of C0 or C1
		port map (
			Q => DAC2_DOUT(I), 	-- 1-bit output data
			C0 => CLK, 				-- 1-bit clock input
			C1 => CLK_N, 			-- 1-bit clock input
			CE => DAC_EN,  		-- 1-bit clock enable input (don't waste power when not writing) 
			D0 => DAC2A_DIN(I),   -- 1-bit data input (associated with C0)
			D1 => DAC2B_DIN(I),   -- 1-bit data input (associated with C1)
			R => '0',    -- 1-bit reset input
			S => '0'     -- 1-bit set input
		);
	end generate;
end generate;

DACxDOUT_GEN1a: if(DAC_SPEEDx = 1) generate
	DACxDOUT_GEN1b: for I in 0 to 15 generate  
		RECLOCK_X: process(CLK)
		begin
			if rising_edge(CLK) then
				if(DAC_EN = '1') then
					DAC1_DOUT(I) <= DAC1A_DIN(I);
					DAC2_DOUT(I) <= DAC2A_DIN(I);
				end if;
			end if;
		end process;
	end generate;
end generate;



end Behavioral;

