-------------------------------------------------------------
-- MSS copyright 2011
--	Filename:  DAC_AD5621.VHD
-- Author: 
--		Adam Kwiatkowski / MSS
--		Alain Zarembowitch / MSS
--	Version: 2
--	Date last modified: 11/21/09
-- Inheritance: DAC.VHD 11/30/05, SPI.vhd
--
-- Description: AD5621 DAC controller
-- Supports 12-bit input.
-- Adjust CLK_DIV_CONST as a function of the input clock frequency so that 
-- the DAC clock meets the 30 MHz max requirement.
--
-- Rev 2 11/21/11 AZ
-- Added generic parameter. No longer need for manual correction of the SPI clock speed.
-- Initialized signals for simulation.
---------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity DAC_AD5621 is 
	generic (
		CLK_FREQUENCY: integer := 120	 -- reference clock frequency in MHz
	);
	port ( 

	--// Clock
	CLK : in std_logic;		-- Main internal processing clock

	--// User Interface
	DATA_IN: in std_logic_vector(11 downto 0);
		-- aligned with DATA_IN_CLK
	DATA_IN_CLK: in std_logic;
		-- 1 CLK pulse indicating that DATA_IN is valid.

	OP_MODE: in std_logic_vector(1 downto 0);
		-- Operation Mode aligned with DATA_IN_CLK
		-- 0 0 - Normal operation
		-- Power-down modes:  
		-- 0 1 - 1 kOhm to GND
		-- 1 0 - 100 kOhm to GND
		-- 1 1 - Three-state

	DONE: out std_logic;
		-- indicates DAC availability	(1 CLK early)
		-- '1' indicates interface is available on NEXT clk. 
		-- goes to '0' immediately after valid DATA_IN_CLK pulse/level
		-- is presented, used for flow control

	--// DAC interface
	SYNC_B: out std_logic;
	SCLK: out std_logic;	
		-- Maximum frequency of 30 MHz.
		-- doesn't toggle when DAC is inactive
	SDIN: out std_logic
);
end entity;

architecture behavioral of DAC_AD5621 is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
--// INPUT SIGNALS
signal DATA_IN_D: std_logic_vector(11 downto 0) := (others => '0');
	-- reclock data DATA_IN
signal OP_MODE_D: std_logic_vector(1 downto 0) := (others => '0');

--// Operational Signals
signal START_DETECT: std_logic := '0';
signal OP_COUNTER: std_logic_vector(5 downto 0) := (others => '0');
   -- Operations Counter 
   -- determines what data is delivered to interface
signal CLK_COUNTER: integer range 0 to 4 := 0;
constant CLK_DIV_CONST: integer range 0 to 4 := 1+(CLK_FREQUENCY/60);			
	-- slow down master clock to 60 MHz max. 
	-- fCLK / CLK_DIV_CONST = 60 MHz (max)
	-- CLK_DIV is then further divided by two
signal CLK_DIV: std_logic := '0';
	-- DAC Interface signals transition only when CLK_DIV = '1'.

--------------------------------------------------------
--      IMPLEMENTATION
--------------------------------------------------------

begin

-- Generate CLK_COUNTER  range 0 - CLK_DIV_CONST
CLK_COUNTER_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(CLK_DIV = '1') then
			CLK_COUNTER <= 0;
		else
			CLK_COUNTER <= CLK_COUNTER + 1;
		end if;
	end if;
end process;

CLK_DIV <= '1' when (CLK_COUNTER = CLK_DIV_CONST) else '0';
	-- valid transition period
	-- max freq of 60MHz

START_DETECT <= '1' when ((OP_COUNTER = 0) and (DATA_IN_CLK = '1')) else '0';
	-- DAC sequence initiation

--// The DAC waveform consists of 16 valid SCLK clock cycles
-- Generate SCLK and OP_COUNTER
-- SCLK = '0' during standby
-- OP_COUNTER = 0 during standby
SCLK_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(START_DETECT = '1') then
			-- new data is ignored until DAC is ready
			OP_COUNTER <= "000001";	-- start operations
			DATA_IN_D <= DATA_IN;  -- save data
			OP_MODE_D <= OP_MODE;  -- save mode value
		elsif((CLK_DIV = '1') and (OP_COUNTER /= 0)) then
			SCLK <= OP_COUNTER(0);  -- toggle clock (16x)
			if(OP_COUNTER = 36) then   
				-- finished 
				OP_COUNTER <= (others => '0');
		  	else
				-- main loop
				OP_COUNTER <= OP_COUNTER + 1;
			end if;
		end if;
	end if;
end process;

DONE  <= '1' when ((OP_COUNTER = 0) and (DATA_IN_CLK = '0')) else '0';
	-- indicates DAC availability	(1 CLK early)
					
-- Generate SYNC_B
SYNC_B <= '1' when ((OP_COUNTER = 2) or (OP_COUNTER = 3)) else '0'; 
	-- SYNC_B pulses high for 2 CLKs between each DAC update
	-- this value must be at least 20ns
	-- Idle SYNC_B at '0' to conserve power (doesn't affect performance)


-- Data Line
SDIN_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		case(OP_COUNTER) is
			when "000011" => SDIN <= OP_MODE_D(1);	
			when "000101" => SDIN <= OP_MODE_D(0); 	
			when "000111" => SDIN <= DATA_IN_D(11); 	
			when "001001" => SDIN <= DATA_IN_D(10); 	
			when "001011" => SDIN <= DATA_IN_D(9);	
			when "001101" => SDIN <= DATA_IN_D(8);	
			when "001111" => SDIN <= DATA_IN_D(7);	
			when "010001" => SDIN <= DATA_IN_D(6);	
			when "010011" => SDIN <= DATA_IN_D(5);	
			when "010101" => SDIN <= DATA_IN_D(4);	
			when "010111" => SDIN <= DATA_IN_D(3);	
			when "011001" => SDIN <= DATA_IN_D(2);	
			when "011011" => SDIN <= DATA_IN_D(1);	
			when "011101" => SDIN <= DATA_IN_D(0);	
			when "011111" => SDIN <= '0';  -- first don't care state
			when others => null;
		end case;
	end if;
end process;


end behavioral;