-- Sim console UART, provides the same interface as potato UART by
-- Kristian Klomsten Skordal.

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

library work;
use work.wishbone_types.all;
use work.sim_console.all;

--! @brief Simple UART module.
--! The following registers are defined:
--! |--------------------|--------------------------------------------|
--! | Address            | Description                                |
--! |--------------------|--------------------------------------------|
--! | 0x00               | Transmit register (write-only)             |
--! | 0x08               | Receive register (read-only)               |
--! | 0x10               | Status register (read-only)                |
--! | 0x18               | Sample clock divisor register (dummy)      |
--! | 0x20               | Interrupt enable register (read/write)     |
--! |--------------------|--------------------------------------------|
--!
--! The status register contains the following bits:
--! - Bit 0: receive buffer empty
--! - Bit 1: transmit buffer empty
--! - Bit 2: receive buffer full
--! - Bit 3: transmit buffer full
--!
--! Interrupts are enabled by setting the corresponding bit in the interrupt
--! enable register. The following bits are available:
--! - Bit 0: data received (receive buffer not empty)
--! - Bit 1: ready to send data (transmit buffer empty)
entity pp_soc_uart is
    generic(
	FIFO_DEPTH : natural := 64 --Unused
	);
    port(
	clk : in std_logic;
	reset : in std_logic;

	-- UART ports:
	txd : out std_logic;
	rxd : in  std_logic;

	-- Wishbone ports:
	wb_adr_in  : in  std_logic_vector(11 downto 0);
	wb_dat_in  : in  std_logic_vector( 7 downto 0);
	wb_dat_out : out std_logic_vector( 7 downto 0);
	wb_we_in   : in  std_logic;
	wb_cyc_in  : in  std_logic;
	wb_stb_in  : in  std_logic;
	wb_ack_out : out std_logic
	);
end entity pp_soc_uart;

architecture behaviour of pp_soc_uart is

    signal sample_clk_divisor : std_logic_vector(7 downto 0);

    -- IRQ enable signals:
    signal irq_recv_enable, irq_tx_ready_enable : std_logic := '0';

    -- Wishbone signals:
    type wb_state_type is (IDLE, WRITE_ACK, READ_ACK);
    signal wb_state : wb_state_type;
    signal wb_ack : std_logic; --! Wishbone acknowledge signal

begin

    wb_ack_out <= wb_ack and wb_cyc_in and wb_stb_in;

    wishbone: process(clk)
	variable sim_tmp : std_logic_vector(63 downto 0);
    begin
	if rising_edge(clk) then
	    if reset = '1' then
		wb_ack <= '0';
		wb_state <= IDLE;
		sample_clk_divisor <= (others => '0');
		irq_recv_enable <= '0';
		irq_tx_ready_enable <= '0';
	    else
		case wb_state is
		when IDLE =>
		    if wb_cyc_in = '1' and wb_stb_in = '1' then
			if wb_we_in = '1' then -- Write to register
			    if wb_adr_in(11 downto 0) = x"000" then
				sim_console_write(x"00000000000000" & wb_dat_in);
			    elsif wb_adr_in(11 downto 0) = x"018" then
				sample_clk_divisor <= wb_dat_in;
			    elsif wb_adr_in(11 downto 0) = x"020" then
				irq_recv_enable <= wb_dat_in(0);
				irq_tx_ready_enable <= wb_dat_in(1);
			    end if;
			    wb_ack <= '1';
			    wb_state <= WRITE_ACK;
			else -- Read from register
			    if wb_adr_in(11 downto 0) = x"008" then
				sim_console_read(sim_tmp);
				wb_dat_out <= sim_tmp(7 downto 0);
			    elsif wb_adr_in(11 downto 0) = x"010" then
				sim_console_poll(sim_tmp);
				wb_dat_out <= "00000" & sim_tmp(0) & '1' & not sim_tmp(0);
			    elsif wb_adr_in(11 downto 0) = x"018" then
				wb_dat_out <= sample_clk_divisor;
			    elsif wb_adr_in(11 downto 0) = x"020" then
				wb_dat_out <= (0 => irq_recv_enable,
					       1 => irq_tx_ready_enable,
					       others => '0');
			    else
				wb_dat_out <= (others => '0');
			    end if;
			    wb_ack <= '1';
			    wb_state <= READ_ACK;
			end if;
		    end if;
		when WRITE_ACK|READ_ACK =>
		    if wb_stb_in = '0' then
			wb_ack <= '0';
			wb_state <= IDLE;
		    end if;
		end case;
	    end if;
	end if;
	end process wishbone;

end architecture behaviour;