From: Michael Neuling Date: Thu, 23 Apr 2020 04:36:05 +0000 (+1000) Subject: XICS interrupt controller X-Git-Url: https://git.libre-soc.org/?a=commitdiff_plain;h=b4f20c20b9a5371ca5f7c8973f756c8908cd65a6;p=microwatt.git XICS interrupt controller 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 --- diff --git a/Makefile b/Makefile index 2f90f22..3dc58ec 100644 --- 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 diff --git a/common.vhdl b/common.vhdl index 9f6e96d..d10d857 100644 --- a/common.vhdl +++ b/common.vhdl @@ -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 diff --git a/core.vhdl b/core.vhdl index d535f7a..0e60905 100644 --- 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, diff --git a/execute1.vhdl b/execute1.vhdl index 2c0a558..e32285d 100644 --- a/execute1.vhdl +++ b/execute1.vhdl @@ -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); diff --git a/soc.vhdl b/soc.vhdl index 9b45b5d..604c6d5 100644 --- 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 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;