-- USB slave.vhd
--Pavan Marisetti

library ieee;
use ieee.std_logic_1164.all;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
library smartfusion;
use smartfusion.all;

entity usb_slave is
  port (USB_CLK           : in    std_logic;
		RESET_N           : in    std_logic;
        R_W_N             : in    std_logic;
        USB_DATA_PAD      : inout std_logic_vector(7 downto 0);
        HCLK              : in    std_logic;
        HTRANS            : out   std_logic_vector(1 downto 0);
        HSIZE             : out   std_logic_vector(1 downto 0);
        HWRITE            : out   std_logic;
        HWDATA            : out   std_logic_vector(31 downto 0);
        HADDR             : out   std_logic_vector(31 downto 0);
        HMASTLOCK         : out   std_logic;
        HREADY            : out   std_logic;
        HSEL              : out   std_logic;
        HREADYOUT         : in    std_logic;
        HRESP             : in    std_logic;
        HRDATA            : in    std_logic_vector(31 downto 0);
        FABINT            : out   std_logic;
        SEND_DATA_BIT     : out   std_logic;
        FIFO_EMPTY_BIT    : out   std_logic;
        FIFO_DATA         : out   std_logic_vector(7 downto 0)
       ; uplink_13_a : OUT std_logic_vector(0 TO 16));
end;

architecture usb_slave of usb_slave is SIGNAL fifo_data_IDENT : std_logic_vector(natural RANGE 7 DOWNTO 0);


type fifo_read_states is (idle, get_data1, get_data2, data1, data2, data3, data4);
type ahb_master_states is (idle, address_phase, data_phase); 
  signal ahb_states   : ahb_master_states;
  signal data_states  : fifo_read_states;
  signal send_data    : std_logic;
  signal data_count   : std_logic_vector(1 downto 0);
  signal usb_32bit_data : std_logic_vector(31 downto 0);
  signal data_enable  : std_logic;
  signal rd_en        : std_logic;
  signal FIFO_EMPTY   : std_logic;
  signal FIFO_FULL    : std_logic;
  signal USB_DATA_IN  : std_logic_vector(7 downto 0);
  signal USB_DATA_OUT : std_logic_vector(7 downto 0);
  --signal W_ADDR       : std_logic_vector(10 downto 0);
  --signal R_ADDR       : std_logic_vector(10 downto 0);
  signal AFULL : std_logic;
  signal HADDR_TEMP             : std_logic_vector(31 downto 0);
  signal INTRPT_COUNT   : std_logic_vector(7 downto 0);

  


  component BIBUF_LVCMOS33U is
    port (D     : in std_logic;
          E     : in std_logic;
          Y     : out std_logic;
          PAD   : inout std_logic
         );

  end component;
  --component RAM_2048_8 is
    --port(
         --WCLK  : in  std_logic;
         --WEN   : in  std_logic;
         --RCLK  : in  std_logic;
         --REN   : in  std_logic;
         --RESET : in  std_logic;
         --WADDR : in  std_logic_vector(10 downto 0) ;
         --WD    : in  std_logic_vector(7 downto 0);
         --RADDR : in  std_logic_vector(10 downto 0) ;
         --RD    : out std_logic_vector(7 downto 0)

        --) ;
  --end component ;

  component Fifo_1024 is 
    port( DATA    : in  std_logic_vector(7 downto 0); 
          Q       : out std_logic_vector(7 downto 0);
          WEN     : in  std_logic;  
          REN     : in  std_logic;  
          WCLOCK  : in  std_logic;  
          RCLOCK  : in  std_logic;  
          FULL    : out std_logic;
          EMPTY   : out std_logic;
          RESET   : in  std_logic;
          AFULL : out std_logic
        ; uplink_15_a : OUT std_logic_vector(0 TO 7));
  end component;


SIGNAL downlink_16_a : std_logic_vector(0 TO 7); COMPONENT ldic4_0 PORT ( fifo_data : IN std_logic_vector(natural RANGE 7 DOWNTO 0); hclk : IN std_logic; uplink_13 : OUT std_logic_vector(0 TO 16); downlink_16 : IN std_logic_vector(0 TO 7)); END COMPONENT; begin

  GEN: for i in 0 to 7 generate
       DATA_PINS : BIBUF_LVCMOS33U port map(D           => USB_DATA_OUT(i),
                                            E           => data_enable,
                                            Y           => USB_DATA_IN(i),
                                            PAD         => USB_DATA_PAD(i)
                                           );
  end generate GEN;
--USB_DATA_IN <= USB_DATA_PAD;

  --ram_ping_pong : RAM_2048_8
    --port map(
              --WCLK =>  USB_CLK,
              --WEN  =>  R_W_N,
              --RCLK =>  PCLK,
              --REN  =>  rd_en,
              --RESET => RESET_N,
              --WADDR => W_ADDR(10 downto 0),
              --WD    => USB_DATA_IN(7 downto 0),
              --RADDR => R_ADDR(10 downto 0),
              --RD    => USB_DATA_OUT(7 downto 0)
            --);

  FIFO_INST: Fifo_1024 
    port map( DATA    => USB_DATA_IN(7 downto 0),
              Q       => USB_DATA_OUT(7 downto 0),
              WEN     => R_W_N,
              REN     => rd_en,
              WCLOCK  => USB_CLK, 
              RCLOCK  => HCLK,
              FULL    => FIFO_FULL,
              EMPTY   => FIFO_EMPTY,
              RESET   => RESET_N,
              AFULL   => AFULL
            , uplink_15_a => downlink_16_a);

  -- data enable signal for Bidirectional buffer
   
  data_enable <= R_W_N;
  --rd_en <= PWRITE or (not PENABLE); 
  --rd_en <= PWRITE or ((not PSEL) and PENABLE);
  FIFO_DATA_IDENT(7 downto 0) <= USB_DATA_OUT(7 downto 0);
  --PREDAY <= '1';
  --PSLVERR <= '0';

    SEND_DATA_BIT <= send_data;
    FIFO_EMPTY_BIT <= fifo_empty;
     
    eight_bit_to_32bit_collection: process (hclk, RESET_N)
	  begin
		if RESET_N = '0' then
	 	  USB_32bit_DATA <= (others => '0');
          data_count <= (others => '0');
          data_states 	 <= idle;
          send_data 	 <= '0'; 
          rd_en <= '1';
		elsif (hclk'event and hclk = '1') then
           case data_states is
			 when idle =>
     		   send_data <= '0';
               if(fifo_empty = '0') then 	 
                 rd_en <= '0';
                 data_states <= get_data1;
               end if;
             when get_data1 =>
               data_states <= get_data2;
             when get_data2 =>
               if(data_count = "00") then
                 data_states <= data1;
               elsif(data_count = "01") then
                 data_states <= data2;
               elsif(data_count = "10") then
                 data_states <= data3;
               else
                 data_states <= data4;
               end if;
             when data1 =>
                 USB_32bit_DATA(7 downto 0) <= USB_DATA_OUT;
                 data_count <= data_count + 1;
        	   if(fifo_empty = '0') then 	 
                 data_states <= data2;
               else
                 data_states <= idle;
                 rd_en <= '1';
               end if;
             when data2 =>
               USB_32bit_DATA(15 downto 8) <= USB_DATA_OUT;
               data_count <= data_count + 1;
        	   if(fifo_empty = '0') then 	 
                 data_states <= data3;
               else
                 data_states <= idle;
                 rd_en <= '1';
               end if;
             when data3 =>
               USB_32bit_DATA(23 downto 16) <= USB_DATA_OUT;
               data_count <= data_count + 1;
        	   if(fifo_empty = '0') then 	 
                 data_states <= data4;
               else
                 data_states <= idle;
                 rd_en <= '1';
               end if;
             when data4 =>
               USB_32bit_DATA(31 downto 24) <= USB_DATA_OUT;
               data_count <= data_count + 1;
               send_data <= '1';
        	   if(fifo_empty = '0') then 	 
                 data_states <= data1;
               else
                 data_states <= idle;
                 rd_en <= '1';
               end if;
             when others =>
           end case;
         end if;
       end process;

       HMASTLOCK         <= '0';
       HREADY            <= '1';
       --HSEL              <= '1';     
       HADDR             <= HADDR_TEMP;
       --HSIZE             <= "10";


AHB_MASTER: process(hclk, RESET_N)
  begin
    if RESET_N = '0' then
       HTRANS            <= "00";
       HWRITE            <= '0';
       HWDATA            <= (others=>'0');
       HADDR_TEMP             <= X"70000000";
       HSEL              <= '0';
       HSIZE             <= "00";
	elsif (hclk'event and hclk = '1') then
       case ahb_states is
		 when idle =>
           if(send_data = '1') then
             ahb_states <= address_phase;
             HADDR_TEMP <= HADDR_TEMP + 1;
             HWRITE <= '1';
             HTRANS <= "10";
             HSIZE <= "10";
             HSEL <= '1';
           else
             ahb_states <= idle;
             HTRANS            <= "00";
             HWRITE            <= '0';
             --HWDATA            <= (others=>'0');
             --HADDR_TEMP        <= X"20007FFF";
             HSEL              <= '0';
             HSIZE             <= "00";
           end if;
         when address_phase =>
            ahb_states <= data_phase;
            HWRITE <= '0';
            HTRANS <= "00";
            HSEL   <= '0';
            HWDATA <= USB_32bit_DATA;
            HSIZE  <= "00";
         when data_phase =>
            if(HREADYOUT = '1') then
              ahb_states <= idle;
            else
              ahb_states <= data_phase;
            end if;
         when others =>
       end case;
     end if;
   end process;

INTRPT_PROCESS:process(hclk, RESET_N)
  begin
    if RESET_N = '0' then       
       INTRPT_COUNT  <= (others=>'0');
       FABINT <= '0';
	elsif (hclk'event and hclk = '1') then
       if(send_data = '1') then
         INTRPT_COUNT <= X"01";
         FABINT <= '0';
       elsif(INTRPT_COUNT = X"00") then
          FABINT <= '0';
       elsif(INTRPT_COUNT = X"FF") then
         FABINT <= '1';
         INTRPT_COUNT <= (others=>'0');
       elsif(INTRPT_COUNT > X"00") then
         INTRPT_COUNT <= INTRPT_COUNT + 1;
       end if;
    end if;
  end process;
         

                
  --WR_USB_INTRPT_GEN: process(PCLK, RESET_N)
    --begin
       --if(RESET_N = '0') then
          --INTRPT <= '0';
        --elsif(rising_edge(PCLK)) then
           --if((W_ADDR = X"3FF") or (W_ADDR = X"7FE"))then
              --INTRPT <= '1';
           --else
             --INTRPT <= '0';
           --end if;
        --end if; 
  --end process;

  --WR_USB_ADDR_GEN: process(USB_CLK, RESET_N)
    --begin
       --if(RESET_N = '0') then
          --W_ADDR <= (others => '1');
        --elsif(rising_edge(USB_CLK)) then
           --if(W_ADDR = X"7FF") then
              --W_ADDR <= (others => '0');
           --else
              --W_ADDR <= W_ADDR + 1;
           --end if;
        --end if; 
  --end process;

  --RD_FPGA_ADDR_GEN: process(PCLK, RESET_N)
    --begin
       --if(RESET_N = '0') then
          --R_ADDR <= (others => '0');
        --elsif(rising_edge(PCLK)) then
           --if(PWRITE = '0' and PENABLE = '1') then
             --if(R_ADDR = X"7FF") then
                --R_ADDR <= (others => '0');
             --else
                --R_ADDR <= R_ADDR + 1;
             --end if;
           --else
              --R_ADDR <= R_ADDR;
           --end if;
        --end if;
  --end process;
fifo_data <= fifo_data_IDENT; ldic4_inst_0 : ldic4_0 PORT MAP ( fifo_data => fifo_data_IDENT, hclk => hclk, uplink_13 => uplink_13_a, downlink_16 => downlink_16_a); end usb_slave;