-----------------------------------------------------------------------
-- PicoBlaze IRQ controller
--
--  Vylepsena verze
--
-- Any interrupt comming to this block (int_1 to int_8) is converted to
-- one interrupt signal connected to PicoBlaze processor. The address of
-- active interrupt input is stored in data register available on address
-- given by the INT_controler generic parameter "data_port_ID".
-- There is a priority on interrupt inputs. If there are
-- several concurrent requests at one time, the one with lower number
-- will be reported to the processor first.
--
-- The request is not cleared by int_ACK signal from processor, but by
-- reading value of the active interrupt vector from the register.
--
-- The priority encoding has the following rule:
--  > The first interrupt comming to the controller is reported to the processor.
--  > If there are more at once and no one is reported to processor, the one with
--    highest priority is forwarded.
--  > If there is an active interrupt, other new interrupts must wait until
--    the active one is serviced (once) no matter how high their priority is.
--
--
-- Status:
--  COMPLETE        YES
--  SIMULATED       YES
--  TESTED          YES
--
--
-- int_en1_port_ID
-- [0] enable interrupt 1
-- [1] enable interrupt 2
-- [2] enable interrupt 3
-- [3] enable interrupt 4
-- [4] enable interrupt 5
-- [5] enable interrupt 6
-- [6] enable interrupt 7
-- [7] enable interrupt 8
--
--  int_edg1_port_ID
-- [0] interrupt 1 edge sensitive (when H)
-- [1] interrupt 2 edge sensitive (when H)
-- [2] interrupt 3 edge sensitive (when H)
-- [3] interrupt 4 edge sensitive (when H)
-- [4] interrupt 5 edge sensitive (when H)
-- [5] interrupt 6 edge sensitive (when H)
-- [6] interrupt 7 edge sensitive (when H)
-- [7] interrupt 8 edge sensitive (when H)
--
--#####################################################################
library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_ARITH.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;
library UNISIM;
use UNISIM.Vcomponents.ALL;
--#####################################################################
ENTITY INT_controler_8 IS
  GENERIC(
    data_port_ID:       STD_LOGIC_VECTOR(7 DOWNTO 0):= "00000010";  -- Interrupt number register port ID
    int_en1_port_ID:    STD_LOGIC_VECTOR(7 DOWNTO 0):= "00000100";  -- Interrupt Enable status/control port ID
    int_edge1_port_ID:  STD_LOGIC_VECTOR(7 DOWNTO 0):= "00001000"); -- Interrupt Edge status/control port ID
  PORT(
    clk:            IN  STD_LOGIC;
    rst:            IN  STD_LOGIC;

    -- interrupt inputs
    int_1:          IN  STD_LOGIC;      -- highest priority
    int_2:          IN  STD_LOGIC;
    int_3:          IN  STD_LOGIC;
    int_4:          IN  STD_LOGIC;
    int_5:          IN  STD_LOGIC;
    int_6:          IN  STD_LOGIC;
    int_7:          IN  STD_LOGIC;
    int_8:          IN  STD_LOGIC;      -- lowest priority

    -- processor interface
    Data_out:       OUT STD_LOGIC_VECTOR(7 DOWNTO 0);   -- Interrupt number OUT (PicoBlaze IN)
    int_en_out_1:   OUT STD_LOGIC_VECTOR(7 DOWNTO 0);   -- Enable status reg OUT (PicoBlaze IN)
    int_edge_out_1: OUT STD_LOGIC_VECTOR(7 DOWNTO 0);   -- Edge status reg OUT (PicoBlaze IN)
    Data_in:        IN  STD_LOGIC_VECTOR(7 DOWNTO 0);   -- Data IN (PicoBlaze OUT)
    Address:        IN  STD_LOGIC_VECTOR(7 DOWNTO 0);   -- Address IN (PicoBlaze Port ID OUT)
    read_strobe:    IN  STD_LOGIC;                      -- PicoBlaze read strobe
    write_strobe:   IN  STD_LOGIC;                      -- PicoBlaze write strobe
    proc_int:       OUT STD_LOGIC;                      -- PicoBlaze interrupt IN
    proc_int_ack:   IN  STD_LOGIC                       -- PicoBlaze interrupt ACK OUT
    );
END INT_controler_8;
--#####################################################################
ARCHITECTURE Behavioral OF INT_controler_8 IS
--#####################################################################
  SIGNAL int_1i:        STD_LOGIC := '0';       -- latched interrupt 1
  SIGNAL int_2i:        STD_LOGIC := '0';       -- latched interrupt 2
  SIGNAL int_3i:        STD_LOGIC := '0';       -- latched interrupt 3
  SIGNAL int_4i:        STD_LOGIC := '0';       -- latched interrupt 4
  SIGNAL int_5i:        STD_LOGIC := '0';       -- latched interrupt 5
  SIGNAL int_6i:        STD_LOGIC := '0';       -- latched interrupt 6
  SIGNAL int_7i:        STD_LOGIC := '0';       -- latched interrupt 7
  SIGNAL int_8i:        STD_LOGIC := '0';       -- latched interrupt 8

  SIGNAL int_1b:        STD_LOGIC := '0';       -- buffered interrupt 1
  SIGNAL int_2b:        STD_LOGIC := '0';       -- buffered interrupt 2
  SIGNAL int_3b:        STD_LOGIC := '0';       -- buffered interrupt 3
  SIGNAL int_4b:        STD_LOGIC := '0';       -- buffered interrupt 4
  SIGNAL int_5b:        STD_LOGIC := '0';       -- buffered interrupt 5
  SIGNAL int_6b:        STD_LOGIC := '0';       -- buffered interrupt 6
  SIGNAL int_7b:        STD_LOGIC := '0';       -- buffered interrupt 7
  SIGNAL int_8b:        STD_LOGIC := '0';       -- buffered interrupt 8

  SIGNAL proc_int_i:    STD_LOGIC := '0';       -- internal interrupt signal
  SIGNAL int_shreg:     STD_LOGIC_VECTOR(7 DOWNTO 0):= X"00";       -- shift register to delay new interrupt request
                                                                    -- so that picoblaze can disable it (by instruction)

  SIGNAL Data_int:      STD_LOGIC_VECTOR(7 DOWNTO 0):= X"00";

  SIGNAL int_en_1:      STD_LOGIC_VECTOR(7 DOWNTO 0):= X"01";   -- Interrupt enable
  SIGNAL int_edge_1:    STD_LOGIC_VECTOR(7 DOWNTO 0):= X"FE";   -- Interrupt edge sensitive
--#####################################################################
BEGIN
--#####################################################################
-- interrupt processing
  int_proc: PROCESS(clk, rst) BEGIN
  ---------------------------------------------------------------------
    IF rst = '1' THEN
      int_1i <= '0';
      int_2i <= '0';
      int_3i <= '0';
      int_4i <= '0';
      int_5i <= '0';
      int_6i <= '0';
      int_7i <= '0';
      int_8i <= '0';
    ELSIF clk'EVENT AND clk = '1' THEN
  ---------------------------------------------------------------------
      -- buffering interrupt signals for edge detection
      int_1b <= int_1;
      int_2b <= int_2;
      int_3b <= int_3;
      int_4b <= int_4;
      int_5b <= int_5;
      int_6b <= int_6;
      int_7b <= int_7;
      int_8b <= int_8;
      -----------------------------------------------------------------
      -----------------------------------------------------------------
    -- Interrupt channel #1
      -- Interrupt enabled
      IF int_en_1(0) = '1' THEN
        -- edge sensitive interrupt
        IF int_edge_1(0) = '1' THEN

          -- when processor is reading interrupt vector
          IF Address = data_port_ID AND read_strobe = '1' AND data_int = X"01" THEN
            int_1i <= '0';          -- clear interrupt request
          -- interrupt signal rising edge detection
          ELSIF int_1 = '1' AND int_1b = '0' THEN
            int_1i <= '1';
          END IF;

        -- level sensitive interrupt
        ELSE
          -- forward value of interrupt request;
          -- request is cleared only by reading data register!!!
          IF int_1 = '1' THEN
            int_1i <= '1';
          -- when processor is reading interrupt vector
          ELSIF Address = data_port_ID AND read_strobe = '1' AND data_int = X"01" THEN
            int_1i <= '0';          -- clear interrupt request
          END IF;
        END IF;

      -- when interrupt is disabled
      ELSE
        int_1i <= '0';
      END IF;
      -----------------------------------------------------------------
    -- Interrupt channel #2
      -- Interrupt enabled
      IF int_en_1(1) = '1' THEN
        -- edge sensitive interrupt
        IF int_edge_1(1) = '1' THEN

          -- when processor is reading interrupt vector
          IF Address = data_port_ID AND read_strobe = '1' AND data_int = X"02" THEN
            int_2i <= '0';          -- clear interrupt request
          -- interrupt signal rising edge detection
          ELSIF int_2 = '1' AND int_2b = '0' THEN
            int_2i <= '1';
          END IF;

        -- level sensitive interrupt
        ELSE
          -- forward value of interrupt request;
          -- request is cleared only by reading data register!!!
          IF int_2 = '1' THEN
            int_2i <= '1';
          -- when processor is reading interrupt vector
          ELSIF Address = data_port_ID AND read_strobe = '1' AND data_int = X"02" THEN
            int_2i <= '0';          -- clear interrupt request
          END IF;
        END IF;

      -- when interrupt is disabled
      ELSE
        int_2i <= '0';
      END IF;
      -----------------------------------------------------------------
    -- Interrupt channel #3
      -- Interrupt enabled
      IF int_en_1(2) = '1' THEN
        -- edge sensitive interrupt
        IF int_edge_1(2) = '1' THEN

          -- when processor is reading interrupt vector
          IF Address = data_port_ID AND read_strobe = '1' AND data_int = X"03" THEN
            int_3i <= '0';          -- clear interrupt request
          -- interrupt signal rising edge detection
          ELSIF int_3 = '1' AND int_3b = '0' THEN
            int_3i <= '1';
          END IF;

        -- level sensitive interrupt
        ELSE
          -- forward value of interrupt request;
          -- request is cleared only by reading data register!!!
          IF int_3 = '1' THEN
            int_3i <= '1';
          -- when processor is reading interrupt vector
          ELSIF Address = data_port_ID AND read_strobe = '1' AND data_int = X"03" THEN
            int_3i <= '0';          -- clear interrupt request
          END IF;
        END IF;

      -- when interrupt is disabled
      ELSE
        int_3i <= '0';
      END IF;
      -----------------------------------------------------------------
    -- Interrupt channel #4
      -- Interrupt enabled
      IF int_en_1(3) = '1' THEN
        -- edge sensitive interrupt
        IF int_edge_1(3) = '1' THEN

          -- when processor is reading interrupt vector
          IF Address = data_port_ID AND read_strobe = '1' AND data_int = X"04" THEN
            int_4i <= '0';          -- clear interrupt request
          -- interrupt signal rising edge detection
          ELSIF int_4 = '1' AND int_4b = '0' THEN
            int_4i <= '1';
          END IF;

        -- level sensitive interrupt
        ELSE
          -- forward value of interrupt request;
          -- request is cleared only by reading data register!!!
          IF int_4 = '1' THEN
            int_4i <= '1';
          -- when processor is reading interrupt vector
          ELSIF Address = data_port_ID AND read_strobe = '1' AND data_int = X"04" THEN
            int_4i <= '0';          -- clear interrupt request
          END IF;
        END IF;

      -- when interrupt is disabled
      ELSE
        int_4i <= '0';
      END IF;
      -----------------------------------------------------------------
    -- Interrupt channel #5
      -- Interrupt enabled
      IF int_en_1(4) = '1' THEN
        -- edge sensitive interrupt
        IF int_edge_1(4) = '1' THEN

          -- when processor is reading interrupt vector
          IF Address = data_port_ID AND read_strobe = '1' AND data_int = X"05" THEN
            int_5i <= '0';          -- clear interrupt request
          -- interrupt signal rising edge detection
          ELSIF int_5 = '1' AND int_5b = '0' THEN
            int_5i <= '1';
          END IF;

        -- level sensitive interrupt
        ELSE
          -- forward value of interrupt request;
          -- request is cleared only by reading data register!!!
          IF int_5 = '1' THEN
            int_5i <= '1';
          -- when processor is reading interrupt vector
          ELSIF Address = data_port_ID AND read_strobe = '1' AND data_int = X"05" THEN
            int_5i <= '0';          -- clear interrupt request
          END IF;
        END IF;

      -- when interrupt is disabled
      ELSE
        int_5i <= '0';
      END IF;
      -----------------------------------------------------------------
    -- Interrupt channel #6
      -- Interrupt enabled
      IF int_en_1(5) = '1' THEN
        -- edge sensitive interrupt
        IF int_edge_1(5) = '1' THEN

          -- when processor is reading interrupt vector
          IF Address = data_port_ID AND read_strobe = '1' AND data_int = X"06" THEN
            int_6i <= '0';          -- clear interrupt request
          -- interrupt signal rising edge detection
          ELSIF int_6 = '1' AND int_6b = '0' THEN
            int_6i <= '1';
          END IF;

        -- level sensitive interrupt
        ELSE
          -- forward value of interrupt request;
          -- request is cleared only by reading data register!!!
          IF int_6 = '1' THEN
            int_6i <= '1';
          -- when processor is reading interrupt vector
          ELSIF Address = data_port_ID AND read_strobe = '1' AND data_int = X"06" THEN
            int_6i <= '0';          -- clear interrupt request
          END IF;
        END IF;

      -- when interrupt is disabled
      ELSE
        int_6i <= '0';
      END IF;
      -----------------------------------------------------------------
    -- Interrupt channel #7
      -- Interrupt enabled
      IF int_en_1(6) = '1' THEN
        -- edge sensitive interrupt
        IF int_edge_1(6) = '1' THEN

          -- when processor is reading interrupt vector
          IF Address = data_port_ID AND read_strobe = '1' AND data_int = X"07" THEN
            int_7i <= '0';          -- clear interrupt request
          -- interrupt signal rising edge detection
          ELSIF int_7 = '1' AND int_7b = '0' THEN
            int_7i <= '1';
          END IF;

        -- level sensitive interrupt
        ELSE
          -- forward value of interrupt request;
          -- request is cleared only by reading data register!!!
          IF int_7 = '1' THEN
            int_7i <= '1';
          -- when processor is reading interrupt vector
          ELSIF Address = data_port_ID AND read_strobe = '1' AND data_int = X"07" THEN
            int_7i <= '0';          -- clear interrupt request
          END IF;
        END IF;

      -- when interrupt is disabled
      ELSE
        int_7i <= '0';
      END IF;
      -----------------------------------------------------------------
    -- Interrupt channel #8
      -- Interrupt enabled
      IF int_en_1(7) = '1' THEN
        -- edge sensitive interrupt
        IF int_edge_1(7) = '1' THEN

          -- when processor is reading interrupt vector
          IF Address = data_port_ID AND read_strobe = '1' AND data_int = X"08" THEN
            int_8i <= '0';          -- clear interrupt request
          -- interrupt signal rising edge detection
          ELSIF int_8 = '1' AND int_8b = '0' THEN
            int_8i <= '1';
          END IF;

        -- level sensitive interrupt
        ELSE
          -- forward value of interrupt request;
          -- request is cleared only by reading data register!!!
          IF int_8 = '1' THEN
            int_8i <= '1';
          -- when processor is reading interrupt vector
          ELSIF Address = data_port_ID AND read_strobe = '1' AND data_int = X"08" THEN
            int_8i <= '0';          -- clear interrupt request
          END IF;
        END IF;

      -- when interrupt is disabled
      ELSE
        int_8i <= '0';
      END IF;
      ---------------------------------------------------------------
      ---------------------------------------------------------------
      IF Address = data_port_ID AND read_strobe = '1' THEN
         data_int <= X"00";     -- celar data register
      ELSIF int_1i = '1' AND data_int = X"00" THEN
        data_int <= X"01";
      ELSIF int_2i = '1' AND data_int = X"00" THEN
        data_int <= X"02";
      ELSIF int_3i = '1' AND data_int = X"00" THEN
        data_int <= X"03";
      ELSIF int_4i = '1' AND data_int = X"00" THEN
        data_int <= X"04";
      ELSIF int_5i = '1' AND data_int = X"00" THEN
        data_int <= X"05";
      ELSIF int_6i = '1' AND data_int = X"00" THEN
        data_int <= X"06";
      ELSIF int_7i = '1' AND data_int = X"00" THEN
        data_int <= X"07";
      ELSIF int_8i = '1' AND data_int = X"00" THEN
        data_int <= X"08";
      END IF;
  ---------------------------------------------------------------------
    END IF;
  END PROCESS int_proc;

-----------------------------------------------------------------------

  proc_int_i <= int_1i OR int_2i OR int_3i OR int_4i OR int_5i OR int_6i OR int_7i OR int_8i;

  interrupt_delay: PROCESS(clk) BEGIN
    IF clk'EVENT AND clk = '1' THEN
      IF proc_int_ack = '1' THEN
        int_shreg <= (OTHERS => '0');
      ELSIF proc_int_i = '1' THEN
        int_shreg <= int_shreg(int_shreg'HIGH-1 DOWNTO 0) & '1';
      ELSE
        int_shreg <= (OTHERS => '0');
      END IF;
    END IF;
  END PROCESS interrupt_delay;
  -- connect internal interrupt signal to the blok output
  proc_int <= int_shreg(int_shreg'HIGH);


  -- Data output is active interrupt input number
  Data_out <= data_int;
  int_en_out_1 <= int_en_1;
  int_edge_out_1 <= int_edge_1;

-----------------------------------------------------------------------
-- Data Input
  IN_conrol: PROCESS(clk) BEGIN
    IF clk'EVENT AND clk = '1' THEN
      -----------------------------------------------------------------
      IF Address = int_en1_port_ID AND write_strobe = '1' THEN
        int_en_1 <= Data_in;
      END IF;
      -----------------------------------------------------------------
      IF Address = int_edge1_port_ID AND write_strobe = '1' THEN
        int_edge_1 <= Data_in;
      END IF;
      -----------------------------------------------------------------
    END IF;
  END PROCESS IN_conrol;
--#####################################################################
END Behavioral;
--#####################################################################
