-------------------------------------------------------------------------------
-- Title      : N3xx serial DAC interface with arbiter
-- Project    : White Rabbit
-------------------------------------------------------------------------------
-- File       : spec_serial_dac.vhd
-- Author     : Tomasz Wlostowski, modifications by dbaker
-- Company    : CERN BE-Co-HT
-- Platform   : fpga-generic
-- Standard   : VHDL'87
-------------------------------------------------------------------------------
--
-- Copyright (c) 2011 CERN
--
-- This source file is free software; you can redistribute it
-- and/or modify it under the terms of the GNU Lesser General
-- Public License as published by the Free Software Foundation;
-- either version 2.1 of the License, or (at your option) any
-- later version.
--
-- This source is distributed in the hope that it will be
-- useful, but WITHOUT ANY WARRANTY; without even the implied
-- warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
-- PURPOSE.  See the GNU Lesser General Public License for more
-- details.
--
-- You should have received a copy of the GNU Lesser General
-- Public License along with this source; if not, download it
-- from http://www.gnu.org/licenses/lgpl-2.1.html
--
-------------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;


entity n3xx_serial_dac_arb is
  generic(
    g_invert_sclk    : boolean;
    g_num_extra_bits : integer
    );
  port(
    clk_i   : in std_logic;
    rst_n_i : in std_logic;

    val1_i  : in std_logic_vector(15 downto 0);
    load1_i : in std_logic;
    val2_i  : in std_logic_vector(15 downto 0);
    load2_i : in std_logic;

    dac_cs_n_o  : out std_logic_vector(1 downto 0);
    dac_clr_n_o : out std_logic;
    dac_sclk_o  : out std_logic;
    dac_din_o   : out std_logic);

end n3xx_serial_dac_arb;

architecture behavioral of n3xx_serial_dac_arb is

  component n3xx_serial_dac
    generic (
      g_num_data_bits  : integer;
      g_num_extra_bits : integer;
      g_num_cs_select  : integer);
    port (
      clk_i         : in  std_logic;
      rst_n_i       : in  std_logic;
      value_i       : in  std_logic_vector(g_num_data_bits-1 downto 0);
      cs_sel_i      : in  std_logic_vector(g_num_cs_select-1 downto 0);
      load_i        : in  std_logic;
      sclk_divsel_i : in  std_logic_vector(2 downto 0);
      dac_cs_n_o    : out std_logic_vector(g_num_cs_select-1 downto 0);
      dac_sclk_o    : out std_logic;
      dac_sdata_o   : out std_logic;
      xdone_o       : out std_logic);
  end component;

  signal d1, d2             : std_logic_vector(15 downto 0) := (others=>'0');
  signal d1_ready, d2_ready : std_logic := '0';


  signal dac_data     : std_logic_vector(15 downto 0) := (others=>'0');
  signal dac_load     : std_logic := '0';
  signal dac_cs_sel   : std_logic_vector(1 downto 0) := (others=>'0');
  signal dac_done     : std_logic := '0';
  signal dac_sclk_int : std_logic := '0';

  type t_state is (WAIT_DONE, LOAD_DAC, WAIT_DATA);

  signal state : t_state;

  signal trig0    : std_logic_vector(31 downto 0);
  signal trig1    : std_logic_vector(31 downto 0);
  signal trig2    : std_logic_vector(31 downto 0);
  signal trig3    : std_logic_vector(31 downto 0);
  signal CONTROL0 : std_logic_vector(35 downto 0);

begin  -- behavioral

  dac_clr_n_o <= '1';

  U_DAC : n3xx_serial_dac
    generic map (
      g_num_data_bits  => 16,
      g_num_extra_bits => g_num_extra_bits,
      g_num_cs_select  => 2)
    port map (
      clk_i         => clk_i,
      rst_n_i       => rst_n_i,
      value_i       => dac_data,
      cs_sel_i      => dac_cs_sel,
      load_i        => dac_load,
      sclk_divsel_i => "001",
      dac_cs_n_o    => dac_cs_n_o,
      dac_sclk_o    => dac_sclk_int,
      dac_sdata_o   => dac_din_o,
      xdone_o       => dac_done);


  p_drive_sclk: process(dac_sclk_int)
    begin
      if(g_invert_sclk) then
        dac_sclk_o <= not dac_sclk_int;
       else
        dac_sclk_o <= dac_sclk_int;
       end if;
      end process;

  process(clk_i)
  begin
    if rising_edge(clk_i) then
      if rst_n_i = '0' then
        d1         <= (others => '0');
        d1_ready   <= '0';
        d2         <= (others => '0');
        d2_ready   <= '0';
        dac_load   <= '0';
        dac_cs_sel <= (others => '0');
        state      <= WAIT_DATA;
      else

        if(load1_i = '1' or load2_i = '1') then

          if(load1_i = '1') then
            d1_ready <= '1';
            d1       <= val1_i;
          end if;

          if(load2_i = '1') then
            d2_ready <= '1';
            d2       <= val2_i;
          end if;
        else
          case state is
            when WAIT_DATA =>
              if(d1_ready = '1') then
                dac_cs_sel <= "01";
                dac_data   <= d1;
                dac_load   <= '1';
                d1_ready   <= '0';
                state      <= LOAD_DAC;
              elsif(d2_ready = '1') then
                dac_cs_sel <= "10";
                dac_data   <= d2;
                dac_load   <= '1';
                d2_ready   <= '0';
                state      <= LOAD_DAC;
              end if;

            when LOAD_DAC=>
              dac_load <= '0';
              state    <= WAIT_DONE;

            when WAIT_DONE =>
              if(dac_done = '1') then
                state <= WAIT_DATA;
              end if;
            when others => null;
          end case;
        end if;
      end if;
    end if;
  end process;




end behavioral;