-- ==============================================================
-- FILE : RS232_UART.VHD  -  May be modified by the USER
-- ==============================================================
--
-- This file is part of the CameraLink IP for HERON-FPGA modules.
--
-- This file can be copied into a new project and modified by the
-- user to implement their own logic.
--
-- ==============================================================
--
--  Date        : 12/06/2006
--  Author      : R. Williams - HUNT ENGINEERING
--  Description : RS232 UART
--
-- ==============================================================
--
--  Ver     Modified By      Date      Changes
--  ---     -----------      ----      -------
--  1.0     R. Williams    12-06-06    Developed from previous
--                                     Camera Link examples.
--  3.1     R. Williams    07-09-07    Changes to CamLink_IF.vhd
--                                     to ensure correct operation
--                                     under ISE9.1.
--
-- ==============================================================


-- ==============================================================
--  Module      : RS232_UART
--  Description : RS232 UART top level module

library IEEE;
  use IEEE.std_logic_1164.ALL;
  use IEEE.std_logic_arith.ALL;
  use IEEE.std_logic_unsigned.ALL;


entity RS232_UART is
  port (
    RESET     : in  std_logic;
    CLOCK     : in  std_logic;
    DIN       : in  std_logic_vector(7 downto 0);
    BAUD_SEL  : in  std_logic_vector(3 downto 0);
    STOP_BITS : in  std_logic_vector(1 downto 0);
    RX        : in  std_logic;
    READ      : in  std_logic;
    WRITE     : in  std_logic;
    DOUT      : out std_logic_vector(7 downto 0);
    TX        : out std_logic;
    RX_EMPTY  : out std_logic;
    TX_FULL   : out std_logic;
    RX_COUNT  : out std_logic_vector(7 downto 0);
    TX_COUNT  : out std_logic_vector(7 downto 0)
  );
end RS232_UART;


architecture RTL of RS232_UART is

  component RS232TX
    port (
      RESET     : in  std_logic;
      CLOCK     : in  std_logic;
      BAUD_X_16 : in  std_logic;
      STOP_BITS : in  std_logic_vector(1 downto 0);
      DIN       : in  std_logic_vector(7 downto 0);
      WRITE     : in  std_logic;
      TX        : out std_logic;
      TX_FULL   : out std_logic;
      TX_COUNT  : out std_logic_vector(4 downto 0) );
  end component;

  component RS232RX
    port (
      RESET     : in  std_logic;
      CLOCK     : in  std_logic;
      BAUD_X_16 : in  std_logic;
      READ      : in  std_logic;
      RX        : in  std_logic;
      DOUT      : out std_logic_vector(7 downto 0);
      RX_EMPTY  : out std_logic;
      RX_COUNT  : out std_logic_vector(4 downto 0) );
  end component;

  signal UARTcount : std_logic_vector(9 downto 0);
  signal UARTtc    : std_logic_vector(9 downto 0);
  signal UARTen    : std_logic;

begin

  -------------------------------------------------------------
  --
  --  Baud Rate    BAUD_SEL
  --
  --     4800        0000
  --     9600        0001
  --    19200        0010
  --    38400        0011
  --   115200        0100
  --

  -- UARTtc is used to create a pulse at the rate of 16x the Baud Rate.
  -- The following values are valid when CLOCK is running at 50MHz.
  process(BAUD_SEL)
  begin
    case BAUD_SEL is
      when "0000" => UARTtc <= "1010001010";
      when "0001" => UARTtc <= "0101000101";
      when "0010" => UARTtc <= "0010100010";
      when "0011" => UARTtc <= "0001010000";
      when "0100" => UARTtc <= "0000011010";
      when others => UARTtc <= "0101000101";
    end case;
  end process;

  -- Count from 0 to UARTtc. Assert UARTen for one
  -- CLOCK cycle during this time.
  process(RESET, CLOCK)
  begin
    if RESET='1' then
      UARTcount <= (others=>'0');
    elsif rising_edge(CLOCK) then
      if (UARTcount = UARTtc) then
        UARTcount <= "0000000000";
      else
        UARTcount <= UARTcount + 1;
      end if;
    end if;
  end process;

  UARTen <= '1' when UARTcount = "0000000000" else '0';

  -- RS232 Transmitter
  iTX : RS232TX
    port map (
      RESET     => RESET,
      CLOCK     => CLOCK,
      BAUD_X_16 => UARTen,
      STOP_BITS => STOP_BITS,
      DIN       => DIN,
      WRITE     => WRITE,
      TX        => TX,
      TX_FULL   => TX_FULL,
      TX_COUNT  => TX_COUNT(4 downto 0) );

  -- RS232 Receiver
  iRX : RS232RX
    port map (
      RESET     => RESET,
      CLOCK     => CLOCK,
      BAUD_X_16 => UARTen,
      READ      => READ,
      RX        => RX,
      DOUT      => DOUT,
      RX_EMPTY  => RX_EMPTY,
      RX_COUNT  => RX_COUNT(4 downto 0) );

  TX_COUNT(7 downto 5) <= (others=>'0');
  RX_COUNT(7 downto 5) <= (others=>'0');

end RTL;


-- ==============================================================
--  Module      : RS232RX
--  Description : RS232 Receiver

library IEEE;
  use IEEE.std_logic_1164.ALL;
  use IEEE.std_logic_arith.ALL;
  use IEEE.std_logic_unsigned.ALL;


entity RS232RX is
  port (
    RESET     : in  std_logic;
    CLOCK     : in  std_logic;
    BAUD_X_16 : in  std_logic;
    RX        : in  std_logic;
    READ      : in  std_logic;
    DOUT      : out std_logic_vector(7 downto 0);
    RX_EMPTY  : out std_logic;
    RX_COUNT  : out std_logic_vector(4 downto 0)
  );
end RS232RX;


architecture Behavioral of RS232RX is

  component RAM16X8
    port (
      CLOCK : in  std_logic;
      WE    : in  std_logic;
      A     : in  std_logic_vector(3 downto 0);
      DPRA  : in  std_logic_vector(3 downto 0);
      D     : in  std_logic_vector(7 downto 0);
      DPO   : out std_logic_vector(7 downto 0)
    );
  end component;

  signal RXcount   : std_logic_vector(3 downto 0);
  signal RXsreg    : std_logic_vector(7 downto 0);

  signal RX_DEL    : std_logic_vector(2 downto 0);
  signal RX_IDLE   : std_logic;

  signal BITcount  : std_logic_vector(3 downto 0);

  signal DATAreg   : std_logic_vector(7 downto 0);

  signal CE        : std_logic;
  signal UP        : std_logic;
  signal EN        : std_logic;
  signal WEN       : std_logic;

  signal FIFOfull  : std_logic;
  signal FIFOempty : std_logic;
  signal FIFOstate : std_logic_vector(4 downto 0);

  signal RD_ADDR   : std_logic_vector(3 downto 0);
  signal WR_ADDR   : std_logic_vector(3 downto 0);

begin

  process(RESET, CLOCK)
  begin
    if RESET='1' then
      RX_DEL <= (others=>'1');
    elsif rising_edge(CLOCK) then
      if BAUD_X_16='1' then
        RX_DEL <= RX_DEL(1 downto 0) & RX;
      end if;
    end if;
  end process;

  process(RESET, CLOCK)
  begin
    if RESET='1' then
      BITcount <= (others=>'0');
      RXcount  <= (others=>'0');
      RX_IDLE  <= '1';
    elsif rising_edge(CLOCK) then
      if BAUD_X_16='1' then
        RXcount <= RXcount + 1;
        if RX_IDLE='1' then
          BITcount <= (others=>'0');
          if RX_DEL(2 downto 1)="00" then
            RXcount  <= (others=>'0');
            RX_IDLE  <='0';
          end if;
        else
          if RXcount="0111" then
            BITcount <= BITcount + 1;
            if BITcount="1001" then
              BITcount <= (others=>'0');
              if RX_DEL(2 downto 1)="11" then
                RX_IDLE <= '1';
              end if;
            end if;
          end if;
        end if;
      end if;
    end if;
  end process;

  process(RESET, CLOCK)
  begin
    if RESET='1' then
      EN <= '0';
      RXsreg  <= (others=>'1');
      DATAreg <= (others=>'1');
    elsif rising_edge(CLOCK) then
      EN <= '0';
      if BAUD_X_16='1' and RXcount="0111" then
        RXsreg <= RX_DEL(2) & RXsreg(7 downto 1);
        if BITcount="1001" then
          DATAreg <= RXsreg;
          EN      <= '1';
        end if;
      end if;
    end if;
  end process;

  process(RESET, CLOCK)
  begin
    if RESET='1' then
      WR_ADDR <= (others=>'0');
    elsif rising_edge(CLOCK) then
      if WEN='1' then
        WR_ADDR <= WR_ADDR + 1;
      end if;
    end if;
  end process;

  process(RESET, CLOCK)
  begin
    if RESET='1' then
      RD_ADDR <= (others=>'0');
    elsif rising_edge(CLOCK) then
      if READ='1' then
        RD_ADDR <= RD_ADDR + 1;
      end if;
    end if;
  end process;

  WEN <= EN AND (NOT FIFOfull);

  CE <= READ XOR WEN;
  UP <= WEN;

  process(RESET, CLOCK)
  begin
    if RESET='1' then
      FIFOstate <= (others=>'0');
    elsif rising_edge(CLOCK) then
      if CE='1' then
        if UP='1' then
          FIFOstate <= FIFOstate + 1;
        else
          FIFOstate <= FIFOstate - 1;
        end if;
      end if;
    end if;
  end process;

  FIFOfull <= FIFOstate(4);

  FIFOempty <= (NOT FIFOstate(4)) AND (NOT FIFOstate(3)) AND (NOT FIFOstate(2)) AND
               (NOT FIFOstate(1)) AND (NOT FIFOstate(0));

  RX_EMPTY <= FIFOempty;

  RX_COUNT <= FIFOstate;

  iRAM16X8 : RAM16X8
    port map (
      CLOCK => CLOCK,
      WE    => WEN,
      A     => WR_ADDR,
      DPRA  => RD_ADDR,
      D     => DATAreg,
      DPO   => DOUT );

end Behavioral;


-- ==============================================================
--  Module      : RS232TX
--  Description : RS232 Transmitter

library IEEE;
  use IEEE.std_logic_1164.ALL;
  use IEEE.std_logic_arith.ALL;
  use IEEE.std_logic_unsigned.ALL;


entity RS232TX is
  port (
    RESET     : in  std_logic;
    CLOCK     : in  std_logic;
    BAUD_X_16 : in  std_logic;
    STOP_BITS : in  std_logic_vector(1 downto 0);
    DIN       : in  std_logic_vector(7 downto 0);
    WRITE     : in  std_logic;
    TX        : out std_logic;
    TX_FULL   : out std_logic;
    TX_COUNT  : out std_logic_vector(4 downto 0)
  );
end RS232TX;


architecture Behavioral of RS232TX is

  component RAM16X8
    port (
      CLOCK : in  std_logic;
      WE    : in  std_logic;
      A     : in  std_logic_vector(3 downto 0);
      DPRA  : in  std_logic_vector(3 downto 0);
      D     : in  std_logic_vector(7 downto 0);
      DPO   : out std_logic_vector(7 downto 0)
    );
  end component;

  signal TXcount   : std_logic_vector(3 downto 0);
  signal TXsreg    : std_logic_vector(9 downto 0);
  signal TXidle    : std_logic;
  signal TXout     : std_logic;

  signal BITcount  : std_logic_vector(3 downto 0);
  signal STOPcount : std_logic_vector(3 downto 0);

  signal REN       : std_logic;
  signal WEN       : std_logic;
  signal CE        : std_logic;
  signal UP        : std_logic;

  signal DATA      : std_logic_vector(7 downto 0);

  signal FIFOempty : std_logic;
  signal FIFOfull  : std_logic;
  signal FIFOstate : std_logic_vector(4 downto 0);

  signal RD_ADDR   : std_logic_vector(3 downto 0);
  signal WR_ADDR   : std_logic_vector(3 downto 0);

begin

  TX <= TXout;

  process(RESET, CLOCK)
  begin
    if RESET='1' then
      TXcount <= (others=>'0');
    elsif rising_edge(CLOCK) then
      if BAUD_X_16='1' then
        TXcount <= TXcount + 1;
      end if;
    end if;
  end process;

  process(RESET, CLOCK)
  begin
    if RESET='1' then
      REN      <= '0';
      TXidle   <= '1';
      TXsreg   <= (others=>'1');
      BITcount <= (others=>'0');
    elsif rising_edge(CLOCK) then
      REN <= '0';
      if TXidle='1' and FIFOempty='0' then
        REN    <= '1';
        TXidle <= '0';
        TXsreg <= '1' & DATA & '0';
      end if;
      if BAUD_X_16='1' and TXcount="1111" then
        TXout  <= TXsreg(0);
        TXsreg <= '1' & TXsreg(9 downto 1);
        if TXidle='0' then
          BITcount <= BITcount + 1;
          if BITcount=STOPcount then
            TXidle <= '1';
            BITcount <= (others=>'0');
          end if;
        end if;
      end if;
    end if;
  end process;

  -- If Stop_Bits="01", 1 stop bit is used
  -- If Stop_Bits="10", 2 stop bits are used
  STOPcount <= "10" & STOP_BITS;

  process(RESET, CLOCK)
  begin
    if RESET='1' then
      WR_ADDR <= (others=>'0');
    elsif rising_edge(CLOCK) then
      if WEN='1' then
        WR_ADDR <= WR_ADDR + 1;
      end if;
    end if;
  end process;

  process(RESET, CLOCK)
  begin
    if RESET='1' then
      RD_ADDR <= (others=>'0');
    elsif rising_edge(CLOCK) then
      if REN='1' then
        RD_ADDR <= RD_ADDR + 1;
      end if;
    end if;
  end process;

  WEN <= WRITE AND (NOT FIFOfull);

  CE <= REN XOR WEN;
  UP <= REN;

  process(RESET, CLOCK)
  begin
    if RESET='1' then
      FIFOstate <= "10000";
    elsif rising_edge(CLOCK) then
      if CE='1' then
        if UP='1' then
          FIFOstate <= FIFOstate + 1;
        else
          FIFOstate <= FIFOstate - 1;
        end if;
      end if;
    end if;
  end process;

  FIFOempty <= FIFOstate(4);

  FIFOfull <= (NOT FIFOstate(4)) AND (NOT FIFOstate(3)) AND (NOT FIFOstate(2)) AND
              (NOT FIFOstate(1)) AND (NOT FIFOstate(0));

  TX_FULL  <= FIFOfull;

  TX_COUNT <= FIFOstate;

  iRAM16X8 : RAM16X8
    port map (
      CLOCK => CLOCK,
      WE    => WEN,
      A     => WR_ADDR,
      DPRA  => RD_ADDR,
      D     => DIN,
      DPO   => DATA );

end Behavioral;


-- ==============================================================
--  Module      : RAM16X8
--  Description : 16x8 Dual-Port RAM

library IEEE;
  use IEEE.std_logic_1164.ALL;

--pragma translate_off
library UNISIM;
  use UNISIM.vcomponents.all;
--pragma translate_on


entity RAM16X8 is
  port (
    CLOCK : in  std_logic;
    WE    : in  std_logic;
    A     : in  std_logic_vector(3 downto 0);
    DPRA  : in  std_logic_vector(3 downto 0);
    D     : in  std_logic_vector(7 downto 0);
    DPO   : out std_logic_vector(7 downto 0)
  );
end RAM16X8;


architecture RTL of RAM16X8 is

  component RAM16X1D
    -- pragma translate_off
    generic (
      -- RAM initialization for functional simulation:
      INIT : bit_vector := X"0000"
    );
    -- pragma translate_on
    port (
      D     : in  std_logic;
      WE    : in  std_logic;
      WCLK  : in  std_logic;
      A0    : in  std_logic;
      A1    : in  std_logic;
      A2    : in  std_logic;
      A3    : in  std_logic;
      DPRA0 : in  std_logic;
      DPRA1 : in  std_logic;
      DPRA2 : in  std_logic;
      DPRA3 : in  std_logic;
      SPO   : out std_logic;
      DPO   : out std_logic
    ); 
  end component;

  attribute INIT: string;

  attribute INIT of iRAM0 : label is "0000";
  attribute INIT of iRAM1 : label is "0000";
  attribute INIT of iRAM2 : label is "0000";
  attribute INIT of iRAM3 : label is "0000";
  attribute INIT of iRAM4 : label is "0000";
  attribute INIT of iRAM5 : label is "0000";
  attribute INIT of iRAM6 : label is "0000";
  attribute INIT of iRAM7 : label is "0000";

begin

  iRAM0 : RAM16X1D
    port map (
      D     => D(0),
      WE    => WE,
      WCLK  => CLOCK,
      A0    => A(0),
      A1    => A(1),
      A2    => A(2),
      A3    => A(3),
      DPRA0 => DPRA(0),
      DPRA1 => DPRA(1),
      DPRA2 => DPRA(2),
      DPRA3 => DPRA(3),
      SPO   => open,
      DPO   => DPO(0) );

  iRAM1 : RAM16X1D
    port map (
      D     => D(1),
      WE    => WE,
      WCLK  => CLOCK,
      A0    => A(0),
      A1    => A(1),
      A2    => A(2),
      A3    => A(3),
      DPRA0 => DPRA(0),
      DPRA1 => DPRA(1),
      DPRA2 => DPRA(2),
      DPRA3 => DPRA(3),
      SPO   => open,
      DPO   => DPO(1) );

  iRAM2 : RAM16X1D
    port map (
      D     => D(2),
      WE    => WE,
      WCLK  => CLOCK,
      A0    => A(0),
      A1    => A(1),
      A2    => A(2),
      A3    => A(3),
      DPRA0 => DPRA(0),
      DPRA1 => DPRA(1),
      DPRA2 => DPRA(2),
      DPRA3 => DPRA(3),
      SPO   => open,
      DPO   => DPO(2) );

  iRAM3 : RAM16X1D
    port map (
      D     => D(3),
      WE    => WE,
      WCLK  => CLOCK,
      A0    => A(0),
      A1    => A(1),
      A2    => A(2),
      A3    => A(3),
      DPRA0 => DPRA(0),
      DPRA1 => DPRA(1),
      DPRA2 => DPRA(2),
      DPRA3 => DPRA(3),
      SPO   => open,
      DPO   => DPO(3) );

  iRAM4 : RAM16X1D
    port map (
      D     => D(4),
      WE    => WE,
      WCLK  => CLOCK,
      A0    => A(0),
      A1    => A(1),
      A2    => A(2),
      A3    => A(3),
      DPRA0 => DPRA(0),
      DPRA1 => DPRA(1),
      DPRA2 => DPRA(2),
      DPRA3 => DPRA(3),
      SPO   => open,
      DPO   => DPO(4) );

  iRAM5 : RAM16X1D
    port map (
      D     => D(5),
      WE    => WE,
      WCLK  => CLOCK,
      A0    => A(0),
      A1    => A(1),
      A2    => A(2),
      A3    => A(3),
      DPRA0 => DPRA(0),
      DPRA1 => DPRA(1),
      DPRA2 => DPRA(2),
      DPRA3 => DPRA(3),
      SPO   => open,
      DPO   => DPO(5) );

  iRAM6 : RAM16X1D
    port map (
      D     => D(6),
      WE    => WE,
      WCLK  => CLOCK,
      A0    => A(0),
      A1    => A(1),
      A2    => A(2),
      A3    => A(3),
      DPRA0 => DPRA(0),
      DPRA1 => DPRA(1),
      DPRA2 => DPRA(2),
      DPRA3 => DPRA(3),
      SPO   => open,
      DPO   => DPO(6) );

  iRAM7 : RAM16X1D
    port map (
      D     => D(7),
      WE    => WE,
      WCLK  => CLOCK,
      A0    => A(0),
      A1    => A(1),
      A2    => A(2),
      A3    => A(3),
      DPRA0 => DPRA(0),
      DPRA1 => DPRA(1),
      DPRA2 => DPRA(2),
      DPRA3 => DPRA(3),
      SPO   => open,
      DPO   => DPO(7) );

end RTL;
