library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

-- Controller for the PMOD AD1 Digilent Board.
-- The controller keeps the AD7476A in "normal mode" throughout its operation, and as long as the enable 
-- signal is high, converts the ADC input signal at a rate of sclk_freq/(TRANSFER_CLK_COUNT+DELAY_CLK_CNT). 
-- On conversion finish the done signal is pulsed high for a single clk cycle. The data outputs contain valid 
-- data only when done is asserted. If enable is pulled low, the controller enters a standby mode and
-- waits until the enable signal is pulled high again. If the enable is pulled low during a 
-- converion/transfer, the conversion/transfer is completed before entering the idle mode.
-- NOTE: The AD7476A supports a max clock frequency of 20 MHz

entity pmod_ad1_ctrl is
    generic(
        TRANSFER_CLK_COUNT  : integer := 16;
        DELAY_CLK_CNT       : integer := 2;
        DATA_WIDTH          : integer := 12
    );
    port (
        clk     : in std_logic;
        reset   : in std_logic;
        enable  : in std_logic;
        sdata1  : in std_logic; -- PMOD-AD1
        sdata2  : in std_logic; -- PMOD-AD1
        sclk    : out std_logic;-- PMOD-AD1
        cs_n    : out std_logic;-- PMOD-AD1
        data1   : out std_logic_vector(DATA_WIDTH-1 downto 0);
        data2   : out std_logic_vector(DATA_WIDTH-1 downto 0);
        done    : out std_logic
    );
end entity;

architecture arch of pmod_ad1_ctrl is
    
    --*****TYPE DECLARATION*****
    type STAGE_TYPE is (IDLE, TRANSFER, DELAY);
    
    --*****SIGNAL DECLARATIONS*****
    signal buf1, buf2, buf1_next, buf2_next : std_logic_vector(DATA_WIDTH-1 downto 0) := (others => '0');
    signal stage, stage_next : STAGE_TYPE := IDLE;
    signal count, count_next : integer range 0 to TRANSFER_CLK_COUNT-1 := 0;
    -- Output Signals
    signal cs_n_next : std_logic := '1';
    signal done_next : std_logic := '0';
    
begin

    state : process(all)
    begin
        -- DEFAULT VALUES
        buf1_next    <= buf1;
        buf2_next    <= buf2;
        stage_next  <= stage;
        count_next  <= count;
        done_next   <= '0';
        cs_n_next   <= '1';
        
        case stage is
            when IDLE =>
                if (enable = '1') then
                    stage_next  <= TRANSFER;
                    cs_n_next   <= '0';
                    count_next  <= 0;
                end if;
            -- NOTE: This state remains longer than the width of the data word. This is by design
            -- to shift out the initial zero bits of the tranfer.
            when TRANSFER =>
                -- Shift Bits into buffer
                buf1_next    <= buf1(DATA_WIDTH-2 downto 0) & sdata1;
                buf2_next    <= buf2(DATA_WIDTH-2 downto 0) & sdata2;
                cs_n_next   <= '0';
                if (count = TRANSFER_CLK_COUNT-1) then
                    stage_next      <= DELAY;
                    count_next      <= 0;
                    cs_n_next       <= '1';
                    done_next       <= '1';
                else
                    count_next  <= count + 1;
                end if;
            when DELAY =>
                if (count = DELAY_CLK_CNT-1) then
                    if(enable = '1') then
                        stage_next  <= TRANSFER;
                        cs_n_next   <= '0';
                        count_next  <= 0;
                    else
                        stage_next <= IDLE;
                    end if;
                else
                    count_next  <= count + 1;
                end if;
        end case;
    end process;
    
    --Connect SCLK (Inverted)
    sclk    <= not clk;
    
    sync : process(clk)
    begin
        if (rising_edge(clk)) then
            if (reset = '1') then
                -- Internal Signals
                buf1        <= (others => '0');
                buf2        <= (others => '0');
                stage       <= IDLE;
                count       <= 0;
                -- Output Signals
                cs_n        <= '1';
                data1       <= (others => '0');
                data2       <= (others => '0');
                done        <= '0';
            else
                -- Internal Signals
                buf1        <= buf1_next;
                buf2        <= buf2_next;
                stage       <= stage_next;
                count       <= count_next;
                -- Output Signals
                cs_n        <= cs_n_next;
                data1       <= buf1_next;
                data2       <= buf2_next;
                done        <= done_next;
            end if;
        end if;
    end process;
    
end architecture;
