----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    09:11:41 06/04/2007 
-- Design Name: 
-- Module Name:    uart_mp - 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;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

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

entity uart_mp is
  Port ( clk : in  STD_LOGIC;
         vamp_clk : in STD_LOGIC;
         reset : in  STD_LOGIC;
         MDat_in : in  STD_LOGIC_VECTOR (31 downto 0);
         MAddr : in  STD_LOGIC_VECTOR (29 downto 0);
         req : in  STD_LOGIC;
         wr : in  STD_LOGIC;
         MDat_out : out  STD_LOGIC_VECTOR (31 downto 0);
         reqp : out  STD_LOGIC;
         BRdy : out  STD_LOGIC;
         TxD : out  STD_LOGIC;
         RxD : in  STD_LOGIC;
         dev_id : in STD_LOGIC_VECTOR(2 downto 0)
         );
end uart_mp;

architecture Behavioral of uart_mp is

  --Control Automaton
  type mprs232_state_type is 
    (mprs232_state_rst, mprs232_state_wait, mprs232_state_read, mprs232_state_write);
  
  type rs232_state_type is 
    (rs232_state_rst, rs232_state_wait, rs232_state_read1, rs232_state_read2, rs232_state_read3, rs232_state_read4, rs232_state_write1, rs232_state_write2, rs232_state_write3, rs232_state_write4);
  
  signal mprs232_state_next: mprs232_state_type := mprs232_state_rst;
  signal rs232_state_next  : rs232_state_type   := rs232_state_rst;
  
  signal unknow32 : std_logic_vector(31 downto 0);
  signal MDat_out_delayed : std_logic_vector (31 downto 0);
  signal reset_n : std_logic;
  
  signal TDataIn  : Std_Logic_Vector(7 downto 0);
  signal RDataOut : Std_Logic_Vector(7 downto 0);
  signal CS_N    : Std_Logic;
  signal RD_N    : Std_Logic;
  signal WR_N    : Std_Logic;
  signal IntRx_N : Std_Logic;  -- Receive interrupt
  signal IntTx_N : Std_Logic;  -- Transmit interrupt
  signal Addr    : Std_Logic_Vector(1 downto 0); -- 
  signal still_reading    : boolean:=false;
  signal go_to_next_state : boolean:=false;
  signal is_done          : boolean:=false;
  
  signal send_buffer : std_logic_vector (31 downto 0);
  signal receive_buffer : std_logic_vector (31 downto 0);
  
  component miniUART is
    port (
      SysClk   : in  Std_Logic;  -- System Clock
      Reset    : in  Std_Logic;  -- Reset input
      CS_N     : in  Std_Logic;
      RD_N     : in  Std_Logic;
      WR_N     : in  Std_Logic;
      RxD      : in  Std_Logic;
      TxD      : out Std_Logic;
      IntRx_N  : out Std_Logic;  -- Receive interrupt
      IntTx_N  : out Std_Logic;  -- Transmit interrupt
      Addr     : in  Std_Logic_Vector(1 downto 0); -- 
      DataIn   : in  Std_Logic_Vector(7 downto 0); -- 
      DataOut  : out Std_Logic_Vector(7 downto 0)); -- 
  end component;
  
begin

  miniUARTDev : miniUART port map (clk,(not reset),CS_N,RD_N,WR_N,RxD,TxD,
                                   IntRx_N,IntTx_N,Addr,TDataIn,RDataOut);



  
  mprs232_control: process (vamp_clk, reset)
  begin
    -- MP
    if (reset = '1') then
      BRdy   <= '0';
      reqp   <= '0';
      MDat_out <= unknow32;
      MDat_out_delayed <= unknow32;
      mprs232_state_next <= mprs232_state_rst;
      
    elsif (rising_edge(vamp_clk)) then

      MDat_out <= MDat_out_delayed;
      
      case (mprs232_state_next) is
        
        when mprs232_state_rst =>
          if reset = '0' then
            BRdy   <= '0';
            reqp   <= '0';
            MDat_out_delayed <= unknow32;
            mprs232_state_next <= mprs232_state_wait;
          end if;

        when mprs232_state_wait =>
          BRdy <= '0';
          MDat_out_delayed <= unknow32;
          
          -- request starts in case there is a request signal and the address is in device-address space
          if (req = '1') and (MAddr(29 downto 13) = b"1111_1111_1111_1111_1") and (MAddr(12 downto 10) = dev_id) then
            if wr = '0' then
              mprs232_state_next  <= mprs232_state_read;
            else
              send_buffer <= MDat_in;   -- in case inputs to devices are not stable!
              mprs232_state_next  <= mprs232_state_write;
            end if;
            reqp <= '1';
          end if;

        when mprs232_state_write =>
          if is_done then
            reqp <= '0';
            BRdy <= '1';
            mprs232_state_next <= mprs232_state_wait;
          end if;

        when mprs232_state_read =>
          if is_done then
            reqp <= '0';
            BRdy <= '1';
            MDat_out_delayed <= receive_buffer;
            mprs232_state_next <= mprs232_state_wait;
          end if;

        when others => null;
      end case;
      
    end if;     
  end process;          
  
  rs232_control: process (clk, reset)
  begin
    if (reset = '1') then
      -- UART
      CS_N <= '1';
      RD_N <= '1';
      WR_N <= '1';

      is_done <= false;
      still_reading <= false;
      go_to_next_state <= false;
      
      rs232_state_next <= rs232_state_rst;
      
    elsif (rising_edge(clk)) then
      case rs232_state_next is
        when rs232_state_rst =>
          
          if reset = '0' then
            CS_N <= '1';
            RD_N <= '1';
            WR_N <= '1';
            
            is_done <= false;
            still_reading <= false;
            go_to_next_state <= false;
            
            rs232_state_next <= rs232_state_wait;
          end if;
          
        when rs232_state_wait =>
          
			 CS_N <= '1';
          RD_N <= '1';
          WR_N <= '1';
			 
          if is_done then
            if mprs232_state_next = mprs232_state_wait then
              is_done  <= false;
              still_reading <= false;
              go_to_next_state <= false;
            end if;
          elsif mprs232_state_next = mprs232_state_write then
              rs232_state_next <= rs232_state_write1;
              
          elsif mprs232_state_next = mprs232_state_read then
              rs232_state_next <= rs232_state_read1;
          end if;

          -- WRITE CASE

        when rs232_state_write1 =>
          if (IntTx_N = '0') then
            Addr <= "00";
            WR_N <= '0';
            CS_N <= '0';
            TDataIn <= send_buffer(31 downto 24);
            
            go_to_next_state <= true;
            if (go_to_next_state) then
              go_to_next_state <= false;
              rs232_state_next <= rs232_state_write2;
            end if;
          else
            WR_N <= '1';
            CS_N <= '1';
          end if;

        when rs232_state_write2 =>
          if (IntTx_N = '0') then
            Addr <= "00";
            WR_N <= '0';
            CS_N <= '0';
            TDataIn <= send_buffer(23 downto 16);
            
            go_to_next_state <= true;
            if (go_to_next_state) then
              go_to_next_state <= false;
              rs232_state_next <= rs232_state_write3;
            end if;
          else
            WR_N <= '1';
            CS_N <= '1';
          end if;
          
        when rs232_state_write3 =>
          if (IntTx_N = '0') then
            Addr <= "00";
            WR_N <= '0';
            CS_N <= '0';
            TDataIn <= send_buffer(15 downto 8);
            
            go_to_next_state <= true;
            if (go_to_next_state) then
              go_to_next_state <= false;
              rs232_state_next <= rs232_state_write4;
            end if;
          else
            WR_N <= '1';
            CS_N <= '1';
          end if;
          
        when rs232_state_write4 =>
          if (IntTx_N = '0') then
            Addr <= "00";
            WR_N <= '0';
            CS_N <= '0';
            TDataIn <= send_buffer(7 downto 0);
            
            go_to_next_state <= true;
            if (go_to_next_state) then
              go_to_next_state <= false;
              is_done <= true;
              rs232_state_next <= rs232_state_wait;
            end if;
          else
            WR_N <= '1';
            CS_N <= '1';
          end if;

          -- READ CASE
          
        when rs232_state_read1 =>
          if (IntRx_N = '0') then
            Addr <= "00";
            RD_N <= '0';
            CS_N <= '0';
            still_reading <= true;
          elsif (still_reading) then
            receive_buffer(31 downto 24) <= RDataOut;
            still_reading <= false;
            rs232_state_next <= rs232_state_read2;
          else
            RD_N <= '1';
            CS_N <= '1';
          end if;
          
        when rs232_state_read2 =>
          if (IntRx_N = '0') then
            Addr <= "00";
            RD_N <= '0';
            CS_N <= '0';
            still_reading <= true;
          elsif (still_reading) then
            receive_buffer(23 downto 16) <= RDataOut;
            still_reading <= false;
            rs232_state_next <= rs232_state_read3;
          else
            RD_N <= '1';
            CS_N <= '1';
          end if;
          
        when rs232_state_read3 =>
          if (IntRx_N = '0') then
            Addr <= "00";
            RD_N <= '0';
            CS_N <= '0';
            still_reading <= true;
          elsif (still_reading) then
            receive_buffer(15 downto 8) <= RDataOut;
            still_reading <= false;
            rs232_state_next <= rs232_state_read4;
          else
            RD_N <= '1';
            CS_N <= '1';
          end if;
          
        when rs232_state_read4 =>
          if (IntRx_N = '0') then
            Addr <= "00";
            RD_N <= '0';
            CS_N <= '0';
            still_reading <= true;
          elsif (still_reading) then
				receive_buffer(7 downto 0) <= RDataOut;
				
				still_reading <= false;
            is_done <= true;
            rs232_state_next <= rs232_state_wait;
          else
            RD_N <= '1';
            CS_N <= '1';
          end if;
          
        when others => null;
      end case;
      
    end if;
  end process;

end Behavioral;
