-----------------------------------------------------------------------------------------------------
--File: clk100Hz
--Author: 	Robert Ross (c) 2004
--Purpose: 	generates a 10ms clk used to drive the stopwatch and the rest of the counters
--Inputs:	
--	clkIn: 25.125MHz onboard clock
--	reset: reset button
-----------------------------------------------------------------------------------------------------
library ieee;
use ieee.std_logic_1164.all;

entity clk100Hz is port(
	clk: in std_logic;					--25.125MHz
	buttonStartStop: in std_logic;		--startStop button
	buttonMode: in std_logic;			--button to select the mode
	dips: in std_logic_vector(7 downto 0); -- The 8 DIP switches

	dispStrobe: out std_logic_vector(5 downto 0);
	dispDataOut: out std_logic_vector(7 downto 0);
	displayEnable: out std_logic_vector(5 downto 0);
	MSDDisp1: out std_logic_vector(7 downto 0);
	MSDDisp2: out std_logic_vector(7 downto 0));
end clk100Hz;

architecture arch of clk100Hz is
	signal count10ms: integer range 0 to 12562501;	--should give 10ms delay
	signal count10msReference: integer range 0 to 12562501;
	signal clk10ms: std_logic;
	signal count1MHz: integer range 0 to 25;
	signal clk1MHz: std_logic;
	signal dbCountSS: integer range 0 to 100;
	signal dbCountMode: integer range 0 to 100;
	signal modeSelect: std_logic_vector(0 to 2);
	signal startStopState: std_logic_vector(0 to 2); --holds whether the particular unit is in start or stop mode - has no relevance for clock, just in so the values still line up! 1 means start
	signal settingMode: std_logic; 	-- when 0 not in setting mode when 1 in setting mode
	signal resetLocal: std_logic; 		--when 1 reset the particular mode of the circuit
	signal resetGlobal: std_logic; 		--when 1 reset the circuit
	signal LEDDsp1, LEDDsp2, LEDDsp3, LEDDsp4, LEDDsp5, LEDDsp6, LEDDsp7, LEDDsp8: integer range 0 to 9;
	signal dispStrobeSig: std_logic_vector(7 downto 0);
	signal LEDFlash: std_logic_vector(7 downto 0);		
	signal oldClk: std_logic;
	signal startStopTrig: std_logic;
	signal rotateTrig: std_logic;
	signal modeTrig: std_logic;
	SIGNAL timerAlarm: std_logic; -- Set by U3 (timer); if '1' indicates the alarm is going off

--All the temporary signals for the stopwatch
	signal msecLSDSW: integer range 0 to 9;
	signal msecMSDSW: integer range 0 to 9;
	signal secLSDSW: integer range 0 to 9;
	signal secMSDSW: integer range 0 to 9;
	signal minLSDSW: integer range 0 to 9;
	signal minMSDSW: integer range 0 to 9;
	signal hourLSDSW: integer range 0 to 9;
	signal hourMSDSW: integer range 0 to 9;
--All the temporary signals for the Timer
	signal msecLSDTiOld: integer range 0 to 9;
	signal msecLSDTi: integer range 0 to 9;
	signal msecMSDTi: integer range 0 to 9;
	signal secLSDTi: integer range 0 to 9;
	signal secMSDTi: integer range 0 to 9;
	signal minLSDTi: integer range 0 to 9;
	signal minMSDTi: integer range 0 to 9;
	signal hourLSDTi: integer range 0 to 9;
	signal hourMSDTi: integer range 0 to 9;

	SIGNAL dispStrobeSigTi: std_logic_vector(7 downto 0);
	SIGNAL LEDFlashTi: std_logic_vector(7 downto 0);

--All the temporary signals for the Clock
	signal msecLSDCl: integer range 0 to 9;
	signal msecMSDCl: integer range 0 to 9;
	signal secLSDCl: integer range 0 to 9;
	signal secMSDCl: integer range 0 to 9;
	signal minLSDCl: integer range 0 to 9;
	signal minMSDCl: integer range 0 to 9;
	signal hourLSDCl: integer range 0 to 9;
	signal hourMSDCl: integer range 0 to 9;

	signal dispStrobeSigCl: std_logic_vector(7 downto 0);
	signal LEDFlashCl: std_logic_vector(7 downto 0);


-- Wade's stuff - *woot woot woot*
	signal invalidMode: std_logic;

component displayOutput port(
	clk1MHz: in std_logic;							--should be a 1Mhz Clock
	LEDDsp1: in integer range 0 to 9;				--normally hoursMSD
	LEDDsp2: in integer range 0 to 9;				--normally hoursLSD
	LEDDsp3: in integer range 0 to 9;				--normally MinutesMSD
	LEDDsp4: in integer range 0 to 9;				--normally minutesLSD
	LEDDsp5: in integer range 0 to 9;				--normally secondsMSD
	LEDDsp6: in integer range 0 to 9;				--normally secondsLSD
	LEDDsp7: in integer range 0 to 9;				--normally ms MSD
	LEDDsp8: in integer range 0 to 9;				--normally ms LSD
	LEDOn: 	 in std_logic_vector(7 downto 0); 		--8 bits each symoblising a Digit. if 1 then digit is lit up
	LEDFlash: in std_logic_vector(7 downto 0);		--8 bits each symoblising a Digit. if 1 then digit will flash
	LEDDsp1Out: out std_logic_vector(7 downto 0);	--the first display (not on expansion board)
	LEDDsp2Out: out std_logic_vector(7 downto 0);	--the second display (not on expansion board)
	strobeOut: out std_logic_vector(5 downto 0);	-- 0 is MSD
	dataOut: out std_logic_vector(7 downto 0));  	-- 0 is the decimal point
end component;

component stopWatch port(
	clkIn: in std_logic;					--100Hz
	startStop: in std_logic;				--toggles start/stop
	resetGlobal: in std_logic;				--global reset for board
	resetCount: in std_logic;				--resets the count when in stop mode to 00:00:00:00
	modeActiveL: in std_logic;				--if 1 this is active and the buttons apply, if 0 is inactive and startStop doesn't work
	msecLSD: buffer integer range 0 to 9;
	msecMSD: buffer integer range 0 to 9;
	secLSD: buffer integer range 0 to 9;
	secMSD: buffer integer range 0 to 9;
	minLSD: buffer integer range 0 to 9;
	minMSD: buffer integer range 0 to 9;
	hourLSD: buffer integer range 0 to 9;
	hourMSD: buffer integer range 0 to 9);
end component;

COMPONENT clock PORT(
	clk				: IN std_logic; -- The clock who's rising edge triggers the Clock
	selectionToggle	: IN std_logic; -- If '1' on a rising clock edge (and 'active' is high, and 'mode' is high), changes the selected field
	inc				: IN std_logic; -- If '1' on a rising clock edge (and 'active' is high, and 'mode' is high) indicates the selected field should be incremented
	reset			: IN std_logic; -- If '1' (and 'active' is high) resets the Clock
	mode			: IN std_logic; -- If '1' the Clock operates in set mode, otherwise it operates in "normal" mode
	active			: IN std_logic; -- If '1' indicates the Clock should respond to it's inputs ('selectionToggle', 'inc' & 'reset')

	-- All the following are 7-segment display outputs.  They are all wired directly to the outputs for each field.

	csecLSD			: BUFFER integer range 0 to 9; -- msecLSD
	csecMSD			: BUFFER integer range 0 to 9; -- msecMSD
	secLSD			: BUFFER integer range 0 to 9;
	secMSD			: BUFFER integer range 0 to 9;
	minLSD			: BUFFER integer range 0 to 9;
	minMSD			: BUFFER integer range 0 to 9;
	hourLSD			: BUFFER integer range 0 to 9;
	hourMSD			: BUFFER integer range 0 to 9;

	enable			: OUT std_logic_vector(7 downto 0); -- Represents the 8 7-segment displays, where a '1' indicates the display should be active, a '0' indicates it should be off
	flash			: BUFFER std_logic_vector (7 DOWNTO 0)); -- As above, but '1' indicates the display should flash on and off, '0' indicates "normal" behaviour
END COMPONENT;

COMPONENT timer PORT(
	clk				: IN std_logic; -- The clock who's rising edge triggers the Clock
	selectionToggle	: IN std_logic; -- If '1' on a rising clock edge (and 'active' is high, and 'mode' is high), changes the selected field
	inc				: IN std_logic; -- If '1' on a rising clock edge (and 'active' is high, and 'mode' is high) indicates the selected field should be incremented
	reset			: IN std_logic; -- If '1' (and 'active' is high) resets the Clock
	mode			: IN std_logic; -- If '1' the Clock operates in set mode, otherwise it operates in "normal" mode
	active			: IN std_logic; -- If '1' indicates the Clock should respond to it's inputs ('selectionToggle', 'inc' & 'reset')

	-- All the following are 7-segment display outputs.  They are all wired directly to the outputs for each field.

	csecLSD			: BUFFER integer range 0 to 9; -- msecLSD
	csecMSD			: BUFFER integer range 0 to 9; -- msecMSD
	secLSD			: BUFFER integer range 0 to 9;
	secMSD			: BUFFER integer range 0 to 9;
	minLSD			: BUFFER integer range 0 to 9;
	minMSD			: BUFFER integer range 0 to 9;
	hourLSD			: BUFFER integer range 0 to 9;
	hourMSD			: BUFFER integer range 0 to 9;

	alarm			: BUFFER std_logic; -- If '1', indicates the alarm is going off

	enable			: OUT std_logic_vector(7 downto 0); -- Represents the 8 7-segment displays, where a '1' indicates the display should be active, a '0' indicates it should be off
	flash			: BUFFER std_logic_vector (7 DOWNTO 0)); -- As above, but '1' indicates the display should flash on and off, '0' indicates "normal" behaviour
END COMPONENT;

begin
	u1: displayOutput port map(clk1MHz, LEDDsp1, LEDDsp2, LEDDsp3, LEDDsp4, LEDDsp5, LEDDsp6, LEDDsp7, LEDDsp8, dispStrobeSig, LEDFlash, MSDDisp1, MSDDisp2, dispStrobe, dispDataOut);
	u2: stopwatch port map(clk10ms, startStopState(0), resetGlobal, resetLocal, modeSelect(0), msecLSDSW, msecMSDSW, secLSDSW, secMSDSW, minLSDSW, minMSDSW, hourLSDSW, hourMSDSW);
	u3: timer PORT MAP(
		clk => clk10ms,
		selectionToggle => rotateTrig,
		inc => startStopTrig,
		reset => resetGlobal,-- or resetLocal, -- May need to be changed... 'clock' assumes a single reset, which only takes effect if active is high
		mode => settingMode,
		active => modeSelect(1),-- or resetGlobal, -- As above; bit of a fudge
		csecLSD => msecLSDTi,
		csecMSD => msecMSDTi,
		secLSD => secLSDTi,
		secMSD => secMSDTi,
		minLSD => minLSDTi,
		minMSD => minMSDTi,
		hourLSD => hourLSDTi,
		hourMSD => hourMSDTi,
		alarm => timerAlarm,
		enable => dispStrobeSigTi,
		flash => LEDFlashTi);
	u4: clock PORT MAP(
		clk => clk10ms,
		selectionToggle => rotateTrig,
		inc => startStopTrig,
		reset => resetGlobal,-- or resetLocal, -- May need to be changed... 'clock' assumes a single reset, which only takes effect if active is high
		mode => settingMode,
		active => modeSelect(2),-- or resetGlobal, -- As above; bit of a fudge
		csecLSD => msecLSDCl,
		csecMSD => msecMSDCl,
		secLSD => secLSDCl,
		secMSD => secMSDCl,
		minLSD => minLSDCl,
		minMSD => minMSDCl,
		hourLSD => hourLSDCl,
		hourMSD => hourMSDCl,
		enable => dispStrobeSigCl,
		flash => LEDFlashCl);

	setOutput:process(clk)
	begin
		if (clk'event and clk = '1') then
			invalidMode <= '0';

			case modeSelect is
				when "001" => LEDDsp1 <= hourMSDCl;
							  LEDDsp2 <= hourLSDCl;
							  LEDDsp3 <= minMSDCl;
							  LEDDsp4 <= minLSDCl;
							  LEDDsp5 <= secMSDCl;
							  LEDDsp6 <= secLSDCl;
							  LEDDsp7	<= msecMSDCl;
							  LEDDsp8 <= msecLSDCl;
							  dispStrobeSig <= dispStrobeSigCl;
							  LEDFlash <= LEDFlashCl;
				when "010" => LEDDsp1 <= hourMSDTi;
							  LEDDsp2 <= hourLSDTi;
							  LEDDsp3 <= minMSDTi;
							  LEDDsp4 <= minLSDTi;
							  LEDDsp5 <= secMSDTi;
							  LEDDsp6 <= secLSDTi;
							  LEDDsp7	<= msecMSDTi;
							  LEDDsp8 <= msecLSDTi;
							  dispStrobeSig <= dispStrobeSigTi;
							  LEDFlash <= LEDFlashTi;
				when "100" => LEDDsp1 <= hourMSDSW;
							  LEDDsp2 <= hourLSDSW;
							  LEDDsp3 <= minMSDSW;
							  LEDDsp4 <= minLSDSW;
							  LEDDsp5 <= secMSDSW;
							  LEDDsp6 <= secLSDSW;
							  LEDDsp7	<= msecMSDSW;
							  LEDDsp8 <= msecLSDSW;	
							  dispStrobeSig <= "11111111";	--
							  LEDFlash <= "00000000";
				when others => invalidMode <= '1';
							   displayEnable <= "000000"; -- Disable all displays until told otherwise
			end case;
		end if;
	end process;

	selectMode:process(clk)
	begin
		if (clk'event and clk = '1') then
--			hourMSDSW <= 0;
--			hourLSDSW <= 0;
--			minMSDSW <= 0;
--			minLSDSW <= 0;
--			secMSDSW <= 0;
--			secLSDSW <= 0;
--			msecMSDSW <= 0;
--			msecLSDSW <= 0;
--			hourMSDCl <= 1;
--			hourLSDCl <= 1;
--			minMSDCl <= 1;
--			minLSDCl <= 1;
--			secMSDCl <= 1;
--			secLSDCl <= 1;
--			msecMSDCl <= 1;
--			msecLSDCl <= 1;
			--hourMSDTi <= 2;
			--hourLSDTi <= 2;
			--minMSDTi <= 2;
			--minLSDTi <= 2;
			--secMSDTi <= 2;
			--secLSDTi <= 2;
			--msecMSDTi <= 2;
			--msecLSDTi <= 2;
			msecLSDTiOld <= msecLSDTi;

			--if ((modeSelect /= "100") AND (modeSelect /= "010") AND (modeSelect /= "001")) then	
			--	modeSelect <= "010";--turn on reset
			--	displayEnable <= "000000";		--show all displays
			--	resetGlobal <= '1';
			--end if;

--global reset of whole device

			if (count10ms > count10msReference) then
				count10ms <= 0;
				clk10ms <= NOT clk10ms;
			else
				if (dips(0) = '1') then
					if (dips(1) = '1') then
						if (dips(2) = '1') then
							count10msReference <= 12562500;
						else
							count10msReference <= 1256250;
						end if;
					else
						if (dips(2) = '1') then
							count10msReference <= 125625;
						else
							count10msReference <= 12562;
						end if;
					end if;
				else
					if (dips(1) = '1') then
						if (dips(2) = '1') then
							count10msReference <= 1256;
						else
							count10msReference <= 126;
						end if;
					else
						if (dips(2) = '1') then
							count10msReference <= 13;
						else
							count10msReference <= 1;
						end if;
					end if;
				end if;

				count10ms <= count10ms + 1;
			end if;

			if (count1MHz = 25) then
				count1MHz <= 0;
				clk1MHz <= NOT clk1MHz;
			else
				count1MHz <= count1MHz + 1;
			end if;			

			oldClk <= clk10ms;

			if ((clk10ms = '1') AND (clk10ms /= oldClk)) then	--positive clock edge
				startStopTrig <= '0';
				rotateTrig <= '0';

				if ((invalidMode = '1') or (resetGlobal = '1')) then -- Synchronous reset
					--reset all counts though individual functions
					modeSelect <= "001";--in clock mode
					startStopState <= "000";
	--				settingMode <= '1';	--in setting mode
					settingMode <= '0';	--in setting mode
					resetLocal <= '0'; --normal value
					resetGlobal <= '0';
				end if;

				if (buttonMode = '0') then 						--button is pressed
					resetLocal <= '0';

					if (dbCountMode /= 100) then			--basic debounce 2 sec
						dbCountMode <= dbCountMode + 1;
					end if;

					if (dbCountMode = 99) then
						if (modeSelect /= "100") then		--if not in stopwatch mode
							settingMode <= NOT settingMode;	--this is were to put into set mode
						else
							if (startStopState(0) = '0') then-- if stopwatch has paused and button held down reset stopwatch
								resetLocal <= '1';
							end if;
						end if;
					end if;
				else
					dbCountMode <= 0;

					if ((dbCountMode < 100) and (dbCountMode > 3)) then -- button was pressed but for less than 2 seconds
						if (settingMode = '1') then
							--rotate through digits
--							rotateTrig <= NOT rotateTrig;
							rotateTrig <= '1';
						else
							--Switch between various modes
							if (modeSelect = "001") then
								modeSelect <= "010";
							end if;

							if (modeSelect = "010") then
								modeSelect <= "100";
							end if;

							if (modeSelect = "100") then
								modeSelect <= "001";
							end if;
						end if;
					end if;
				end if;
			
				if (buttonStartStop = '0') then --startStop button pressed
					if (dbCountSS /= 100) then		--basic debounce 2 sec
						dbCountSS <= dbCountSS + 1;
					end if;
				
					if ((dbCountSS = 99) AND (modeSelect = "010") AND (msecLSDTiOld = msecLSDTi)) then --button pressed for 2 sec in timer mode and timer is stopped
						--local reset
						resetLocal <= '1';
					end if;

					if ((dbCountSS = 5) and (modeSelect = "100")) then -- quick trigger when in stopwatch mode
						startStopState(0) <= NOT startStopState(0);	 --startStopTrig not required
					end if;
				else
					dbCountSS <= 0;

					if ((dbCountSS < 100) and (dbCountSS > 5) and (modeSelect /= "100")) then -- button was pressed but for less than 2 seconds
--						startStopTrig <= NOT startStopTrig;
						startStopTrig <= '1';
					end if;
				end if;
	
				if ((dbCountSS = 100) AND (dbCountMode = 100)) then
					--global reset of whole device
					resetGlobal <= '1';
				else
					resetGlobal <= '0';
				end if;
			end if;

			if (timerAlarm = '1') then
				settingMode <= '0';
				modeSelect <= "010";
			end if;
		end if;
	end process;
end arch;
