----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    16:04:58 09/23/2009 
-- Design Name: 
-- Module Name:    UARTNumberConvert - 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;
use IEEE.NUMERIC_STD.all;

-- clk and reset needed for UART state machine. tx_wire is output wire to UART connector. 
-- rx_wire is input wire from UART connector. Enter pushbutton is used to send result to
-- screen, other pushbuttons to be connected to microcontroller instructions. LEDs are
-- just for fun -- use them any way you want
entity UARTNumberConvert is
   Port ( 
      clk : in  std_logic;
      reset : in  std_logic;
      but1 : in std_logic;
      but2: in std_logic;
      but3: in std_logic;
      LED_0 : out std_logic;
      LED_1 : out std_logic;
      LED_2 : out std_logic;
      LED_3 : out std_logic;
      rx_wire : in  std_logic;
      tx_wire : out  std_logic);
end UARTNumberConvert;

architecture beh of UARTNumberConvert is
   signal tx_full, rx_empty : std_logic;
   signal ascii_receive_data: std_logic_vector(7 downto 0);
   signal ascii_send_data: std_logic_vector(7 downto 0);
   signal but1_pb, but1_pb_stable, but1_button_level : std_logic;
   signal but2_pb, but2_pb_stable, but2_button_level : std_logic;
   signal but3_pb, but3_pb_stable, but3_button_level : std_logic;
   signal send_one_byte : std_logic;

   signal BTA_ready, convert_BTA_done_tick, start_BTA_convert : std_logic;
   signal result_binary_val: std_logic_vector(11 downto 0); 
   signal R0, R1, R2, R3 : std_logic_vector(7 downto 0);
   signal BTA_sign_char: std_logic_vector(7 downto 0);

   signal A0_reg, A0_next, A1_reg, A1_next, A2_reg, A2_next, A3_reg, A3_next : std_logic_vector(7 downto 0);
   signal A_op_reg, A_op_next, B_op_reg, B_op_next, ATB_out_operand: std_logic_vector(11 downto 0); 
   signal ATB_sign_char_reg, ATB_sign_char_next: std_logic_vector(7 downto 0);
   signal ATB_ready, convert_ATB_done_tick, start_ATB_convert : std_logic;
   signal char_ptr_reg, char_ptr_next : unsigned(1 downto 0);
   signal A_or_B_reg, A_or_B_next : std_logic;
   signal read_byte : std_logic;

   type state_type is (idle, convert_BTA, get_char, clear_chars, send, done);
   signal state_reg, state_next: state_type;
   signal cnt_reg, cnt_next : unsigned(2 downto 0);

   begin

   UARTComponent: entity work.UARTUnit(beh)
      port map ( clk=>clk, reset=>reset, rd_uart=>read_byte, wr_uart=>send_one_byte, rx_wire=>rx_wire,
         tx_loc_sys_data=>ascii_send_data, tx_full=>tx_full, rx_empty=>rx_empty, rx_loc_sys_data=>ascii_receive_data, tx_wire=>tx_wire);

-- Take the bounce out of the pushbutton (only works for buttons normally at 0 and then go to 1 when pressed!)
   DebounceInst1: entity work.DEBOUNCE(beh) 
      port map (clk=>clk, reset=>reset, button=>but1, button_level=>but1_button_level, button_stable=>but1_pb_stable);
   DebounceInst2: entity work.DEBOUNCE(beh) 
      port map (clk=>clk, reset=>reset, button=>but2, button_level=>but2_button_level, button_stable=>but2_pb_stable);
   DebounceInst3: entity work.DEBOUNCE(beh) 
      port map (clk=>clk, reset=>reset, button=>but3, button_level=>but3_button_level, button_stable=>but3_pb_stable);

-- BinToASCII is called to convert a binary number to ASCII with start_BTA_convert. Since numbers
-- are 12 bits, this restricts there size to be between -2048 and +2047 -- the sign plus 4 digits. 
   BTAInst: entity work.BinToASCII(beh) 
      port map (clk=>clk, reset=>reset, ready=>BTA_ready, convert_done_tick=>convert_BTA_done_tick, start_convert=>start_BTA_convert,
         result_binary_val=>result_binary_val, dig0=>R0, dig1=>R1, dig2=>R2, dig3=>R3, sign_char=>BTA_sign_char);

-- ASCIIToBin converts an ASCII string (terminated with a carriage return) into a binary value.
   ATBInst: entity work.ASCIIToBin(beh) 
      port map (clk=>clk, reset=>reset, ready=>ATB_ready, convert_done_tick=>convert_ATB_done_tick, start_convert=>start_ATB_convert,
         op_binary_val=>ATB_out_operand, dig0=>A0_reg, dig1=>A1_reg, dig2=>A2_reg, dig3=>A3_reg, sign_char=>ATB_sign_char_reg);

-- LED is on when '0' is written. If rx_empty is '1' (NO DATA), then it writes a 0 turning ON the led.
   LED_0 <= A_or_B_reg;
   LED_1 <= reset;
   LED_2 <= tx_full;
   LED_3 <= rx_empty;

-- =====================================================================================================
   result_binary_val <= std_logic_vector(signed(A_op_reg) + signed(B_op_reg));
-- =====================================================================================================

-- =====================================================================================================
-- =====================================================================================================
-- =====================================================================================================

-- state and register logic
   process(clk, reset)
      begin
      if (reset = '1') then
         state_reg <= idle;

-- This counter used when outputting the result -- counts from 0 to 6, which includes sign
-- character, 4 digits of number and carriage return and line feed
         cnt_reg <= to_unsigned(0, 3);

-- Determine which operand (A, B) is loaded with the next set of chars typed by the user.
-- Initialize with 1 because this register is inverted in 'clear_chars' state BEFORE the
-- actual conversion is completed. So it will be inverted to '0' (the A_op_reg) after the
-- user enters the FIRST number.
         A_or_B_reg <= '1';

-- One ASCII character at a time is read and processed -- this keeps track of which character
-- is being entered (1 of 4 positions).
         char_ptr_reg <= to_unsigned(0, 2);

-- The actual character registers
         A0_reg <= (others => '0');
         A1_reg <= (others => '0');
         A2_reg <= (others => '0');
         A3_reg <= (others => '0');

-- The sign register
         ATB_sign_char_reg <= (others => '0');

-- The converted binary operands
         A_op_reg <= (others => '0');
         B_op_reg <= (others => '0');

-- Update registers with next state value
      elsif (clk'event and clk = '1') then
         state_reg <= state_next;
         cnt_reg <= cnt_next;
         A_or_B_reg <= A_or_B_next;
         char_ptr_reg <= char_ptr_next;
         A0_reg <= A0_next;
         A1_reg <= A1_next;
         A2_reg <= A2_next;
         A3_reg <= A3_next;
         ATB_sign_char_reg <= ATB_sign_char_next;
         A_op_reg <= A_op_next;
         B_op_reg <= B_op_next;
      end if; 
   end process;

-- =====================================================================================================
-- Combinational Block -- little more involved than I would have liked it but manageable
-- =====================================================================================================

   process(state_reg, cnt_reg, R0, R1, R2, R3, BTA_ready, convert_BTA_done_tick, but1_pb_stable, BTA_sign_char, 
      A_or_B_reg, char_ptr_reg, rx_empty, ascii_receive_data, A0_reg, A1_reg, A2_reg, A3_reg, 
      ATB_sign_char_reg, A_op_reg, B_op_reg, convert_ATB_done_tick, ATB_out_operand) 
      begin

-- Default assignments to wires and registers (latter just preserves current value)
      state_next <= state_reg;
      cnt_next <= cnt_reg;
      ascii_send_data <= R0;
      start_BTA_convert <= '0';
      send_one_byte <= '0';
      A_or_B_next <= A_or_B_reg;
      char_ptr_next <= char_ptr_reg;
      A0_next <= A0_reg;
      A1_next <= A1_reg;
      A2_next <= A2_reg;
      A3_next <= A3_reg;
      ATB_sign_char_next <= ATB_sign_char_reg;
      A_op_next <= A_op_reg;
      B_op_next <= B_op_reg;
      start_ATB_convert <= '0';
      read_byte <= '0';

      case state_reg is

-- ---------------------------------------
-- ASCII INPUT AND OUTPUT
         when idle =>

-- Pushbutton is pressed -- start process of conversion from binary to ASCII, and then
-- transmit the bytes through UART to screen
            if (but1_pb_stable = '1') then

-- initialize cnter and start conversion
               cnt_next <= (others => '0');
               state_next <= convert_BTA;
            end if;

-- If the receive buffer has a character, process it
            if (rx_empty = '0') then

-- Skip all characters that are not valid -- only valid chars are carriage return, '-' and '+',
-- and numbers '0' through '9'. If character is left over linefeed -- eliminate it -- under 
-- Linux, this doesn't happen 
               if ( unsigned(ascii_receive_data) = to_unsigned(13, 8) or
                    unsigned(ascii_receive_data) = to_unsigned(character'pos('-'), 8) or
                    unsigned(ascii_receive_data) = to_unsigned(character'pos('+'), 8) or
                    (unsigned(ascii_receive_data) >= to_unsigned(character'pos('0'), 8) and
                     unsigned(ascii_receive_data) <= to_unsigned(character'pos('9'), 8))) then
                  state_next <= get_char;
               else
                  read_byte <= '1';
               end if;
            end if;

-- Monitor for the convert_ATB_done_tick which indicates that a set of characters have
-- been converted to a binary value. Save the ASCIIToBin output value (ATB_out_operand)
-- into either the A_op or B_op, depending on the value of A_or_B_reg
            if ( convert_ATB_done_tick = '1' ) then
               if ( A_or_B_reg = '0' ) then
                  A_op_next <= ATB_out_operand;
               else 
                  B_op_next <= ATB_out_operand;
               end if;
            end if;

-- ---------------------------------------
-- ASCII INPUT: If the user presses a number key, get character and save in one of four A registers
-- Also, process '+', '-' and carriage return (13)
         when get_char =>

-- Check if the received value is a carriage return -- linefeed are filtered above in idle
            if ( unsigned(ascii_receive_data) = to_unsigned(13, 8) ) then 

-- Start ascii to binary conversion -- I should check ATB_ready before I do this but
-- I assume there will be plenty of cycles between carriage returns
               start_ATB_convert <= '1';

-- Last thing to do is clear registers for next operand
               state_next <= clear_chars;

-- If the character received in a '+' or '-', save sign.
            elsif ( unsigned(ascii_receive_data) = to_unsigned(character'pos('-'), 8) or
                 unsigned(ascii_receive_data) = to_unsigned(character'pos('+'), 8)) then 
               ATB_sign_char_next <= ascii_receive_data;
               state_next <= idle;

-- Else assume it is a number character (0 to 9) -- save it into a corresponding character
-- register
            else
               char_ptr_next <= char_ptr_reg + 1;

-- Create a shift reg -- this allows the user to enter fewer than 4 number characters
               A3_next <= A2_reg;
               A2_next <= A1_reg;
               A1_next <= A0_reg;
               A0_next <= ascii_receive_data;
               state_next <= idle;
            end if;

-- Remove the byte from the read buffer
            read_byte <= '1';

-- ---------------------------------------
-- ASCII INPUT: Reset pointer and character registers after the ATB routine has latched and processed
-- This gets called one clock cycle AFTER carriage return is received
         when clear_chars =>
               A_or_B_next <= not A_or_B_reg;
               char_ptr_next <= to_unsigned(0, 2);
               A0_next <= (others => '0');
               A1_next <= (others => '0');
               A2_next <= (others => '0');
               A3_next <= (others => '0');
               ATB_sign_char_next <= std_logic_vector(to_unsigned(character'pos('+'), 8));
               state_next <= idle;

-- ---------------------------------------
-- ---------------------------------------
-- ASCII OUTPUT: Convert the 12-bit binary value to 4 ascii bytes
         when convert_BTA =>
            if ( BTA_ready = '1' ) then
               start_BTA_convert <= '1';
            end if;

-- Wait for conversion from binary to ASCII to finish
            if ( convert_BTA_done_tick = '1' ) then
               state_next <= send;
            end if;

-- ---------------------------------------
-- ASCII OUTPUT: Load up the FIFO and let the transmitter do its work
         when send =>
            cnt_next <= cnt_reg + 1;

-- sign bit is first.
            if ( cnt_reg = 0 ) then
               send_one_byte <= '1';
               ascii_send_data <= BTA_sign_char;
            end if;

-- followed by 4 digits
            if ( cnt_reg = 1 ) then
               send_one_byte <= '1';
               ascii_send_data <= R3;
            end if;
            if ( cnt_reg = 2 ) then
               send_one_byte <= '1';
               ascii_send_data <= R2;
            end if;
            if ( cnt_reg = 3 ) then
               send_one_byte <= '1';
               ascii_send_data <= R1;
            end if;
            if ( cnt_reg = 4 ) then
               send_one_byte <= '1';
               ascii_send_data <= R0;
            end if;

-- carriage return
            if ( cnt_reg = 5 ) then
               send_one_byte <= '1';
               ascii_send_data <= std_logic_vector(to_unsigned(10, 8));
            end if;

-- line feed -- exit after the last byte is transmitted
            if ( cnt_reg = 6 ) then
               send_one_byte <= '1';
               ascii_send_data <= std_logic_vector(to_unsigned(13, 8));
               state_next <= done;
            end if;

-- ---------------------------------------
-- ASCII OUTPUT: Wait for the user to release the button
         when done =>
            if (but1_pb_stable = '0') then
               state_next <= idle;
            end if;
      end case;
   end process;
end beh;
