LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY uart_wb IS
	Port
	(
		CLK_I		: in  STD_LOGIC;
		RST_I		: in  STD_LOGIC;
		CYC_I		: in  STD_LOGIC;
		STB_I		: in  STD_LOGIC;
		SEL_I		: in  unsigned(3 downto 0);
		WE_I		: in  STD_LOGIC;
		ACK_O		: out STD_LOGIC;
		SRDY_O		: out STD_LOGIC;
		MRDY_I		: in  STD_LOGIC;
		ADDR_I		: in  unsigned(31 downto 0);
		DAT_I		: in  unsigned(31 downto 0);
		DAT_O		: out unsigned(31 downto 0);
		INT_O		: out STD_LOGIC;
		ser_rx		: in std_logic;
		ser_tx		: out std_logic
	);
END uart_wb;

ARCHITECTURE behavior OF uart_wb IS

	COMPONENT uart_tx
    Port
    (
		data_in				: in std_logic_vector(7 downto 0);
		write_buffer		: in std_logic;
		reset_buffer		: in std_logic;
		en_16_x_baud		: in std_logic;
		serial_out			: out std_logic;
		buffer_full			: out std_logic;
		buffer_half_full	: out std_logic;
		tx_complete			: out std_logic;
		tx_empty			: out std_logic;
		clk					: in std_logic
	);
	END COMPONENT;

	COMPONENT uart_rx
	Port 
	(
		serial_in			: in std_logic;
		data_out			: out std_logic_vector(7 downto 0);
		read_buffer			: in std_logic;
		reset_buffer		: in std_logic;
		en_16_x_baud		: in std_logic;
		buffer_data_present	: out std_logic;
		buffer_full			: out std_logic;
		buffer_half_full	: out std_logic;
		clk					: in std_logic
	);
	END COMPONENT;

	-- Signals for UART connections
	signal baud_count		: unsigned(15 downto 0);
	signal en_16_x_baud		: std_logic;
	signal reg_we_uart_tx	: std_logic;
	signal tx_full			: std_logic;
	signal tx_half_full		: std_logic;
	signal reg_re_uart_rx	: std_logic;
	signal reg_uart_tx		: unsigned(7 downto 0);
	signal reg_uart_rx		: std_logic_vector(7 downto 0);
	signal rx_data_present	: std_logic;
	signal rx_full			: std_logic;
	signal rx_half_full		: std_logic;
	signal uart_status_port	: unsigned(15 downto 0);
	signal reg_uart_baud	: unsigned(15 downto 0);
	signal rx_int_en		: std_logic;
	signal tx_int_en		: std_logic;
	signal irq_rx			: std_logic;
	signal irq_tx			: std_logic;
	signal tx_complete		: std_logic;
	signal tx_empty			: std_logic;
	
begin

	SRDY_O	<= CYC_I;
	
------------------------------------------------------------------
registers_write: 
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			reg_we_uart_tx	<= '0';
			if RST_I = '1' then
				reg_uart_baud	<= to_unsigned(53, 16);
				rx_int_en		<= '0';
				tx_int_en		<= '0';
			elsif (STB_I and CYC_I and WE_I) = '1' then
				case ADDR_I(5 downto 2) is
				
				when "0000" =>
					reg_we_uart_tx	<= '1';
					reg_uart_tx		<= DAT_I(7 downto 0);
					
				when "0001" =>
					rx_int_en		<= DAT_I(6);
					tx_int_en		<= DAT_I(5);
					
				when "0010" =>
					reg_uart_baud	<= DAT_I(15 downto 0);

				when others => null;
				end case;
			end if;	
		end if;	
	end process;

registers_read: 
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			reg_re_uart_rx	<= '0';
			ACK_O			<= '0';
			if (STB_I and CYC_I) = '1' then
				ACK_O		<= not WE_I;
				DAT_O		<= (others => '0');
				case ADDR_I(5 downto 2) is

				when "0000" =>
					reg_re_uart_rx		<= not WE_I;
					DAT_O(7 downto 0)	<= unsigned(reg_uart_rx);

				when "0001" =>
					DAT_O(15 downto 0)	<= uart_status_port;

				when "0010" =>
					DAT_O(15 downto 0)	<= reg_uart_baud;

				when others => null;
				end case;
			end if;
		end if;	
	end process;

baud_timer:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			if RST_I = '1' then
				baud_count <= (others => '0');
			elsif baud_count = reg_uart_baud then
				baud_count <= (others => '0');
				en_16_x_baud <= '1';
			else
				baud_count <= baud_count + 1;
				en_16_x_baud <= '0';
			end if;
		end if;
	end process;

irq_register:
	process(CLK_I)
	begin
		if rising_edge(CLK_I) then
			irq_tx		<= tx_empty and tx_int_en;
			irq_rx		<= rx_data_present and rx_int_en;
		end if;
	end process;

inst_uart_tx: uart_tx 
	port map
	(
		data_in				=> std_logic_vector(reg_uart_tx), 
		write_buffer		=> reg_we_uart_tx,
		reset_buffer		=> RST_I,
		en_16_x_baud		=> en_16_x_baud,
		serial_out			=> ser_tx,
		buffer_full			=> tx_full,
		buffer_half_full	=> tx_half_full,
		tx_complete			=> tx_complete,
		tx_empty			=> tx_empty,
		clk					=> CLK_I
	);
	
inst_uart_rx: uart_rx
	port map
	(
		serial_in			=> ser_rx,
		data_out			=> reg_uart_rx,
		read_buffer			=> reg_re_uart_rx,
		reset_buffer		=> RST_I,
		en_16_x_baud		=> en_16_x_baud,
		buffer_data_present => rx_data_present,
		buffer_full			=> rx_full,
		buffer_half_full	=> rx_half_full,
		clk					=> CLK_I
	);  
	
	uart_status_port	<= "000000" & irq_rx & irq_tx & '0' & rx_int_en & tx_int_en & rx_data_present & rx_full & rx_half_full & tx_full & tx_half_full;
	INT_O				<= irq_rx or irq_tx;

end behavior;
