--------------------------------------------------------------------------------
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
--use IEEE.NUMERIC_STD.ALL;
--------------------------------------------------------------------------------
ENTITY Encoder_input IS
  PORT(
    clk             : IN  STD_LOGIC;
    ROT_A           : IN  STD_LOGIC;
    ROT_B           : IN  STD_LOGIC;
    ROT_CENTRE      : IN  STD_LOGIC;
    Operand_1       : OUT STD_LOGIC_VECTOR (3 DOWNTO 0);
    Operand_2       : OUT STD_LOGIC_VECTOR (3 DOWNTO 0));
END Encoder_input;
--------------------------------------------------------------------------------
ARCHITECTURE Behavioral OF Encoder_input IS
--------------------------------------------------------------------------------

  SIGNAL rotary_a_in        : STD_LOGIC := '0';
  SIGNAL rotary_b_in        : STD_LOGIC := '0';
  SIGNAL rotary_in          : STD_LOGIC_VECTOR(1 DOWNTO 0)  := "00";
  SIGNAL rotary_q1          : STD_LOGIC := '0';
  SIGNAL rotary_q2          : STD_LOGIC := '0';
  SIGNAL delay_rotary_q1    : STD_LOGIC := '0';
  SIGNAL rotary_event       : STD_LOGIC := '0';
  SIGNAL rotary_left        : STD_LOGIC := '0';

  SIGNAL cnt_EN_1M          : STD_LOGIC_VECTOR(5 DOWNTO 0)  := (OTHERS => '0');
  SIGNAL clk_EN_1M          : STD_LOGIC := '0';

  SIGNAL cnt_EN_1k          : STD_LOGIC_VECTOR(9 DOWNTO 0)  := (OTHERS => '0');
  SIGNAL clk_EN_1k          : STD_LOGIC := '0';

  SIGNAL cnt_debounce       : STD_LOGIC_VECTOR(3 DOWNTO 0)  := (OTHERS => '0');
  SIGNAL ROT_CENTRE_i       : STD_LOGIC := '0';
  SIGNAL ROT_CENTRE_ii      : STD_LOGIC := '0';
  SIGNAL ROT_CENTRE_o       : STD_LOGIC := '0';
  SIGNAL ROT_CENTRE_oo      : STD_LOGIC := '0';

  SIGNAL operand_select     : STD_LOGIC := '0';
  SIGNAL operand_1_i        : STD_LOGIC_VECTOR(3 DOWNTO 0)  := "0000";
  SIGNAL operand_2_i        : STD_LOGIC_VECTOR(3 DOWNTO 0)  := "0000";


--------------------------------------------------------------------------------
BEGIN
--------------------------------------------------------------------------------

  ------------------------------------------------------------------------------
  -- Interface to rotary encoder.
  -- Detection of movement and direction.
  ------------------------------------------------------------------------------
  --
  -- The rotary switch contacts are filtered using their offset (one-hot) style
  -- to clean them. Circuit concept by Peter Alfke.
  -- Note that the clock rate is fast compared with the switch rate.

  rotary_filter: PROCESS(clk) BEGIN
    IF rising_edge(clk) THEN

      --Synchronise inputs to clock domain using flip-flops in input/output blocks.
      rotary_a_in       <= ROT_A;
      rotary_b_in       <= ROT_B;

      --concatinate rotary input signals to form vector for case construct.
      rotary_in         <= rotary_b_in & rotary_a_in;

      CASE rotary_in IS

        WHEN   "00" => rotary_q1 <= '0';         
                       rotary_q2 <= rotary_q2;
              
        WHEN   "01" => rotary_q1 <= rotary_q1;
                       rotary_q2 <= '0';
              
        WHEN   "10" => rotary_q1 <= rotary_q1;
                       rotary_q2 <= '1';
              
        WHEN   "11" => rotary_q1 <= '1';
                       rotary_q2 <= rotary_q2; 

        WHEN OTHERS => rotary_q1 <= rotary_q1; 
                       rotary_q2 <= rotary_q2; 
      END CASE;

    END IF;
  END PROCESS rotary_filter;

  --
  -- The rising edges of 'rotary_q1' indicate that a rotation has occurred and the 
  -- state of 'rotary_q2' at that time will indicate the direction. 
  --
  direction: PROCESS(clk) BEGIN
    IF rising_edge(clk) THEN

      delay_rotary_q1 <= rotary_q1;

      IF rotary_q1='1' AND delay_rotary_q1='0' THEN
        rotary_event  <= '1';
        rotary_left   <= rotary_q2;
      ELSE
        rotary_event  <= '0';
        rotary_left   <= rotary_left;
      END IF;

    END IF;
  END PROCESS direction;


  --
  -- Clock enable signal generator for debouncer
  --

  clk_EN_gen: PROCESS(clk) BEGIN
    IF rising_edge(clk) THEN

      IF cnt_EN_1M = CONV_STD_LOGIC_VECTOR(49,6) THEN
        cnt_EN_1M <= (OTHERS => '0');
        clk_EN_1M <= '1';
      ELSE
        cnt_EN_1M <= cnt_EN_1M + 1;
        clk_EN_1M <= '0';
      END IF;

      IF clk_EN_1M = '1' THEN
        IF cnt_EN_1k = CONV_STD_LOGIC_VECTOR(999,10) THEN
          cnt_EN_1k <= (OTHERS => '0');
          clk_EN_1k <= '1';
        ELSE
          cnt_EN_1k <= cnt_EN_1k + 1;
          clk_EN_1k <= '0';
        END IF;
      END IF;

    END IF;
  END PROCESS clk_EN_gen;



  debouncer: PROCESS(clk) BEGIN
    IF rising_edge(clk) THEN
      ROT_CENTRE_i  <= ROT_CENTRE;
      IF clk_EN_1k = '1' THEN
        ROT_CENTRE_ii <= ROT_CENTRE_i;
        IF cnt_debounce(cnt_debounce'HIGH) = '1' THEN       -- update enabled?
          IF NOT (ROT_CENTRE_ii = ROT_CENTRE_i) THEN        -- input changed?
            ROT_CENTRE_o <= ROT_CENTRE_i;                   -- update
            cnt_debounce <= (OTHERS => '0');                -- disable update
          END IF;
        ELSE
          cnt_debounce <= cnt_debounce + 1;                 -- wait
        END IF;
      END IF;
    END IF;
  END PROCESS debouncer;


  ------------------------------------------------------------------------------
  -- Interface to rotary encoder.
  -- Detection of movement and direction.
  ------------------------------------------------------------------------------

  operand_set: PROCESS(clk) BEGIN
    IF rising_edge(clk) THEN


      -- press detector / operand changer
      ROT_CENTRE_oo <= ROT_CENTRE_o;
      IF ROT_CENTRE_o = '1' AND ROT_CENTRE_oo = '0' THEN
        operand_select <= NOT operand_select;
      END IF;


      -- openard 1 counter
      IF operand_select = '0' THEN
        IF rotary_event = '1' THEN
          IF rotary_left = '0' THEN
            operand_1_i <= operand_1_i - 1;
          ELSE
            operand_1_i <= operand_1_i + 1;
          END IF;
        END IF;

      -- operand 2 counter
      ELSE
        IF rotary_event = '1' THEN
          IF rotary_left = '0' THEN
            operand_2_i <= operand_2_i - 1;
          ELSE
            operand_2_i <= operand_2_i + 1;
          END IF;
        END IF;
      END IF;


    END IF;
  END PROCESS operand_set;

  Operand_1 <= operand_1_i;
  Operand_2 <= operand_2_i;

--------------------------------------------------------------------------------
END Behavioral;
--------------------------------------------------------------------------------
