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

entity uart_rec is
	port (
		clk, rst : in std_logic;
		rx : in std_logic;
		tick : in std_logic;
		data_ready : out std_logic;
		data_out : out std_logic_vector (7 downto 0)
	);
end uart_rec;

architecture arch of uart_rec is
	type states is (idle, start, data, stop);
	signal this_state, next_state : states; -- state of the FSM
	signal this_count, next_count : unsigned (3 downto 0); -- the number of baud samples taken (0 - 15)
	signal this_bits, next_bits : unsigned (3 downto 0); -- the number of bits received (0 - 7)
	signal this_data, next_data : std_logic_vector (7 downto 0); -- the actual incoming data
	
	begin
	process (clk) -- sensitive to the system clock
		begin
		if rising_edge(clk) then
			if(rst = '1') then -- reset signals
				this_state <= idle;
				this_count <= (others => '0');
				this_bits <= (others => '0');
				this_data <= (others => '0');
			else -- assign the current signals to their next value
				this_state <= next_state;
				this_count <= next_count;
				this_bits <= next_bits;
				this_data <= next_data;
			end if;
		end if;
	end process;
	
	process (this_state, this_count, this_bits, this_data, tick, rx) -- whenever a signal or input (tick, rx) changes
	begin
		-- defensively assign the next signal values as the current signal values
		next_data <= this_data;
		next_state <= this_state;
		next_count <= this_count;
		next_bits <= this_bits;
		data_ready <= '0'; -- data isn't ready
		case this_state is
			when idle =>
				if (rx = '0') then -- sign of a start signal bit
					next_state <= start; -- move to start state
					next_count <= (others => '0'); -- reset counter
				end if;
			when start =>
				if (tick = '1') then -- align ourselves to the baudrate counter
					if (this_count = 7) then -- if we are in the middle of the start bit (7 samples in)
						next_state <= data; -- move to data state
						next_data <= (others => '0'); -- reset data byte
						next_count <= (others => '0'); -- reset counter
						next_bits <= (others => '0'); -- reset the number of bits read
					else -- increment counter
						next_count <= this_count + 1; 
					end if;
				end if;
			when data =>
				if (tick = '1') then -- align ourselves to the baudrate counter
					if (this_count = 15) then -- if we count 15 samples of the input
						next_count <= (others => '0'); -- reset the counter (for the next step)
						next_data <= rx & this_data(7 downto 1); -- shift the LSB out and add rx as the MSB of the data
						if (this_bits = 7) then -- we have read a full byte
							next_state <= stop; -- move to stop stage
						else -- we need to read another byte
							next_bits <= this_bits + 1; -- increment our bit count
						end if;
					else -- we haven't taken enough samples of the signal
						next_count <= this_count + 1; -- increment counter
					end if;
				end if;
			when stop =>
				if (tick = '1') then -- align ourselves to the baudrate counter
					if (this_count = 15) then -- if we count 15 samples of the input
						next_state <= idle; -- move back to the idle state
						data_ready <= '1'; -- assert the "done" status signal high
					else -- we haven't taken enough samples of the signal
						next_count <= this_count + 1; -- increment counter
					end if;
				end if;
		end case;
	end process;
	data_out <= this_data; -- update output
end arch;