----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    16:04:58 09/23/2009 
-- Design Name: 
-- Module Name:    UARTUnit - beh 
-- Project Name: 
-- Target Devices: 
-- Tool versions: 
-- Description: 
--
-- Dependencies: 
--
-- Revision: 
-- Revision 0.01 - File Created
-- Additional Comments: 
--
----------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity UARTUnit is
   Generic(

-- BAUDGEN_REG_SIZE is the size of the register that holds the counter in bits.
-- BAUDGEN_CLOCK_TO_BAUD_MOD represents the modulo value, i.e., if 10, counter counts to 9 and wraps

-- FOR SIMULATION ONLY!!!!! OTHERWISE, USE ONE OF THE FOLLOWING. For simulation, also set N to 2 in
-- DEBOUNCE.vhd, i.e., Change FROM 'constant N: integer:= 22;' to 'constant N: integer:= 2;' 
--      BAUDGEN_CLOCK_TO_BAUD_MOD: integer := 2;

-- 9600 with a 100MHz clock requires 50,000,000/(16*9600) = 50,000,000/153,600 = 326
      BAUDGEN_CLOCK_TO_BAUD_MOD: integer := 326;
      BAUDGEN_REG_SIZE: integer := 10;

      DATA_BITS: integer := 8;

-- # TICKS for stop bits (16, 24, 32 for 1, 1.5 and 2)
      STOP_BITS_TICKS: integer := 16;

-- Number of addr bits for FIFO sets it's size as FIFO = 2^FIFO_NUM_ADDR_BITS
      FIFO_NUM_ADDR_BITS: integer := 4);

-- clk and reset needed for UART state machine. rd_uart causes RX FIFO to move pointer
-- to next byte in FIFO and therefore rx_loc_sys_data will change following the rising
-- edge of clock (after the propagation delay). So rd_uart is really REMOVE current
-- data on rx_loc_sys_data and go to next element.

-- tx_wire is output wire to UART connector. rx_wire is input wire from UART
   Port ( 
      clk, reset : in  std_logic;
      rd_uart, wr_uart : in  std_logic;
      rx_wire : in  std_logic;
      tx_loc_sys_data : in std_logic_vector(DATA_BITS-1 downto 0);
      tx_full, rx_empty: out std_logic;
      rx_loc_sys_data : out std_logic_vector(DATA_BITS-1 downto 0);
      tx_wire : out std_logic
   );
end UARTUnit;

architecture beh of UARTUnit is
   signal baud_clk: std_logic;
   signal rx_move_to_fifo: std_logic;
   signal fifo_to_TX_data: std_logic_vector(DATA_BITS-1 downto 0);
   signal RX_data_to_fifo: std_logic_vector(DATA_BITS-1 downto 0);
   signal tx_empty, tx_fifo_not_empty: std_logic;
   signal tx_done_tick: std_logic;

   begin

   BaudGenInst: entity work.BaudGen(beh) 
      generic map(N=>BAUDGEN_REG_SIZE, M=>BAUDGEN_CLOCK_TO_BAUD_MOD)
      port map(clk=>clk, reset=>reset, baud_tick=>baud_clk, count_out=>open);

-- Receiver -- asychronously reads bits sent to it by remote system through rx_wire. When
-- byte received to loc_sys, move data (RX_data_to_fifo) to fifo automatically (rx_move_to_fifo)
-- Note that this is safe because rx_done_tick is asserted for ONE clock cycle AFTER the
-- stop bit is completely received -- therefore the dout (byte just read) is stable and
-- ready to be written to the FIFO. 
   RX_Inst: entity work.UART_RX(beh) 
      generic map (DBIT=>DATA_BITS, STOP_BITS_TICKS=>STOP_BITS_TICKS)
      port map (clk=>clk, reset=>reset, rx=>rx_wire, baud_in=>baud_clk, rx_done_tick=>rx_move_to_fifo, 
         dout=>RX_data_to_fifo);

-- When loc_sys requests read (rd_uart), we actually move the read pointer to the next
-- element in the FIFO so r_data changes after the register propagation delay following
-- the rising edge of clock.
   FIFO_RX_Inst: entity work.FIFO(beh) 
      generic map (NUM_DATA_BITS=>DATA_BITS, NUM_ADDR_BITS=>FIFO_NUM_ADDR_BITS)
      port map (clk=>clk, reset=>reset, rd=>rd_uart, wr=>rx_move_to_fifo, 
         w_data=>RX_data_to_fifo, empty=>rx_empty, full=>open, r_data=>rx_loc_sys_data);

-- When wr_uart is asserted, current byte on tx_loc_sys_data is written into the FIFO at the
-- current value of the w_ptr_data pointer and THEN the pointer is advanced to the next empty
-- element in the FIFO. The data on the output of the RX unit (rx_loc_sys_data) is latched on
-- the rising edge (good thing, because it is about to change). The write does NOT occur if 
-- tx_full is asserted. 
-- Also, fifo_to_TX_data does NOT change until after the clock edge. The tx_empty signal goes to 0 
-- after the byte is written to the FIFO. This value is inverted and then used to tell the transmitter
-- to send a value. If more than one data value exists in FIFO, after TX transmits a byte,
-- it pulses tx_done_tick which causes the FIFO to put the next value on fifo_to_TX_data 
-- and the process starts again. 
   FIFO_TX_Inst: entity work.FIFO(beh) 
      generic map (NUM_DATA_BITS=>DATA_BITS, NUM_ADDR_BITS=>FIFO_NUM_ADDR_BITS)
      port map (clk=>clk, reset=>reset, rd=>tx_done_tick, wr=>wr_uart, 
         w_data=>tx_loc_sys_data, empty=>tx_empty, full=>tx_full, r_data=>fifo_to_TX_data);

   tx_fifo_not_empty <= not tx_empty;

-- Transmitter -- synchronously send bits to remote system through tx_wire. tx_done_tick is pulsed
-- when the transmission is complete.
   TX_Inst: entity work.UART_TX(beh) 
      generic map(DBIT=>DATA_BITS, STOP_BITS_TICKS=>STOP_BITS_TICKS)
      port map(clk=>clk, reset=>reset, tx_start=>tx_fifo_not_empty, baud_in=>baud_clk, din=>fifo_to_TX_data, 
         tx_done_tick=>tx_done_tick, tx=>tx_wire);

end beh;

