OpenCores
URL https://opencores.org/ocsvn/open8_urisc/open8_urisc/trunk

Subversion Repositories open8_urisc

[/] [open8_urisc/] [trunk/] [VHDL/] [o8_int_mgr16.vhd] - Rev 309

Go to most recent revision | Compare with Previous | Blame | View Log

-- Copyright (c)2020 Jeremy Seth Henry
-- All rights reserved.
--
-- Redistribution and use in source and binary forms, with or without
-- modification, are permitted provided that the following conditions are met:
--     * Redistributions of source code must retain the above copyright
--       notice, this list of conditions and the following disclaimer.
--     * Redistributions in binary form must reproduce the above copyright
--       notice, this list of conditions and the following disclaimer in the
--       documentation and/or other materials provided with the distribution,
--       where applicable (as part of a user interface, debugging port, etc.)
--
-- THIS SOFTWARE IS PROVIDED BY JEREMY SETH HENRY ``AS IS'' AND ANY
-- EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-- WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
-- DISCLAIMED. IN NO EVENT SHALL JEREMY SETH HENRY BE LIABLE FOR ANY
-- DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-- (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
-- LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
-- ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-- (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-- THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
--
-- VHDL Units :  o8_int_mgr16
-- Description:  Provides an 8-bit microsecond resolution timer for generating
--            :   periodic interrupts for the Open8 CPU as well as providing a
--            :   second level interrupt manager for I/O interrupts
--
-- Register Map:
-- Offset  Bitfield Description                        Read/Write
--   0x00  AAAAAAAA PIT Timer Interval (0 = disabled)    (RW)
--   0x01  -------- (unused)
--   0x02  AAAAAAAA External Interrupt Mask (lower)      (RW)
--   0x03  AAAAAAAA External Interrupt Mask (upper)      (RW)
--   0x04  AAAAAAAA Pending External Ints* (lower)       (RW)
--   0x05  AAAAAAAA Pending External Ints* (upper)       (RW)
--   0x06  -------- (unused)
--   0x07  A------- Interrupt Requested (write to clear) (RW)
--
-- Note: Each bit in the pending register is individually clearable by writing
--        a '1' to it, allowing interrupts to be cleared individually
--
-- Revision History
-- Author          Date     Change
------------------ -------- ---------------------------------------------------
-- Seth Henry      05/21/20 Design Start
 
library ieee;
use ieee.std_logic_1164.all;
  use ieee.std_logic_unsigned.all;
  use ieee.std_logic_arith.all;
  use ieee.std_logic_misc.all;
 
library work;
  use work.open8_pkg.all;
 
entity o8_int_mgr16 is
generic(
  Default_Int_Mask           : ADDRESS_TYPE := x"0000";
  Address                    : ADDRESS_TYPE
);
port(
  Open8_Bus                  : in  OPEN8_BUS_TYPE;
  Interrupts                 : in  ADDRESS_TYPE := x"0000";
  Rd_Data                    : out DATA_TYPE;
  PIT_Interrupt              : out std_logic;
  EXT_Interrupt              : out std_logic
);
end entity;
 
architecture behave of o8_int_mgr16 is
 
  alias  Clock               is Open8_Bus.Clock;
  alias  Reset               is Open8_Bus.Reset;
  alias  uSec_Tick           is Open8_Bus.uSec_Tick;
  alias  CPU_ISR_En          is Open8_Bus.GP_Flags(EXT_ISR);
  alias  CPU_Wr_En           is Open8_Bus.Wr_En;
  alias  CPU_Rd_En           is Open8_Bus.Rd_En;
 
  constant User_Addr         : std_logic_vector(15 downto 3) :=
                                Address(15 downto 3);
  alias  Comp_Addr           is Open8_Bus.Address(15 downto 3);
  signal Addr_Match          : std_logic := '0';
 
  alias  Reg_Sel_d           is Open8_Bus.Address(2 downto 0);
  signal Reg_Sel_q           : std_logic_vector(2 downto 0);
  signal Wr_En_d             : std_logic;
  signal Wr_En_q             : std_logic := '0';
  alias  Wr_Data_d           is Open8_Bus.Wr_Data;
  signal Wr_Data_q           : DATA_TYPE := x"00";
  signal Rd_En_d               : std_logic := '0';
  signal Rd_En_q             : std_logic := '0';
 
  signal Interval            : DATA_TYPE := x"00";
  signal Update_Interval     : std_logic;
  signal Timer_Cnt           : DATA_TYPE := x"00";
 
  signal Int_Mask            : ADDRESS_TYPE := x"0000";
  alias  Int_Mask_l          is Int_Mask(7 downto 0);
  alias  Int_Mask_h          is Int_Mask(15 downto 8);
 
  signal Clear_Pending       : ADDRESS_TYPE := x"0000";
  alias  Clear_Pending_l     is Clear_Pending(7 downto 0);
  alias  Clear_Pending_h     is Clear_Pending(15 downto 8);
 
  signal Ack_IO_Ints         : std_logic;
 
  signal Pending             : ADDRESS_TYPE := x"0000";
  alias  Pending_l           is Pending(7 downto 0);
  alias  Pending_h           is Pending(15 downto 8);
  signal Pending_q           : ADDRESS_TYPE := x"0000";
  signal Pending_RE          : ADDRESS_TYPE := x"0000";
 
  signal IO_Int_Pending      : std_logic;
 
begin
 
  Addr_Match                 <= '1' when Comp_Addr = User_Addr else '0';
  Wr_En_d                    <= Addr_Match and CPU_Wr_En and CPU_ISR_En;
  Rd_En_d                    <= Addr_Match and CPU_Rd_En;
 
  io_reg: process( Clock, Reset )
  begin
    if( Reset = Reset_Level )then
      Reg_Sel_q              <= (others => '0');
      Wr_En_q                <= '0';
      Wr_Data_q              <= x"00";
      Rd_En_q                <= '0';
      Rd_Data                <= OPEN8_NULLBUS;
      Interval               <= x"00";
      Update_Interval        <= '0';
      Int_Mask               <= Default_Int_Mask;
      Clear_Pending          <= x"0000";
      Ack_IO_Ints            <= '0';
    elsif( rising_edge( Clock ) )then
      Reg_Sel_q              <= Reg_Sel_d;
      Wr_En_q                <= Wr_En_d;
      Wr_Data_q              <= Wr_Data_d;
 
      Update_Interval        <= Wr_En_q;
      Clear_Pending          <= x"0000";
      Ack_IO_Ints            <= '0';
      if( Wr_En_q = '1' )then
        case( Reg_Sel_q )is
          when "000" =>
            Interval         <= Wr_Data_q;
          when "010" =>
            Int_Mask_l       <= Wr_Data_q;
          when "011" =>
            Int_Mask_h       <= Wr_Data_q;
 
          when "100" =>
            Clear_Pending_l  <= Wr_Data_q;
          when "101" =>
            Clear_Pending_h  <= Wr_Data_q;
 
          when "111" =>
            Ack_IO_Ints      <= '1';
          when others =>
            null;
        end case;
      end if;
 
      Rd_Data                <= (others => '0');
      Rd_En_q                <= Rd_En_d;
      if( Rd_En_q = '1' )then
        case( Reg_Sel_q )is
          when "000" =>
            Rd_Data          <= Interval;
          when "010" =>
            Rd_Data          <= Int_Mask_l;
          when "011" =>
            Rd_Data          <= Int_Mask_h;
          when "100" =>
            Rd_Data          <= Pending_l;
          when "101" =>
            Rd_Data          <= Pending_h;
          when "111" =>
            Rd_Data          <= IO_Int_Pending & "0000000";
          when others =>
            null;
        end case;
      end if;
    end if;
  end process;
 
  Interval_proc: process( Clock, Reset )
  begin
    if( Reset = Reset_Level )then
      Timer_Cnt              <= x"00";
      PIT_Interrupt          <= '0';
    elsif( rising_edge(Clock) )then
      PIT_Interrupt              <= '0';
      Timer_Cnt              <= Timer_Cnt - uSec_Tick;
      if( Update_Interval = '1' )then
        Timer_Cnt            <= Interval;
      elsif( or_reduce(Timer_Cnt) = '0' )then
        Timer_Cnt            <= Interval;
        PIT_Interrupt        <= or_reduce(Interval); -- Only trigger on Int > 0
      end if;
    end if;
  end process;
 
  Interrupt_proc: process( Clock, Reset )
    variable i               : integer := 0;
  begin
    if( Reset = Reset_Level )then
      Pending                <= x"0000";
      Pending_q              <= x"0000";
      Pending_RE             <= x"0000";
      IO_Int_Pending         <= '0';
      EXT_Interrupt          <= '0';
    elsif( rising_edge(Clock) )then
      for i in 0 to 15 loop
        if( Interrupts(i) = '1' and Int_Mask(i) = '1' )then
          Pending(i)         <= '1';
        elsif( Clear_Pending(i) = '1' )then
          Pending(i)         <= '0';
        end if;
        Pending_q(i)         <= Pending(i);
        Pending_RE(i)        <= Pending(i) and not Pending_q(i);
      end loop;
 
      EXT_Interrupt          <= '0';
      if( or_reduce(Pending_RE) = '1' and IO_Int_Pending = '0' )then
        IO_Int_Pending       <= '1';
        EXT_Interrupt        <= '1';
      elsif( Ack_IO_Ints = '1' )then
        IO_Int_Pending       <= '0';
      end if;
 
    end if;
  end process;
 
end architecture;
 

Go to most recent revision | Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.