----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    10:03:20 04/19/2011 
-- Design Name: 
-- Module Name:    hardware_processor - Behavioral 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.all;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.ALL;

-- Uncomment the following library declaration if instantiating
-- any Xilinx primitives in this code.
library UNISIM;
use UNISIM.VComponents.all;


entity hardware_controller is
  port (
		clk									: in  std_logic;
		hdw_cmd_available					: in  std_logic;
		fifo_target							: in  std_logic_vector(3 downto 0);
		fifo_cmd								: in  std_logic_vector(7 downto 0);
		fifo_data							: in  std_logic_vector(19 downto 0);
		hdw_cmd_busy						: out std_logic;
		hdw_reply							: out std_logic_vector(31 downto 0);
		firmware_version					: in  std_logic_vector(15 downto 0);
		GPIO_Switches						: in  std_logic_vector(7 downto 0);
		GPIO_Buttons						: in  std_logic_vector(4 downto 0);
		Disp_seg								: out std_logic_vector(7 downto 0);
		Disp_an								: out std_logic_vector(3 downto 0)
	);
end hardware_controller;


architecture Behavioral of hardware_controller is

	signal display_clock_counter					: unsigned(14 downto 0) := (others => '0');
	signal display_clock								: std_logic;
	
	signal display_sel								: std_logic_vector(1 downto 0) := (others => '0');
	
	signal Disp_seg_i									: std_logic_vector(7 downto 0) := (others => '0');
	signal Disp_an_i									: std_logic_vector(3 downto 0) := (others => '0');
	
	component led_7seg_display
	port(
		clk					: in std_logic;
		init					: in std_logic;
		display_hex			: in std_logic_vector(3 downto 0);          
		display_segments	: out std_logic_vector(7 downto 0)
	);
	end component;
	
	signal firmware_segment_0						: std_logic_vector(7 downto 0);
	signal firmware_segment_1						: std_logic_vector(7 downto 0);
	signal firmware_segment_2						: std_logic_vector(7 downto 0);
	signal firmware_segment_3						: std_logic_vector(7 downto 0);
	
	signal hdw_counter								: unsigned(15 downto 0) := (others => '0');

	type   hdw_cmd_type is (idle, cmdSel, cmdErr, readGPIOSwitches, setDisp,
										setCounter, incCounter, decCounter, readCounter, dontUse);
	signal hdw_cmd_state								: hdw_cmd_type := idle;
	signal hdw_reply_i								: std_logic_vector(31 downto 0) := (others => '0');
	signal hdw_display_override					: std_logic := '0';
	signal hdw_display_segment_0					: std_logic_vector(7 downto 0) := (others => '0');
	signal hdw_display_segment_1					: std_logic_vector(7 downto 0) := (others => '0');
	signal hdw_display_segment_2					: std_logic_vector(7 downto 0) := (others => '0');
	signal hdw_display_segment_3					: std_logic_vector(7 downto 0) := (others => '0');
	signal hdw_display_init							: std_logic := '0';
	signal hdw_set_counter							: std_logic;
	signal hdw_inc_counter							: std_logic;
	signal hdw_dec_counter							: std_logic;
	
begin

	Disp_seg <= Disp_seg_i;
	Disp_an <= Disp_an_i;
	
   DisplayClk_BUFG : BUFG
   port map (
      O => display_clock,
      I => display_clock_counter(14)
   );
	
	display_clock_proc : process(clk)
	begin
		if (rising_edge(clk)) then
			display_clock_counter <= display_clock_counter + 1;
		end if;
	end process display_clock_proc;
	
	
	display_control_proc : process(display_clock)
	begin
		if (rising_edge(display_clock)) then
			case display_sel is
				when "00" =>
					if (hdw_display_override = '1') then
						Disp_seg_i <= hdw_display_segment_0;
					else
						Disp_seg_i <= firmware_segment_0;
					end if;
					Disp_an_i <= "1110";
					display_sel <= "01";
				when "01" =>
					if (hdw_display_override = '1') then
						Disp_seg_i <= hdw_display_segment_1;
					else
						Disp_seg_i <= firmware_segment_1;
					end if;
					Disp_an_i <= "1101";
					display_sel <= "10";
				when "10" =>
					if (hdw_display_override = '1') then
						Disp_seg_i <= hdw_display_segment_2;
					else
						Disp_seg_i <= firmware_segment_2;
					end if;
					Disp_an_i <= "1011";
					display_sel <= "11";
				when others =>
					if (hdw_display_override = '1') then
						Disp_seg_i <= hdw_display_segment_3;
					else
						Disp_seg_i <= firmware_segment_3;
					end if;
					Disp_an_i <= "0111";
					display_sel <= "00";
			end case;
		end if;
		
	end process display_control_proc;
	
	
	led_7seg_display_0: led_7seg_display
	port map (
		clk					=> clk,
		init					=> hdw_display_init,
		display_hex			=> firmware_version(3 downto 0),
		display_segments	=> firmware_segment_0
	);
	
	led_7seg_display_1: led_7seg_display
	port map (
		clk					=> clk,
		init					=> hdw_display_init,
		display_hex			=> firmware_version(7 downto 4),
		display_segments	=> firmware_segment_1
	);
	
	led_7seg_display_2: led_7seg_display
	port map (
		clk					=> clk,
		init					=> hdw_display_init,
		display_hex			=> firmware_version(11 downto 8),
		display_segments	=> firmware_segment_2
	);
	
	led_7seg_display_3: led_7seg_display
	port map (
		clk					=> clk,
		init					=> hdw_display_init,
		display_hex			=> firmware_version(15 downto 12),
		display_segments	=> firmware_segment_3
	);
	
	
	hdw_counter_proc : process (clk)
	begin
		if rising_edge(clk) then
			if (hdw_set_counter = '1') then
				hdw_counter <= unsigned(fifo_data(15 downto 0));
			elsif (hdw_inc_counter = '1') then
				hdw_counter <= hdw_counter + 1;
			elsif (hdw_dec_counter = '1') then
				hdw_counter <= hdw_counter - 1;
			end if;
		end if;
	end process hdw_counter_proc;
	
	
	hdw_reply <= hdw_reply_i;

	hdw_control_proc : process(clk)
	begin
		if (rising_edge(clk)) then
			hdw_display_init <= '0';
			hdw_set_counter <= '0';
			hdw_inc_counter <= '0';
			hdw_dec_counter <= '0';
					
			case hdw_cmd_state is
				when idle =>
					if (hdw_cmd_available = '1') then
						hdw_cmd_state <= cmdSel;
						hdw_cmd_busy <= '1';
					else
						hdw_cmd_state <= idle;
						hdw_cmd_busy <= '0';
					end if;
				
				
				when cmdSel =>
					
					case fifo_cmd is
						when x"01" =>
							hdw_cmd_state <= readGPIOSwitches;
							
						when x"02" =>
							hdw_cmd_state <= setDisp;
						
						
						when x"10" =>
							hdw_cmd_state <= setCounter;
						
						when x"11" =>
							hdw_cmd_state <= incCounter;
						
						when x"12" =>
							hdw_cmd_state <= decCounter;
						
						when x"13" =>
							hdw_cmd_state <= readCounter;
							
						
						when x"FF" =>
							hdw_cmd_state <= dontUse;
						
						when others =>
							hdw_cmd_state <= cmdErr;
					end case;
				
				
				when cmdErr =>
					hdw_reply_i <= "1" & fifo_target(2 downto 0) & fifo_cmd & fifo_data;
					hdw_cmd_state <= idle;
					
				
				when readGPIOSwitches =>
					hdw_reply_i <= fifo_target & fifo_cmd & "0000" & "000" & GPIO_Buttons & GPIO_Switches;
					hdw_cmd_state <= idle;
				
				
				when setDisp =>
					hdw_display_override <= fifo_data(10);
					case fifo_data(9 downto 8) is
						when "00" =>
							hdw_display_segment_0 <= fifo_data(7 downto 0);
						when "01" =>
							hdw_display_segment_1 <= fifo_data(7 downto 0);
						when "10" =>
							hdw_display_segment_2 <= fifo_data(7 downto 0);
						when others =>
							hdw_display_segment_3 <= fifo_data(7 downto 0);
					end case;
					hdw_reply_i <= fifo_target & fifo_cmd & fifo_data;
					hdw_cmd_state <= idle;
				
				
				when setCounter =>
					hdw_set_counter <= '1';
					hdw_reply_i <= fifo_target & fifo_cmd & fifo_data;
					hdw_cmd_state <= idle;
				
				when incCounter =>
					hdw_inc_counter <= '1';
					hdw_reply_i <= fifo_target & fifo_cmd & fifo_data;
					hdw_cmd_state <= idle;
				
				when decCounter =>
					hdw_dec_counter <= '1';
					hdw_reply_i <= fifo_target & fifo_cmd & fifo_data;
					hdw_cmd_state <= idle;
				
				when readCounter =>
					hdw_reply_i <= fifo_target & fifo_cmd & "0000" & std_logic_vector(hdw_counter);
					hdw_cmd_state <= idle;
					
				
				when dontUse =>
					hdw_display_init <= fifo_data(0);
					hdw_reply_i <= fifo_target & fifo_cmd & fifo_data;
					hdw_cmd_state <= idle;
			end case;
		end if;
	end process hdw_control_proc;


end Behavioral;
