From b4f20c20b9a5371ca5f7c8973f756c8908cd65a6 Mon Sep 17 00:00:00 2001 From: Michael Neuling Date: Thu, 23 Apr 2020 14:36:05 +1000 Subject: [PATCH] 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 --- Makefile | 3 +- common.vhdl | 5 ++ core.vhdl | 3 + execute1.vhdl | 18 ++++- soc.vhdl | 38 ++++++++- xics.vhdl | 207 ++++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 268 insertions(+), 6 deletions(-) create mode 100644 xics.vhdl 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; -- 2.30.2