-----------------------------------------------------------------------------
-- Polyamp vhdl function library
-----------------------------------------------------------------------------

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

package polyamplib is

  component interface_ads1271
    port (
      fastclk  : in  std_logic;         -- Fast clock
      adclk    : in  std_logic;         -- A/D converter clock
      resync   : in  std_logic;         -- Clock change; resync
      ser_data : in  std_logic;         -- Serial data in
      n_drdy   : in  std_logic;         -- Data is ready, active low
      data_out : out std_logic_vector(23 downto 0);  -- Parallell data out
      nsync    : out std_logic;         -- Synchronize (restart) A/D conv.
      busy     : out std_logic;         -- Busy reading serial data
      g_sclk   : out std_logic);        -- Gated SPI clock, FOR SIMULATION ONLY
  end component;

  component priority_resolver2 is
    port (
      inp    : in  std_logic_vector(11 downto 0);
      prio   : out unsigned(3 downto 0);  -- Output highest signal
      active : out std_logic);            -- High when any input is active
  end component;

  component one_of_n_encoder is
    port (
      number     : in  unsigned(3 downto 0);
      enable     : in  std_logic;
      single_out : out std_logic_vector(11 downto 0));  -- Only a single wire is active at a time
  end component;

  component active_input is
    port (
      fastclk    : in  std_logic;       -- Main clock
      enable     : in  std_logic;       -- Enable signal
      busy_n     : in  std_logic;       -- Busy input signal, active low
      clear      : in  std_logic;       -- Clear the corresponding flip-flop
      active_out : out std_logic);      -- Active output
  end component;

  component master_state_machine is
    port (
      fastclk    : in    std_logic := '0';               -- Main clock
      enable_lo8 : in    std_logic;     -- Enable the 8 low inputs
      busy_n_vec : in    std_logic_vector(11 downto 0);  -- Busy inputs
      id_code    : inout unsigned(3 downto 0);           -- Id code output
      fifo_write : out   std_logic);    -- Save to fifo
  end component;

  component fifo_memory is
    generic (
      fifo_size : natural := 100);
    port (
      clock   : in  std_logic;
      sclr    : in  std_logic;
      datain  : in  std_logic_vector(31 downto 0);
      wrreq   : in  std_logic;
      rdreq   : in  std_logic;
      dataout : out std_logic_vector(31 downto 0);
      full    : out std_logic;
      empty   : out std_logic;
      meter   : out unsigned(7 downto 0));
  end component;

  component fifo_ft_memory is
    generic (
      fifo_size : natural := 100);
    port (
      clock   : in  std_logic;
      sclr    : in  std_logic;
      datain  : in  std_logic_vector(31 downto 0);
      wrreq   : in  std_logic;
      rdreq   : in  std_logic;
      dataout : out std_logic_vector(31 downto 0);
      full    : out std_logic;
      empty   : out std_logic;
      meter   : out unsigned(7 downto 0));
  end component;

  component ads1271_model is
    port (
      analog_in : in    signed(23 downto 0);  -- Analog input signal
      clk       : in    std_logic;            -- Conversion clock
      sclk      : in    std_logic;            -- SPI clock
      n_sync    : in    std_logic;            -- Input sync signal, active low
      din       : in    std_logic;            -- Serial data in
      dout      : out   std_logic;            -- Serial data out
      n_drdy    : out   std_logic);           -- Data ready, active low
  end component;

  component sr_sipo is
    generic (
      LENGTH : positive := 8;           --! Shift register length
      EDGE   : natural  := 1;           --! Active edge; 1=positive, 0=negative
      DIR    : natural  := 1);          --! Direction; 1=left, 0=right
    port (
      clk     : in  std_logic;          --! Clock input
      ser_in  : in  std_logic;          --! Serial input data
      par_out : out std_logic_vector(1 to LENGTH));  --! Parallel output data
  end component;

  component sr_piso is
    generic (
      LENGTH : positive := 8;           --! Shift register length
      EDGE   : natural  := 1;           --! Active edge; 1=positive, 0=negative
      DIR    : natural  := 1);          --! Direction; 1=left, 0=right
    port (
      clk     : in  std_logic;          --! Clock input
      load    : in  std_logic;          --! Load on next clock
      par_in  : in  std_logic_vector(1 to LENGTH);  --! Parallel input data
      ser_in  : in  std_logic;          --! Serial input data
      ser_out : out std_logic);         --! Serial output data
  end component;

  component sr_piso_s is
    generic (
      LENGTH : positive := 8;           --! Shift register length
      DIR    : natural  := 1);          --! Direction; 1=left, 0=right
    port (
      fastclk : in  std_logic;          --! Synchronous clock
      clk_en  : in  std_logic;          --! Clock enable input
      load_en : in  std_logic;          --! Load enable input
      par_in  : in  std_logic_vector(1 to LENGTH);  --! Parallel input data
      ser_in  : in  std_logic;          --! Serial input data
      ser_out : out std_logic);         --! Serial output data
  end component;

  component spi_slave is
    port (
      -- Main controls
      fastclk : in  std_logic;
      spi_tx  : in  std_logic_vector(31 downto 0);  -- Data to be sent
      spi_rx  : out std_logic_vector(31 downto 0);  -- Data to be received
      spi_op  : out std_logic;          -- Read/Write status/data/command

      -- Slave port, connected to CPU
      mosi    : in  std_logic;
      miso    : out std_logic;
      sck     : in  std_logic;          -- SPI clock
      en_adc  : in  std_logic;          -- Active low, enable ADC
      en_incl : in  std_logic;          -- Active low, enable inclinometer

      -- Master port, connected to inclinometer
      incl_miso : in  std_logic;
      incl_mosi : out std_logic;
      incl_sck  : out std_logic;
      incl_ena  : out std_logic);       -- Active low, enable inclinometer
  end component;

  component command_decoder is
    port (
      addr_data  : in  std_logic_vector(31 downto 0);  -- Input address/data
      decode     : in  std_logic;       -- Single cycle decode pulse
      fastclk    : in  std_logic;       -- Master clock (not used for now)
      sel_nulcmd : out std_logic;       -- NULL command (no operation)
      sel_adclk0 : out std_logic;       -- Select sampling clock, ad0.
      sel_adclk1 : out std_logic;       -- Select sampling clock, ad1.
      sel_adclk2 : out std_logic;       -- Select sampling clock, ad2.
      sel_adclk3 : out std_logic;       -- Select sampling clock, ad3.
      sel_adclk4 : out std_logic;       -- Select sampling clock, ad4.
      sel_adclk5 : out std_logic;       -- Select sampling clock, ad5.
      sel_adclk6 : out std_logic;       -- Select sampling clock, ad6.
      sel_adclk7 : out std_logic;       -- Select sampling clock, ad7.
      resync_adc : out std_logic;       -- Resynchronize all ADC's
      write_ctrl : out std_logic;       -- Write to control-signal register
      start_adcs : out std_logic;       -- Start AD-conversion
      stop_adcs  : out std_logic);      -- Stop AD-conversion
  end component;

  component clockmux is
    port (
      clk24M  : in  std_logic;             -- Input clocks, 24 576 000
      clk4M   : in  std_logic;             -- 4 096 000
      clk2M   : in  std_logic;             -- 2 048 000
      clk1M   : in  std_logic;             -- 1 024 000
      clk512k : in  std_logic;             -- 512 000
      clk256k : in  std_logic;             -- 256 000
      clk128k : in  std_logic;             -- 128 000
      sel     : in  unsigned(2 downto 0);  -- Mux select input
      clkout  : out std_logic);            -- Output clock
  end component;

  component spi_master is
    port (
      -- Hardware ports
      miso     : in    std_logic;
      mosi     : out   std_logic;
      sck      : inout std_logic;
      en_adval : out   std_logic := '1';
      en_incl  : out   std_logic := '1';

      -- Simulation ports
      data_to_spi   : in  std_logic_vector(31 downto 0);
      data_from_spi : out std_logic_vector(31 downto 0);
      start         : in  std_logic;
      busy          : out std_logic := '0';
      running       : in  std_logic);
  end component;

  component spi_slave_burst is
    port (
      -- Main controls
      fastclk   : in  std_logic;
      spi_tx    : in  std_logic_vector(31 downto 0);  -- Data to be sent
      spi_rx    : out std_logic_vector(31 downto 0);  -- Data to be received
      exec_cmd  : out std_logic;                      -- Write command/data
      fifo_read : out std_logic;                      -- Read status/data

      -- Slave port, connected to CPU
      mosi    : in  std_logic;
      miso    : out std_logic;
      sck     : in  std_logic;          -- SPI clock
      en_adc  : in  std_logic;          -- Active low, enable ADC
      en_incl : in  std_logic;          -- Active low, enable inclinometer

      -- Master port, connected to inclinometer
      incl_miso : in  std_logic;
      incl_mosi : out std_logic;
      incl_sck  : out std_logic;
      incl_ena  : out std_logic);       -- Active low, enable inclinometer
  end component;

  component sync_logic_2 is
    port (
      start_adcs       : in  std_logic;   -- Start command
      stop_adcs        : in  std_logic;   -- Stop command
      reset            : in  std_logic;   -- Active high
      hwsync           : in  std_logic;   -- Hardware sync
      fastclk          : in  std_logic;   -- Master clock
      enable_adcvalues : out std_logic);  -- Enable reception of values
  end component;

end polyamplib;


-----------------------------------------------------------------------------
-- Shift register for adc_interface, spi_interface etc.
-----------------------------------------------------------------------------

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


--! General serial input, parallell output shift register

--! The shift register length is generic, from 2 and up. It can trigger on
--! positive or negative edges, and the shift direction can be choosen as well.
entity sr_sipo is
  generic (
    LENGTH : positive := 8;             --! Shift register length
    EDGE   : natural  := 1;             --! Active edge; 1=positive, 0=negative
    DIR    : natural  := 1);            --! Direction; 1=left, 0=right
  port (
    clk     : in  std_logic;            --! Clock input
    ser_in  : in  std_logic;            --! Serial input data
    par_out : out std_logic_vector(1 to LENGTH));  --! Parallel output data
end sr_sipo;

architecture sr_arch of sr_sipo is

  signal my_sr : std_logic_vector(1 to LENGTH);

begin  -- sr_arch
  
  posedge_right : if (EDGE = 1) and (DIR = 1) generate
    shift : process(clk)

    begin
      if clk'event and clk = '1' then   -- Positive clock edge
        my_sr(1 to LENGTH) <= (my_sr(2 to LENGTH) & ser_in);
      end if;
    end process shift;
  end generate posedge_right;

  posedge_left : if (EDGE = 1) and (DIR /= 1) generate
    shift : process(clk)

    begin
      if clk'event and clk = '1' then   -- Positive clock edge
        my_sr(1 to LENGTH) <= (ser_in & my_sr(1 to LENGTH-1));
      end if;
    end process shift;
  end generate posedge_left;

  negedge_right : if (EDGE /= 1) and (DIR = 1) generate
    shift : process(clk)

    begin
      if clk'event and clk = '0' then   -- Negative clock edge
        my_sr(1 to LENGTH) <= (my_sr(2 to LENGTH) & ser_in);
      end if;
    end process shift;
  end generate negedge_right;

  negedge_left : if (EDGE /= 1) and (DIR /= 1) generate
    shift : process(clk)

    begin
      if clk'event and clk = '0' then   -- Negative clock edge
        my_sr(1 to LENGTH) <= (ser_in & my_sr(1 to LENGTH-1));
      end if;
    end process shift;
  end generate negedge_left;

  par_out <= my_sr(1 to LENGTH) after 2 ns;

end sr_arch;





-----------------------------------------------------------------------------
-- Shift register for spi_interface
-----------------------------------------------------------------------------

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


--! General parallell input, serial output shift register

--! The shift register length is generic, from 2 and up. It can trigger on
--! positive or negative edges, and the shift direction can be choosen as well.

entity sr_piso is
  generic (
    LENGTH : positive := 8;             --! Shift register length
    EDGE   : natural  := 1;             --! Active edge; 1=positive, 0=negative
    DIR    : natural  := 1);            --! Direction; 1=left, 0=right
  port (
    clk     : in  std_logic;            --! Clock input
    load    : in  std_logic;            --! Load on next clock
    par_in  : in  std_logic_vector(1 to LENGTH);  --! Parallel input data
    ser_in  : in  std_logic;            --! Serial input data
    ser_out : out std_logic);           --! Serial output data
end sr_piso;

architecture sr_arch of sr_piso is

  signal my_sr : std_logic_vector(1 to LENGTH);

begin  -- sr_arch
  
  shift_left : if DIR = 1 generate
    ser_out <= my_sr(1) after 2 ns;
  end generate shift_left;

  shift_right : if DIR /= 1 generate
    ser_out <= my_sr(LENGTH) after 2 ns;
  end generate shift_right;

  posedge_left : if (EDGE = 1) and (DIR = 1) generate
    shift : process(clk)

    begin
      if clk'event and clk = '1' then   -- Positive clock edge
        if load = '1' then
          my_sr <= par_in;
        else
          my_sr(1 to (LENGTH-1)) <= my_sr(2 to LENGTH);
          my_sr(LENGTH)          <= ser_in;
        end if;
      end if;
    end process shift;
  end generate posedge_left;

  posedge_right : if (EDGE = 1) and (DIR /= 1) generate
    shift : process(clk)

    begin
      if clk'event and clk = '1' then   -- Positive clock edge
        if load = '1' then
          my_sr <= par_in;
        else
          my_sr(2 to LENGTH) <= my_sr(1 to LENGTH-1);
          my_sr(1)           <= ser_in;
        end if;
      end if;
    end process shift;
  end generate posedge_right;

  negedge_left : if (EDGE /= 1) and (DIR = 1) generate
    shift : process(clk)

    begin
      if clk'event and clk = '0' then   -- Negative clock edge
        if load = '1' then
          my_sr <= par_in;
        else
          my_sr(1 to (LENGTH-1)) <= my_sr(2 to LENGTH);
          my_sr(LENGTH)          <= ser_in;
        end if;
      end if;
    end process shift;
  end generate negedge_left;

  negedge_right : if (EDGE /= 1) and (DIR /= 1) generate
    shift : process(clk)

    begin
      if clk'event and clk = '0' then   -- Negative clock edge
        if load = '1' then
          my_sr <= par_in;
        else
          my_sr(2 to LENGTH) <= my_sr(1 to LENGTH-1);
          my_sr(1)           <= ser_in;
        end if;
      end if;
    end process shift;
  end generate negedge_right;

end sr_arch;




-----------------------------------------------------------------------------
-- Synchronously clocked shift register for spi_interface
-----------------------------------------------------------------------------

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


--! Synchronously clocked parallell input, serial output shift register

--! The shift register length is generic, from 2 and up.
--! The shift direction can be choosen.

entity sr_piso_s is
  generic (
    LENGTH : positive := 8;             --! Shift register length
    DIR    : natural  := 1);            --! Direction; 1=left, 0=right
  port (
    fastclk : in  std_logic;            --! Synchronous clock
    clk_en  : in  std_logic;            --! Clock enable input
    load_en : in  std_logic;            --! Load enable input
    par_in  : in  std_logic_vector(1 to LENGTH);  --! Parallel input data
    ser_in  : in  std_logic;            --! Serial input data
    ser_out : out std_logic);           --! Serial output data
end sr_piso_s;


architecture ar_piso_s_arch of sr_piso_s is

  signal my_sr : std_logic_vector(1 to LENGTH);

begin  -- ar_piso_s_arch

  shift_left_out : if DIR = 1 generate
    ser_out <= my_sr(1) after 2 ns;
  end generate shift_left_out;

  shift_right_out : if DIR /= 1 generate
    ser_out <= my_sr(LENGTH) after 2 ns;
  end generate shift_right_out;

  shift_left : if (DIR = 1) generate
    shift : process(fastclk)

    begin
      if rising_edge(fastclk) then      -- Positive clock edge
        if load_en = '1' then
          my_sr <= par_in;
        elsif clk_en = '1' then
          my_sr(1 to (LENGTH-1)) <= my_sr(2 to LENGTH);
          my_sr(LENGTH)          <= ser_in;
        end if;
      end if;
    end process shift;
  end generate shift_left;

  shift_right : if (DIR /= 1) generate
    shift : process(fastclk)

    begin
      if rising_edge(fastclk) then      -- Positive clock edge
        if load_en = '1' then
          my_sr <= par_in;
        elsif clk_en = '1' then
          my_sr(2 to LENGTH) <= my_sr(1 to LENGTH-1);
          my_sr(1)           <= ser_in;
        end if;
      end if;
    end process shift;
  end generate shift_right;
  

end ar_piso_s_arch;






-----------------------------------------------------------------------------
-- The adc_interface
-----------------------------------------------------------------------------

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

entity interface_ads1271 is
  
  port (
    fastclk  : in  std_logic;           -- Fast clock
    adclk    : in  std_logic;           -- A/D converter clock
    resync   : in  std_logic;           -- Clock change; resync
    ser_data : in  std_logic;           -- Serial data in
    n_drdy   : in  std_logic;           -- Data is ready, active low
    data_out : out std_logic_vector(23 downto 0);  -- Parallell data out
    nsync    : out std_logic := '1';    -- Synchronize (restart) A/D conv.
    busy     : out std_logic;           -- Busy reading serial data
    g_sclk   : out std_logic);          -- Gated SPI clock, FOR SIMULATION ONLY

end interface_ads1271;


architecture interface_ads1271_arch of interface_ads1271 is

  type   state_type is (idle, counting, freeze);
  signal state         : state_type                    := idle;  -- Controlling state machine
  signal unlatch_data  : std_logic_vector(23 downto 0) := (others => '0');  -- From shift register
  signal bitcounter    : unsigned(4 downto 0)          := to_unsigned(0, 5);  -- Range 0..31
  signal g_sclk_enable : std_logic                     := '0';

  type   rsync_sm_type is (rs_idle, rs_clo1, rs_chi, rs_clo2);
  signal rsync_sm : rsync_sm_type := rs_idle;

  component sr_sipo is
    generic (
      LENGTH : positive;                --! Shift register length
      EDGE   : natural;                 --! Active edge; 1=positive, 0=negative
      DIR    : natural);                --! Direction; 1=left, 0=right
    port (
      clk     : in  std_logic;          --! Clock input
      ser_in  : in  std_logic;          --! Serial input data
      par_out : out std_logic_vector(1 to LENGTH));  --! Parallel output data
  end component;

  
begin  -- interface_ads1271_arch

  sr1 : sr_sipo generic map (
    LENGTH => 24,
    EDGE   => 1,
    DIR    => 1)
    port map (
      clk     => adclk,
      ser_in  => ser_data,
      par_out => unlatch_data);


-- Static interconnects
  g_sclk <= g_sclk_enable and adclk;



-- purpose: Freeze output data 
-- type   : sequential
-- inputs : adclk, n_drdy

  freeze_proc : process (adclk)
  begin  -- process freeze_proc
    if rising_edge(adclk) then          -- rising clock edge
      case state is
        when idle => if n_drdy = '0' then
                       busy          <= '1';
                       state         <= counting;
                       bitcounter    <= to_unsigned(23, 5);
                       g_sclk_enable <= '1';
                     else
                       busy <= '0';
                     end if;
        when counting => busy <= '1';
                         if bitcounter > 0 then
                           bitcounter <= bitcounter - 1;
                         else
                           state         <= freeze;
                           data_out      <= unlatch_data;
                           g_sclk_enable <= '0';
                         end if;
        when freeze => busy <= '0';
                       if n_drdy = '1' then
                         state <= idle;
                       end if;
        when others => state <= idle; busy <= '0';
      end case;
    end if;
  end process freeze_proc;



-- purpose: Handle resyncronization of A/D converters
--          Keep nsync low for at least one adclk cycle
-- type   : sequential
-- inputs : fastclk, adclk, resync
-- outputs: nsync
  resync_proc : process (fastclk)
  begin  -- process resync_proc
    if rising_edge(fastclk) then        -- rising clock edge
      nsync <= '0';
      case rsync_sm is
        when rs_idle => if resync = '1' then
                          rsync_sm <= rs_clo1;
                        else
                          nsync <= '1';
                        end if;
        when rs_clo1 => if adclk = '0' then
                          rsync_sm <= rs_chi;
                        end if;
        when rs_chi => if adclk = '1' then
                         rsync_sm <= rs_clo2;
                       end if;
    when rs_clo2 => if adclk = '0' then
                      rsync_sm <= rs_idle;
                    end if;
    when others => rsync_sm <= rs_idle;
    nsync                   <= '1';
  end case;
end if;
end process resync_proc;

end interface_ads1271_arch;




-----------------------------------------------------------------------------
-- The priority_resolver2
-----------------------------------------------------------------------------

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

entity priority_resolver2 is
  port (
    inp    : in  std_logic_vector(11 downto 0);  -- Active low inputs
    prio   : out unsigned(3 downto 0);  -- Output highest signal
    active : out std_logic);            -- High when any input is active
end priority_resolver2;


architecture priority_resolver2_arch of priority_resolver2 is

begin  -- priority_resolver2_arch

  -- Any input active ???
  active <= inp(0) or inp(1) or inp(2) or inp(3) or inp(4) or inp(5) or
            inp(6) or inp(7) or inp(8) or inp(9) or inp(10) or inp(11) after 1 ns;

  -- purpose: Priority resolver
  -- type   : combinational
  -- inputs : inp
  -- outputs: prio, active
  mainloop : process (inp)
  begin  -- process mainloop
    if inp(11) = '1' then
      prio <= to_unsigned(11, 4) after 1 ns;
    elsif inp(10) = '1' then
      prio <= to_unsigned(10, 4) after 1 ns;
    elsif inp(9) = '1' then
      prio <= to_unsigned(9, 4) after 1 ns;
    elsif inp(8) = '1' then
      prio <= to_unsigned(8, 4) after 1 ns;
    elsif inp(7) = '1' then
      prio <= to_unsigned(7, 4) after 1 ns;
    elsif inp(6) = '1' then
      prio <= to_unsigned(6, 4) after 1 ns;
    elsif inp(5) = '1' then
      prio <= to_unsigned(5, 4) after 1 ns;
    elsif inp(4) = '1' then
      prio <= to_unsigned(4, 4) after 1 ns;
    elsif inp(3) = '1' then
      prio <= to_unsigned(3, 4) after 1 ns;
    elsif inp(2) = '1' then
      prio <= to_unsigned(2, 4) after 1 ns;
    elsif inp(1) = '1' then
      prio <= to_unsigned(1, 4) after 1 ns;
    else
      prio <= to_unsigned(0, 4) after 1 ns;
    end if;
  end process mainloop;
  
end priority_resolver2_arch;




-----------------------------------------------------------------------------
-- The active_input state machine
-----------------------------------------------------------------------------

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

entity active_input is
  port (
    fastclk    : in  std_logic;         -- Main clock
    enable     : in  std_logic;         -- Enable signal
    busy_n     : in  std_logic;         -- Busy input signal, active low
    clear      : in  std_logic;         -- Clear the corresponding flip-flop
    active_out : out std_logic);        -- Active output
end active_input;


architecture active_input_arch of active_input is

  type   input_state_type is (i_idle, i_active, i_decay);
  signal i_state         : input_state_type := i_idle;
  signal temp_active_out : std_logic        := '0';  -- Temporary signal'


begin  -- active_input_arch

  -- Static delayed connex
  active_out <= temp_active_out after 1 ns;


  -- purpose: Input state machine
  -- type   : sequential
  -- inputs : fastclk, fastclk, busy_n, clear
  -- outputs: temp_active_out
  input_active : process (fastclk)

  begin  -- process input_active
    if rising_edge(fastclk) then        -- rising clock edge
      temp_active_out <= '0';
      case i_state is
        when i_idle => if (busy_n = '0') and (enable = '1') then
                         temp_active_out <= '1';
                         i_state         <= i_active;
                       end if;

        when i_active => if clear = '0' then
                           temp_active_out <= '1';
                         else
                           i_state <= i_decay;
                         end if;

        when i_decay => if busy_n = '1' then
                          i_state <= i_idle;
                        end if;
                        
        when others => i_state <= i_idle;
      end case;

    end if;
  end process input_active;
end active_input_arch;






-----------------------------------------------------------------------------
-- The one_of_n_encoder
-----------------------------------------------------------------------------

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

entity one_of_n_encoder is
  
  port (
    number     : in  unsigned(3 downto 0);
    enable     : in  std_logic;
    single_out : out std_logic_vector(11 downto 0));  -- Only a single wire is active at a time

end one_of_n_encoder;



architecture one_of_n_encoder_arch of one_of_n_encoder is

  type     out_table_type is array (0 to 15) of std_logic_vector(11 downto 0);
  constant out_table : out_table_type :=
    ("000000000001",
     "000000000010",
     "000000000100",
     "000000001000",
     "000000010000",
     "000000100000",
     "000001000000",
     "000010000000",
     "000100000000",
     "001000000000",
     "010000000000",
     "100000000000",
     "000000000000",
     "000000000000",
     "000000000000",
     "000000000000");

begin  -- one_of_n_encoder_arch

  -- purpose: Translate unsigned number into a signle output
  -- type   : sequential
  -- inputs : number, number, enable
  -- outputs: single_out
  xlate : process (number, enable)
  begin  -- process xlate
    if enable = '1' then
      single_out <= out_table(to_integer(number)) after 1 ns;
    else
      single_out <= "000000000000" after 1 ns;
    end if;
    
  end process xlate;

end one_of_n_encoder_arch;








-----------------------------------------------------------------------------
-- The master_state_machine
-----------------------------------------------------------------------------

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

entity master_state_machine is
  
  port (

    fastclk    : in    std_logic := '0';               -- Main clock
    enable_lo8 : in    std_logic;       -- Enable the 8 low inputs
    busy_n_vec : in    std_logic_vector(11 downto 0);  -- Busy inputs
    id_code    : inout unsigned(3 downto 0);           -- Id code output
    fifo_write : out   std_logic);      -- Save to fifo

end master_state_machine;



architecture master_state_machine_arch of master_state_machine is

  signal active_data   : std_logic_vector(11 downto 0);
  signal clear_vec     : std_logic_vector(11 downto 0);
  signal any_active    : std_logic := '0';  -- Is any input active ???
  signal clear_enable  : std_logic := '0';  -- Clear the current active input flip-flop
  signal temp_clear_en : std_logic := '0';
  signal priority      : unsigned(3 downto 0);

  type   master_state_type is (m_idle, m_latch, m_clear, m_decay);
  signal m_state : master_state_type := m_idle;
  

  
begin  -- master_state_machine_arch

  -- The first 8 are controlled by the enable_lo8 signal
  gen1 : for i in 0 to 7 generate
    inp_sm : active_input port map (
      fastclk    => fastclk,
      enable     => enable_lo8,
      busy_n     => busy_n_vec(i),
      clear      => clear_vec(i),
      active_out => active_data(i));
  end generate gen1;

  -- The other inputs are always enabled
  gen2 : for i in 8 to 11 generate
    inp_sm : active_input port map (
      fastclk    => fastclk,
      enable     => '1',
      busy_n     => busy_n_vec(i),
      clear      => clear_vec(i),
      active_out => active_data(i));
  end generate gen2;

  resolve : priority_resolver2 port map (
    inp    => active_data,
    prio   => priority,
    active => any_active);

  encode : one_of_n_encoder port map (
    number     => id_code,
    enable     => clear_enable,
    single_out => clear_vec);


  clear_enable <= temp_clear_en after 1 ns;  -- After some delay
  fifo_write   <= clear_enable;              -- Same-same


  -- purpose: Master state machine
  -- type   : sequential
  -- inputs : fastclk, s0..s11,c0..c11
  -- outputs: fifo_write, data_id
  master_loop : process (fastclk)

  begin  -- process master_loop

    if rising_edge(fastclk) then        -- rising clock edge
      temp_clear_en <= '0';
      case m_state is
        when m_idle => if any_active = '1' then
                         id_code <= priority after 1 ns;
                         m_state <= m_latch;
                       end if;

        when m_latch =>
          temp_clear_en <= '1';
          m_state       <= m_clear;

        when m_clear =>
          m_state <= m_decay;

        when m_decay =>
          if any_active = '1' then
            id_code <= priority after 1 ns;
            m_state <= m_latch;
          else
            m_state <= m_idle;
          end if;

        when others => m_state <= m_idle;
      end case;
    end if;

  end process master_loop;
  
end master_state_machine_arch;





-----------------------------------------------------------------------------
-- The fifo_memory
-----------------------------------------------------------------------------

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--use work.polyamplib.all;

entity fifo_memory is
  generic (
    fifo_size : natural := 100);
  port (
    clock   : in  std_logic;
    sclr    : in  std_logic;
    datain  : in  std_logic_vector(31 downto 0);
    wrreq   : in  std_logic;
    rdreq   : in  std_logic;
    dataout : out std_logic_vector(31 downto 0);
    full    : out std_logic;
    empty   : out std_logic;
    meter   : out unsigned(7 downto 0));
end fifo_memory;



architecture fifo_memory_arch of fifo_memory is

begin  -- fifo_memory_arch

  -- purpose: Fifo memory simulator
  -- type   : sequential
  -- inputs : clock, datain, wrreq, rdreq, clock
  -- outputs: dataout, full, empty
  fifo_mem : process (clock)

    subtype  word is std_logic_vector(31 downto 0);
    type     memory_area is array(0 to fifo_size) of word;
    variable fifo : memory_area;        -- The fifo memory area

    variable head_ix : natural := 0;    -- Write data to this address
    variable tail_ix : natural := 0;    -- Read data from this address
    variable test_ix : natural := 0;    -- Used for test of overflow/underflow
    
  begin  -- process fifo
    if rising_edge(clock) then          -- rising clock edge

      -- Synchronous clear
      if sclr = '1' then
        head_ix := 0;
        tail_ix := 0;
      elsif wrreq = '1' then            -- Fifo write op's
        test_ix := head_ix + 1;
        if test_ix > fifo_size then
          test_ix := 0;
        end if;
        -- Writing to full fifo discards the last value
        if test_ix /= tail_ix then      -- NOT full
          fifo(head_ix) := datain;      -- Write data
          head_ix       := test_ix;     -- And adjust the pointer
        end if;
      end if;

      -- Reading empty fifo returns the last value
      if (rdreq = '1') and (head_ix /= tail_ix) then
        dataout <= fifo(tail_ix) after 1 ns;
        tail_ix := tail_ix + 1;
        if tail_ix > fifo_size then
          tail_ix := 0;
        end if;
      end if;

      -- Fifo empty signal
      if head_ix = tail_ix then
        empty <= '1' after 1 ns;
      else
        empty <= '0' after 1 ns;
      end if;

      -- Fifo full signal
      if (tail_ix = (head_ix+1)) or ((tail_ix = 0) and (head_ix = fifo_size)) then
        full <= '1' after 1 ns;
      else
        full <= '0' after 1 ns;
      end if;

      -- Fifo fill meter operations
      if head_ix >= tail_ix then
        meter <= to_unsigned(head_ix - tail_ix, 8);
      else
        meter <= to_unsigned(fifo_size + 1 + head_ix - tail_ix, 8);
      end if;
      
    end if;
  end process fifo_mem;

end fifo_memory_arch;






-----------------------------------------------------------------------------
-- The fifo_ft_memory
-----------------------------------------------------------------------------

-- Fallthrough fifo memory model


library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;
--use work.polyamplib.all;

entity fifo_ft_memory is
  generic (
    fifo_size : natural := 100);
  port (
    clock   : in  std_logic;
    sclr    : in  std_logic;
    datain  : in  std_logic_vector(31 downto 0);
    wrreq   : in  std_logic;
    rdreq   : in  std_logic;
    dataout : out std_logic_vector(31 downto 0);
    full    : out std_logic;
    empty   : out std_logic;
    meter   : out unsigned(7 downto 0));
end fifo_ft_memory;



architecture fifo_ft_memory_arch of fifo_ft_memory is

begin  -- fifo_ft_memory_arch

  -- purpose: Fifo memory simulator
  -- type   : sequential
  -- inputs : clock, datain, wrreq, rdreq, clock
  -- outputs: dataout, full, empty
  fifo_mem : process (clock)

    subtype  word is std_logic_vector(31 downto 0);
    type     memory_area is array(0 to fifo_size) of word;
    variable fifo : memory_area;        -- The fifo memory area

    variable head_ix : natural := 0;    -- Write data to this address
    variable tail_ix : natural := 0;    -- Read data from this address
    variable test_ix : natural := 0;    -- Used for test of overflow/underflow

  begin  -- process fifo
    if rising_edge(clock) then          -- rising clock edge

      -- Synchronous clear
      if sclr = '1' then
        head_ix := 0;
        tail_ix := 0;
      else
        -- Write to fifo
        if wrreq = '1' then             -- Fifo write op's
          test_ix := head_ix + 1;
          if test_ix > fifo_size then
            test_ix := 0;
          end if;

          -- Fifo is empty
          if head_ix = tail_ix then
            dataout       <= datain;     -- Let data fall-through
            fifo(head_ix) := datain;     -- Write data
            head_ix       := test_ix;    -- And adjust the pointer
          elsif test_ix /= tail_ix then  -- NOT full
            fifo(head_ix) := datain;     -- Write data
            head_ix       := test_ix;    -- And adjust the pointer
          end if;
        end if;

        -- Reading empty fifo returns the last value
        if (rdreq = '1') and (head_ix /= tail_ix) then
          tail_ix := tail_ix + 1;
          if tail_ix > fifo_size then
            tail_ix := 0;
          end if;
          if tail_ix /= head_ix then
            dataout <= fifo(tail_ix) after 1 ns;
          end if;
        end if;
      end if;


      -- Fifo empty signal
      if head_ix = tail_ix then
        empty <= '1' after 1 ns;
      else
        empty <= '0' after 1 ns;
      end if;

      -- Fifo full signal
      if (tail_ix = (head_ix+1)) or ((tail_ix = 0) and (head_ix = fifo_size)) then
        full <= '1' after 1 ns;
      else
        full <= '0' after 1 ns;
      end if;

      -- Fifo fill meter operations
      if head_ix >= tail_ix then
        meter <= to_unsigned(head_ix - tail_ix, 8);
      else
        meter <= to_unsigned(fifo_size + 1 + head_ix - tail_ix, 8);
      end if;
      
    end if;
  end process fifo_mem;

end fifo_ft_memory_arch;






-----------------------------------------------------------------------------
-- VHDL model of A/D-converter Texas ADS1271
-----------------------------------------------------------------------------

-- NOTE! This is a model for simulation only. It is not
--       coded to be synthezised.

-- SPI interface format, 512 clock cycles/conversion

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


entity ads1271_model is
  port (
    analog_in : in  signed(23 downto 0);  -- Analog input signal
    clk       : in  std_logic;            -- Conversion clock
    sclk      : in  std_logic;            -- SPI clock
    n_sync    : in  std_logic;            -- Input sync signal, active low
    din       : in  std_logic;            -- Serial data in
    dout      : out std_logic;            -- Serial data out
    n_drdy    : out std_logic := '1');    -- Data ready, active low
end ads1271_model;



architecture ads1271_model_arch of ads1271_model is

  signal analog_data            : signed(23 downto 0) := to_signed(0, 24);
  shared variable conv_timer    : integer             := 2;  -- Data conversion rate
  shared variable sdata_counter : integer             := -1;  -- Controls dout bits
  signal din_mem                : std_logic           := '0';  -- 'din' memory
  signal drdy_ff                : std_logic;
  signal clk_del                : std_logic           := '0';  -- Delayed clk signal
  signal sclk_del               : std_logic           := '0';  -- Delayed sclk signal
  signal ct_is_one              : std_logic;  -- Locally generated sync
  signal ct_is_zero             : std_logic;  -- Locally generated sync

  
begin  -- ads1271_model_arch

  n_drdy   <= drdy_ff or ct_is_one after 8 ns;
  sclk_del <= sclk                 after 2 ns;
  clk_del  <= clk                  after 1 ns;


  -- purpose: Data conversion circuitry
  -- type   : sequential
  -- inputs : clk_del
  -- outputs: dout, ct_is_one
  convert : process (clk_del)
  begin  -- process convert
    if falling_edge(clk_del) then       -- falling clock edge
      ct_is_one  <= '0';
      ct_is_zero <= '0';
      if n_sync = '0' then
        conv_timer := (128*512);        -- Reloading the FIR takes a while
        ct_is_one  <= '1';
      else
        conv_timer    := conv_timer - 1;
        if conv_timer <= 0 then         -- Conversion rate number
          conv_timer    := 512;
          sdata_counter := 23;          -- MSBit comes first
          ct_is_zero    <= '1';
        end if;  -- conv_timer <= 0
        if conv_timer = 1 then
          ct_is_one <= '1';
        end if;  -- conv_timer = 1
        if conv_timer = 512 then
          analog_data <= analog_in;  -- Data sampling, data av. after 512 clock's
        end if;  -- conv_timer = 512
      end if;  -- n_sync = '0'
    end if;  -- falling_edge(clk_del)

  end process convert;



  -- purpose: Serial data interface control
  -- type   : sequential
  -- inputs : sclk_del
  -- outputs: dout
  dataout : process (sclk_del)

  begin  -- process dataout
    if sclk_del'event then
      if sclk_del = '0' then            -- falling clock edge
        if sdata_counter >= 0 then
          dout          <= 'X'                        after 5 ns;
          dout          <= analog_data(sdata_counter) after 12 ns;
          sdata_counter := sdata_counter - 1;
        else
          dout <= 'X'     after 5 ns;
          dout <= din_mem after 12 ns;
        end if;
      else
        -- rising clock edge samples din
        din_mem <= din;
      end if;
    end if;
  end process dataout;



  -- purpose: Controls the drdy_ff signal
  -- type   : sequential
  -- inputs : sclk_del, ct_is_zero
  -- outputs: drdy_ff
  readyctl : process (sclk_del, ct_is_zero)
  begin  -- process readyctl
    if ct_is_zero = '1' then            -- asynchronous reset (active high)
      drdy_ff <= '0';
    elsif falling_edge(sclk_del) then   -- falling clock edge
      drdy_ff <= '1';
    end if;
  end process readyctl;

end ads1271_model_arch;





-----------------------------------------------------------------------------
-- SPI slave with secondary spi master port 
-----------------------------------------------------------------------------

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


entity spi_slave is
  
  port (
    -- Main controls
    fastclk : in  std_logic;
    spi_tx  : in  std_logic_vector(31 downto 0);  -- Data to be sent
    spi_rx  : out std_logic_vector(31 downto 0);  -- Data to be received
    spi_op  : out std_logic;            -- Read/Write status/data/command

    -- Slave port, connected to CPU
    mosi    : in  std_logic;
    miso    : out std_logic;
    sck     : in  std_logic;            -- SPI clock
    en_adc  : in  std_logic;            -- Active low, enable ADC
    en_incl : in  std_logic;            -- Active low, enable inclinometer

    -- Master port, connected to inclinometer
    incl_miso : in  std_logic;
    incl_mosi : out std_logic;
    incl_sck  : out std_logic;
    incl_ena  : out std_logic);         -- Active low, enable inclinometer
end spi_slave;



architecture spi_slave_arch of spi_slave is

  type   state_type is (idle, load, shift, relax);
  signal state : state_type := idle;    -- Controlling state machine

  signal enable_inclinometer : std_logic := '0';
  signal enable_adc          : std_logic := '0';
  signal adc_miso            : std_logic;
  signal adc_load            : std_logic := '0';
--  signal g_miso              : std_logic register := '0';  -- guarded miso signal
  signal adc_sck             : std_logic;
  signal tx_sck              : std_logic;
  signal delayed_adc_sck     : std_logic;  -- Delayed, for edge detection
  

begin  -- spi_slave_arch

  -- Transmit shift register
  tx_sr : sr_piso_s generic map (
    LENGTH => 32,
    DIR    => 1)                        -- Shift left => MSB first
    port map (
      fastclk => fastclk,
      clk_en  => tx_sck,
      load_en => adc_load,
      par_in  => spi_tx,
      ser_in  => '0',
      ser_out => adc_miso);

  -- Receive shift register
  rx_sr : sr_sipo generic map (
    LENGTH => 32,
    EDGE   => 1,                        -- Positive edge
    DIR    => 1)                        -- Shift left => MSB first
    port map (
      clk     => adc_sck,
      ser_in  => mosi,
      par_out => spi_rx);


  -- Passthru signals
  incl_sck  <= sck                     after 1 ns;
  incl_mosi <= mosi                    after 1 ns;
  incl_ena  <= not enable_inclinometer after 1 ns;  -- Active low output

  -- Enable and clock
  enable_inclinometer <= not en_incl and en_adc            after 1 ns;
  enable_adc          <= not en_adc                        after 1 ns;
  adc_sck             <= sck or en_adc                     after 1 ns;  -- Clock is driven high at inactive state
  tx_sck              <= (not adc_sck) and delayed_adc_sck after 1 ns;  -- sck negative edge trigger

  -- Output data
--  miso <= g_miso;


  -- purpose: ADC controls miso
--  talkto_adc : block (enable_adc = '1')
--  begin  -- block talkto_adc
--    g_miso <= guarded adc_miso;
--  end block talkto_adc;


  -- purpose: Inclinometer controls miso
--  talkto_incl : block (enable_inclinometer = '1')
--  begin  -- block talkto_incl
--    g_miso <= guarded incl_miso;
--  end block talkto_incl;


  -- Instead of the guarded assignments above
  miso <= adc_miso when enable_inclinometer = '0' else incl_miso;


  -- purpose: SPI slave controller state machine
  -- type   : sequential
  -- inputs : fastclk, enable_adc
  -- outputs: adc_load
  input_active : process (fastclk)

  begin  -- process input_active
    if rising_edge(fastclk) then        -- rising clock edge
      delayed_adc_sck <= adc_sck;
      adc_load        <= '1' after 1 ns;
      spi_op          <= '0' after 1 ns;
      case state is
        when idle => if enable_adc = '1' then
                       state <= load;
                     end if;

        when load => state <= shift;

        when shift => adc_load <= '0' after 1 ns;
                      if enable_adc = '0' then
                        state  <= relax;
                        spi_op <= '1' after 1 ns;
                      end if;

        when relax => state <= idle;

        when others => state <= idle;
      end case;
    end if;
  end process input_active;
  
end spi_slave_arch;




-----------------------------------------------------------------------------
-- SPI slave with burst capabilities and with secondary spi master port 
-- NOTE! Spi mode 2
-----------------------------------------------------------------------------

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


entity spi_slave_burst is
  
  port (
    -- Main controls
    fastclk   : in  std_logic;
    spi_tx    : in  std_logic_vector(31 downto 0);  -- Data to be sent
    spi_rx    : out std_logic_vector(31 downto 0);  -- Data to be received
    exec_cmd  : out std_logic;                      -- Write command/data
    fifo_read : out std_logic;                      -- Read status/data

    -- Slave port, connected to CPU
    mosi    : in  std_logic;
    miso    : out std_logic;
    sck     : in  std_logic;            -- SPI clock
    en_adc  : in  std_logic;            -- Active low, enable ADC
    en_incl : in  std_logic;            -- Active low, enable inclinometer

    -- Master port, connected to inclinometer
    incl_miso : in  std_logic;
    incl_mosi : out std_logic;
    incl_sck  : out std_logic;
    incl_ena  : out std_logic);         -- Active low, enable inclinometer
end spi_slave_burst;



architecture spi_slave_burst_arch of spi_slave_burst is

--  type   state_type is (idle, load, shift, relax);
--  signal state : state_type := idle;    -- Controlling state machine

  signal bitcounter          : unsigned(4 downto 0) := to_unsigned(0, 5);  -- Range 0..31
  signal enable_inclinometer : std_logic            := '0';
  signal enable_adc          : std_logic            := '0';
  signal adc_miso            : std_logic;
  signal clk_tx_sr           : std_logic            := '0';
  signal load_tx_sr          : std_logic            := '0';

  signal sck_r1             : std_logic;  -- Delayed, for edge detection
  signal sck_r2             : std_logic;  -- Delayed, for edge detection
  signal sck_negedge        : std_logic;  -- Negative edge
  signal sck_posedge        : std_logic;  -- Positive edge
  signal enable_adc_r1      : std_logic;  -- Delayed, for edge detection
  signal enable_adc_r2      : std_logic;  -- Delayed, for edge detection
  signal enable_adc_negedge : std_logic;  -- Negative edge

  signal bitcounter_zero : std_logic;
  signal bitcounter_one  : std_logic;
  signal exec_ff         : std_logic := '0';  -- Enable the execute signal
  signal load_ff         : std_logic := '0';  -- Enable the load signal
  
  
begin  -- spi_slave_burst_arch

  -- Transmit shift register
  tx_sr : sr_piso_s generic map (
    LENGTH => 32,
    DIR    => 1)                        -- Shift left => MSB first
    port map (
      fastclk => fastclk,
      clk_en  => clk_tx_sr,
      load_en => load_tx_sr,
      par_in  => spi_tx,
      ser_in  => '0',
      ser_out => adc_miso);

  -- Receive shift register
  rx_sr : sr_sipo generic map (
    LENGTH => 32,
    EDGE   => 1,                        -- Positive edge
    DIR    => 1)                        -- Shift left => MSB first
    port map (
      clk     => sck_negedge,
      ser_in  => mosi,
      par_out => spi_rx);


  -- Passthru signals
  incl_sck  <= sck                     after 1 ns;
  incl_mosi <= mosi                    after 1 ns;
  incl_ena  <= not enable_inclinometer after 1 ns;  -- Active low output

  -- Enable and clock
  enable_inclinometer <= not en_incl and en_adc after 1 ns;
  enable_adc          <= not en_adc             after 1 ns;

  sck_posedge <= sck_r1 and (not sck_r2) after 1 ns;  -- sck Positive edge
  sck_negedge <= sck_r2 and (not sck_r1) after 1 ns;  -- sck Negative edge

  enable_adc_negedge <= enable_adc_r1 and (not enable_adc_r2) after 1 ns;
  fifo_read          <= sck_posedge and bitcounter_one        after 1 ns;
  load_tx_sr         <= enable_adc_negedge or
                        (sck_negedge and bitcounter_zero and (not load_ff)) after 1 ns;
  exec_cmd  <= bitcounter_zero and exec_ff and sck_posedge after 1 ns;
  clk_tx_sr <= sck_posedge or enable_adc_negedge           after 1 ns;

  -- Instead of the guarded assignments above
  miso <= adc_miso when enable_inclinometer = '0' else incl_miso after 1 ns;


  -- purpose: SPI slave controller state machine
  -- type   : sequential
  -- inputs : fastclk, sck, enable_adc, bitcounter, load_tx_sr
  -- outputs: sck_r1, sck_r2, enable_adc_r1, enable_adc_r2, bitcounter
  input_active : process (fastclk)

  begin  -- process input_active
    if rising_edge(fastclk) then        -- rising clock edge
      sck_r2 <= sck_r1 after 1 ns;
      sck_r1 <= sck    after 1 ns;

      enable_adc_r2 <= enable_adc_r1 after 1 ns;
      enable_adc_r1 <= enable_adc    after 1 ns;

      if enable_adc_negedge = '1' then
        bitcounter <= to_unsigned(0, 5) after 1 ns;
      else if sck_negedge = '1' then
             bitcounter <= bitcounter + 1 after 1 ns;
           end if;
      end if;

      if bitcounter = 0 then
        bitcounter_zero <= '1' after 1 ns;
      else
        bitcounter_zero <= '0' after 1 ns;
      end if;

      if bitcounter = 1 then
        bitcounter_one <= '1' after 1 ns;
      else
        bitcounter_one <= '0' after 1 ns;
      end if;

      if enable_adc = '0' then
        exec_ff <= '0' after 1 ns;
      elsif bitcounter = 2 then
        exec_ff <= '1' after 1 ns;
      end if;

      if load_tx_sr = '1' then
        load_ff <= '1' after 1 ns;
      elsif bitcounter = 2 then
        load_ff <= '0' after 1 ns;
      end if;

    end if;
  end process input_active;

end spi_slave_burst_arch;





----------------------------------------------------------------------
-- Command decoder function
----------------------------------------------------------------------

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


entity command_decoder is
  port (
    addr_data  : in  std_logic_vector(31 downto 0);  -- Input address/data
    decode     : in  std_logic;         -- Single cycle decode pulse
    fastclk    : in  std_logic;         -- Master clock (not used for now)
    sel_nulcmd : out std_logic;         -- NULL command (no operation)
    sel_adclk0 : out std_logic;         -- Select sampling clock, ad0.
    sel_adclk1 : out std_logic;         -- Select sampling clock, ad1.
    sel_adclk2 : out std_logic;         -- Select sampling clock, ad2.
    sel_adclk3 : out std_logic;         -- Select sampling clock, ad3.
    sel_adclk4 : out std_logic;         -- Select sampling clock, ad4.
    sel_adclk5 : out std_logic;         -- Select sampling clock, ad5.
    sel_adclk6 : out std_logic;         -- Select sampling clock, ad6.
    sel_adclk7 : out std_logic;         -- Select sampling clock, ad7.
    resync_adc : out std_logic;         -- Resynchronize all ADC's
    write_ctrl : out std_logic;         -- Write to control-signal register
    start_adcs : out std_logic;         -- Start AD-conversion
    stop_adcs  : out std_logic);        -- Stop AD-conversion
end command_decoder;


architecture command_decoder_arch of command_decoder is

begin  -- command_decoder_arch

  sel_nulcmd <= '1' when (addr_data(27 downto 24) = "0000") and decode = '1' else '0';
  sel_adclk0 <= '1' when (addr_data(27 downto 24) = "0001") and decode = '1' else '0';
  sel_adclk1 <= '1' when (addr_data(27 downto 24) = "0010") and decode = '1' else '0';
  sel_adclk2 <= '1' when (addr_data(27 downto 24) = "0011") and decode = '1' else '0';
  sel_adclk3 <= '1' when (addr_data(27 downto 24) = "0100") and decode = '1' else '0';
  sel_adclk4 <= '1' when (addr_data(27 downto 24) = "0101") and decode = '1' else '0';
  sel_adclk5 <= '1' when (addr_data(27 downto 24) = "0110") and decode = '1' else '0';
  sel_adclk6 <= '1' when (addr_data(27 downto 24) = "0111") and decode = '1' else '0';
  sel_adclk7 <= '1' when (addr_data(27 downto 24) = "1000") and decode = '1' else '0';
  resync_adc <= '1' when (addr_data(27 downto 24) = "1001") and decode = '1' else '0';
  write_ctrl <= '1' when (addr_data(27 downto 24) = "1010") and decode = '1' else '0';
  start_adcs <= '1' when (addr_data(27 downto 24) = "1011") and decode = '1' else '0';
  stop_adcs  <= '1' when (addr_data(27 downto 24) = "1100") and decode = '1' else '0';

end command_decoder_arch;




----------------------------------------------------------------------
-- ADC clock multiplexer function
----------------------------------------------------------------------

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

entity clockmux is
  port (
    clk24M  : in  std_logic;             -- Input clocks, 24 576 000
    clk4M   : in  std_logic;             -- 4 096 000
    clk2M   : in  std_logic;             -- 2 048 000
    clk1M   : in  std_logic;             -- 1 024 000
    clk512k : in  std_logic;             -- 512 000
    clk256k : in  std_logic;             -- 256 000
    clk128k : in  std_logic;             -- 128 000
    sel     : in  unsigned(2 downto 0);  -- Mux select input
    clkout  : out std_logic);            -- Output clock
end clockmux;


architecture clockmux_arch of clockmux is

begin  -- clockmux_arch

  with sel select
    clkout <=
    clk128k when "000",
    clk256k when "001",
    clk512k when "010",
    clk1M   when "011",
    clk2M   when "100",
    clk4M   when "101",
    clk24M  when "110",
    clk24M  when others;

end clockmux_arch;




-------------------------------------------------------------------------------
-- SPI master for simulation and test (no synth.)
-------------------------------------------------------------------------------

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


entity spi_master is
  port (
    -- Hardware ports
    miso     : in    std_logic;
    mosi     : out   std_logic;
    sck      : inout std_logic := '0';
    en_adval : out   std_logic := '1';
    en_incl  : out   std_logic := '1';

    -- Simulation ports
    data_to_spi   : in  std_logic_vector(31 downto 0);
    data_from_spi : out std_logic_vector(31 downto 0);
    start         : in  std_logic;
    busy          : out std_logic := '0';
    running       : in  std_logic);
end spi_master;


architecture spi_master_arch of spi_master is

  type   state_type is (idle, load, shift);
  signal state     : state_type := idle;
  signal start_mem : std_logic  := '0';
  
  
begin  -- spi_master_arch


  -----------------------------------------------------------------------------

  -- Assume that data_to_spi is defined before this process is triggered
  spi_rxtx : process (sck)

    variable bit_index : integer := 0;
    
  begin  -- process spi_rxtx
    if sck'event then
      if sck = '1' then                 -- rising clock edge
        start_mem <= start;
        case state is
          when idle => if start = '1' and start_mem = '0' then  -- Start rising
                                                                -- edge
                         en_adval  <= '0' after 15 ns;
                         busy      <= '1' after 2 ns;
                         state     <= load;
                         bit_index := 31;
                       end if;

          when load => data_from_spi(bit_index) <= miso;
                       state <= shift;

          when shift => bit_index := bit_index - 1;
                        if bit_index < 0 then
                          state    <= idle;
                          en_adval <= '1' after 15 ns;
                          busy     <= '0' after 2 ns;
                        else
                          data_from_spi(bit_index) <= miso;
                        end if;
          when others => state <= idle;
        end case;
      end if;  -- sck = '1' else ...

      if sck = '0' and (state = load or state = shift) then
        mosi <= data_to_spi(bit_index) after 1 ns;
      end if;

    end if;  -- sck'event
  end process spi_rxtx;

  -----------------------------------------------------------------------------

  spi_clk : process
  begin  -- process spi_clk
    while running = '1' loop
      sck <= not sck;
      wait for 50 ns;
    end loop;
    sck <= not sck;
    wait for 50 ns;
    sck <= not sck;
    wait for 50 ns;
    sck <= not sck;
    wait for 50 ns;
    sck <= not sck;
    wait for 50 ns;
    wait;
  end process spi_clk;

end spi_master_arch;



----------------------------------------------------------------------
-- Synchronization logic
----------------------------------------------------------------------

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

entity sync_logic_2 is
  
  port (
    start_adcs       : in  std_logic;   -- Start command
    stop_adcs        : in  std_logic;   -- Stop command
    reset            : in  std_logic;   -- Active high
    hwsync           : in  std_logic;   -- Hardware sync
    fastclk          : in  std_logic;   -- Master clock
    enable_adcvalues : out std_logic);  -- Enable reception of values

end sync_logic_2;



architecture sync_logic_2_arch of sync_logic_2 is

  signal del_sync_1 : std_logic;         -- 1'st delay
  signal del_sync_2 : std_logic;         -- 2'nd delay
  signal sr_set     : std_logic;         -- Set input to SR-latch
  signal sr         : std_logic := '0';  -- sr Q output, entity output

  
begin  -- sync_logic_2_arch

  enable_adcvalues <= sr;
  sr_set           <= (del_sync_1 and (not del_sync_2)) xor start_adcs;


  -- purpose: Two D-latches and one SR-latch is controlled
  -- type   : sequential
  -- inputs : fastclk, reset, start_adcs, stop_adcs, hwsync
  -- outputs: sr
  registered_logic : process (fastclk, reset)
  begin  -- process registered_logic
    if reset = '1' then                 -- asynchronous reset
      del_sync_1 <= '0' after 2 ns;
      del_sync_2 <= '0' after 2 ns;
      sr         <= '0' after 2 ns;
    elsif rising_edge(fastclk) then     -- rising clock edge
      del_sync_1 <= hwsync     after 2 ns;
      del_sync_2 <= del_sync_1 after 2 ns;
      if stop_adcs = '1' then
        sr <= '0' after 2 ns;
      elsif sr_set = '1' then
        sr <= '1' after 2 ns;
      end if;

    end if;
  end process registered_logic;

end sync_logic_2_arch;