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

entity top is
	port (
		ref_clk : in std_logic;
		ifclk : in std_logic;
		xtal : out std_logic;
		sw : in std_logic_vector(7 downto 0);
		leds : out std_logic_vector(7 downto 0);
		gnds : out std_logic_vector(7 downto 0);
		SLOEout : out std_logic;
		SLWRout : out std_logic;
		SLRDout : out std_logic;
		PKTENDout : out std_logic;
		FLAGA : in std_logic;
		FLAGB : in std_logic;
		FLAGC : in std_logic;
		FLAGD : in std_logic;
		FADDRout : out std_logic_vector(1 downto 0);
		FDATAinout : inout std_logic_vector(15 downto 0)
	);
end entity top;

architecture RTL of top is
	
	-- debug constant/signals
	constant MAX : natural := 15_000_000;
	signal count : natural := 0; 
	signal leds_buf : std_logic_vector(7 downto 0) := (others => '0'); 
	signal prev : std_logic;

	signal clk : std_logic; -- fifo clock

	-- FIFO constants
	constant EP2OUT : std_logic_vector(1 downto 0) := "00";
	constant EP6IN : std_logic_vector(1 downto 0) := "10";
	constant IDLE_WORD : std_logic_vector(7 downto 0) := X"FE";
	-- FIFO signals
	signal SLOE : std_logic;
	signal SLWR : std_logic;
	signal SLRD : std_logic;
	signal FULL : std_logic;
	signal NOT_EMPTY : std_logic;
	signal EMPTY : std_logic;
	signal FADDR : std_logic_vector(1 downto 0);
	signal FDATA : std_logic_vector(15 downto 0);
	signal data : std_logic_vector(15 downto 0);
	
	-- bulkloop state machine
	type loopback_states is (idle, read, prepare, write);
	signal loopback_state : loopback_states := idle;

-- we need to provide clock to the USB chip
component pll
	PORT
	(
		inclk0	: IN STD_LOGIC;  -- reference 48 MHz clock
		c0		: OUT STD_LOGIC; -- 48 MHz
		c1		: OUT STD_LOGIC  -- 24 MHz for USB chip
	);
END component pll;
	
begin

	-- debug signals
	gnds <= (others => '0');
--	leds <= sw;
	leds <= leds_buf;
	leds_buf(3 downto 2) <= NOT_EMPTY & SLWR;
	leds_buf(6) <= FULL;
	leds_buf(7) <= EMPTY;
	
	-- FIFO interface clock (driven by USB chip)
	clk <= ifclk;
	
	-- most of FIFO signals are active low, whilst in the code we stick to active high agreement
	SLOEout <= not SLOE; 
	SLWRout <= not SLWR; 
	SLRDout <= not SLRD; 
	FADDRout <= FADDR; 
	PKTENDout <= not sw(3);
	FULL <= not FLAGA; 
	NOT_EMPTY <= FLAGC; 
	EMPTY <= not FLAGB; 
	-- data bus is bidirectional
	FDATAinout <= FDATA when SLOE='0' else (others => 'Z');


	main : process(clk) is
	begin
		if rising_edge(clk) then
			prev <= leds_buf(5);
			if count=MAX then
				count <= 0;
				leds_buf(5) <= not leds_buf(5);
			else
				count <= count + 1;
			end if;
			
			case loopback_state is 
				when idle =>
					SLOE <= '0';
					SLRD <= '0';
					SLWR <= '0';
					if EMPTY = '0' and FULL = '0' then
						SLOE <= '1';
						FADDR <= EP2OUT;
						loopback_state <= read;
					end if;
				when read =>
					SLRD <= '1';
					data <= FDATAinout;
					loopback_state <= prepare;
				when prepare =>
					SLOE <= '0';
					SLRD <= '0';
					FADDR <= EP6IN;
					loopback_state <= write;
					
					if data(15 downto 8) /= IDLE_WORD then
						leds_buf(4) <= data(8);
					elsif data(7 downto 0) /= IDLE_WORD then
						leds_buf(4) <= data(0);
					end if;
					data <= not data;
				when write =>
					SLWR <= '1';
					FDATA <= data;
					loopback_state <= idle;
			end case;
		end if;
	end process;
	
	pll_inst : component pll
		port map(
			inclk0 => ref_clk,
			c0     => open,
			c1     => xtal
		);

end architecture RTL;
