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

-- Controller for the PMOD DA3 Digilent Board.
-- The controller sends the input 'data' word to the DAC when 'start' is asserted. The 'data' input has 
-- to be valid only during the period 'start' is asserted, as for the transfer the word is latched 
-- internally. When the transfer is done, the 'done' signal is asserted for a single clock cycle. The 'start'
-- and 'done' cycle can be high at the same time, allowing the done signal to be asynchronously connected
-- to the 'start' signal to save on latency.
-- NOTE: The AD5541A supports a maximum clk frequency of 50 MHz


entity pmod_da3_ctrl is
    generic(
        TRANSFER_CLK_COUNT  : integer := 16;
        DATA_WIDTH          : integer := 16
    );
    port (
        clk     : in std_logic;
        reset   : in std_logic;
        start   : in std_logic;
        data    : in std_logic_vector(DATA_WIDTH-1 downto 0);
        cs_n    : out std_logic;-- PMOD-DA3
        sdata   : out std_logic;-- PMOD-DA3
        ldac    : out std_logic;-- PMOD-DA3
        sclk    : out std_logic;-- PMOD-DA3
        done    : out std_logic
    );
end entity;

architecture arch of pmod_da3_ctrl is
    
    --*****TYPE DECLARATION*****
    type STAGE_TYPE is (IDLE, TRANSFER);
    
    --*****SIGNAL DECLARATIONS*****
    signal buf, buf_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 sdata_next : std_logic := '0';
    signal done_next : std_logic := '0';
    
begin
    
    --LDAC Hardwired to ground
    ldac <= '0';
    
    state : process(all)
    begin
        -- DEFAULT VALUES
        buf_next    <= buf;
        stage_next  <= stage;
        count_next  <= count;
        done_next   <= '0';
        cs_n_next   <= '1';
        sdata_next  <= '0';
        
        case stage is
            when IDLE =>
                if (start = '1') then
                    stage_next  <= TRANSFER;
                    cs_n_next   <= '0';
                    count_next  <= 0;
                    -- Shift first bit into DAC
                    buf_next    <= data(DATA_WIDTH-2 downto 0) & '0';
                    sdata_next  <= data(DATA_WIDTH-1);
                end if;
            when TRANSFER =>
                -- Shift Bits into DAC
                buf_next    <= buf(DATA_WIDTH-2 downto 0) & '0';
                sdata_next  <= buf(DATA_WIDTH-1);
                cs_n_next   <= '0';
                if (count = TRANSFER_CLK_COUNT-1) then
                    cs_n_next   <= '1';
                    stage_next  <= IDLE;
                    done_next   <= '1';
                else
                    count_next  <= count + 1;
                end if;
        end case;
    end process;
    
    --Connect SCLK
    sclk    <= clk;
    
    sync : process(clk)
    begin
        if (rising_edge(clk)) then
            if (reset = '1') then
                -- Internal Signals
                buf         <= (others => '0');
                stage       <= IDLE;
                count       <= 0;
                -- Output Signals
                cs_n        <= '1';
                sdata       <= '0';
                done        <= '0';
            else
                -- Internal Signals
                buf         <= buf_next;
                stage       <= stage_next;
                count       <= count_next;
                -- Output Signals
                cs_n        <= cs_n_next;
                sdata       <= sdata_next;
                done        <= done_next;
            end if;
        end if;
    end process;
    
end architecture;
