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 <mikey@neuling.org>
pull/165/head
Michael Neuling 5 years ago
parent e5a30a1358
commit b4f20c20b9

@ -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 rotator_tb.o: common.o glibc_random.o ppc_fx_insns.o insn_helpers.o rotator.o
sim_console.o: sim_console.o:
sim_uart.o: wishbone_types.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_arbiter.o: wishbone_types.o
wishbone_types.o: wishbone_types.o:
writeback.o: common.o crhelpers.o writeback.o: common.o crhelpers.o

@ -305,6 +305,11 @@ package common is
constant WritebackToCrFileInit : WritebackToCrFileType := (write_cr_enable => '0', write_xerc_enable => '0', constant WritebackToCrFileInit : WritebackToCrFileType := (write_cr_enable => '0', write_xerc_enable => '0',
write_xerc_data => xerc_init, write_xerc_data => xerc_init,
others => (others => '0')); others => (others => '0'));

type XicsToExecute1Type is record
irq : std_ulogic;
end record;

end common; end common;


package body common is package body common is

@ -29,6 +29,8 @@ entity core is
dmi_wr : in std_ulogic; dmi_wr : in std_ulogic;
dmi_ack : out std_ulogic; dmi_ack : out std_ulogic;


xics_in : in XicsToExecute1Type;

terminated_out : out std_logic terminated_out : out std_logic
); );
end core; end core;
@ -237,6 +239,7 @@ begin
flush_out => flush, flush_out => flush,
stall_out => ex1_stall_out, stall_out => ex1_stall_out,
e_in => decode2_to_execute1, e_in => decode2_to_execute1,
i_in => xics_in,
l_out => execute1_to_loadstore1, l_out => execute1_to_loadstore1,
f_out => execute1_to_fetch1, f_out => execute1_to_fetch1,
e_out => execute1_to_writeback, e_out => execute1_to_writeback,

@ -24,6 +24,8 @@ entity execute1 is


e_in : in Decode2ToExecute1Type; e_in : in Decode2ToExecute1Type;


i_in : in XicsToExecute1Type;

-- asynchronous -- asynchronous
l_out : out Execute1ToLoadstore1Type; l_out : out Execute1ToLoadstore1Type;
f_out : out Execute1ToFetch1Type; f_out : out Execute1ToFetch1Type;
@ -370,10 +372,17 @@ begin
ctrl_tmp.dec <= std_ulogic_vector(unsigned(ctrl.dec) - 1); ctrl_tmp.dec <= std_ulogic_vector(unsigned(ctrl.dec) - 1);


irq_valid := '0'; irq_valid := '0';
if ctrl.msr(63 - 48) = '1' and ctrl.dec(63) = '1' then if ctrl.msr(63 - 48) = '1' then
report "IRQ valid"; 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'; irq_valid := '1';
end if; end if;
end if;


terminate_out <= '0'; terminate_out <= '0';
icache_inval <= '0'; icache_inval <= '0';
@ -412,11 +421,12 @@ begin
-- Don't deliver the interrupt until we have a valid instruction -- Don't deliver the interrupt until we have a valid instruction
-- coming in, so we have a valid NIA to put in SRR0. -- coming in, so we have a valid NIA to put in SRR0.
exception := e_in.valid; exception := e_in.valid;
ctrl_tmp.irq_nia <= std_logic_vector(to_unsigned(16#900#, 64));
ctrl_tmp.srr1 <= msr_copy(ctrl.msr); ctrl_tmp.srr1 <= msr_copy(ctrl.msr);


elsif e_in.valid = '1' then elsif e_in.valid = '1' then


report "execute nia " & to_hstring(e_in.nia);

v.e.valid := '1'; v.e.valid := '1';
v.e.write_reg := e_in.write_reg; v.e.write_reg := e_in.write_reg;
v.slow_op_dest := gspr_to_gpr(e_in.write_reg); v.slow_op_dest := gspr_to_gpr(e_in.write_reg);

@ -12,6 +12,7 @@ use work.wishbone_types.all;


-- 0x00000000: Main memory (1 MB) -- 0x00000000: Main memory (1 MB)
-- 0xc0002000: UART0 (for host communication) -- 0xc0002000: UART0 (for host communication)
-- 0xc0004000: XICS ICP
entity soc is entity soc is
generic ( generic (
MEMORY_SIZE : positive; MEMORY_SIZE : positive;
@ -55,6 +56,13 @@ architecture behaviour of soc is
signal wb_uart0_out : wishbone_slave_out; signal wb_uart0_out : wishbone_slave_out;
signal uart_dat8 : std_ulogic_vector(7 downto 0); 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: -- Main memory signals:
signal wb_bram_in : wishbone_master_out; signal wb_bram_in : wishbone_master_out;
signal wb_bram_out : wishbone_slave_out; signal wb_bram_out : wishbone_slave_out;
@ -95,7 +103,8 @@ begin
dmi_din => dmi_dout, dmi_din => dmi_dout,
dmi_wr => dmi_wr, dmi_wr => dmi_wr,
dmi_ack => dmi_core_ack, 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 -- Wishbone bus master arbiter & mux
@ -122,6 +131,7 @@ begin
-- Selected slave -- Selected slave
type slave_type is (SLAVE_UART_0, type slave_type is (SLAVE_UART_0,
SLAVE_MEMORY, SLAVE_MEMORY,
SLAVE_ICP_0,
SLAVE_NONE); SLAVE_NONE);
variable slave : slave_type; variable slave : slave_type;
begin begin
@ -133,6 +143,9 @@ begin
if wb_master_out.adr(23 downto 12) = x"002" then if wb_master_out.adr(23 downto 12) = x"002" then
slave := SLAVE_UART_0; slave := SLAVE_UART_0;
end if; end if;
if wb_master_out.adr(23 downto 12) = x"004" then
slave := SLAVE_ICP_0;
end if;
end if; end if;


-- Wishbone muxing. Defaults: -- Wishbone muxing. Defaults:
@ -140,6 +153,12 @@ begin
wb_bram_in.cyc <= '0'; wb_bram_in.cyc <= '0';
wb_uart0_in <= wb_master_out; wb_uart0_in <= wb_master_out;
wb_uart0_in.cyc <= '0'; 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 case slave is
when SLAVE_MEMORY => when SLAVE_MEMORY =>
wb_bram_in.cyc <= wb_master_out.cyc; wb_bram_in.cyc <= wb_master_out.cyc;
@ -147,6 +166,9 @@ begin
when SLAVE_UART_0 => when SLAVE_UART_0 =>
wb_uart0_in.cyc <= wb_master_out.cyc; wb_uart0_in.cyc <= wb_master_out.cyc;
wb_master_in <= wb_uart0_out; 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 => when others =>
wb_master_in.dat <= (others => '1'); wb_master_in.dat <= (others => '1');
wb_master_in.ack <= wb_master_out.stb and wb_master_out.cyc; wb_master_in.ack <= wb_master_out.stb and wb_master_out.cyc;
@ -170,6 +192,7 @@ begin
reset => rst, reset => rst,
txd => uart0_txd, txd => uart0_txd,
rxd => uart0_rxd, rxd => uart0_rxd,
irq => int_level_in(0),
wb_adr_in => wb_uart0_in.adr(11 downto 0), wb_adr_in => wb_uart0_in.adr(11 downto 0),
wb_dat_in => wb_uart0_in.dat(7 downto 0), wb_dat_in => wb_uart0_in.dat(7 downto 0),
wb_dat_out => uart_dat8, wb_dat_out => uart_dat8,
@ -181,6 +204,19 @@ begin
wb_uart0_out.dat <= x"00000000000000" & uart_dat8; 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; 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 -- BRAM Memory slave
bram0: entity work.wishbone_bram_wrapper bram0: entity work.wishbone_bram_wrapper
generic map( generic map(

@ -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;
Loading…
Cancel
Save