-------------------------------------------------------------
--	Filename:  AD7276_ADC_DRIVER.VHD
-- Author: 
--		Alain Zarembowitch / MSS
--	Version: 2
--	Date last modified: 11/21/11
-- Inheritance: n/a
--
-- Description: AD7276 ADC controller
-- starts a 12-bit A/D conversions upon receiving a START pulse. Maximum frequency: 3 MSamples/s.
-- When START stays high, the A/D conversions are consecutive.
-- Adjust CLK_DIV_CONST as a function of the input clock frequency so that 
-- the ADC clock meets the 48 MHz max requirement.
--
-- Rev 2 11/21/11 AZ
-- Added generic parameter. No longer need for manual correction of the SPI clock speed.
---------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

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

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

	--// User Interface
	START: in std_logic;
		-- 1 CLK-wide pulse to trigger a 12-bit A/D conversion.
		-- Can stay high for back-to-back periodic A/D conversions.
	DATA_OUT: out std_logic_vector(11 downto 0) := x"000";
		-- aligned with DATA_OUT_CLK. 
		-- format: unsigned
	DATA_OUT_CLK: out std_logic := '0';
		-- 1 CLK pulse indicating that DATA_OUT is valid.

	--// DAC interface
	CS_B: out std_logic;
	SCLK: out std_logic;	
		-- Maximum frequency of 48 MHz (B-grade) or 16 MHz (Y-grade)
		-- fclk/2*(CLK_DIV_CONST + 1)
	SDATA: in std_logic
);
end entity;

architecture behavioral of AD7276_ADC_DRIVER is
--------------------------------------------------------
--      COMPONENTS
--------------------------------------------------------
--------------------------------------------------------
--     SIGNALS
--------------------------------------------------------
--// Operational Signals
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 := 1;			-- B-grade
constant CLK_DIV_CONST: integer := CLK_FREQUENCY/32;			-- Y-grade
	-- slow down the CLK_DIV clock to 2*48 MHz max (B-grade) / 2*16 MHz (Y-grade) or less. 
	-- fCLK / (CLK_DIV_CONST + 1) < 2*48 MHz or 2*16 MHz
	-- ADC clock is half.
	-- Example 1: fclk = 130 MHz, CLK_DIV_CONST = 1 => ADC clock frequency = 32.5 MHz < 48 MHz max
	-- ADC sampling rate = 1.911 MSamples/s
	-- Example 2: fclk = 130 MHz, CLK_DIV_CONST = 4 => ADC clock frequency = 13 MHz < 16 MHz max
	-- ADC sampling rate = 0.765 MSamples/s
signal CLK_DIV: std_logic := '0';
	-- ADC Interface signals transition only when CLK_DIV = '1'.
signal DATA_OUT_LOCAL: std_logic_vector(11 downto 0) := x"000";
--------------------------------------------------------
--      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 2*48MHz or 2*16 MHz depending on IC grade.

--// The ADC waveform consists of 16 SCLK clock cycles
-- Generate SCLK and OP_COUNTER
-- SCLK = '1' during standby
-- OP_COUNTER = 0 during standby
SCLK_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(START = '1') and (OP_COUNTER = 0) then
			-- new start is ignored until last transaction is complete
			OP_COUNTER <= "000001";	-- start operations
			SCLK <= '1';
		elsif((CLK_DIV = '1') and (OP_COUNTER = 33)) then
			-- finished 
			OP_COUNTER <= (others => '0');
			SCLK <= '1';
		 elsif((CLK_DIV = '1') and (OP_COUNTER /= 0)) then
			-- main loop
			OP_COUNTER <= OP_COUNTER + 1;
			SCLK <= not OP_COUNTER(0);  -- toggle clock at least 16x
		end if;
	end if;
end process;

CS_B <= '1' when (OP_COUNTER = 0) else '0'; 

-- Data Line
SDIN_GEN_001: process(CLK)
begin
	if rising_edge(CLK) then
		if(CLK_DIV = '1') and (OP_COUNTER /= 0) and (OP_COUNTER(0) = '0') and (OP_COUNTER < 27) then
			-- latch in a data bit. Shift in MSb first.
			DATA_OUT_LOCAL(0) <= SDATA;
			DATA_OUT_LOCAL(11 downto 1) <= DATA_OUT_LOCAL(10 downto 0);
		end if;
		
		if(CLK_DIV = '1') and(OP_COUNTER = 33) then
			-- output 12-bit ADC sample
			DATA_OUT <= DATA_OUT_LOCAL;
			DATA_OUT_CLK <= '1';
		else 
			DATA_OUT_CLK <= '0';
		end if;
	end if;
end process;

--DATA_OUT_CLK  <= '1' when ((OP_COUNTER = 0) and (DATA_IN_CLK = '0')) else '0';
	-- indicates DAC availability	(1 CLK early)

end behavioral;