----------------------------------------------------------------------------------
-- Company: 
-- Engineer: 
-- 
-- Create Date:    16:04:58 09/23/2009 
-- Design Name: 
-- Module Name:    UART_InstrRead - 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 UARTInstrRead is
   Port ( 
      clk : in  std_logic;
      reset : in  std_logic;
      enter_pb_not : in std_logic;
      up_pb_not: in std_logic;
      down_pb_not: in std_logic;
      left_pb_not: in std_logic;
      right_pb_not: in std_logic;
      LED_0_not : out std_logic;
      LED_1_not : out std_logic;
      LED_2_not : out std_logic;
      LED_3_not : out std_logic;
      rx_wire : in  std_logic;
      tx_wire : out  std_logic);
end UARTInstrRead;

architecture beh of UARTInstrRead is

-- State machine declarations
   type state_type is (idle, get_char, clear_chars, dump_IR_mem, wait1, convert_BTA, send, done);
   signal state_reg, state_next: state_type;

-- UART signals
   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 send_one_byte : std_logic;
   signal read_byte : std_logic;

-- Pushbutton and DEBOUNCE signals
   signal enter_pb, enter_pb_stable, enter_button_level : std_logic;
   signal up_pb, up_pb_stable, up_button_level : std_logic;
   signal down_pb, down_pb_stable, down_button_level : std_logic;
   signal left_pb, left_pb_stable, left_button_level : std_logic;
   signal right_pb, right_pb_stable, right_button_level : std_logic;

-- BinToASCII signals
   signal BTA_ready, convert_BTA_done_tick, start_BTA_convert : std_logic;
   signal BTA_OUT, DEST_OUT: 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);

-- ASCIIToBin signals
   signal A0_reg, A0_next, A1_reg, A1_next, A2_reg, A2_next, A3_reg, A3_next : std_logic_vector(7 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 ATB_out_operand: std_logic_vector(11 downto 0); 

-- State machine signals
   signal A_op_reg, A_op_next, B_op_reg, B_op_next: std_logic_vector(11 downto 0); 
   signal cnt_reg, cnt_next : unsigned(2 downto 0);

-- ALU signals
   signal add_ins, sub_ins, inv_ins, and_ins, or_ins, xor_ins, shltf_ins, shrtf_ins, skwlf_ins, skwgf_ins : std_logic;
   signal aluwf_ins, call_ins, goto_ins, retn_ins, noop_ins, skwcf_ins, movwf_ins, movfw_ins, read_ctrl, write_ctrl : std_logic;
   signal skwnf_ins, skwef_ins, movf_ins, movlw_ins : std_logic;
   signal skw_success : std_logic;

-- Instruction register signals
   signal IR_reg: std_logic_vector(11 downto 0); 
   signal d_bit : std_logic;

-- Instruction memory signals
   signal scan_in, scan_en, load_sig, scan_out, go_scan_emulation : std_logic;
   signal PC, scan_PC_reg: std_logic_vector(7 downto 0);
   signal dump_PC_reg, dump_PC_next: unsigned(7 downto 0);
-- DEBUG
   signal scan_reg: std_logic_vector(11 downto 0);

-- Scan Emulation signals
   signal ScanIRIn_reg, ScanIRIn_next : std_logic_vector(11 downto 0); 
   signal go_dump_IR_mem : std_logic;

   begin

-- Invert button value for proper operation of debounce circuit
   enter_pb <= not enter_pb_not;
   up_pb <= not up_pb_not;
   down_pb <= not down_pb_not;
   left_pb <= not left_pb_not;
   right_pb <= not right_pb_not;

-- 38400 with a 100MHz clock requires 100,000,000/(16*38400) = 163 # TICKS for stop bits (16, 24, 32 for 1, 1.5 and 2)
-- Number of addr bits for FIFO sets it's size as FIFO = 2^FIFO_NUM_ADDR_BITS
   UARTComponent: entity work.UARTUnit(beh)
      generic map (BAUDGEN_CLOCK_TO_BAUD_MOD=>163, BAUDGEN_REG_SIZE=>10, DATA_BITS=>8, STOP_BITS_TICKS=>16, FIFO_NUM_ADDR_BITS=>2)
      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=>enter_pb, button_level=>enter_button_level, button_stable=>enter_pb_stable);
   DebounceInst2: entity work.DEBOUNCE(beh) 
      port map (clk=>clk, reset=>reset, button=>up_pb, button_level=>up_button_level, button_stable=>up_pb_stable);
   DebounceInst3: entity work.DEBOUNCE(beh) 
      port map (clk=>clk, reset=>reset, button=>down_pb, button_level=>down_button_level, button_stable=>down_pb_stable);
   DebounceInst4: entity work.DEBOUNCE(beh) 
      port map (clk=>clk, reset=>reset, button=>left_pb, button_level=>left_button_level, button_stable=>left_pb_stable);
   DebounceInst5: entity work.DEBOUNCE(beh) 
      port map (clk=>clk, reset=>reset, button=>right_pb, button_level=>right_button_level, button_stable=>right_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. 
-- NOTE: I used this to debug my module by replacing BTA_OUT with other quantities I wanted to
-- monitor!!!!!
   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=>BTA_OUT, 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. Assign these to whatever signals you want to observe
   LED_0_not <= not go_dump_IR_mem; 
   LED_1_not <= '0' when scan_PC_reg = "00000000" else '1';
   LED_2_not <= not reset;
   LED_3_not <= not skw_success;

-- Instantiate the instruction decoder unit
-- This get eliminated by synthesis because the instructions movwf_ins and movfw_ins are not used yet!
   d_bit <= IR_reg(5);
   InstrDecoderUnit: entity work.InstrDecoder(beh) 
      port map (opcode=>IR_reg(11 downto 9), subcode=>IR_reg(8 downto 6), d_bit=>d_bit, prog_mode=>up_button_level,
         aluwf_ins=>aluwf_ins, movf_ins=>movf_ins, call_ins=>call_ins, movlw_ins=>movlw_ins, goto_ins=>goto_ins, 
         retn_ins=>retn_ins, noop_ins=>noop_ins, skwcf_ins=>skwcf_ins, add_ins=>add_ins, sub_ins=>sub_ins, 
         and_ins=>and_ins, or_ins=>or_ins, xor_ins=>xor_ins, inv_ins=>inv_ins, shltf_ins=>shltf_ins, shrtf_ins=>shrtf_ins,
         skwgf_ins=>skwgf_ins, skwlf_ins=>skwlf_ins, skwef_ins=>skwef_ins, skwnf_ins=>skwnf_ins, movwf_ins=>movwf_ins, 
         movfw_ins=>movfw_ins, read_ctrl=>read_ctrl, write_ctrl=>write_ctrl);

-- Instantiate the ALU component
   ALUUnit: entity work.ALU(beh) 
      port map (A=>A_op_reg, B=>B_op_reg, DEST_OUT=>DEST_OUT, skw_success=>skw_success, inv_ins=>inv_ins,
         and_ins=>and_ins, or_ins=>or_ins, xor_ins=>xor_ins, add_ins=>add_ins, sub_ins=>sub_ins, shltf_ins=>shltf_ins,
         shrtf_ins=>shrtf_ins, skwlf_ins=>skwlf_ins, skwgf_ins=>skwgf_ins, skwnf_ins=>skwnf_ins, skwef_ins=>skwef_ins,
         movf_ins=>movf_ins, movlw_ins=>movlw_ins);

-- ===================================================================================================================
-- ADD YOUR InstrMemUnit and ScanEmulation components HERE -- see lab assignment
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
-- NOTE: Use PC (NOT scan_PC_reg) as the actual port connection to PC in the InstrMemUnit, 
-- i.e., PC=>PC 
-- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!


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

   PC <= std_logic_vector(dump_PC_reg) when go_dump_IR_mem = '1' else scan_PC_reg;
   BTA_OUT <= IR_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);

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

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

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

-- local PC used when dumping instruction memory
         dump_PC_reg <= (others => '0');

-- Update registers with next state value
      elsif (clk'event and clk = '1') then
         state_reg <= state_next;
         cnt_reg <= cnt_next;

         ATB_sign_char_reg <= ATB_sign_char_next;
         A0_reg <= A0_next;
         A1_reg <= A1_next;
         A2_reg <= A2_next;
         A3_reg <= A3_next;

         A_op_reg <= A_op_next;
         B_op_reg <= B_op_next;
         ScanIRIn_reg <= ScanIRIn_next;

         dump_PC_reg <= dump_PC_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, enter_pb_stable, BTA_sign_char, 
      rx_empty, ascii_receive_data, A0_reg, A1_reg, A2_reg, A3_reg, tx_full,
      ATB_sign_char_reg, A_op_reg, B_op_reg, convert_ATB_done_tick, ATB_out_operand, ScanIRIn_reg, 
      dump_PC_reg, scan_PC_reg)
      begin

-- Default assignments to wires and registers (latter just preserves current value)
      state_next <= state_reg;
      cnt_next <= cnt_reg;
      A_op_next <= A_op_reg;
      B_op_next <= B_op_reg;
      ScanIRIn_next <= ScanIRIn_reg;

      ascii_send_data <= R0;
      send_one_byte <= '0';
      read_byte <= '0';

      ATB_sign_char_next <= ATB_sign_char_reg;
      A0_next <= A0_reg;
      A1_next <= A1_reg;
      A2_next <= A2_reg;
      A3_next <= A3_reg;
      start_ATB_convert <= '0';

      start_BTA_convert <= '0';

-- Used to start the scan emulation engine. PC register defined in the scan emulation engine by default
-- drives the PC address of the instruction memory. When we dump memory, we override this PC with a 
-- local register.
      go_scan_emulation <= '0';
      go_dump_IR_mem <= '0';
      dump_PC_next <= dump_PC_reg;

      case state_reg is

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

-- Pushbutton is pressed -- start process of reading instruction memory and writing to UART
            if (enter_pb_stable = '1') then
               state_next <= dump_IR_mem;

-- Initialize the PC to 0 and begin dumping the contents of the instruction memory
               dump_PC_next <= (others => '0');
            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 ScanIRIn and start the scan emulation state machine
            if ( convert_ATB_done_tick = '1' ) then
               ScanIRIn_next <= ATB_out_operand;
               go_scan_emulation <= '1';
            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) -- create a shift reg -- this allows the user to 
-- enter fewer than 4 number characters register
            else
               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 =>
               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: Dump IR memory 
         when dump_IR_mem =>

-- clear cnter for conversion from binary to ASCII
            cnt_next <= (others => '0');

-- Force the IR memory address to be the dump_PC_reg -- on the next rising edge,
-- the instruction on the output of the instruction memory should be given by
-- this new PC (instead of the scan_PC_reg)
            go_dump_IR_mem <= '1';

-- If these become equal, then we are done dumping the instruction memory
            if ( dump_PC_reg = unsigned(scan_PC_reg) ) then
               state_next <= done;
            else
               state_next <= wait1;
            end if;

-- ASCII OUTPUT: Two clk cycle latency on reads -- if you don't wait, you get
-- zeros!
         when wait1 =>
            go_dump_IR_mem <= '1';
            state_next <= convert_BTA;

-- ---------------------------------------
-- ASCII OUTPUT: Convert the 12-bit binary value to 4 ascii bytes
         when convert_BTA =>

-- Keep this high because we may stall on BTA_ready and BTA doesn't
-- take the snapshot until start_BTA_convert is asserted.
            go_dump_IR_mem <= '1';

-- Increment the dump_PC_reg -- start_BTA_convert will take a snapshot of the
-- instruction memory output and carry out the conversion
            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;
               dump_PC_next <= dump_PC_reg + 1;
            end if;

-- ---------------------------------------
-- ASCII OUTPUT: Load up the FIFO and let the transmitter do its work
         when send =>
            if ( tx_full = '0' ) then
               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 -- go back to dump_IR_mem state after this instruction is printed to the screen
               if ( cnt_reg = 6 ) then
                  send_one_byte <= '1';
                  ascii_send_data <= std_logic_vector(to_unsigned(13, 8));
                  state_next <= dump_IR_mem;
               end if;
            end if;

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