-- -- This is a simple XICS compliant interrupt controller. This is a -- Presenter (ICP) and Source (ICS) in a single unit with no routing -- layer. -- -- The sources have a fixed IRQ priority set by HW_PRIORITY. The -- source id starts at 16 for int_level_in(0) and go up from -- there (ie int_level_in(1) is source id 17). -- -- The presentation layer will pick an interupt that is more -- favourable than the current CPPR and present it via the XISR and -- send an interrpt to the processor (via e_out). This may not be the -- highest priority interrupt currently presented (which is allowed -- via XICS) -- library ieee; use ieee.std_logic_1164.all; use ieee.numeric_std.all; library work; use work.common.all; use work.wishbone_types.all; entity xics is generic ( LEVEL_NUM : positive := 16 ); port ( clk : in std_logic; rst : in std_logic; wb_in : in wb_io_master_out; wb_out : out wb_io_slave_out; int_level_in : in std_ulogic_vector(LEVEL_NUM - 1 downto 0); core_irq_out : out std_ulogic ); end xics; architecture behaviour of xics is type reg_internal_t is record xisr : std_ulogic_vector(23 downto 0); cppr : std_ulogic_vector(7 downto 0); pending_priority : std_ulogic_vector(7 downto 0); mfrr : std_ulogic_vector(7 downto 0); mfrr_pending : std_ulogic; irq : std_ulogic; wb_rd_data : std_ulogic_vector(31 downto 0); wb_ack : std_ulogic; end record; constant reg_internal_init : reg_internal_t := (wb_ack => '0', mfrr_pending => '0', mfrr => x"00", -- mask everything on reset irq => '0', others => (others => '0')); signal r, r_next : reg_internal_t; -- hardwire the hardware IRQ priority constant HW_PRIORITY : std_ulogic_vector(7 downto 0) := x"80"; -- 8 bit offsets for each presentation constant XIRR_POLL : std_ulogic_vector(7 downto 0) := x"00"; constant XIRR : std_ulogic_vector(7 downto 0) := x"04"; constant RESV0 : std_ulogic_vector(7 downto 0) := x"08"; constant MFRR : std_ulogic_vector(7 downto 0) := x"0c"; begin regs : process(clk) begin if rising_edge(clk) then r <= r_next; end if; end process; wb_out.dat <= r.wb_rd_data; wb_out.ack <= r.wb_ack; wb_out.stall <= '0'; -- never stall wishbone core_irq_out <= r.irq; comb : process(all) variable v : reg_internal_t; variable xirr_accept_rd : std_ulogic; variable irq_eoi : std_ulogic; begin v := r; v.wb_ack := '0'; xirr_accept_rd := '0'; irq_eoi := '0'; if wb_in.cyc = '1' and wb_in.stb = '1' then v.wb_ack := '1'; -- always ack if wb_in.we = '1' then -- write -- writes to both XIRR are the same case wb_in.adr(7 downto 0) is when XIRR_POLL => report "XICS XIRR_POLL write"; if wb_in.sel = x"f" then -- 4 bytes v.cppr := wb_in.dat(31 downto 24); elsif wb_in.sel = x"1" then -- 1 byte v.cppr := wb_in.dat(7 downto 0); end if; when XIRR => if wb_in.sel = x"f" then -- 4 byte report "XICS XIRR write word:" & to_hstring(wb_in.dat); v.cppr := wb_in.dat(31 downto 24); irq_eoi := '1'; elsif wb_in.sel = x"1" then -- 1 byte report "XICS XIRR write byte:" & to_hstring(wb_in.dat(7 downto 0)); v.cppr := wb_in.dat(7 downto 0); else report "XICS XIRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel); end if; when MFRR => if wb_in.sel = x"f" then -- 4 bytes report "XICS MFRR write word:" & to_hstring(wb_in.dat); v.mfrr_pending := '1'; v.mfrr := wb_in.dat(31 downto 24); elsif wb_in.sel = x"1" then -- 1 byte report "XICS MFRR write byte:" & to_hstring(wb_in.dat(7 downto 0)); v.mfrr_pending := '1'; v.mfrr := wb_in.dat(7 downto 0); else report "XICS MFRR UNSUPPORTED write ! sel=" & to_hstring(wb_in.sel); end if; when others => end case; else -- read v.wb_rd_data := (others => '0'); case wb_in.adr(7 downto 0) is when XIRR_POLL => report "XICS XIRR_POLL read"; if wb_in.sel = x"f" then v.wb_rd_data(23 downto 0) := r.xisr; v.wb_rd_data(31 downto 24) := r.cppr; elsif wb_in.sel = x"1" then v.wb_rd_data(7 downto 0) := r.cppr; end if; when XIRR => report "XICS XIRR read"; if wb_in.sel = x"f" then v.wb_rd_data(23 downto 0) := r.xisr; v.wb_rd_data(31 downto 24) := r.cppr; xirr_accept_rd := '1'; elsif wb_in.sel = x"1" then v.wb_rd_data(7 downto 0) := r.cppr; end if; when MFRR => report "XICS MFRR read"; if wb_in.sel = x"f" then -- 4 bytes v.wb_rd_data(31 downto 24) := r.mfrr; elsif wb_in.sel = x"1" then -- 1 byte v.wb_rd_data( 7 downto 0) := r.mfrr; end if; when others => end case; end if; end if; -- generate interrupt if r.irq = '0' then -- Here we just present any interrupt that's valid and -- below cppr. For ordering, we ignore hardware -- priorities. if unsigned(HW_PRIORITY) < unsigned(r.cppr) then -- -- lower HW sources are higher priority for i in LEVEL_NUM - 1 downto 0 loop if int_level_in(i) = '1' then v.irq := '1'; v.xisr := std_ulogic_vector(to_unsigned(16 + i, 24)); v.pending_priority := HW_PRIORITY; -- hardware HW IRQs end if; end loop; end if; -- Do mfrr as a higher priority so mfrr_pending is cleared if unsigned(r.mfrr) < unsigned(r.cppr) then -- report "XICS: MFRR INTERRUPT"; -- IPI if r.mfrr_pending = '1' then v.irq := '1'; v.xisr := x"000002"; -- special XICS MFRR IRQ source number v.pending_priority := r.mfrr; v.mfrr_pending := '0'; end if; end if; end if; -- Accept the interrupt if xirr_accept_rd = '1' then report "XICS: ACCEPT" & " cppr:" & to_hstring(r.cppr) & " xisr:" & to_hstring(r.xisr) & " mfrr:" & to_hstring(r.mfrr); v.cppr := r.pending_priority; end if; if irq_eoi = '1' then v.irq := '0'; end if; if rst = '1' then v := reg_internal_init; end if; r_next <= v; end process; end architecture behaviour;