library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_arith.all;
use ieee.std_logic_unsigned.all;


entity sx2_interface is

  generic(
    dwidth:natural:=8
    );
  
  port( 
    
    -- the usual suspects here...
    clk                 : in std_logic;
    async_reset         : in std_logic;
    buffer_flush : in std_logic;
    --
    allow_full_speed: in std_logic;
    --
    enum_vid            : in std_logic_vector(15 downto 0) := x"0bd7";
    enum_pid            : in std_logic_vector(15 downto 0) := x"1003";
    enum_did            : in std_logic_vector(15 downto 0) := x"0000";

    enumok: out std_logic;
    -- sx2 control signals
    sx2_interrupt       : in std_logic;
    sx2_ready           : in std_logic;

    --
    sx2_reset: out std_logic;
    
    -- sx2 strobes
    sx2_sloe            : out std_logic;
    sx2_slrd            : out std_logic;
    sx2_slwr            : out std_logic;
    sx2_pkt_end         : out std_logic;
    
    -- fifo address control
    -- can only access bulk fifos, not control endpoint fifo
    sx2_fifo_address    : out std_logic_vector(2 downto 0);

    -- sx2 flags
    sx2_flag_a          : in std_logic; -- programmable flag (not used)
    sx2_flag_b          : in std_logic; -- full flag
    sx2_flag_c          : in std_logic; -- empty flag
--    sx2_flag_d : out std_logic;
    sx2_wakeup: out std_logic;
    sx2_n_cs  : out std_logic;

    -- buffer write and read strobes
    ep2_write_en        : out std_logic := '0';
    ep4_write_en        : out std_logic := '0';
    ep6_read_en         : out std_logic := '0';
    ep8_read_en         : out std_logic := '0';

    -- buffer full and empty flags
    ep2_full            : in std_logic;
    ep4_full            : in std_logic;
    ep6_empty           : in std_logic;
    ep8_empty           : in std_logic;
    
    -- data ports
    sx2_fifo_data       : inout std_logic_vector(dwidth-1 downto 0);
    
    -- there are now two endpoint pairs for different i/o streams
    -- these ones are typically used for a wishbone-bus or other slow-control system
    ep2_fifo_data       : out std_logic_vector(dwidth-1 downto 0) := (others => '0');
    ep4_fifo_data       : out std_logic_vector(dwidth-1 downto 0) := (others => '0');
    ep6_fifo_data       : in std_logic_vector(dwidth-1 downto 0);
    ep8_fifo_data       : in std_logic_vector(dwidth-1 downto 0)
    
    );

end sx2_interface ;

architecture v2 of sx2_interface is

  signal packet_length_hs : integer := 512;
  signal packet_length_fs : integer := 64;

  constant packet_delay  : integer := 4;

  -- the endpoint monitor cycles through the
  -- endpoints looking for a state change
  type ep_arbiter_state_type is
    ( idle,
      addrchangepause,
      manageep2,
      manageep4,
      manageep6,
      manageep8,
      assertpktend,
      assertpktend2,
      assertpktend3,
      assertpktend4,
      flushbuffer0,
      flushbuffer1,
      flushbuffer2,
      flushbuffer3,
      flushbuffersleep
      );

  signal ep_arbiter_state: ep_arbiter_state_type := idle;

  -- negative true logic
  signal sx2_rd_int : std_logic := '0';
  signal sx2_oe_int : std_logic := '0';
  signal sx2_write_int : std_logic := '0';
  signal sx2_pkt_end_int : std_logic := '0';

  -- positive true logic for enum fsm:
  signal enum_sx2_rd_int : std_logic := '0';
  signal enum_sx2_oe_int : std_logic := '0';
  signal enum_sx2_write_int : std_logic := '0';
  signal enum_sx2_pkt_end_int : std_logic := '0';


  signal sx2_oe_int_comb : std_logic := '0';
  

  
  signal ep2_write_en_int : std_logic := '0';
  signal ep4_write_en_int : std_logic := '0';
  signal ep6_read_en_int : std_logic := '0';
  signal ep8_read_en_int : std_logic := '0';

  -- internal data register needed for flow control
  signal elastic_transfer_register_ep2 : std_logic_vector(dwidth-1 downto 0) := (others => '0');
  signal elastic_register_valid_ep2 : std_logic := '1';
  signal elastic_transfer_register_ep4 : std_logic_vector(dwidth-1 downto 0) := (others => '0');
  signal elastic_register_valid_ep4 : std_logic := '1';
  --signal elastic_transfer_register_ep6 : std_logic_vector(dwidth-1 downto 0) := (others => '0');
  --signal elastic_register_valid_ep6 : std_logic := '1';
  --signal elastic_transfer_register_ep8 : std_logic_vector(dwidth-1 downto 0) := (others => '0');
  --signal elastic_register_valid_ep8 : std_logic := '1';

  -- internal address for fifo
  signal sx2_fifo_address_int : std_logic_vector(2 downto 0);
  signal enum_sx2_fifo_address_int : std_logic_vector(2 downto 0);

  signal fifo_data_int_comb: std_logic_vector(dwidth-1 downto 0);
  
  -- data buffer internal signal
  signal sx2_fifo_data_int : std_logic_vector(dwidth-1 downto 0) := (others => '0');
  signal ep2_fifo_data_int : std_logic_vector(dwidth-1 downto 0) := (others => '0');
  signal ep4_fifo_data_int : std_logic_vector(dwidth-1 downto 0) := (others => '0');

  -- debug hack
  --signal debug_previous_data : std_logic_vector(13 downto 0) := std_logic_vector(conv_unsigned(16383, 14));

  signal packet_length : integer := 512;

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

  -- counter times
  constant desc_length : integer := 17;
  constant usb_2_length : integer := 38;
  constant usb_1_length : integer := 71;

  constant sx2_transaction_delay : integer := 6;
  constant sx2_enum_wait_max : integer := 200000000; -- five seconds at 40mhz

  -- 200us at 40mhz
  constant sx2_reset_time     : std_logic_vector(15 downto 0) := std_logic_vector(conv_unsigned(8000, 16));
  constant sx2_activate_time  : std_logic_vector(15 downto 0) := std_logic_vector(conv_unsigned(8000, 16));

  -- define mem type and create constant array for enumeration
  type enum_array is array(0 to usb_1_length-1) of std_logic_vector(7 downto 0);
  signal enum_values : enum_array := (x"b0", x"00", x"06", x"00", x"00",   -- 0 begin descriptor download
                                      x"0d", x"07", x"00", x"0b",          -- 5 vid 0x0bd7 (default)
                                      x"00", x"03", x"01", x"00",          -- 9 pid 0x1003 (default)
                                      x"00", x"00", x"00", x"00",          -- 13 did 0x0000 (default)

                                      x"a0", x"0f", x"00",                 -- 17 flush endpoint fifos
                                      
                                      x"8a", x"02", x"02",                 -- 20 ep2 to 8-bit 512 deep (8a, dh, dl)
                                      x"8c", x"02", x"02",                 -- 23 ep4 to 8-bit 512 deep (8a, dh, dl)
                                      x"8e", x"02", x"02",                 -- 26 set ep6 to 8-bit 512 deep (8a, dh, dl)
                                      x"90", x"02", x"02",                 -- 29 set ep8 to 8-bit 512 deep (8a, dh, dl)
                                      x"a0", x"0f", x"00",                 -- 32 flush endpoint fifos
                                      x"81", x"04", x"00",                 -- 35 set ifconfig register to synchronous mode

                                      x"a0", x"0f", x"00",                 -- 38 flush endpoint fifos
                                      
                                      x"8a", x"02", x"00",                 -- 41 set ep2 to 8-bit 64 deep (8a, dh, dl)
                                      x"8b", x"04", x"00",                 -- 44 as above...
                                      x"8c", x"02", x"00",                 -- 47 set ep4 to 8-bit 64 deep (8a, dh, dl)
                                      x"8d", x"04", x"00",                 -- 50 as above...
                                      x"8e", x"02", x"00",                 -- 53 set ep6 to 8-bit 64 deep (8a, dh, dl)
                                      x"8f", x"04", x"00",                 -- 56 as above...
                                      x"90", x"02", x"00",                 -- 59 set ep8 to 8-bit 64 deep (8a, dh, dl)
                                      x"91", x"04", x"00",                 -- 62 as above...
                                      x"a0", x"0f", x"00",                 -- 65 flush endpoint fifos
                                      x"81", x"04", x"00");                -- 68 set ifconfig register to synchronous mode

  -- state type for the enumeration fsm:
  type state_type is
    (
      reset_sx2,
      activate_sx2,
      activation_wait,

      -- the hello bit...
      wait_for_sx2_interrupt,
      read_sx2_interrupt_pre_delay,
      read_sx2_interrupt,
      read_sx2_interrupt_post_delay,

      -- descriptor download
      begin_descriptor_download,
      download_descriptor_1,
      download_descriptor_2,
      download_descriptor_3,
      download_descriptor_4,
      
      -- enumeration with host pc
      wait_for_sx2_enum_ok,
      read_enumeration_ok_interrupt_1,
      read_enumeration_ok_interrupt_2,
      read_enumeration_ok_interrupt_3,
      enumeration_complete,

      -- enumeration mode read
      read_enum_mode_1,
      read_enum_mode_2,
      read_enum_mode_3,
      read_enum_mode_4,
      read_enum_mode_5,
      read_enum_mode_6,
      read_enum_mode_7,

      -- settings download
      download_settings_1,
      download_settings_2,
      download_settings_3,
      download_settings_4,
      boot_complete
      );

  signal enumeration_state: state_type;

  signal int_buf_flush_cntr : std_logic_vector (15 downto 0);
  -- reset counter internal signals
  signal reset_count          : std_logic_vector (15 downto 0);
  signal reset_count_max      : std_logic_vector (15 downto 0) := sx2_reset_time;
  signal reset_counter_reset  : std_logic;
  signal reset_counter_done   : std_logic;

  -- metastability signals
  signal meta_sx2_interrupt    : std_logic := '1';
  signal meta_sx2_ready        : std_logic := '0';
  signal latched_sx2_interrupt : std_logic := '1';
  signal latched_sx2_ready     : std_logic := '0';

  -- internal enum type signal
  signal int_enum_high_speed   : std_logic := '0';

  signal enum_data : std_logic_vector(7 downto 0);
  signal enum_data_op : std_logic_vector(dwidth-1 downto 0):=(others=>'0');

  
  ---------------------------------------------------------------------------
  -- signal that sx2 is active

  signal sx2_enumeration_ok :  std_logic;
  signal fsm_enumeration_ok :  std_logic;
  signal sx2_high_speed     : std_logic;
  signal int_sx2_reset: std_logic;

  signal sx2_clk: std_logic;

  signal int_buf_flush: std_logic;
  
begin
  
  sx2_n_cs<='0';
  sx2_wakeup<='1';
  
  sx2_clk<=clk;
  
  enumok<=sx2_enumeration_ok;

  -- valid values for dwidth are 8 and 16. This code assumes that. 
  packet_length_hs<=512 when (dwidth=8) else 256;
  packet_length_fs<=64 when (dwidth=8) else 32;
  
  packet_length <= packet_length_hs when sx2_high_speed = '1' else packet_length_fs;

  -- negative-true logic for the strobes (convert at the end to make my head less confused)
  
  sx2_sloe<=sx2_oe_int_comb;
  sx2_oe_int_comb <= not (sx2_oe_int) when sx2_enumeration_ok='1' else (enum_sx2_oe_int);
  
  sx2_slrd <= not (sx2_rd_int) when sx2_enumeration_ok='1' else (enum_sx2_rd_int);
  sx2_slwr <= not (sx2_write_int) when sx2_enumeration_ok='1' else (enum_sx2_write_int);
  sx2_pkt_end <= not (sx2_pkt_end_int) when sx2_enumeration_ok='1' else (enum_sx2_pkt_end_int);
  
  -- we need internal signals here so that
  -- we can monitor the transactions in the state machine...
  -- the timings here are an absolute monster...
  ep2_write_en <= ep2_write_en_int;
  ep4_write_en <= ep4_write_en_int;
  ep6_read_en <= ep6_read_en_int;
  ep8_read_en <= ep8_read_en_int;

  -- map the fifo_address internal signal out to the port
  sx2_fifo_address <= sx2_fifo_address_int when sx2_enumeration_ok='1' else enum_sx2_fifo_address_int;

  --sx2_flag_d<='0';

  
  -- ep_arbiter is a prioritising mux for the sx2 fifos
  -- it continually scans the fifo status
  -- 
  -- ep4/8 always wins within a clock cycle over ep2/6
  -- not only is this what we want, it's also relatively easy to code as there's no need
  -- to make the prioritisation dynamic...

  ep_arbiter : process (clk, async_reset, sx2_enumeration_ok)
    variable commit_counter : integer;-- := packet_length_hs;
  begin

    if ( (async_reset = '1') or (sx2_enumeration_ok = '0') ) then

      ep_arbiter_state <= idle;
      sx2_oe_int <= '0';
      sx2_rd_int <= '0';
      sx2_write_int <= '0';
      sx2_pkt_end_int <= '0';
      sx2_fifo_address_int <= "000"; -- resets to ep2 input (doesn't matter as changes it's mind every cycle)

      -- clear the fifo strobes
      ep2_write_en_int <= '0';
      ep4_write_en_int <= '0';
      ep6_read_en_int <= '0';
      ep8_read_en_int <= '0';

      -- tristate the sx2 fifo data lines
      sx2_fifo_data_int <= (others => 'Z');
      ep2_fifo_data_int <= (others => '0');
      ep4_fifo_data_int <= (others => '0');

      elastic_transfer_register_ep2 <= (others => '0');
      elastic_register_valid_ep2 <= '0';
      elastic_transfer_register_ep4 <= (others => '0');
      elastic_register_valid_ep4 <= '0';
      --elastic_transfer_register_ep6 <= (others => '0');
      --elastic_register_valid_ep6 <= '0';
      --elastic_transfer_register_ep8 <= (others => '0');
      --elastic_register_valid_ep8 <= '0';

      int_buf_flush_cntr<=conv_std_logic_vector(0,16);

      int_buf_flush<='0';

    elsif ( rising_edge(clk) ) then
      if(buffer_flush='1')then 
        int_buf_flush<='1';
      end if;
      -- decide which 
      case ep_arbiter_state is
        when idle =>

          -- reset the packet commit counter
          commit_counter := packet_length;

          sx2_oe_int <= '0';
          sx2_rd_int <= '0';
          sx2_write_int <= '0'; -- setting this low will tristate the output
          sx2_pkt_end_int <= '0';

          -- remain in idle state
          ep_arbiter_state <= idle;

          if ( sx2_fifo_address_int = "000" ) then
            sx2_fifo_address_int <= "001"; -- switch to ep4 for next clk cycle
          else
            sx2_fifo_address_int <= "000"; -- switch to ep2 for next clk cycle
          end if;

          ep2_write_en_int <= '0';
          ep4_write_en_int <= '0';
          ep6_read_en_int <= '0';
          ep8_read_en_int <= '0';

          sx2_fifo_data_int <= (others => 'Z');

          -- in order for this to work properly, there musn't be nested if clauses
          -- this increases the combinatorial complexity but avoids stalling because of fifo full/empty conditions
          if ( (sx2_flag_c = '1') and (sx2_fifo_address_int = "001") and (ep4_full = '0') ) then -- negative true empty flag (arghh)

            -- data came in on ep4
            -- set the strobes and data should be available next clock cycle
            sx2_write_int <= '0';
            --sx2_oe_int <= '1';
            --sx2_rd_int <= '1';
            ep_arbiter_state <= manageep4;
            sx2_fifo_address_int <= "001";
            
          elsif ( (sx2_flag_c = '1') and (sx2_fifo_address_int = "000") and (ep2_full = '0') ) then -- negative true empty flag (arghh)
            
            -- data came in on ep2
            -- set the strobes and data should be available next clock cycle
            sx2_write_int <= '0';
            --sx2_oe_int <= '1';
            --sx2_rd_int <= '1';
            ep_arbiter_state <= manageep2;
            sx2_fifo_address_int <= "000";
            
          elsif ( ep8_empty = '0' ) then
            -- priority read pathway has data

            -- switch fifo address to endpoint 8
            sx2_fifo_address_int <= "011";

            ep_arbiter_state <= addrchangepause;

          elsif ( ep6_empty = '0' ) then
            -- non-priority read pathway has data

            -- switch fifo address to endpoint 6
            sx2_fifo_address_int <= "010";

            ep_arbiter_state <= addrchangepause;

          end if;

          if(int_buf_flush='1')then
            sx2_fifo_address_int<="100";
            sx2_fifo_data_int(7 downto 0)<=x"a0";
            ep_arbiter_state<=flushbuffer0;
          end if;
          
          
        when addrchangepause =>

          if ( sx2_fifo_address_int = "011" ) then

            if ( (ep8_empty = '0') and (sx2_flag_b = '1') ) then                         
              -- priority read pathway has data

              -- jump to ep8 management state
              ep_arbiter_state <= manageep8;

              -- disable read on the sx2
              sx2_rd_int <= '0';
              sx2_oe_int <= '0';

            elsif ( ep6_empty = '0' ) then

              -- try ep6 if it's not empty as ep8 failed
              sx2_fifo_address_int <= "010";
              ep_arbiter_state <= addrchangepause;

            elsif ( ep4_full = '0' ) then -- negative true empty flag (arghh)

              -- try ep4 if it's not full as ep6 failed
              sx2_fifo_address_int <= "001";
              ep_arbiter_state <= addrchangepause;
              
            else

              -- return to idle state if both endpoints not ready
              ep_arbiter_state <= idle;
              sx2_fifo_address_int <= "000";

            end if;

          elsif ( sx2_fifo_address_int = "010" ) then

            if ( (ep6_empty = '0') and (sx2_flag_b = '1') ) then

              -- jump to ep6 management state
              ep_arbiter_state <= manageep6;

              -- disable read on the sx2
              sx2_rd_int <= '0';
              sx2_oe_int <= '0';

            elsif ( ep4_full = '0' ) then -- negative true empty flag (arghh)

              -- try ep6 if it's not empty as ep8 failed
              sx2_fifo_address_int <= "001";
              ep_arbiter_state <= addrchangepause;                            

            else

              -- return to idle state if both endpoints not ready
              ep_arbiter_state <= idle;
              sx2_fifo_address_int <= "000";

            end if;

          elsif ( (sx2_flag_c = '1') and (sx2_fifo_address_int = "001") and (ep4_full = '0') ) then -- negative true empty flag (arghh)

            -- do ep4 as ep6 and ep8 aren't accessible
            -- set the strobes and data should be available next clock cycle
            sx2_write_int <= '0';
            ep_arbiter_state <= manageep4;

          else

            ep_arbiter_state <= idle;
            sx2_fifo_address_int <= "000";

          end if;

        when manageep2 =>

          if ( sx2_flag_c = '1' ) then

            if ( ep2_full = '0' ) then

              if ( elastic_register_valid_ep2 = '1' ) then

                if ( ep2_write_en_int = '1' ) then

                  -- for the internal register to be valid we must have already stalled
                  -- therefore as the full flag is low this cycle, it has now been stored,
                  -- so clear the elastic register this cycle and leave a single cycle gap
                  -- for continuing to read the input fifo
                  ep2_fifo_data_int <= elastic_transfer_register_ep2;
                  elastic_register_valid_ep2 <= '0';
                  sx2_rd_int <= '1';
                  sx2_oe_int <= '1';

                else

                  ep2_write_en_int <= '1';

                end if;

              else

                -- strobe read if there's room in the output buffer
                sx2_rd_int <= '1';
                sx2_oe_int <= '1';
                ep2_write_en_int <= '0';
                
                if ( sx2_rd_int = '1' ) then

                  -- copy data direct to output
                  ep2_fifo_data_int <= sx2_fifo_data;
                  -- strobe write on output fifo
                  ep2_write_en_int <= '1';

                end if;

              end if;

            else

              -- return to init state
              ep_arbiter_state <= idle;

              -- we've clocked valid data in but the output fifo is full,
              -- so we use the elastic transfer register to hold the value
              if ( sx2_rd_int = '1' ) then

                -- store and signal valid
                elastic_transfer_register_ep2 <= sx2_fifo_data;
                elastic_register_valid_ep2 <= '1';

                -- release the read strobe
                ep2_write_en_int <= '0';
                sx2_rd_int <= '0';
                sx2_oe_int <= '0';

              end if;

            end if;

          else

            -- clear the read strobe
            sx2_rd_int <= '0';
            sx2_oe_int <= '0';
            -- last data clocked is invalid so ignore

            -- need to check if ep fifo is full during write sequence
            if ( ep2_full = '0' ) then

              if ( elastic_register_valid_ep2 = '1' ) then

                if ( ep2_write_en_int = '1' ) then

                  -- for the internal register to be valid we must have already stalled
                  -- therefore as the full flag is low this cycle, it has now been stored,
                  -- so clear the elastic register this cycle and leave a single cycle gap
                  -- for continuing to read the input fifo
                  ep2_fifo_data_int <= elastic_transfer_register_ep2;
                  elastic_register_valid_ep2 <= '0';

                else

                  ep2_write_en_int <= '1';

                end if;

              else

                -- can release the write strobe if we're here but not if the fifo's full
                ep2_write_en_int <= '0';

                -- jump to packet end here
                ep_arbiter_state <= idle;

              end if;

            else

              -- return to init state
              ep_arbiter_state <= idle;

              -- we've clocked valid data in but the output fifo is full,
              -- so we use the elastic transfer register to hold the value
              if ( sx2_rd_int = '1' ) then

                -- store and signal valid
                elastic_transfer_register_ep2 <= ep2_fifo_data_int;
                elastic_register_valid_ep2 <= '1';

                -- release the read strobe
                ep2_write_en_int <= '0';
                sx2_rd_int <= '0';
                sx2_oe_int <= '0';

              end if;

            end if;

          end if;

        when manageep4 =>

          if ( sx2_flag_c = '1' ) then

            if ( ep4_full = '0' ) then

              if ( elastic_register_valid_ep4 = '1' ) then

                if ( ep4_write_en_int = '1' ) then

                  -- for the internal register to be valid we must have already stalled
                  -- therefore as the full flag is low this cycle, it has now been stored,
                  -- so clear the elastic register this cycle and leave a single cycle gap
                  -- for continuing to read the input fifo
                  ep4_fifo_data_int <= elastic_transfer_register_ep4;
                  elastic_register_valid_ep4 <= '0';
                  sx2_rd_int <= '1';
                  sx2_oe_int <= '1';

                else

                  ep4_write_en_int <= '1';

                end if;

              else

                -- strobe read if there's room in the output buffer
                sx2_rd_int <= '1';
                sx2_oe_int <= '1';
                ep4_write_en_int <= '0';
                
                if ( sx2_rd_int = '1' ) then

                  -- copy data direct to output
                  ep4_fifo_data_int <= sx2_fifo_data;
                  -- strobe write on output fifo
                  ep4_write_en_int <= '1';

                end if;


              end if;

            else

              -- return to init state
              ep_arbiter_state <= idle;

              -- we've clocked valid data in but the output fifo is full,
              -- so we use the elastic transfer register to hold the value
              if ( sx2_rd_int = '1' ) then

                -- store and signal valid
                elastic_transfer_register_ep4 <= sx2_fifo_data;
                elastic_register_valid_ep4 <= '1';

                -- release the read strobe
                ep4_write_en_int <= '0';
                sx2_rd_int <= '0';
                sx2_oe_int <= '0';

              end if;

            end if;

          else

            -- clear the read strobe
            sx2_rd_int <= '0';
            sx2_oe_int <= '0';
            -- last data clocked is invalid so ignore

            -- need to check if ep fifo is full during write sequence
            if ( ep4_full = '0' ) then

              if ( elastic_register_valid_ep4 = '1' ) then

                if ( ep4_write_en_int = '1' ) then

                  -- for the internal register to be valid we must have already stalled
                  -- therefore as the full flag is low this cycle, it has now been stored,
                  -- so clear the elastic register this cycle and leave a single cycle gap
                  -- for continuing to read the input fifo
                  ep4_fifo_data_int <= elastic_transfer_register_ep4;
                  elastic_register_valid_ep4 <= '0';

                else

                  ep4_write_en_int <= '1';

                end if;

              else

                -- can release the write strobe if we're here but not if the fifo's full
                ep4_write_en_int <= '0';

                -- jump to packet end here
                ep_arbiter_state <= idle;

              end if;

            else

              -- return to init state
              ep_arbiter_state <= idle;

              -- we've clocked valid data in but the output fifo is full,
              -- so we use the elastic transfer register to hold the value
              if ( sx2_rd_int = '1' ) then

                -- store and signal valid
                elastic_transfer_register_ep4 <= sx2_fifo_data;
                elastic_register_valid_ep4 <= '1';

                -- release the read strobe
                ep4_write_en_int <= '0';
                sx2_rd_int <= '0';
                sx2_oe_int <= '0';

              end if;

            end if;

          end if;

        when manageep6 =>

          if ( sx2_write_int = '1' ) then
            commit_counter := commit_counter - 1;
          end if;

          if ( ep6_read_en_int = '1' ) then
            -- strobe write on output fifo
            sx2_write_int <= '1';
          end if;

          if ( (ep6_empty = '0') and (commit_counter /= 0) ) then

            if ( commit_counter /= 1 ) then
              -- strobe read if there's room in the output buffer
              ep6_read_en_int <= '1';
            else
              ep6_read_en_int <= '0';
            end if;

            -- copy data direct to output
            sx2_fifo_data_int <= ep6_fifo_data;

          else

            -- clear the read strobe
            ep6_read_en_int <= '0';
            sx2_write_int <= '0';

            if ( commit_counter /= 0 ) then

              -- jump to packet end here
              ep_arbiter_state <= assertpktend;

              -- assert the packet end strobe as we're under the packet length boundary
              --sx2_pkt_end_int <= '1';

            else

              -- jump to packet end here
              ep_arbiter_state <= assertpktend2;
              
            end if;

            commit_counter := packet_delay;

          end if;

        when manageep8 =>

          if ( sx2_write_int = '1' ) then
            commit_counter := commit_counter - 1;
          end if;

          if ( ep8_read_en_int = '1' ) then
            -- strobe write on output fifo
            sx2_write_int <= '1';
          end if;

          if ( (ep8_empty = '0') and (commit_counter /= 0) ) then

            if ( commit_counter /= 1 ) then
              -- strobe read if there's room in the output buffer
              ep8_read_en_int <= '1';
            else
              ep8_read_en_int <= '0';
            end if;

            -- copy data direct to output
            sx2_fifo_data_int <= ep8_fifo_data;

          else

            -- clear the read strobe
            ep8_read_en_int <= '0';
            sx2_write_int <= '0';

            if ( commit_counter /= 0 ) then

              -- jump to packet end here
              ep_arbiter_state <= assertpktend;

              -- assert the packet end strobe as we're under the packet length boundary
              --sx2_pkt_end_int <= '1';

            else

              -- jump to packet end here
              ep_arbiter_state <= assertpktend2;
              
            end if;

            commit_counter := packet_delay;

          end if;

        when assertpktend =>
          -- setup time for pktend is huge, so we need two cycles at high clock speeds
          --ep_arbiter_state <= assertpktend2;
          -- just a wait loop
          commit_counter := commit_counter - 1;
          if ( commit_counter = 0 ) then
            ep_arbiter_state <= assertpktend2;
            sx2_pkt_end_int <= '1';
            commit_counter := packet_delay;                         
          else
            ep_arbiter_state <= assertpktend;
          end if;

        when assertpktend2 =>
          --ep_arbiter_state <= assertpktend3;
          commit_counter := commit_counter - 1;
          if ( commit_counter = 0 ) then
            ep_arbiter_state <= assertpktend3;
            --commit_counter := packet_delay;
          else
            ep_arbiter_state <= assertpktend2;
          end if;

        when assertpktend3 =>
          ep_arbiter_state <= assertpktend4;
          sx2_pkt_end_int <= '0';
          commit_counter := (5*packet_delay); 

        when assertpktend4 =>
          -- just a wait loop
          commit_counter := commit_counter - 1;
          if ( commit_counter = 0 ) then
            ep_arbiter_state <= idle;
            sx2_fifo_address_int <= "000";
          else
            ep_arbiter_state <= assertpktend4;
          end if;


        when flushbuffer0 =>
          
          int_buf_flush_cntr<=conv_std_logic_vector(0,16);
          sx2_fifo_address_int<="100";
          sx2_pkt_end_int <= '0';
          sx2_oe_int <= '0';
          sx2_rd_int <= '0';
          sx2_write_int <= '1';
          sx2_fifo_data_int(7 downto 0)<=x"a0";
          ep_arbiter_state<=flushbuffer1;
          
        when flushbuffer1 =>
          sx2_fifo_address_int<="100";
          sx2_pkt_end_int <= '0';
          sx2_oe_int <= '0';
          sx2_rd_int <= '0';
          sx2_write_int <= '0';
          ep_arbiter_state<=flushbuffer1;
          if(latched_sx2_ready='1')then 
            ep_arbiter_state<=flushbuffer2;
            sx2_fifo_data_int(7 downto 0)<=x"0f";
            sx2_write_int<='1';
          end if;
          
        when flushbuffer2 =>
          sx2_fifo_address_int<="100";
          sx2_pkt_end_int <= '0';
          sx2_oe_int <= '0';
          sx2_rd_int <= '0';
          sx2_write_int <= '0';
          ep_arbiter_state<=flushbuffer2;
          if(latched_sx2_ready='1')then 
            ep_arbiter_state<=flushbuffer3;
            sx2_fifo_data_int(7 downto 0)<=x"00";
            sx2_write_int<='1';
          end if;

          
        when flushbuffer3 =>
          sx2_fifo_address_int<="100";
          sx2_pkt_end_int <= '0';
          sx2_oe_int <= '0';
          sx2_rd_int <= '0';
          sx2_write_int <= '0';
          ep_arbiter_state<=flushbuffer3;
          if(latched_sx2_ready='1')then 
            ep_arbiter_state<=flushbuffersleep;
                        
          end if;

        when flushbuffersleep=>
          ep_arbiter_state<=flushbuffersleep;
          int_buf_flush_cntr<=int_buf_flush_cntr+1;
          if(int_buf_flush_cntr>=8000)then
            ep_arbiter_state<=idle;
            int_buf_flush<='0';
            sx2_fifo_address_int<="000";
          end if;
          
        when others =>
          -- undefined state, return to idle
          -- clear controls
          ep_arbiter_state <= idle;

      end case;

    end if;

  end process ep_arbiter;

  -- tristate bus when not writing
  --sx2_fifo_data <= sx2_fifo_data_int when (sx2_oe_int = '0') and (sx2_enumeration_ok = '1') else enum_data_op;
  
  sx2_fifo_data <= fifo_data_int_comb when sx2_oe_int_comb='1' else (others=>'Z');

  fifo_data_int_comb<=sx2_fifo_data_int when sx2_enumeration_ok='1' else enum_data_op;

  enum_data_op(7 downto 0)<=enum_data;

  
  --enum_data_op<=enum_data when (sx2_enumeration_ok='0')   else (others=>'Z');
  
  -- internal endpoint buffers
  ep2_fifo_data <= ep2_fifo_data_int;
  ep4_fifo_data <= ep4_fifo_data_int;


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

  
  -- just pass-through
  sx2_high_speed <= int_enum_high_speed;

  sx2_enumeration_ok<=fsm_enumeration_ok;
  
  sx2_reset<=int_sx2_reset;
  
-- jj october 2005
--
-- the reset counter has now been absorbed into the enumeration fsm for
-- ease of use

  reset_counter : process (reset_counter_reset, sx2_clk)
  begin
    
    if (reset_counter_reset = '1') then
      reset_counter_done <= '0';
      reset_count <= (others => '0');
    elsif ( rising_edge(sx2_clk) ) then
      if ( reset_count < reset_count_max ) then
        reset_count <= reset_count + 1; 
      else 
        --reset_count <= (others => '0');
        reset_counter_done <= '1'; 
      end if;
    end if;

  end process reset_counter;	 

-- asynchronous input signals from the sx2 need to be double-latched
-- to avoid metastability, otherwise the sx2 interface will fail in
-- some synthesis runs and not others (joy!)

  metastability_check : process(sx2_clk, async_reset)
  begin
    if ( async_reset = '1' ) then
      meta_sx2_ready <= '0';
      meta_sx2_interrupt <= '1';
      latched_sx2_ready <= '0';
      latched_sx2_interrupt <= '1';
    elsif ( rising_edge(sx2_clk) ) then
      meta_sx2_ready <= sx2_ready;
      if ( meta_sx2_ready = sx2_ready ) then
        latched_sx2_ready <= meta_sx2_ready;
      end if;          
      meta_sx2_interrupt <= sx2_interrupt;
      if ( meta_sx2_interrupt = sx2_interrupt ) then
        latched_sx2_interrupt <= meta_sx2_interrupt;
      end if;
    end if;
  end process metastability_check;


  enum_values(21)<=x"02" when (dwidth=8) else x"03";
  enum_values(24)<=x"02" when (dwidth=8) else x"03";
  enum_values(27)<=x"02" when (dwidth=8) else x"03";
  enum_values(30)<=x"02" when (dwidth=8) else x"03";
  enum_values(42)<=x"02" when (dwidth=8) else x"03";
  enum_values(48)<=x"02" when (dwidth=8) else x"03";
  enum_values(54)<=x"02" when (dwidth=8) else x"03";
  enum_values(60)<=x"02" when (dwidth=8) else x"03";
  
  enumeration_fsm : process (async_reset, sx2_clk)
    variable sx2_enum_interrupt : std_logic := '0';
    variable sx2_ready_interrupt : std_logic := '0';
    variable delay_counter : integer := sx2_transaction_delay;
    variable enum_counter : integer := 0;
  begin

    if ( async_reset = '1' ) then

      -- reset the fsm state
      enumeration_state <= reset_sx2;

      --signals : initial values
      int_sx2_reset <= '0'; --sx2 inactive on power on
      enum_sx2_oe_int <= '1'; --disable output enable
      enum_sx2_rd_int <= '1'; --disable read access
      enum_sx2_write_int <= '1'; --disable write access
      enum_sx2_fifo_address_int <= "100"; -- initialise address bus for command endpoint

      reset_counter_reset <= '1'; --reset delay counter

      -- 8000 clock cycles is 200 us - minimum reset period for the sx2 "from powerup"...
      -- this actually doesn't need to be very big, but i can prune later
      reset_count_max <= sx2_reset_time;
      delay_counter := sx2_transaction_delay;
      enum_counter := 0;

      -- enumeration is not okay (we're in reset)
      fsm_enumeration_ok <= '0';
      int_enum_high_speed <= '0';

      -- interrupts
      sx2_enum_interrupt := '0';
      sx2_ready_interrupt := '0';

      -- reset enum data
      enum_data <= enum_values(0);

      -- reset pid, vid and did

      -- vid
      enum_values(5) <= "0000" & enum_vid(7 downto 4);
      enum_values(6) <= "0000" & enum_vid(3 downto 0);
      enum_values(7) <= "0000" & enum_vid(15 downto 12);
      enum_values(8) <= "0000" & enum_vid(11 downto 8);

      -- pid
      enum_values(9) <= "0000" & enum_pid(7 downto 4);
      enum_values(10) <= "0000" & enum_pid(3 downto 0);
      enum_values(11) <= "0000" & enum_pid(15 downto 12);
      enum_values(12) <= "0000" & enum_pid(11 downto 8);

      -- did
      enum_values(13) <= "0000" & enum_did(7 downto 4);
      enum_values(14) <= "0000" & enum_did(3 downto 0);
      enum_values(15) <= "0000" & enum_did(15 downto 12);
      enum_values(16) <= "0000" & enum_did(11 downto 8);

    elsif ( rising_edge(sx2_clk) ) then

      -- case statements for the fsm
      case enumeration_state is

        when reset_sx2 =>

          reset_counter_reset <= '0';
          int_sx2_reset <= '0';
          enum_counter := 0;

          -- wait for reset timer to clear
          if ( reset_counter_done = '0' ) then
            enumeration_state <= reset_sx2;
          else
            enumeration_state <= activate_sx2;
            --  enumeration_state <= activate_sx2;
            int_sx2_reset <= '1';   -- release reset on sx2
            -- reset delay time
            reset_count_max <= sx2_activate_time;
            -- reset delay counter
            reset_counter_reset <= '1';
          end if;

        when activate_sx2 =>
          reset_counter_reset <= '0';
          enumeration_state <= activation_wait;

        when activation_wait =>

          -- wait for reset timer to clear
          if ( reset_counter_done = '0' ) then
            enumeration_state <= activation_wait;
          else
            enumeration_state <= wait_for_sx2_interrupt;
            
            -- reset delay counter
            reset_counter_reset <= '1';
          end if;

        when wait_for_sx2_interrupt =>

          -- should really check to see if this is an interrupt for enumeration here...
          if ( latched_sx2_interrupt = '1' ) then
            -- sx2 is still mucking about
            enumeration_state <= wait_for_sx2_interrupt;
          else
            -- go into wait states
            enumeration_state <= read_sx2_interrupt_pre_delay;
            delay_counter := sx2_transaction_delay;
            -- sx2 may be ready for enumeration so let us begin...
          end if;

          -- as the sx2 is operating in effectively an asynchronous mode at this point,
          -- we have to hold the pulse for two extra clock cycles
          -- sx2_interrupt is cleared after we release slrd (4-phase asynchronous handshake)

        when read_sx2_interrupt_pre_delay =>
          
          -- enable sx2 output onto bus
          enum_sx2_oe_int <= '0';
          -- enable read strobe
          enum_sx2_rd_int <= '0';

          delay_counter := delay_counter - 1;
          if ( delay_counter = 0 ) then
            -- just a wait state
            enumeration_state <= read_sx2_interrupt;
            delay_counter := sx2_transaction_delay;
          else
            enumeration_state <= read_sx2_interrupt_pre_delay;
          end if;

        when read_sx2_interrupt =>

          -- release the strobes
          enum_sx2_oe_int <= '1';
          enum_sx2_rd_int <= '1';
          enumeration_state <= read_sx2_interrupt_post_delay;

          if ( sx2_fifo_data(0) = '1' ) then 
            -- ready interrupt
            sx2_ready_interrupt := '1';
          end if;

        when read_sx2_interrupt_post_delay =>
          delay_counter := delay_counter - 1;
          if ( delay_counter = 0 ) then
            -- just a wait state
            if ( sx2_ready_interrupt = '1' ) then
              enumeration_state <= begin_descriptor_download;
            else
              enumeration_state <= wait_for_sx2_interrupt;
            end if;
            delay_counter := sx2_transaction_delay;
          else
            enumeration_state <= read_sx2_interrupt_post_delay;
          end if;

        when begin_descriptor_download =>
          -- wait for sx2 to be ready
          -- make sure no extra interrupts around...
          if ( latched_sx2_interrupt = '1' ) then
            if ( latched_sx2_ready = '0' ) then
              enumeration_state <= begin_descriptor_download;
            else
              enumeration_state <= download_descriptor_1;
              -- put the next enum value on the bus...
              enum_data <= enum_values(enum_counter);
            end if;
          else
            enumeration_state <= reset_sx2;
            reset_count_max <= sx2_reset_time;
          end if;

          -- download configuration registers & device descriptors (i.e. vid, pid, did)
        when download_descriptor_1 =>
          enumeration_state <= download_descriptor_2;

        when download_descriptor_2 =>
          -- just a wait state to guarantee timing for asynchronous operation
          delay_counter := delay_counter - 1;
          enum_sx2_write_int <= '0';
          if ( delay_counter = 0 ) then
            delay_counter := sx2_transaction_delay;
            enumeration_state <= download_descriptor_3;
            enum_sx2_write_int <= '1';
          else
            enumeration_state <= download_descriptor_2;
          end if;

        when download_descriptor_3 =>
          -- just a wait state to guarantee timing for asynchronous operation
          delay_counter := delay_counter - 1;
          if ( delay_counter = 0 ) then
            delay_counter := sx2_transaction_delay;
            enumeration_state <= download_descriptor_4;
            enum_counter := enum_counter + 1;
          else
            enumeration_state <= download_descriptor_3;
          end if;

        when download_descriptor_4 =>
          enum_data <= enum_values(enum_counter);
          if ( enum_counter /= desc_length ) then
            if ( latched_sx2_ready = '1' ) then
              enumeration_state <= download_descriptor_1;
            else
              enumeration_state <= download_descriptor_4;
            end if;
          else
            enumeration_state <= wait_for_sx2_enum_ok;
            delay_counter := sx2_enum_wait_max;
          end if;

        when wait_for_sx2_enum_ok =>
          if ( latched_sx2_interrupt = '1' ) then
            enumeration_state <= wait_for_sx2_enum_ok;
            delay_counter := delay_counter - 1;
            if ( delay_counter = 0 ) then
              enumeration_state <= reset_sx2;
              reset_count_max <= sx2_reset_time;
            end if;
          else
            enumeration_state <= read_enumeration_ok_interrupt_1;
            enum_sx2_oe_int <= '0';
            enum_sx2_rd_int <= '0';
            delay_counter := sx2_transaction_delay;
          end if;

        when read_enumeration_ok_interrupt_1 =>
          delay_counter := delay_counter - 1;
          if ( delay_counter = 0 ) then
            delay_counter := sx2_transaction_delay;
            enumeration_state <= read_enumeration_ok_interrupt_2;
            -- release the strobes
            enum_sx2_oe_int <= '1';
            enum_sx2_rd_int <= '1';
            if ( sx2_fifo_data(2) = '1' ) then
              -- ready interrupt
              sx2_enum_interrupt := '1';
            end if;
          else
            enumeration_state <= read_enumeration_ok_interrupt_1;
          end if;

        when read_enumeration_ok_interrupt_2 =>
          delay_counter := delay_counter - 1;
          if ( delay_counter = 0 ) then
            delay_counter := sx2_transaction_delay;
            enumeration_state <= read_enumeration_ok_interrupt_3;
          else
            enumeration_state <= read_enumeration_ok_interrupt_2;
          end if;

        when read_enumeration_ok_interrupt_3 =>
          if ( sx2_enum_interrupt = '0' ) then
            enumeration_state <= wait_for_sx2_enum_ok;
            delay_counter := sx2_enum_wait_max;
          else
            -- move to completion state
            enumeration_state <= enumeration_complete;
          end if;

        when enumeration_complete =>
          if ( latched_sx2_interrupt = '1' ) then
            if ( latched_sx2_ready = '0' ) then
              enumeration_state <= enumeration_complete;
            else
              -- query enumeration mode
              enum_data <= x"ed";
              enumeration_state <= read_enum_mode_1;
            end if;
          else
            enumeration_state <= reset_sx2;
            reset_count_max <= sx2_reset_time;
          end if;

        when read_enum_mode_1 =>
          enumeration_state <= read_enum_mode_2;

        when read_enum_mode_2 =>
          enum_sx2_write_int <= '0';
          delay_counter := delay_counter - 1;
          if ( delay_counter = 0 ) then
            delay_counter := sx2_transaction_delay;
            enumeration_state <= read_enum_mode_3;
            enum_sx2_write_int <= '1';
          else
            enumeration_state <= read_enum_mode_2;
          end if;

        when read_enum_mode_3 =>
          -- just a wait state to guarantee timing for asynchronous operation
          delay_counter := delay_counter - 1;
          if ( delay_counter = 0 ) then
            delay_counter := sx2_transaction_delay;
            enumeration_state <= read_enum_mode_4;
          else
            enumeration_state <= read_enum_mode_3;
          end if;

        when read_enum_mode_4 =>
          -- should really check to see if this is an interrupt for enumeration here...
          if ( latched_sx2_interrupt = '1' ) then
            -- sx2 is still mucking about
            enumeration_state <= read_enum_mode_4;
          else
            -- go into wait states
            enumeration_state <= read_enum_mode_5;
            delay_counter := sx2_transaction_delay;
          end if;

        when read_enum_mode_5 =>
          
          -- enable sx2 output onto bus
          enum_sx2_oe_int <= '0';
          -- enable read strobe
          enum_sx2_rd_int <= '0';

          delay_counter := delay_counter - 1;
          if ( delay_counter = 0 ) then
            -- just a wait state
            enumeration_state <= read_enum_mode_6;
            delay_counter := sx2_transaction_delay;
          else
            enumeration_state <= read_enum_mode_5;
          end if;

        when read_enum_mode_6 =>
          -- release the strobes
          enum_sx2_oe_int <= '1';
          enum_sx2_rd_int <= '1';
          enum_sx2_write_int <= '1';
          enumeration_state <= read_enum_mode_7;

          if ( sx2_fifo_data(7) = '1' ) then 
            int_enum_high_speed <= '1';
          else
            int_enum_high_speed <= '0';
            enum_counter := usb_2_length;

            if(allow_full_speed='0')then 
              -- hack to prevent the sx2 enumerating "full speed" on the
              -- calice daq pcs. i don't understand why this happens,
              -- but paul's daq code bombs out if it does. the fix seems to
              -- be simply to reset the board: this will do it automatically. 
              enumeration_state<=reset_sx2;
              reset_counter_reset<='1';
            end if;
            
          end if;

        when read_enum_mode_7 =>
          delay_counter := delay_counter - 1;
          if ( delay_counter = 0 ) then
            -- just a wait state
            if ( latched_sx2_ready = '1' ) then
              enumeration_state <= download_settings_1;
              enum_data <= enum_values(enum_counter);
            else
              enumeration_state <= read_enum_mode_7;
            end if;
            delay_counter := sx2_transaction_delay;
          else
            enumeration_state <= read_enum_mode_7;
          end if;

          -- download configuration registers & device descriptors (i.e. vid, pid, did)
        when download_settings_1 =>
          enumeration_state <= download_settings_2;

        when download_settings_2 =>
          -- just a wait state to guarantee timing for asynchronous operation                    
          enum_sx2_write_int <= '0';
          delay_counter := delay_counter - 1;
          if ( delay_counter = 0 ) then
            delay_counter := sx2_transaction_delay;
            enumeration_state <= download_settings_3;
            enum_sx2_write_int <= '1';
          else
            enumeration_state <= download_settings_2;
          end if;

        when download_settings_3 =>
          -- just a wait state to guarantee timing for asynchronous operation
          delay_counter := delay_counter - 1;
          if ( delay_counter = 0 ) then
            delay_counter := sx2_transaction_delay;
            enumeration_state <= download_settings_4;
            enum_counter := enum_counter + 1;
          else
            enumeration_state <= download_settings_3;
          end if;

        when download_settings_4 =>

          if ( int_enum_high_speed = '1' ) then
            if ( enum_counter /= usb_2_length ) then
              if ( latched_sx2_ready = '1' ) then
                enumeration_state <= download_settings_1;
                enum_data <= enum_values(enum_counter);
              else
                enumeration_state <= download_settings_4;
              end if;
            else
              enumeration_state <= boot_complete;
            end if;
          else
            if ( enum_counter /= usb_1_length ) then
              if ( latched_sx2_ready = '1' ) then
                enumeration_state <= download_settings_1;
                enum_data <= enum_values(enum_counter);
              else
                enumeration_state <= download_settings_4;
              end if;
            else
              enumeration_state <= boot_complete;
            end if;
          end if;

        when boot_complete =>
          -- sit here until we see an async_reset
          enumeration_state <= boot_complete;
          reset_counter_reset <= '0';
          enum_sx2_fifo_address_int <= "000";

          -- see whether the counter is done, if not stay here
          -- otherwise move on to waiting for the sx2
          if ( reset_counter_done = '1' ) then 	
            -- set enumeration ok flag
            fsm_enumeration_ok <= '1';
            reset_counter_reset <= '1'; -- reset delay counter  
          end if;

          -- jj october 2005
          -- note: if a usb reset occurs this will require modification as it needs
          -- to be detected and the device re-enumerated - this isn't currently handled

        when others =>
          
          enumeration_state <= reset_sx2;

          --signals : initial values
          int_sx2_reset <= '1'; --sx2 inactive on power on
          enum_sx2_oe_int <= '1'; --disable output enable
          enum_sx2_rd_int <= '1'; --disable read access
          enum_sx2_write_int <= '1'; --disable write access
          enum_sx2_fifo_address_int <= "100"; -- initialise address bus for command endpoint
          
          --rom_counter_reset <= '1'; --keep rom-address counter in reset state
          --rom_counter_clk <= '0'; --rom-address clock 
          --rom_oe <= '0';	--set rom output to high z
          reset_counter_reset <= '1'; --reset delay counter

          -- 8000 clock cycles is 200 us - minimum reset period for the sx2 "from powerup"...
          -- this actually doesn't need to be very big, but i can prune later
          reset_count_max <= sx2_reset_time;
          delay_counter := sx2_transaction_delay;
          enum_counter := 0;

          -- enumeration is not okay (we're in reset)
          fsm_enumeration_ok <= '0';

          -- interrupts
          sx2_enum_interrupt := '0';
          sx2_ready_interrupt := '0';

          enum_data <= enum_values(0);

      end case;

    end if;

  end process enumeration_fsm;

  
end architecture v2;
