XICS interrupt controller
authorMichael Neuling <mikey@neuling.org>
Thu, 23 Apr 2020 04:36:05 +0000 (14:36 +1000)
committerMichael Neuling <mikey@neuling.org>
Thu, 23 Apr 2020 06:36:49 +0000 (16:36 +1000)
New unified ICP and ICS XICS compliant interrupt controller.
Configurable number of hardware sources.

Fixed hardware source number based on hardware line taken. All
hardware interrupts are a fixed priority. Level interrupts supported
only.

Hardwired to 0xc0004000 in SOC (UART is kept at 0xc0002000).

Signed-off-by: Michael Neuling <mikey@neuling.org>
Makefile
common.vhdl
core.vhdl
execute1.vhdl
soc.vhdl
xics.vhdl [new file with mode: 0644]

index 2f90f222a5303bb62068514b7ab0129d83aa4907..3dc58ecc3a3ebcf6bc6a8dea07556373f2fdf8d6 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -70,7 +70,8 @@ rotator.o: common.o
 rotator_tb.o: common.o glibc_random.o ppc_fx_insns.o insn_helpers.o rotator.o
 sim_console.o:
 sim_uart.o: wishbone_types.o sim_console.o
-soc.o: common.o wishbone_types.o core.o wishbone_arbiter.o sim_uart.o wishbone_bram_wrapper.o dmi_dtm_xilinx.o wishbone_debug_master.o
+xics.o: wishbone_types.o common.o
+soc.o: common.o wishbone_types.o core.o wishbone_arbiter.o sim_uart.o wishbone_bram_wrapper.o dmi_dtm_xilinx.o wishbone_debug_master.o xics.o
 wishbone_arbiter.o: wishbone_types.o
 wishbone_types.o:
 writeback.o: common.o crhelpers.o
index 9f6e96d03b7b65f0a3819d70b70e36549ba72e3c..d10d857d48a4da8c186dc696bb3daedb29ce5c48 100644 (file)
@@ -305,6 +305,11 @@ package common is
     constant WritebackToCrFileInit : WritebackToCrFileType := (write_cr_enable => '0', write_xerc_enable => '0',
                                                               write_xerc_data => xerc_init,
                                                               others => (others => '0'));
+
+    type XicsToExecute1Type is record
+       irq : std_ulogic;
+    end record;
+
 end common;
 
 package body common is
index d535f7ae67efb6ccb580f7d088378e622bce3de2..0e6090546a9332dbb9857f690be3ee19068b15a2 100644 (file)
--- a/core.vhdl
+++ b/core.vhdl
@@ -29,6 +29,8 @@ entity core is
        dmi_wr          : in std_ulogic;
        dmi_ack         : out std_ulogic;
 
+       xics_in         : in XicsToExecute1Type;
+
        terminated_out   : out std_logic
         );
 end core;
@@ -237,6 +239,7 @@ begin
             flush_out => flush,
            stall_out => ex1_stall_out,
             e_in => decode2_to_execute1,
+            i_in => xics_in,
             l_out => execute1_to_loadstore1,
             f_out => execute1_to_fetch1,
             e_out => execute1_to_writeback,
index 2c0a558950e2e66d0b4e83013ed9366fe7cc682e..e32285d0ecdf29cc56e450ab000bdaf32fb24fea 100644 (file)
@@ -24,6 +24,8 @@ entity execute1 is
 
        e_in  : in Decode2ToExecute1Type;
 
+       i_in : in XicsToExecute1Type;
+
        -- asynchronous
         l_out : out Execute1ToLoadstore1Type;
        f_out : out Execute1ToFetch1Type;
@@ -370,9 +372,16 @@ begin
        ctrl_tmp.dec <= std_ulogic_vector(unsigned(ctrl.dec) - 1);
 
        irq_valid := '0';
-       if ctrl.msr(63 - 48) = '1' and ctrl.dec(63) = '1' then
-           report "IRQ valid";
-           irq_valid := '1';
+       if ctrl.msr(63 - 48) = '1' then
+           if ctrl.dec(63) = '1' then
+               ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#900#, 64));
+               report "IRQ valid: DEC";
+               irq_valid := '1';
+           elsif i_in.irq = '1' then
+               ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#500#, 64));
+               report "IRQ valid: External";
+               irq_valid := '1';
+           end if;
        end if;
 
        terminate_out <= '0';
@@ -412,11 +421,12 @@ begin
             -- Don't deliver the interrupt until we have a valid instruction
             -- coming in, so we have a valid NIA to put in SRR0.
            exception := e_in.valid;
-           ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#900#, 64));
            ctrl_tmp.srr1 <= msr_copy(ctrl.msr);
 
        elsif e_in.valid = '1' then
 
+           report "execute nia " & to_hstring(e_in.nia);
+
            v.e.valid := '1';
            v.e.write_reg := e_in.write_reg;
            v.slow_op_dest := gspr_to_gpr(e_in.write_reg);
index 9b45b5df38a8311b803de681cb54d23798e847e9..604c6d5a189d2011ffb643bc72c0f6f2628c0f54 100644 (file)
--- a/soc.vhdl
+++ b/soc.vhdl
@@ -12,6 +12,7 @@ use work.wishbone_types.all;
 
 -- 0x00000000: Main memory (1 MB)
 -- 0xc0002000: UART0 (for host communication)
+-- 0xc0004000: XICS ICP
 entity soc is
     generic (
        MEMORY_SIZE   : positive;
@@ -55,6 +56,13 @@ architecture behaviour of soc is
     signal wb_uart0_out  : wishbone_slave_out;
     signal uart_dat8     : std_ulogic_vector(7 downto 0);
 
+    -- XICS0 signals:
+    signal wb_xics0_in   : wishbone_master_out;
+    signal wb_xics0_out  : wishbone_slave_out;
+    signal int_level_in  : std_ulogic_vector(15 downto 0);
+
+    signal xics_to_execute1 : XicsToExecute1Type;
+
     -- Main memory signals:
     signal wb_bram_in     : wishbone_master_out;
     signal wb_bram_out    : wishbone_slave_out;
@@ -95,7 +103,8 @@ begin
            dmi_din => dmi_dout,
            dmi_wr => dmi_wr,
            dmi_ack => dmi_core_ack,
-           dmi_req => dmi_core_req
+           dmi_req => dmi_core_req,
+           xics_in => xics_to_execute1
            );
 
     -- Wishbone bus master arbiter & mux
@@ -122,6 +131,7 @@ begin
        -- Selected slave
        type slave_type is (SLAVE_UART_0,
                            SLAVE_MEMORY,
+                           SLAVE_ICP_0,
                            SLAVE_NONE);
        variable slave : slave_type;
     begin
@@ -133,6 +143,9 @@ begin
            if wb_master_out.adr(23 downto 12) = x"002" then
                slave := SLAVE_UART_0;
            end if;
+           if wb_master_out.adr(23 downto 12) = x"004" then
+               slave := SLAVE_ICP_0;
+           end if;
        end if;
 
        -- Wishbone muxing. Defaults:
@@ -140,6 +153,12 @@ begin
        wb_bram_in.cyc  <= '0';
        wb_uart0_in <= wb_master_out;
        wb_uart0_in.cyc <= '0';
+
+        -- Only give xics 8 bits of wb addr
+       wb_xics0_in <= wb_master_out;
+       wb_xics0_in.adr <= (others => '0');
+       wb_xics0_in.adr(7 downto 0) <= wb_master_out.adr(7 downto 0);
+       wb_xics0_in.cyc  <= '0';
        case slave is
        when SLAVE_MEMORY =>
            wb_bram_in.cyc <= wb_master_out.cyc;
@@ -147,6 +166,9 @@ begin
        when SLAVE_UART_0 =>
            wb_uart0_in.cyc <= wb_master_out.cyc;
            wb_master_in <= wb_uart0_out;
+       when SLAVE_ICP_0 =>
+           wb_xics0_in.cyc <= wb_master_out.cyc;
+           wb_master_in <= wb_xics0_out;
        when others =>
            wb_master_in.dat <= (others => '1');
            wb_master_in.ack <= wb_master_out.stb and wb_master_out.cyc;
@@ -170,6 +192,7 @@ begin
            reset => rst,
            txd => uart0_txd,
            rxd => uart0_rxd,
+           irq => int_level_in(0),
            wb_adr_in => wb_uart0_in.adr(11 downto 0),
            wb_dat_in => wb_uart0_in.dat(7 downto 0),
            wb_dat_out => uart_dat8,
@@ -181,6 +204,19 @@ begin
     wb_uart0_out.dat <= x"00000000000000" & uart_dat8;
     wb_uart0_out.stall <= '0' when wb_uart0_in.cyc = '0' else not wb_uart0_out.ack;
 
+    xics0: entity work.xics
+       generic map(
+           LEVEL_NUM => 16
+           )
+       port map(
+           clk => system_clk,
+           rst => rst,
+           wb_in => wb_xics0_in,
+           wb_out => wb_xics0_out,
+           int_level_in => int_level_in,
+           e_out => xics_to_execute1
+           );
+
     -- BRAM Memory slave
     bram0: entity work.wishbone_bram_wrapper
        generic map(
diff --git a/xics.vhdl b/xics.vhdl
new file mode 100644 (file)
index 0000000..09a1ba6
--- /dev/null
+++ b/xics.vhdl
@@ -0,0 +1,207 @@
+--
+-- 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 wishbone_master_out;
+        wb_out  : out wishbone_slave_out;
+
+       int_level_in : in std_ulogic_vector(LEVEL_NUM - 1 downto 0);
+
+       e_out : out XicsToExecute1Type
+        );
+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 : wishbone_data_type;
+       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";
+
+    -- 32 bit offsets for each presentation
+    constant XIRR_POLL : std_ulogic_vector(31 downto 0) := x"00000000";
+    constant XIRR      : std_ulogic_vector(31 downto 0) := x"00000004";
+    constant RESV0     : std_ulogic_vector(31 downto 0) := x"00000008";
+    constant MFRR      : std_ulogic_vector(31 downto 0) := x"0000000c";
+
+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
+    e_out.irq <= 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
+           -- wishbone addresses we get are 64 bit alligned, so we
+           -- need to use the sel bits to get 32 bit chunks.
+           v.wb_ack := '1'; -- always ack
+           if wb_in.we = '1' then -- write
+               -- writes to both XIRR are the same
+               if wb_in.adr = XIRR_POLL then
+                   report "XICS XIRR_POLL/XIRR write";
+                   if wb_in.sel = x"0f" then -- 4 bytes
+                       v.cppr := wb_in.dat(31 downto 24);
+                   elsif wb_in.sel = x"f0"  then -- 4 byte
+                       v.cppr := wb_in.dat(63 downto 56);
+                       irq_eoi := '1';
+                   elsif wb_in.sel = x"01"  then -- 1 byte
+                       v.cppr := wb_in.dat(7 downto 0);
+                   elsif wb_in.sel = x"10"  then -- 1 byte
+                       v.cppr := wb_in.dat(39 downto 32);
+                   end if;
+
+               elsif wb_in.adr = RESV0 then
+                   report "XICS MFRR write";
+                   if wb_in.sel = x"f0" then -- 4 bytes
+                       v.mfrr_pending := '1';
+                       v.mfrr := wb_in.dat(63 downto 56);
+                   elsif wb_in.sel = x"10" then -- 1 byte
+                       v.mfrr_pending := '1';
+                       v.mfrr := wb_in.dat(39 downto 32);
+                   end if;
+
+               end if;
+
+           else -- read
+               v.wb_rd_data := (others => '0');
+
+               if wb_in.adr = XIRR_POLL then
+                   report "XICS XIRR_POLL/XIRR read";
+                   if wb_in.sel = x"0f" then
+                       v.wb_rd_data(23 downto  0) := r.xisr;
+                       v.wb_rd_data(31 downto 24) := r.cppr;
+                   elsif wb_in.sel = x"f0" then
+                       v.wb_rd_data(55 downto 32) := r.xisr;
+                       v.wb_rd_data(63 downto 56) := r.cppr;
+                       xirr_accept_rd := '1';
+                   elsif wb_in.sel = x"01" then
+                       v.wb_rd_data(7 downto  0) := r.cppr;
+                   elsif wb_in.sel = x"10" then
+                       v.wb_rd_data(39 downto 32) := r.cppr;
+                   end if;
+
+               elsif wb_in.adr = RESV0 then
+                   report "XICS MFRR read";
+                   if wb_in.sel = x"f0" then -- 4 bytes
+                       v.wb_rd_data(63 downto 56) := r.mfrr;
+                   elsif wb_in.sel = x"10" then -- 1 byte
+                       v.wb_rd_data( 7 downto  0) := r.mfrr;
+                   end if;
+               end if;
+           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;