-- Read 16 bits at a time from 3 ADCs, present them broadside.
-- Write 16 bits to 1 DAC.

-- The spi port declaration below is the only place that the number
-- of chip selects is ever defined; everything else inherits that from
-- this single point.  SSEL(2 downto 0) are ADCs, SSEL(3) is the DAC.
--
use work.spi_bus_pkg.all;
entity spi_communicator is
    port (
        spi : bus spi_master_r(master)(ssel(3 downto 0));
        
        dac  : in  std_logic_vector(15 downto 0);
        adc0 : out std_logic_vector(15 downto 0);
        adc1 : out std_logic_vector(15 downto 0);
        adc2 : out std_logic_vector(15 downto 0);
        
        clk  : in  std_logic;
        rst  : in  std_logic
    );
end entity spi_communicator;

architecture Behavioral of spi_communicator is
begin

    MACHINE: process
        variable data : std_logic_vector(15 downto 0);
        variable bitn : integer range data'range;
        variable dev  : integer range spi_ssel'range;
        type state is (RESET, SSEL_GO, SCLK_FALL, SCLK_RISE, SSEL_STOP);
            
    begin
        wait until rising_edge(clk);
        case state is
            when RESET =>
                spi.mosi <= 'U';    
                spi.ssel <= (others => '1');
                spi.sclk <= '1';
                adc0 <= (others => 'U');
                adc1 <= (others => 'U');
                adc2 <= (others => 'U');
                state := SSEL_GO;
                dev   := 0;
                
            when SSEL_GO =>
                spi.ssel(dev) <= '0';
                state := SCLK_FALL;
                bitn  := data'high;
                
            when SCLK_FALL =>
                spi.sclk <= '0';
                state := SCLK_RISE;
                
            when SCLK_RISE =>
                spi.sclk <= '1';
                spi.mosi <= dac(bitn);
                data(bitn) := spi.miso;
                if bitn = 0 then
                    state := SSEL_STOP;
                else
                    state := SCLK_FALL;
                    bitn  := bitn - 1;
                end if;
                
            when SSEL_STOP =>
                spi.ssel <= (others => '1');
                case adc is
                    when 0 => adc0 <= data;
                    when 1 => adc1 <= data;
                    when 2 => adc2 <= data;
                    when 3 => null;
                end case;
                adc := 0 when adc = 3 else (adc + 1);
                state := SSEL_GO;
        end case;
        
        if rst then
            state := RESET;
        end if;
    end process MACHINE;
    
end architecture spi_communicator;

----------------------------------------------------------------------
-- Wrap it in an FPGA that does, presumably, other things as well.

use work.spi_bus_pkg.all;
entity FPGA is
    port(
        -- Number of chip selects here is undefined; the spi_communicator is
        -- sufficient to provide a definite size.
        spi             : bus spi_master_r(master);
--      ...
        clk             : in  std_logic;
        rst             : in  std_logic
    );
end entity FPGA

architecture Structural of FPGA is
--        ...
begin
    SPIMST: entity work.spi_communicator
        port map (
            spi =>  spi,
            adc0 => intl_adc0,
            adc1 => intl_adc1,
            adc2 => intl_adc2,
            dac  => dac,
            clk => clk,
            rst => rst
        );
--        ...
end architecture Structural;
